Comment mettre à niveau le conteneur Docker après la modification de son image

518

Disons que j'ai tiré l'image officielle de mysql: 5.6.21 .

J'ai déployé cette image en créant plusieurs conteneurs docker.

Ces conteneurs fonctionnent depuis un certain temps jusqu'à la sortie de MySQL 5.6.22. L'image officielle de mysql: 5.6 est mise à jour avec la nouvelle version, mais mes conteneurs fonctionnent toujours en 5.6.21.

Comment puis-je propager les changements dans l'image (c'est-à-dire mettre à jour la distribution MySQL) vers tous mes conteneurs existants? Quelle est la bonne façon Docker de procéder?

Yaroslav Stavnichiy
la source

Réponses:

579

Après avoir évalué les réponses et étudié le sujet, je voudrais résumer.

La façon Docker de mettre à niveau les conteneurs semble être la suivante:

Les conteneurs d'applications ne doivent pas stocker de données d'application . De cette façon, vous pouvez remplacer le conteneur d'application par sa version la plus récente à tout moment en exécutant quelque chose comme ceci:

docker pull mysql
docker stop my-mysql-container
docker rm my-mysql-container
docker run --name=my-mysql-container --restart=always \
  -e MYSQL_ROOT_PASSWORD=mypwd -v /my/data/dir:/var/lib/mysql -d mysql

Vous pouvez stocker les données sur l'hôte (dans un répertoire monté en tant que volume) ou dans des conteneurs spéciaux uniquement pour les données . En savoir plus

La mise à niveau des applications (par exemple avec la mise à niveau yum / apt-get) dans les conteneurs est considérée comme un anti-modèle . Les conteneurs d'application sont censés être immuables , ce qui garantit un comportement reproductible. Certaines images d'application officielles (mysql: 5.6 en particulier) ne sont même pas conçues pour s'auto-mettre à jour (la mise à niveau apt-get ne fonctionnera pas).

Je remercie tous ceux qui ont donné leurs réponses, afin que nous puissions voir toutes les différentes approches.

Yaroslav Stavnichiy
la source
31
Et si une migration des données est nécessaire? Le nouveau serveur ne peut pas monter les données car il est dans un ancien format, il doit savoir qu'une migration est en cours et modifier la représentation des données.
Dor Rotman
12
Je pense que les concepteurs d'images devraient en tenir compte et autoriser le lancement de commandes personnalisées (par exemple, la migration des données) lors de la première exécution du conteneur.
Yaroslav Stavnichiy
4
@static_rtti Et docker rename my-mysql-container trash-containeravant de créer le nouveau?
Franklin Yu
4
Y aurait-il une commande tout-en-un pour mettre à jour le conteneur sans avoir à l'arrêter manuellement, à le supprimer et à le recréer (en fonction de la nouvelle image qui a été extraite)?
Michaël Perrin
83

Je n'aime pas monter des volumes comme lien vers un répertoire hôte, j'ai donc proposé un modèle pour mettre à niveau les conteneurs Docker avec des conteneurs entièrement gérés par Docker. La création d'un nouveau conteneur Docker avec --volumes-from <container>donnera au nouveau conteneur avec les images mises à jour la propriété partagée des volumes gérés Docker.

docker pull mysql
docker create --volumes-from my_mysql_container [...] --name my_mysql_container_tmp mysql

En ne supprimant pas encore immédiatement l'original my_mysql_container, vous avez la possibilité de revenir au conteneur de travail connu si le conteneur mis à niveau n'a pas les bonnes données ou échoue à un test d'intégrité.

À ce stade, je vais généralement exécuter les scripts de sauvegarde que j'ai pour le conteneur afin de me donner un filet de sécurité en cas de problème

docker stop my_mysql_container
docker start my_mysql_container_tmp

Vous avez maintenant la possibilité de vous assurer que les données que vous attendez dans le nouveau conteneur sont là et d'effectuer un contrôle de cohérence.

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

Les volumes de docker resteront en place tant que n'importe quel conteneur les utilisera, vous pouvez donc supprimer le conteneur d'origine en toute sécurité. Une fois le conteneur d'origine supprimé, le nouveau conteneur peut prendre le nom de l'original pour que tout soit aussi joli qu'il était au début.

Il existe deux avantages majeurs à utiliser ce modèle pour mettre à niveau les conteneurs Docker. Premièrement, il élimine la nécessité de monter des volumes sur des répertoires hôtes en permettant aux volumes d'être directement transférés vers des conteneurs mis à niveau. Deuxièmement, vous n'êtes jamais dans une position où il n'y a pas de conteneur docker fonctionnel; donc si la mise à niveau échoue, vous pouvez facilement revenir à la façon dont elle fonctionnait auparavant en faisant tourner à nouveau le conteneur Docker d'origine.

kMaiSmith
la source
3
Pourquoi n'aimez-vous pas monter des volumes d'hôtes dans un conteneur Docker? (Je fais précisément cela, donc je suis intéressé par des arguments contre cela: -) J'ai monté par exemple: ./postgres-data/:/var/lib/postgres/data- c'est-à-dire monté le ./postgres-data/
répertoire
4
@KajMagnus J'utilise beaucoup d'essaims de docker et j'aime écrire mes conteneurs pour bien fonctionner dans un essaim. Lorsque je fais tourner un conteneur dans un essaim, je n'ai aucune idée du nœud d'essaim sur lequel le conteneur va vivre, donc je ne peux pas compter sur le chemin de l'hôte contenant les données que je veux. Depuis Docker 1.9 (je pense), les volumes peuvent être partagés entre les hôtes, ce qui rend la mise à niveau et la migration des conteneurs un jeu d'enfant en utilisant la méthode que j'ai décrite. Une alternative serait de s'assurer qu'un volume réseau est monté sur tous les nœuds swarm, mais cela semble être une énorme difficulté à maintenir.
kMaiSmith
Merci! Ok, monter des volumes d'hôte semble être quelque chose que je veux aussi éviter, maintenant. Au moins un peu plus tard si mon application devient populaire et doit
évoluer
32

Juste pour fournir une réponse plus générale (non spécifique à mysql) ...

  1. En bref

Synchronisez avec le registre d'images de service ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Recréez le conteneur si le fichier ou l'image de composition de docker a changé:

docker-compose up -d
  1. Contexte

La gestion des images de conteneurs est l'une des raisons de l'utilisation de docker-compose (voir https://docs.docker.com/compose/reference/up/ )

S'il existe des conteneurs existants pour un service et que la configuration ou l'image du service a été modifiée après la création du conteneur, Docker-compose ramasse les modifications en arrêtant et en recréant les conteneurs (en préservant les volumes montés). Pour empêcher Compose de détecter les modifications, utilisez l'indicateur --no-recréer.

L'aspect de la gestion des données est également couvert par docker-compose via des "volumes" externes montés (voir https://docs.docker.com/compose/compose-file/#volumes ) ou un conteneur de données.

Cela laisse les problèmes potentiels de rétrocompatibilité et de migration des données intacts, mais ce sont des problèmes "applicatifs", non spécifiques à Docker, qui doivent être vérifiés par rapport aux notes de publication et aux tests ...

Ronan Fauglas
la source
Comment procédez-vous avec le versioning? par exemple, la nouvelle image est foo / image: 2 et docker-compose.yml a image: foo / image: 1?
dman
MERCI. Meilleure réponse!
Mick
Bien que ce soit certainement le chemin à parcourir, il faut savoir que toutes les modifications apportées au conteneur seront toujours perdues une fois le conteneur recréé. Il est donc toujours nécessaire de conserver les modifications de conteneur uniquement dans les volumes montés.
Petr Bodnár
23

Je voudrais ajouter que si vous souhaitez effectuer ce processus automatiquement (télécharger, arrêter et redémarrer un nouveau conteneur avec les mêmes paramètres que ceux décrits par @Yaroslav), vous pouvez utiliser WatchTower. Un programme qui met automatiquement à jour vos conteneurs lorsqu'ils sont modifiés https://github.com/v2tec/watchtower

Ricardo Polo Jaramillo
la source
20

Considérez pour ces réponses:

  • Le nom de la base de données est app_schema
  • Le nom du conteneur est app_db
  • Le mot de passe root est root123

Comment mettre à jour MySQL lors du stockage des données d'application dans le conteneur

Ceci est considéré comme une mauvaise pratique , car si vous perdez le conteneur, vous perdrez les données. Bien que ce soit une mauvaise pratique, voici une façon possible de le faire:

1) Faites un vidage de base de données en SQL:

docker exec app_db sh -c 'exec mysqldump app_schema -uroot -proot123' > database_dump.sql

2) Mettez à jour l'image:

docker pull mysql:5.6

3) Mettez à jour le conteneur:

docker rm -f app_db
docker run --name app_db --restart unless-stopped \
-e MYSQL_ROOT_PASSWORD=root123 \
-d mysql:5.6

4) Restaurez le vidage de la base de données:

docker exec app_db sh -c 'exec mysql -uroot -proot123' < database_dump.sql

Comment mettre à jour le conteneur MySQL à l'aide d'un volume externe

L'utilisation d'un volume externe est une meilleure façon de gérer les données et facilite la mise à jour de MySQL. La perte du conteneur ne perdra aucune donnée. Vous pouvez utiliser docker-compose pour faciliter la gestion des applications Docker multi-conteneurs dans un seul hôte:

1) Créez le docker-compose.ymlfichier afin de gérer vos applications:

version: '2'
services:
  app_db:
    image: mysql:5.6
    restart: unless-stopped
    volumes_from: app_db_data
  app_db_data:
    volumes: /my/data/dir:/var/lib/mysql

2) Mettre à jour MySQL (depuis le même dossier que le docker-compose.ymlfichier):

docker-compose pull
docker-compose up -d

Remarque: la dernière commande ci-dessus mettra à jour l'image MySQL, recréera et démarrera le conteneur avec la nouvelle image.

Alexandre V.
la source
Disons que j'ai une énorme base de données (plusieurs Go), mes données seront-elles inaccessibles jusqu'à ce que toute la base de données soit importée? Cela pourrait être un énorme "temps d'arrêt"
hellimac
Puisque vous l'avez mentionné docker-compose, cela fonctionnera-t-il? stackoverflow.com/a/31485685/65313
sivabudh
1
volumes_fromla clé est désormais obsolète (même supprimée dans la version 3 du fichier compose) au profit de la nouvelle volumesclé.
Franklin Yu
docker pull image_uri:tag && docker restart container_running_that_imagetravaillé pour moi. Pas besoin de docker-compose pull && docker-compose up -d.
Yuriy Pozniak
16

Réponse similaire à ci-dessus

docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull
Eddie Jaoude
la source
1
Brillant! Assez surpris de ne pas avoir obtenu plus de votes. Maintenant, la seule chose qui manque serait de déclencher un redémarrage de tous les conteneurs mis à jour.
sorin
7
Malheureusement, cela ne mettra pas à jour le conteneur existant. Cela ne fera que mettre à jour l'image extraite, mais le conteneur existant est immuable et utilise toujours l'image d'origine utilisée pour la créer. Cela ne fonctionne que si vous créez un nouveau conteneur à partir de l'image, mais tout conteneur existant est toujours basé sur l'image d'origine.
Eric B.
Incroyable. Si vous devez extraire la version spécifique du conteneur, faites-le comme ceci: images docker | awk '{print $ 1 ":" $ 2}' | grep -v 'aucun' | grep -iv 'repo' | xargs -n1 docker pull
rogervila
11

Voici à quoi cela ressemble docker-composelors de la création d'une personnalisation Dockerfile.

  1. Créez d'abord votre Dockerfile personnalisé, en ajoutant un numéro de version suivant pour différencier. Ex: docker build -t imagename:version . Cela stockera votre nouvelle version localement.
  2. Courir docker-compose down
  3. Modifiez votre docker-compose.ymlfichier pour refléter le nouveau nom d'image que vous avez défini à l'étape 1.
  4. Courez docker-compose up -d. Il recherchera l'image localement et utilisera votre image mise à niveau.

-ÉDITER-

Mes étapes ci-dessus sont plus verbeuses qu'elles ne devraient l'être. J'ai optimisé mon flux de travail en incluant le build: .paramètre dans mon fichier docker-compose. Les étapes se présentent maintenant:

  1. Vérifiez que mon Dockerfile est ce à quoi je veux qu'il ressemble.
  2. Définissez le numéro de version de mon nom d'image dans mon fichier docker-compose.
  3. Si mon image n'est pas encore construite: exécutez docker-compose build
  4. Courir docker-compose up -d

Je ne m'en rendais pas compte à l'époque, mais docker-compose est assez intelligent pour simplement mettre à jour mon conteneur vers la nouvelle image avec la seule commande, au lieu d'avoir à le réduire en premier.

gdbj
la source
Dans une situation réelle, vous ne pouvez pas utiliser vos propres mains et effectuer ces changements. Votre solution ne prend pas en charge les moyens automatiques de résoudre le problème.
Carlos Vázquez Losada
7
vous dites donc que parce que ma solution n'est pas automatisée, elle n'est pas valide? Est-ce une exigence du PO? Et les autres réponses impliquent-elles l'automatisation? Vraiment confus. Et, je pense que les downvotes rendent un mauvais service aux autres qui viennent ici. Ma réponse est valide à 100% pour la question posée.
gdbj
merci pour cette réponse, je ne savais pas non plus que vous pouviez simplement courir docker-compose up -dsans avoir à tout arrêter en premier.
radicand
4

Si vous ne souhaitez pas utiliser Docker Compose, je peux recommander portainer . Il a une fonction de recréation qui vous permet de recréer un conteneur tout en tirant la dernière image.

béruique
la source
2

Vous devez soit reconstruire toutes les images et redémarrer tous les conteneurs, soit mettre à jour le logiciel et redémarrer la base de données. Il n'y a pas de chemin de mise à niveau mais que vous concevez vous-même.

seanmcl
la source
Que voulez-vous dire exactement par redémarrage des conteneurs? Il y a une docker restartcommande, mais je ne suis pas sûr qu'elle détectera les changements d'image. Et qu'advient-il de mes données à l'intérieur des conteneurs?
Yaroslav Stavnichiy
1
Désolé, je ne voulais pas dire redémarrage de docker. Je veux dire docker rm -f CONTANER; docker exécuter NEW_IMAGE. Les données de votre conteneur SQL disparaîtront. C'est pourquoi les gens utilisent généralement des volumes pour stocker les données.
seanmcl
Si vous avez toutes vos données montées en volumes dans des conteneurs séparés ou une machine hôte, le nas @seanmcl a dit de créer de nouveaux conteneurs avec un nouveau mysql connecté aux mêmes données. Si vous ne l'avez pas fait (vous devriez) mais vous pouvez utiliser la commande docker exec disponible dans docker 1.3 pour mettre à jour mysql et le redémarrer à l'intérieur du conteneur.
Usman Ismail
2

Extrait de http://blog.stefanxo.com/2014/08/update-all-docker-images-at-once/

Vous pouvez mettre à jour toutes vos images existantes à l'aide du pipeline de commandes suivant:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull
gvlx
la source
6
Cela mettra à jour les images, mais pas le conteneur. Le conteneur est immuable et son image de base ne peut pas être modifiée sans créer un nouveau conteneur à partir d'une image mise à jour.
Eric B.
2

Assurez-vous que vous utilisez des volumes pour toutes les données persistantes (configuration, journaux ou données d'application) que vous stockez sur les conteneurs liés à l'état des processus à l'intérieur de ce conteneur. Mettez à jour votre Dockerfile et reconstruisez l'image avec les modifications souhaitées, puis redémarrez les conteneurs avec vos volumes montés à leur emplacement approprié.

Daniel Dinnyes
la source
1

C'est quelque chose avec lequel je me bats également pour mes propres images. J'ai un environnement de serveur à partir duquel je crée une image Docker. Lorsque je mets à jour le serveur, j'aimerais que tous les utilisateurs qui exécutent des conteneurs basés sur mon image Docker puissent mettre à niveau vers le dernier serveur.

Idéalement, je préfère générer une nouvelle version de l'image Docker et que tous les conteneurs basés sur une version précédente de cette image soient automatiquement mis à jour vers la nouvelle image «en place». Mais ce mécanisme ne semble pas exister.

Donc, la meilleure conception suivante que j'ai pu trouver jusqu'à présent est de fournir un moyen de mettre le conteneur à jour lui-même - de la même manière qu'une application de bureau vérifie les mises à jour puis se met à niveau. Dans mon cas, cela signifiera probablement la création d'un script impliquant des pulls Git à partir d'une balise bien connue.

L'image / le conteneur ne change pas réellement, mais les "internes" de ce conteneur changent. Vous pourriez imaginer faire la même chose avec apt-get, yum ou tout ce qui est approprié pour votre environnement. Parallèlement à cela, je mettrais à jour l'image myserver: latest dans le registre afin que tout nouveau conteneur soit basé sur la dernière image.

J'aimerais savoir s'il existe un état de la technique qui traite de ce scénario.

bjlevine
la source
7
Cela va à l'encontre du concept d'infrastructure immuable et de certains de ses avantages. Vous pouvez tester votre application / environnement pour voir s'il fonctionne et ce n'est pas garanti si vous mettez à jour des composants à l'intérieur. Le fractionnement du code conteneur des données de la configuration vous permet de mettre à jour, de tester que nous travaillons actuellement et de le déployer en production, sachant qu'il n'y a pas de ligne de code différente de l'image testée à l'image de production. Quoi qu'il en soit, le système vous permet de le gérer comme vous le dites aussi, c'est votre choix.
gmuslera
Très bon point gmuslera. Convenu qu'il s'agit d'un anti-modèle pour mettre à jour les «internes» d'un conteneur docker existant.
bjlevine
Alors, quelle est la meilleure solution pour mettre à jour automatiquement le conteneur Docker en fonction des mises à jour de l'image Docker pour fournir les mises à jour à tous les conteneurs sans effort?
tarek salem
1

Mise à jour

Il s'agit principalement d'interroger le conteneur pour ne pas le mettre à jour car la construction d'images est la façon de procéder

J'ai eu le même problème, j'ai donc créé docker-run , un outil de ligne de commande très simple qui s'exécute à l'intérieur d'un conteneur docker pour mettre à jour les packages dans d'autres conteneurs en cours d'exécution.

Il utilise docker-py pour communiquer avec les conteneurs docker en cours d'exécution et mettre à jour les packages ou exécuter n'importe quelle commande unique arbitraire

Exemples:

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run exec

par défaut, cela exécutera la datecommande dans tous les conteneurs en cours d'exécution et renverra les résultats, mais vous pouvez exécuter n'importe quelle commande, par exempledocker-run exec "uname -a"

Pour mettre à jour des packages (actuellement en utilisant uniquement apt-get):

docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run update

Vous pouvez créer et alias et l'utiliser comme une ligne de commande régulière, par exemple

alias docker-run='docker run --rm -v /var/run/docker.sock:/tmp/docker.sock itech/docker-run'

iTech
la source
Est-ce une bonne idée? (Si vous le faites apt update; apt upgrade, l'image grandira.)
ctrl-alt-delor
L'image de @yaroslav est une meilleure solution à ce problème. Ce qui précède n'est pas vraiment la manière Docker de faire les choses.
Joost van der Laan