web-dev-qa-db-fra.com

Reconstruire le conteneur Docker lors de modifications de fichiers

Pour exécuter une application ASP.NET Core, j'ai généré un fichier docker qui construit l'application et copie le code source dans le conteneur, qui est récupéré par Git à l'aide de Jenkins. Donc, dans mon espace de travail, je fais ce qui suit dans le fichier de docker:

WORKDIR /app
COPY src src

Alors que Jenkins met correctement à jour les fichiers de mon hôte avec Git, Docker ne l’applique pas à mon image.

Mon script de base pour la construction:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)

if [ "$containerRunning" == "true" ]; then
        docker stop $containerName
        docker start $containerName
else
        docker run -d -p 5000:5000 --name $containerName $imageName
fi

J'ai essayé différentes choses comme --rm et --no-cache paramètre pour docker run et également arrêter/supprimer le conteneur avant le nouveau est construit. Je ne suis pas sûr de ce que je fais mal ici. Il semble que le menu fixe met à jour l'image correctement, car l'appel de COPY src src entraînerait un identifiant de couche et aucun appel de cache:

Step 6 : COPY src src
 ---> 382ef210d8fd

Quelle est la méthode recommandée pour mettre à jour un conteneur?

Mon scénario typique serait: L'application est en cours d'exécution sur le serveur dans un conteneur Docker. Maintenant, certaines parties de l'application sont mises à jour, par exemple. en modifiant un fichier. Le conteneur doit maintenant exécuter la nouvelle version. Docker semble recommander la création d'une nouvelle image plutôt que la modification d'un conteneur existant. Je pense donc que la méthode générale de reconstruction, comme je le fais, est correcte, mais certains détails de la mise en œuvre doivent être améliorés.

51
Lion

Après des recherches et des tests, j'ai découvert que j'avais quelques malentendus sur la durée de vie des conteneurs Docker. Le simple redémarrage d'un conteneur n'oblige pas Docker à utiliser une nouvelle image, alors que l'image a été reconstruite entre-temps. Au lieu de cela, Docker récupère l'image uniquement avant l'exécution du conteneur . Donc, l'état après l'exécution d'un conteneur est persistant.

Pourquoi est-il nécessaire de retirer

Par conséquent, la reconstruction et le redémarrage ne suffisent pas. Je pensais que les conteneurs fonctionnaient comme un service: arrêter le service, faire vos modifications, le redémarrer et elles s’appliqueraient. C'était ma plus grosse erreur.

Les conteneurs étant permanents, vous devez les supprimer en utilisant d'abord docker rm <ContainerName>. Une fois qu'un conteneur est supprimé, vous ne pouvez pas simplement le démarrer avec docker start. Ceci doit être fait en utilisant docker run, qui utilise lui-même la dernière image pour créer une nouvelle instance de conteneur.

Les conteneurs doivent être aussi indépendants que possible

Avec cette connaissance, il est compréhensible que le stockage des données dans des conteneurs soit qualifié de mauvaise pratique et Docker recommande volumes de données/répertoires de montage des hôtes à la place: puisqu’un conteneur doit être détruit pour être mis à jour applications, les données stockées à l’intérieur seraient également perdues. Cela entraîne un travail supplémentaire pour arrêter les services, les données de sauvegarde, etc.

C'est donc une solution intelligente pour exclure complètement ces données du conteneur: nous n'avons pas à nous soucier de nos données, lorsqu'elles sont stockées en toute sécurité sur l'hôte et que le conteneur ne contient que l'application elle-même.

Pourquoi -rf peut ne pas vous aider vraiment

La commande docker run a un commutateur Clean up appelé -rf. Cela arrêtera le comportement de conservation permanente des conteneurs docker. En utilisant -rf, Docker détruira le conteneur après sa sortie. Mais cet interrupteur a deux problèmes:

  1. Docker supprime également les volumes sans nom associé au conteneur, ce qui peut tuer vos données.
  2. Avec cette option, il n’est pas possible d’exécuter des conteneurs en arrière-plan avec le commutateur -d.

Bien que le commutateur -rf soit une bonne option pour enregistrer le travail pendant le développement pour des tests rapides, il est moins approprié en production. Surtout en raison de l'option manquante pour exécuter un conteneur en arrière-plan, ce qui serait généralement nécessaire.

Comment enlever un conteneur

Nous pouvons contourner ces limitations en supprimant simplement le conteneur:

docker rm --force <ContainerName>

Le commutateur --force (ou -f) qui utilise SIGKILL sur les conteneurs en cours d'exécution. Au lieu de cela, vous pouvez également arrêter le conteneur avant:

docker stop <ContainerName>
docker rm <ContainerName>

Les deux sont égaux. docker stop utilise également SIGTERM . Toutefois, l'utilisation de la commande --force raccourcira votre script, en particulier si vous utilisez des serveurs CI: docker stop renvoie une erreur si le conteneur n'est pas en cours d'exécution. Cela ferait en sorte que Jenkins et de nombreux autres serveurs CI considéreraient à tort que la construction échouait. Pour résoudre ce problème, vous devez d'abord vérifier si le conteneur est en cours d'exécution comme je l'ai fait dans la question (voir variable containerRunning).

Script complet pour la reconstruction d'un conteneur Docker

Selon cette nouvelle connaissance, j'ai corrigé mon script de la manière suivante:

#!/bin/bash
imageName=xx:my-image
containerName=my-container

docker build -t $imageName -f Dockerfile  .

echo Delete old container...
docker rm -f $containerName

echo Run new container...
docker run -d -p 5000:5000 --name $containerName $imageName

Cela fonctionne parfaitement :)

96
Lion

Chaque fois que des modifications sont apportées dans dockerfile ou composition ou exigences, exécutez-le à nouveau avec docker-compose up --build. Pour que les images soient reconstruites et rafraîchies

16
yunus