GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Aktualisieren von Docker-Containern ohne oder mit minimaler Ausfallzeit

Angenommen, Sie führen einen Dienst in einem Container aus und es gibt eine neue Version des Dienstes, die über ihr Docker-Image verfügbar ist. In einem solchen Fall möchten Sie den Docker-Container aktualisieren.

Das Aktualisieren eines Docker-Containers ist kein Problem, aber das Aktualisieren eines Docker-Containers ohne Ausfallzeit ist eine Herausforderung.

Verwirrt? Lassen Sie mich Ihnen beide Wege nacheinander zeigen.

Methode 1:Aktualisieren des Docker-Containers auf das neueste Image (führt zu Ausfallzeiten)

Diese Methode besteht im Wesentlichen aus diesen Schritten:

  • Laden Sie das neueste Docker-Image herunter
  • Beenden und entfernen Sie den Container, auf dem das alte Docker-Image ausgeführt wird
  • Erstellen Sie einen neuen Container mit dem neu gezogenen Docker-Image

Willst du die Befehle? Hier bitte.

Listen Sie die Docker-Images auf und rufen Sie das Docker-Image mit einem Update ab. Holen Sie sich die neuesten Änderungen an diesem Image mit dem Docker-Pull-Befehl:

docker pull image_name

Rufen Sie nun die Container-ID oder den Namen des Containers ab, der das ältere Docker-Image ausführt. Verwenden Sie dazu den Befehl docker ps. Beenden Sie diesen Container:

docker stop container_ID

Und entfernen Sie den Container:

docker rm container_id

Der nächste Schritt besteht darin, einen neuen Container mit denselben Parametern auszuführen, die Sie zum Ausführen des vorherigen Containers verwendet haben. Ich glaube, Sie kennen diese Parameter, weil Sie sie ursprünglich erstellt haben.

docker run --name=container_name [options] docker_image

Sehen Sie das Problem bei diesem Ansatz? Sie müssen den laufenden Container stoppen und dann einen neuen erstellen. Dies führt zu einer Ausfallzeit des laufenden Dienstes.

Die Ausfallzeit, auch nur für eine Minute, kann große Auswirkungen haben, wenn es sich um ein geschäftskritisches Projekt oder einen Webdienst mit hohem Datenverkehr handelt.

Möchten Sie einen sichereren und besseren Ansatz für dieses Problem kennen? Lesen Sie den nächsten Abschnitt.

Methode 2:Aktualisieren des Docker-Containers in einem Reverse-Proxy-Setup (mit keine oder minimale Ausfallzeit)

Wenn Sie nach einer unkomplizierten Lösung gesucht haben, muss ich Sie leider enttäuschen, aber das wird keine sein, denn hier müssen Sie Ihre Container in einer Reverse-Proxy-Architektur mit Docker Compose bereitstellen.

Wenn Sie kritische Dienste mithilfe von Docker-Containern verwalten möchten, wird Ihnen die Reverse-Proxy-Methode auf lange Sicht sehr helfen.

Lassen Sie mich drei Hauptvorteile des Reverse-Proxy-Setups auflisten:

  • Sie können mehrere öffentlich zugängliche Dienste auf demselben Server bereitstellen. Keine Portblockierung hier.
  • Der Server von Let's Encrypt kümmert sich um die SSL-Bereitstellung für alle Dienste und alle Container.
  • Sie können die Container aktualisieren, ohne die laufenden Dienste zu beeinträchtigen (für die meisten Webdienste).

Wenn Sie mehr erfahren möchten, können Sie sich das offizielle Nginx-Glossar ansehen, das die allgemeinen Verwendungszwecke eines Reverse-Proxys und den Vergleich mit einem Load Balancer hervorhebt.

Wir haben ein großartiges, ausführliches Tutorial zum Einrichten von Nginx-Reverse-Proxys, um mehr als eine Instanz von Webdiensten zu hosten, die in Containern auf demselben Server ausgeführt werden. Daher werde ich hier nicht noch einmal darauf eingehen. Sie sollten Ihre Container zuerst mit dieser Architektur einrichten. Vertrauen Sie mir, das ist die Mühe wert.

In diesem Tutorial habe ich eine Schritt-für-Schritt-Methode entwickelt, die bei Ihren täglichen DevOps-Aktivitäten sehr hilfreich sein kann. Diese Anforderung kann nicht nur sehr notwendig sein, wenn Sie Ihre Container aktualisieren, sondern auch, wenn Sie eine sehr notwendige Änderung an einer Ihrer laufenden Apps vornehmen möchten, ohne die unschätzbare Betriebszeit zu opfern.

Von hier an gehen wir davon aus, dass Sie Ihre Webanwendungen unter einem Reverse-Proxy-Setup ausführen, das sicherstellt, dass die Umleitung für den neuen aktuellen Container nach den Konfigurationsänderungen, die wir vornehmen werden, wie erwartet funktioniert.

Ich werde zuerst die Schritte dieser Methode zeigen, gefolgt von einem Beispiel aus dem wirklichen Leben.

Schritt 1:Docker-Compose-Datei aktualisieren

Zunächst müssen Sie die vorhandene Docker-Compose-Datei mit der Versionsnummer des neuesten Images bearbeiten. Es kann im Docker Hub angezeigt werden, insbesondere im Abschnitt „Tags“ der Anwendung.

Wechseln Sie in das Anwendungsverzeichnis und bearbeiten Sie die Docker-Compose-Datei mit einem Befehlszeilen-Texteditor. Ich habe hier Nano verwendet.

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

Innerhalb von services: , aktualisieren Sie image:web-app:x.x.x mit der neuesten Versionsnummer und speichern Sie die Datei.

Sie fragen sich vielleicht, warum Sie nicht trotzdem das neueste Tag verwenden, anstatt die Versionsnummer manuell anzugeben? Ich habe es absichtlich getan, da ich bemerkt habe, dass es beim Aktualisieren von Containern zu einer zeitweiligen Verzögerung des neuesten Tags kommen kann, wenn tatsächlich die neueste Version der dockerisierten Anwendung abgerufen wird. Wenn Sie die Versionsnummer direkt verwenden, können Sie immer absolut sein sicher.

Schritt 2:Skalieren Sie einen neuen Container hoch

Wenn Sie den folgenden Befehl verwenden, wird basierend auf den neuen Änderungen, die in der Docker-Compose-Datei vorgenommen wurden, ein neuer Container erstellt.

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

Beachten Sie, dass der vorherige Container noch aktiv ist. Die --scale Flag wird verwendet, um wie angegeben zusätzliche Container zu erstellen. Hier, Web-App wurde als Dienstname für die Webanwendung festgelegt.

Obwohl Sie die Skalierung auf bis zu 2 Container angeben, wird --no-recreate stellt sicher, dass nur einer hinzugefügt wird, da Ihr alter Container bereits ausgeführt wird.

Um mehr über die --scale zu erfahren und --no-recreate Flag finden Sie auf der offiziellen Docker-Compose-Up-Dokumentationsseite.

Schritt 3:Entfernen Sie den alten Container

Geben Sie nach Schritt 2 etwa 15–20 Sekunden Zeit, damit die neuen Änderungen wirksam werden, und entfernen Sie dann den alten Container:

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

Bei verschiedenen Web-Apps unterscheiden sich die reflektierten Änderungen, nachdem Sie den obigen Befehl ausgeführt haben (als Bonus-Tipp ganz unten in diesem Tutorial besprochen).

Schritt 4:Herunterskalieren auf das Single-Container-Setup wie zuvor

Für den letzten Schritt skalieren Sie noch einmal auf die Konfiguration mit einem einzelnen Container herunter:

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

Ich habe diese Methode mit Ghost-, WordPress-, Rocket.Chat- und Nextcloud-Instanzen getestet. Abgesehen davon, dass Nextcloud für einige Sekunden in den Wartungsmodus wechselt, funktioniert das Verfahren bei den anderen drei sehr gut.

Discourse ist jedoch eine andere Geschichte und kann in diesem Fall aufgrund seines hybriden Modells eine sehr knifflige Ausnahme darstellen.

Das Fazit lautet:Je mehr die Web-App beim Dockerisieren die Standard-Docker-Praxis verwendet, desto bequemer wird es, alle Web-App-Container täglich zu verwalten.

Real-Life-Beispiel:Aktualisieren einer Live-Ghost-Instanz ohne Ausfallzeit

Wie versprochen, werde ich Ihnen ein Beispiel aus dem wirklichen Leben zeigen. Ich werde Ihnen zeigen, wie Sie Ghost, das im Docker-Container ausgeführt wird, ohne Ausfallzeiten auf eine neuere Version aktualisieren können.

Ghost ist ein CMS und wir verwenden es für das Linux-Handbuch. Das hier gezeigte Beispiel verwenden wir, um unsere Ghost-Instanz zu aktualisieren, auf der diese Website ausgeführt wird.

Angenommen, ich habe eine vorhandene Konfiguration, die auf einer älteren Version basiert, die sich unter /home/avimanyu/ghost befindet :

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

Beachten Sie, dass die obige Docker-Compose-Konfiguration auf einer bereits vorhandenen, hier beschriebenen Nginx-Docker-Konfiguration basiert, die in einem Netzwerk namens net ausgeführt wird . Sein Docker-Volume wurde ebenfalls manuell mit docker volume create ghost-blog erstellt .

Wenn ich es mit docker ps überprüfe :

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

Zum jetzigen Zeitpunkt ist dies eine ältere Version von Ghost. Zeit, es auf die neueste Version 3.37.1 zu aktualisieren! Also überarbeite ich es im Bildabschnitt wie folgt:

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

Nun, um die Skalierungsmethode sinnvoll einzusetzen:

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

Mit dem obigen Befehl bleibt der ältere Container unberührt, aber ein neuer kommt mit derselben Konfiguration, aber basierend auf der neuesten Version von Ghost hinzu:

[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

Hätte ich den konventionellen Ansatz mit docker-compose up -d verwendet Stattdessen wäre ich nicht umhin gekommen, den bestehenden Container auf Basis des neuesten Ghost-Images neu zu erstellen.

Bei der Neuerstellung wird der ältere Container entfernt und an seiner Stelle ein neuer mit denselben Einstellungen erstellt. Dies ist der Fall, wenn Ausfallzeiten auftreten und die Site nicht mehr zugänglich ist.

Aus diesem Grund sollten Sie den --no-recreate verwenden Flag beim Hochskalieren.

Jetzt habe ich also zwei Container, die auf der Grundlage derselben Geisterkonfiguration ausgeführt werden. Dies ist der entscheidende Teil, in dem wir Ausfallzeiten vermeiden:

[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

Beachten Sie, dass der Name des älteren Containers ghost_ghost-blog_1 ist . Überprüfen Sie Ihren Domainnamen und Sie finden ihn unter blog.domain.com weiterhin erreichbar . Wenn Sie das Ghost-Admin-Panel unter blog.domain.com/ghost aktualisieren Nach dem Hochskalieren würde es versuchen, sich selbst zu laden, bis Sie den alten Container entfernen:

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

Aber für den Ghost-Blog selbst gibt es überhaupt keine Ausfallzeit! Auf diese Weise können Sie also sicherstellen, dass beim Aktualisieren von Ghost-Blogs keine Ausfallzeiten auftreten.

Verkleinern Sie schließlich die Konfiguration auf ihre ursprüngliche Einstellung:

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

Wie bereits erwähnt, spiegeln sich die Änderungen nach dem Entfernen alter Container in den jeweiligen Web-Apps wider, aber sie verhalten sich aufgrund unterschiedlicher App-Designs offensichtlich unterschiedlich.

Hier sind einige Beobachtungen:

Auf WordPress :Stellen Sie sicher, dass Sie define( 'AUTOMATIC_UPDATER_DISABLED', true ) hinzufügen; als letzte Zeile in der Datei wp-config.php unter /var/www/html und mounten Sie /var/www/html/wp-content anstelle von /var/www/html als Volume. Einzelheiten finden Sie hier. Nach Schritt 3 zeigt das WordPress-Admin-Panel an, dass Ihr WordPress auf dem neuesten Stand ist, und fordert Sie auf, fortzufahren und Ihre Datenbank zu aktualisieren. Das Update erfolgt schnell ohne Ausfallzeiten auf der WordPress-Seite und das war's!

Auf Rocket.Chat :Die Admin>Info kann ca. 15-20 Sekunden dauern Seite, um anzuzeigen, dass Sie die neueste Version verwenden, noch bevor Sie Schritt 3 ausführen. Keine Ausfallzeit mehr!

Auf Nextcloud :Nach Schritt 2 würde Nextcloud für einige Sekunden in den Wartungsmodus wechseln und dann Ihre Dateien erneut laden. Unter Administration> Übersicht> Sicherheits- und Einrichtungswarnungen , erhalten Sie möglicherweise eine Warnung wie „Ihr Webserver ist nicht richtig eingerichtet, um „./well-known/carddav“ aufzulösen“. Dies liegt daran, dass Ihr alter Container noch ausgeführt wird. Sobald Sie ihn mit Schritt 3 entfernen, würde diese Warnung angezeigt nicht mehr vorhanden. Stellen Sie sicher, dass Sie ihm etwas Zeit geben, bevor Sie auf Ihre Nextcloud-URL zugreifen, da diese einen 502 Bad Gateway-Fehler anzeigen könnte, bis Ihr Nginx-Container den neu skalierten Nextcloud-Container basierend auf der neuesten Version sieht.

Bonustipps

Hier sind ein paar Tipps und Dinge, die Sie bei dieser Methode beachten sollten.

Tipp 1

Stellen Sie sicher, dass Sie den neu skalierten und aktuellen Containern genügend Zeit geben, damit die Nginx-Container sie endlich bestätigen können, bevor Sie die alten in Schritt 2 entfernen, um die Ausfallzeit über verschiedene Anwendungen hinweg auf ein Minimum zu reduzieren.

Bevor Sie mit dem oben beschriebenen Schritt 2 fortfahren, ist es ratsam, das Verhalten Ihrer Anwendung in Ihrem Browser zu beobachten (sowohl als Seitenaktualisierung nach der Anmeldung als auch als neuer Seitenzugriff in einem privaten Browserfenster ohne Cache), nachdem Sie Schritt 1 ausgeführt haben.

Da jede Anwendung anders gestaltet ist, wäre diese Maßnahme sehr hilfreich, bevor Sie Ihre alten Container herunterfahren.

Tipp 2

Obwohl dies eine sehr einfallsreiche Methode sein kann, um Ihre Container auf die neuesten Versionen der Apps zu aktualisieren, die sie ausführen, beachten Sie, dass Sie dieselbe Methode auch verwenden können, um Konfigurationsänderungen vorzunehmen oder Umgebungseinstellungen zu ändern, ohne dass es zu Ausfallzeiten kommt.

Dies kann entscheidend sein, wenn Sie ein Problem beheben oder eine Änderung vornehmen müssen, die Sie möglicherweise in einem Live-Container für erforderlich halten, ihn dabei aber nicht herunterfahren möchten. Nachdem Sie Ihre Änderungen vorgenommen haben und sicher sind, dass das Problem behoben ist, können Sie das ältere problemlos herunterfahren.

Wir waren selbst damit konfrontiert, als wir feststellten, dass die Protokollrotation in einem unserer Live-Container nicht aktiviert war. Wir haben die notwendigen Änderungen vorgenommen, um es zu aktivieren, und gleichzeitig mit dieser Methode Ausfallzeiten vermieden.

Tipp 3

Wenn Sie Ihren Container in der YML-Datei speziell benannt haben, indem Sie container_name verwenden , funktioniert die Methode nicht wie erwartet, da sie die Erstellung eines neuen Containernamens beinhaltet.

Sie können diesen Konflikt vermeiden, indem Sie diese Aufgabe der Containerbenennung Docker Compose überlassen (wird automatisch gemäß seiner Namenskonvention ausgeführt). Docker Compose benennt seine Container als directory-name_service-name_1 . Die Zahl am Ende würde sich jedes Mal erhöhen, wenn der Container aktualisiert wird (bis Sie aus irgendeinem Grund docker-compose down verwenden).

Falls Sie bereits einen Dienst verwenden, indem Sie Container mit benutzerdefinierten Namen in Ihrer Docker-Compose-Datei verwenden, kommentieren Sie zur Verwendung dieser Methode einfach die Zeile (mit einem #-Präfix) aus, die container_name enthält innerhalb der Dienstdefinition.

Nachdem Sie den neuen Container für das obige mit Schritt 1 wie in diesem Tutorial beschrieben erstellt haben, wäre für Schritt 2 der Name des alten Containers (der nicht gestoppt wurde, um Ausfallzeiten zu vermeiden) so, wie er zuvor mit container_name (kann auch mit `docker ps überprüft werden ` bevor Sie den alten Container entfernen).

Haben Sie etwas Neues gelernt?

Ich weiß, dass dieser Artikel etwas lang ist, aber ich hoffe, er hilft unserer DevOps-Community bei der Bewältigung von Ausfallzeiten bei Produktionsservern, auf denen dockerisierte Webanwendungen ausgeführt werden. Ausfallzeiten haben sowohl auf individueller als auch auf geschäftlicher Ebene enorme Auswirkungen, weshalb die Behandlung dieses Themas eine absolute Notwendigkeit war.

Bitte nehmen Sie an der Diskussion teil und teilen Sie Ihre Gedanken, Rückmeldungen oder Vorschläge im Kommentarbereich unten mit.


Linux
  1. So installieren Sie WordPress mit Docker auf Ubuntu

  2. Installieren Sie ModSecurity mit Apache in einem Docker-Container

  3. Aktualisieren eines bereitgestellten Containers basierend auf einem Docker-Image

  4. Docker aktualisiert Änderungen im Verzeichnis nicht

  5. LXD-Container und Netzwerke mit statischer IP

So bearbeiten Sie Code in Docker-Containern mit Visual Studio-Code

So führen Sie Docker-Container aus

Arbeiten mit Docker-Containern über die Befehlszeile

So benennen oder umbenennen Sie Docker-Container

Gewusst wie:Erste Schritte mit Windows-Containern und Docker

So verwalten Sie Docker-Container