Dans cet article, nous allons implémenter une fonctionnalité permettant d'arrêter des conteneurs en arrière-plan, similaire à la commande docker stop. Le processus consiste à localiser le PID principal du conteneur, lui envoyer un signal de terminaison, puis mettre à jour son état.
Environnement de développement
Voici l'environnement utilisé pour cette implémentation :
root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal
root@mydocker:~# uname -r
5.4.0-74-generic
Note : l'utilisation d'un utilisateur root est requise.
Implémentation de la commande stop
Nous commençons par définir la commande CLI pour arrêter un conteneur. Cette commande prend en paramètre l'identifiant du conteneur et appelle une fonction d'arrêt.
var commandeArret = cli.Command{
Nom: "stop",
Usage: "arrêter un conteneur, par exemple : mydocker stop 1234567890",
Action: func(contexte *cli.Context) error {
if len(contexte.Args()) < 1 {
return fmt.Errorf("identifiant du conteneur manquant")
}
nomConteneur := contexte.Args().Get(0)
arreterConteneur(nomConteneur)
return nil
},
}
Cette commande est ansuite ajoutée à la liste des commandes de l'application principale.
func main(){
// Autres configurations omises
app.Commands = []cli.Command{
initCommand,
runCommand,
commitCommand,
listCommand,
logCommand,
execCommand,
commandeArret,
}
}
Fonction d'arrêt du conteneur
La fonction arreterConteneur effectue les étapes clés : récupérer les informations du conteneur, envoyer un signal SIGTERM au processus, puis mettre à jour le fichier de configuration.
func arreterConteneur(identifiantConteneur string) {
// Récupérer les informations du conteneur
infoConteneur, err := obtenirInfoParIdentifiant(identifiantConteneur)
if err != nil {
log.Errorf("Erreur lors de la récupération des informations du conteneur %s : %v", identifiantConteneur, err)
return
}
pidNum, err := strconv.Atoi(infoConteneur.Pid)
if err != nil {
log.Errorf("Erreur de conversion du PID de chaîne en entier : %v", err)
return
}
// Envoyer le signal SIGTERM au processus
if err = syscall.Kill(pidNum, syscall.SIGTERM); err != nil {
log.Errorf("Erreur lors de l'arrêt du conteneur %s : %v", identifiantConteneur, err)
return
}
// Mettre à jour le statut du conteneur et vider le PID
infoConteneur.Statut = container.STOP
infoConteneur.Pid = " "
nouveauxOctets, err := json.Marshal(infoConteneur)
if err != nil {
log.Errorf("Erreur de sérialisation JSON pour %s : %v", identifiantConteneur, err)
return
}
// Réécrire le fichier d'information du conteneur
cheminRep := fmt.Sprintf(container.InfoLocFormat, identifiantConteneur)
cheminConfig := path.Join(cheminRep, container.ConfigName)
if err := os.WriteFile(cheminConfig, nouveauxOctets, constant.Perm0622); err != nil {
log.Errorf("Erreur d'écriture du fichier %s : %v", cheminConfig, err)
}
}
Fonction de récupération des informations
La fonction obtenirInfoParIdentifiant lit le fichier de configuration du conteneur pour extraire son PID et d'autres détails.
func obtenirInfoParIdentifiant(identifiantConteneur string) (*container.Info, error) {
cheminRep := fmt.Sprintf(container.InfoLocFormat, identifiantConteneur)
cheminConfig := path.Join(cheminRep, container.ConfigName)
octetsContenu, err := os.ReadFile(cheminConfig)
if err != nil {
return nil, errors.Wrapf(err, "lecture du fichier %s", cheminConfig)
}
var infoConteneur container.Info
if err = json.Unmarshal(octetsContenu, &infoConteneur); err != nil {
return nil, err
}
return &infoConteneur, nil
}
Tests de la fonctionnalité
Pour tester, nous créons d'abord un conteneur en arrière-plan, puis l'arrêtons et vérifions son état.
# Créer un conteneur détaché
./mydocker run -d -name oiseau top
# Vérifier le PID et l'état du conteneur
./mydocker ps
ps -ef|grep top
# Arrêter le conteneur par son identifiant
./mydocker stop ${identifiantConteneur}
# Confirmer l'arrêt
./mydocker ps
ps -ef|grep top
Après exécution, l'état du conteneur passe à stopped, et le processus correspondant est terminé, confirmant le bon fonctionnement de la commande d'arrêt.