GNU/Linux >> Tutoriels Linux >  >> Linux

Mise à jour des conteneurs Docker avec un temps d'arrêt nul ou minimum

Supposons que vous exécutez un service dans un conteneur et qu'une nouvelle version du service est disponible via leur image Docker. Dans ce cas, vous souhaitez mettre à jour le conteneur Docker.

La mise à jour d'un conteneur Docker n'est pas un problème, mais la mise à jour d'un conteneur Docker sans temps d'arrêt est un défi.

Confus? Laissez-moi vous montrer les deux manières une par une.

Méthode 1 :mise à jour du conteneur Docker vers la dernière image (résulte en temps d'arrêt)

Cette méthode consiste essentiellement en ces étapes :

  • Extraire la dernière image docker
  • Arrêtez et supprimez le conteneur exécutant l'ancienne image Docker
  • Créer un nouveau conteneur avec l'image Docker nouvellement extraite

Vous voulez les commandes ? Voilà.

Répertoriez les images Docker et obtenez l'image Docker qui a une mise à jour. Obtenez les dernières modifications apportées à cette image à l'aide de la commande docker pull :

docker pull image_name

Obtenez maintenant l'ID de conteneur ou le nom du conteneur qui exécute l'ancienne image Docker. Utilisez la commande docker ps à cet effet. Arrêtez ce conteneur :

docker stop container_ID

Et retirez le conteneur :

docker rm container_id

L'étape suivante consiste à exécuter un nouveau conteneur avec les mêmes paramètres que vous avez utilisés pour exécuter le conteneur précédent. Je crois que vous savez quels sont ces paramètres parce que vous les avez créés en premier lieu.

docker run --name=container_name [options] docker_image

Voyez-vous le problème avec cette approche? Vous devez arrêter le conteneur en cours d'exécution, puis en créer un nouveau. Cela entraînera un temps d'arrêt pour le service en cours d'exécution.

Le temps d'arrêt, même s'il ne dure qu'une minute, peut avoir un impact important s'il s'agit d'un projet stratégique ou d'un service Web à fort trafic.

Vous voulez connaître une approche plus sûre et meilleure pour ce problème? Lisez la section suivante.

Méthode 2 :mise à jour du conteneur Docker dans une configuration de proxy inverse (avec temps d'arrêt nul ou minimal)

Si vous cherchiez une solution simple, désolé de vous décevoir mais ce n'en sera pas une car ici vous devrez déployer vos conteneurs dans une architecture reverse-proxy avec Docker Compose.

Si vous cherchez à gérer des services critiques à l'aide de conteneurs Docker, la méthode du proxy inverse vous aidera beaucoup à long terme.

Permettez-moi d'énumérer trois principaux avantages de la configuration du proxy inverse :

  • Vous pouvez déployer plusieurs services publics sur le même serveur. Pas de blocage de port ici.
  • Le serveur Let's Encrypt s'occupe du déploiement SSL pour tous les services, tous les conteneurs.
  • Vous pouvez mettre à jour les conteneurs sans affecter les services en cours d'exécution (pour la plupart des services Web).

Si vous êtes curieux d'en savoir plus, vous pouvez consulter le glossaire officiel de Nginx qui met en évidence les utilisations courantes d'un proxy inverse et comment il se compare à un équilibreur de charge.

Nous avons un excellent didacticiel détaillé sur la configuration du proxy inverse Nginx pour héberger plusieurs instances de services Web s'exécutant dans des conteneurs sur le même serveur. Je ne vais donc pas en reparler ici. Vous devez commencer par configurer vos conteneurs en utilisant cette architecture. Croyez-moi, cela en vaut la peine.

Dans ce didacticiel, j'ai conçu une méthodologie étape par étape qui peut être très utile dans vos activités DevOps quotidiennes. Cette exigence peut non seulement être très nécessaire lorsque vous mettez à jour vos conteneurs, mais également lorsque vous souhaitez apporter une modification très nécessaire à l'une de vos applications en cours d'exécution sans sacrifier une disponibilité inestimable.

À partir de maintenant, nous supposons que vous exécutez vos applications Web sous une configuration de proxy inverse qui garantirait que le reroutage fonctionne pour le nouveau conteneur à jour comme prévu après les modifications de configuration que nous allons effectuer.

Je vais d'abord montrer les étapes de cette méthode, suivies d'un exemple concret.

Étape 1 :Mettre à jour le fichier de composition docker

Tout d'abord, vous devez modifier le fichier de composition Docker existant avec le numéro de version de la dernière image. Il peut être consulté sur Docker Hub, plus précisément dans la section "tags" de l'application.

Déplacez-vous dans le répertoire de l'application et modifiez le fichier docker-compose avec un éditeur de texte en ligne de commande. J'ai utilisé Nano ici.

[email protected]:~/web-app$ nano docker-compose.yml

Au sein des services: , mettez à jour image: web-app:x.x.x avec le numéro de version le plus récent et enregistrez le fichier.

Vous vous demandez peut-être pourquoi ne pas utiliser la dernière balise au lieu de spécifier manuellement le numéro de version ? Je l'ai fait exprès car j'ai remarqué que lors de la mise à jour des conteneurs, il peut y avoir un retard intermittent de la dernière balise dans la récupération de la dernière version de l'application dockerisée. Lorsque vous utilisez directement le numéro de version, vous pouvez toujours être absolument bien sûr.

Étape 2 :Faites évoluer un nouveau conteneur

Lorsque vous utilisez la commande suivante, un nouveau conteneur est créé en fonction des nouvelles modifications apportées au fichier docker compose.

[email protected]:~/web-app$ docker-compose up -d --scale web-app=2 --no-recreate

Notez que le conteneur précédent est toujours opérationnel. L'--scale flag est utilisé pour créer des conteneurs supplémentaires comme spécifié. Ici, web-app a été défini comme nom de service pour l'application Web.

Même si vous spécifiez une mise à l'échelle jusqu'à 2 conteneurs, --no-recreate garantit qu'un seul est ajouté puisque votre ancien conteneur est déjà en cours d'exécution.

Pour en savoir plus sur l'--scale et --no-recreate flag, consultez la page de documentation officielle de docker-compose up.

Étape 3 :Supprimer l'ancien conteneur

Après l'étape 2, attendez environ 15 à 20 secondes pour que les nouvelles modifications prennent effet, puis supprimez l'ancien conteneur :

[email protected]:~/web-app$ docker rm -f old-web-app

Sur différentes applications Web, les modifications reflétées sont différentes sur le plan du comportement après l'exécution de la commande ci-dessus (discutée comme un conseil bonus tout en bas de ce didacticiel).

Étape 4 :Passez à la configuration d'un conteneur unique comme avant

Pour la dernière étape, vous réduisez à nouveau la configuration d'un conteneur unique :

[email protected]:~/web-app$ docker-compose up -d --scale web-app=1 --no-recreate

J'ai testé cette méthode avec des instances Ghost, WordPress, Rocket.Chat et Nextcloud. Hormis le passage de Nextcloud en mode maintenance pendant quelques secondes, la procédure fonctionne extrêmement bien pour les trois autres.

Discourse cependant, est une autre histoire et peut être une exception très délicate dans ce cas en raison de son modèle hybride.

L'essentiel est :plus l'application Web utilise la pratique Docker standard lors de sa dockerisation, plus il devient pratique de gérer tous les conteneurs d'applications Web au jour le jour.

Exemple concret :mise à jour d'une instance Ghost en direct sans temps d'arrêt

Comme promis, je vais vous montrer un exemple concret. Je vais vous montrer comment mettre à jour Ghost exécuté dans le conteneur Docker vers une version plus récente sans aucun temps d'arrêt.

Ghost est un CMS et nous l'utilisons pour Linux Handbook. L'exemple montré ici est ce que nous utilisons pour mettre à jour notre instance Ghost exécutant ce site Web.

Supposons que j'ai une configuration existante basée sur une ancienne version située dans /home/avimanyu/ghost :

version: '3.5'
services:
  ghost:
    image: ghost:3.36
    volumes:
      - ghost:/var/lib/ghost/content
    environment:
      - VIRTUAL_HOST=blog.domain.com
      - LETSENCRYPT_HOST=blog.domain.com
      - url=https://blog.domain.com
      - NODE_ENV=production
    restart: always
    networks:
      - net

volumes:
  ghost:
    external: true

networks:
  net:
    external: true

Notez que la configuration de composition de docker ci-dessus est basée sur une configuration de docker Nginx préexistante décrite ici, s'exécutant sur un réseau nommé net . Son volume docker avait également été créé manuellement avec docker volume create ghost-blog .

Quand je le vérifie avec docker ps :

CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS                                      NAMES
2df6c27c1fe3        ghost:3.36                             "docker-entrypoint.s…"   9 days ago          Up 7 days           2368/tcp                                   ghost_ghost-blog_1
89a5a7fdcfa4        jrcs/letsencrypt-nginx-proxy-companion   "/bin/bash /app/entr…"   9 days ago          Up 7 days                                                      letsencrypt-helper
90b72e217516        jwilder/nginx-proxy                      "/app/docker-entrypo…"   9 days ago          Up 7 days           0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   reverse-proxy

Au moment de la rédaction, il s'agit d'une ancienne version de Ghost. Il est temps de le mettre à jour vers la dernière version 3.37.1 ! Donc, je le révise dans la section image comme :

version: '3.5'
services:
  ghost-blog:
    image: ghost:3.37.1
    volumes:
      - ghost-blog:/var/lib/ghost/content
    environment:
      - VIRTUAL_HOST=blog.domain.com
      - LETSENCRYPT_HOST=blog.domain.com
      - url=https://blog.domain.com
      - NODE_ENV=production
    restart: always
    networks:
      - net

volumes:
  ghost-blog:
    external: true

networks:
  net:
    external: true

Maintenant, pour faire bon usage de la méthode de mise à l'échelle :

[email protected]:~/ghost$ docker-compose up -d --scale ghost-blog=2 --no-recreate

Avec la commande ci-dessus, l'ancien conteneur reste inchangé mais un nouveau se joint avec la même configuration mais basé sur la dernière version de Ghost :

[email protected]:~/ghost$ docker-compose up -d --scale ghost-blog=2 --no-recreate
Pulling ghost (ghost:3.37.1)...
3.37.1: Pulling from library/ghost
bb79b6b2107f: Already exists
99ce436c3449: Already exists
f7bdc31da5f5: Already exists
7a1300b9ff59: Already exists
a495c68fa838: Already exists
6e362a39ec35: Already exists
b68b4f3c36f7: Already exists
41f8b02d4a71: Pull complete
3ecc736ea4e5: Pull complete
Digest: sha256:595c759980cd22e99037811397012908d89efb799776db222a4be6d4d892917c
Status: Downloaded newer image for ghost:3.37.1
Starting ghost_ghost-blog_1 ... done
Creating ghost_ghost-blog_2 ... done

Si j'avais utilisé l'approche conventionnelle avec docker-compose up -d au lieu de cela, je n'aurais pas pu éviter la recréation du conteneur existant pour qu'il soit basé sur la dernière image de Ghost.

La récréation implique la suppression de l'ancien conteneur et la création d'un nouveau à sa place avec les mêmes paramètres. C'est alors qu'un temps d'arrêt se produit et que le site devient inaccessible.

C'est pourquoi vous devez utiliser le --no-recreate drapeau lors de la mise à l'échelle.

Alors maintenant, j'ai deux conteneurs en cours d'exécution basés sur la même configuration fantôme. C'est la partie cruciale où nous évitons les temps d'arrêt :

[email protected]:~/ghost$ docker ps
CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS                                      NAMES
f239f677de54        ghost:3.37.1                               "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        2368/tcp                                   ghost_ghost-blog_2
2df6c27c1fe3        ghost:3.36                             "docker-entrypoint.s…"   9 days ago          Up 7 days           2368/tcp                                   ghost_ghost-blog_1
89a5a7fdcfa4        jrcs/letsencrypt-nginx-proxy-companion   "/bin/bash /app/entr…"   9 days ago          Up 7 days                                                      letsencrypt-helper
90b72e217516        jwilder/nginx-proxy                      "/app/docker-entrypo…"   9 days ago          Up 7 days           0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   reverse-proxy

Notez que le nom de l'ancien conteneur est ghost_ghost-blog_1 . Vérifiez votre nom de domaine et vous le trouverez toujours accessible sur blog.domain.com . Si vous actualisez le panneau d'administration de Ghost, qui se trouve sur blog.domain.com/ghost après la mise à l'échelle, il continuerait d'essayer de se charger jusqu'à ce que vous supprimiez l'ancien conteneur :

[email protected]:~/ghost$ docker rm -f ghost_ghost-blog_1

Mais pour le blog Ghost lui-même, il n'y a pas de temps d'arrêt du tout ! Ainsi, de cette manière, vous pouvez garantir l'absence de temps d'arrêt lors de la mise à jour des blogs Ghost.

Enfin, réduisez la configuration à son paramètre d'origine :

[email protected]:~/ghost$ docker-compose up -d --scale ghost-blog=1 --no-recreate
Starting ghost_ghost-blog_2 ... done

Comme mentionné précédemment, après la suppression des anciens conteneurs, les modifications sont reflétées dans les applications Web respectives, mais elles se comportent évidemment différemment en raison de la diversité des conceptions d'applications.

Voici quelques remarques :

Sur WordPress :Assurez-vous d'avoir ajouté define( 'AUTOMATIC_UPDATER_DISABLED', true ); comme ligne de fond sur le fichier wp-config.php situé dans /var/www/html et montez /var/www/html/wp-content au lieu de /var/www/html en tant que volume. Vérifiez ici pour plus de détails. Après l'étape 3, le panneau d'administration de WordPress indiquera que votre WordPress est à jour et vous demandera de continuer et de mettre à jour votre base de données. La mise à jour se fait rapidement sans aucun temps d'arrêt sur le site WordPress et c'est tout !

Sur Rocket.Chat  :Cela peut prendre environ 15 à 20 secondes pour les Admin>Info pour montrer que vous utilisez la dernière version avant même d'avoir effectué l'étape 3. Plus de temps d'arrêt !

Sur Nextcloud :Après l'étape 2, Nextcloud passerait en mode maintenance pendant quelques secondes puis rechargerait vos fichiers. Sous Administration > Overview > Security & setup warnings , vous pourriez recevoir un avertissement du type "Votre serveur Web n'est pas correctement configuré pour résoudre "./well-known/carddav". Cela est dû au fait que votre ancien conteneur est toujours en cours d'exécution. Une fois que vous l'avez supprimé à l'étape 3, cet avertissement Assurez-vous de lui laisser un peu de temps avant d'accéder à votre URL Nextcloud car il pourrait afficher une erreur de passerelle 502 incorrecte jusqu'à ce que votre conteneur Nginx voie le nouveau conteneur Nextcloud mis à l'échelle basé sur la dernière version.

Conseils bonus

Voici quelques conseils et éléments à garder à l'esprit lorsque vous suivez cette méthode.

Astuce 1

Pour maintenir les temps d'arrêt au minimum à zéro dans différentes applications, assurez-vous de laisser suffisamment de temps aux conteneurs nouvellement mis à l'échelle et à jour afin que les conteneurs Nginx puissent enfin les reconnaître avant de supprimer les anciens à l'étape 2.

Avant de passer à l'étape 2 décrite ci-dessus, il est conseillé d'observer le comportement de votre application sur votre navigateur (à la fois en tant qu'actualisation de la page après la connexion ou en tant qu'accès à une nouvelle page sur une fenêtre de navigateur privée et sans cache) après avoir effectué l'étape 1.

Étant donné que chaque application est conçue différemment, cette mesure s'avérerait très utile avant de démonter vos anciens conteneurs.

Astuce 2

Bien que cela puisse être une méthode très ingénieuse pour mettre à jour vos conteneurs vers les dernières versions des applications qu'ils exécutent, notez que vous pouvez utiliser la même méthode pour apporter également des modifications de configuration ou modifier les paramètres environnementaux sans rencontrer de problèmes de temps d'arrêt.

Cela peut être crucial si vous devez résoudre un problème ou effectuer une modification que vous jugez nécessaire dans un conteneur en direct, mais que vous ne souhaitez pas supprimer en le faisant. Une fois que vous avez apporté vos modifications et que vous êtes sûr que le problème est résolu, vous pouvez supprimer facilement l'ancien.

Nous avons nous-mêmes été confrontés à ce problème lorsque nous avons constaté que la rotation des journaux n'était pas activée dans l'un de nos conteneurs en direct. Nous avons apporté les modifications nécessaires pour l'activer et en même temps, nous avons évité tout temps d'arrêt avec cette méthode.

Astuce 3

Si vous avez spécifiquement nommé votre conteneur dans le fichier YML en utilisant container_name , la méthode ne fonctionnera pas comme prévu car elle implique la création d'un nouveau nom de conteneur.

Vous pouvez éviter ce conflit en laissant cette tâche de nommage du conteneur à Docker Compose (fait automatiquement selon sa convention de nommage). Docker Compose nomme ses conteneurs comme directory-name_service-name_1 . Le nombre à la fin augmenterait chaque fois que le conteneur est mis à jour (jusqu'à ce que vous utilisiez docker-compose pour une raison quelconque).

Si vous utilisez déjà un service en utilisant des conteneurs nommés personnalisés dans votre fichier de composition docker, pour utiliser cette méthode, commentez simplement la ligne (avec un préfixe #) qui inclut container_name à l'intérieur de la définition de service.

Après avoir créé le nouveau conteneur pour ce qui précède en utilisant l'étape 1 comme décrit dans ce didacticiel, pour l'étape 2, le nom de l'ancien conteneur (qui n'a pas été arrêté pour éviter les temps d'arrêt) serait tel qu'il a été spécifié précédemment à l'aide de container_name (peut également être vérifié avec `docker ps ` avant de supprimer l'ancien conteneur).

Avez-vous appris quelque chose de nouveau ?

Je sais que cet article est un peu long, mais j'espère qu'il aidera notre communauté DevOps à gérer les problèmes de temps d'arrêt avec les serveurs de production exécutant des applications Web dockerisées. Les temps d'arrêt ont un impact énorme tant au niveau individuel que commercial et c'est pourquoi couvrir ce sujet était une nécessité absolue.

Veuillez vous joindre à la discussion et partager vos réflexions, commentaires ou suggestions dans la section des commentaires ci-dessous.


Linux
  1. Installer ModSecurity avec Apache dans un conteneur Docker

  2. 7 fonctionnalités amusantes de conteneurs/transports d'images Linux

  3. Comment répertorier les conteneurs Docker

  4. Docker ne met pas à jour les modifications dans le répertoire

  5. Réglage du noyau avec le conteneur Docker privilégié

Comment modifier le code dans les conteneurs Docker avec Visual Studio Code

Comment exécuter des conteneurs Docker

Utilisation des conteneurs Docker à partir de la ligne de commande

Comment nommer ou renommer les conteneurs Docker

Comment :démarrer avec les conteneurs Windows et Docker

Comment gérer les conteneurs Docker