So aktualisieren Sie den Docker-Container, nachdem sich das Image geändert hat

518

Angenommen , ich habe das offizielle mysql: 5.6.21-Bild gezogen .

Ich habe dieses Image bereitgestellt, indem ich mehrere Docker-Container erstellt habe.

Diese Container werden seit einiger Zeit ausgeführt, bis MySQL 5.6.22 veröffentlicht wird. Das offizielle Image von mysql: 5.6 wird mit der neuen Version aktualisiert, aber meine Container laufen immer noch mit 5.6.21.

Wie übertrage ich die Änderungen im Image (dh Upgrade der MySQL-Distribution) auf alle meine vorhandenen Container? Was ist der richtige Docker-Weg, um dies zu tun?

Jaroslaw Stavnichiy
quelle

Antworten:

578

Nachdem ich die Antworten ausgewertet und das Thema studiert habe, möchte ich es zusammenfassen.

Die Docker-Methode zum Aktualisieren von Containern scheint folgende zu sein:

Anwendungscontainer sollten keine Anwendungsdaten speichern . Auf diese Weise können Sie den App-Container jederzeit durch die neuere Version ersetzen, indem Sie Folgendes ausführen:

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

Sie können Daten entweder auf dem Host (in einem als Volume bereitgestellten Verzeichnis) oder in speziellen Nur-Daten-Containern speichern . Lesen Sie mehr darüber

Das Aktualisieren von Anwendungen (z. B. mit yum / apt-get-Upgrade) in Containern wird als Anti-Pattern angesehen . Anwendungsbehälter sollen unveränderlich sein , was ein reproduzierbares Verhalten gewährleisten soll. Einige offizielle Anwendungsbilder (insbesondere MySQL: 5.6) sind nicht einmal für die Selbstaktualisierung ausgelegt (apt-get-Upgrade funktioniert nicht).

Ich möchte mich bei allen bedanken, die ihre Antworten gegeben haben, damit wir alle unterschiedlichen Ansätze sehen können.

Jaroslaw Stavnichiy
quelle
31
Was ist, wenn eine Datenmigration erforderlich ist? Der neue Server kann die Daten nicht bereitstellen, da sie in einem alten Format vorliegen. Er muss wissen, dass eine Migration stattfindet, und die Datendarstellung ändern.
Dor Rotman
12
Ich denke, Bilddesigner sollten dies berücksichtigen und das Starten von benutzerdefinierten Befehlen (z. B. Datenmigration) beim ersten Ausführen des Containers ermöglichen.
Jaroslaw Stavnichiy
2
Ein relevanter Artikel: intercityup.com/blog/running-upgrading-mysql-server-docker
Dor Rotman
4
@static_rtti Wie wäre es, docker rename my-mysql-container trash-containerbevor Sie das neue erstellen?
Franklin Yu
4
Würde es einen All-in-One-Befehl geben, um den Container zu aktualisieren, ohne ihn manuell stoppen, entfernen und erneut erstellen zu müssen (basierend auf dem neuen Image, das gezogen wurde)?
Michaël Perrin
83

Ich mag es nicht, Volumes als Link zu einem Host-Verzeichnis bereitzustellen, daher habe ich ein Muster für das Upgrade von Docker-Containern mit vollständig von Docker verwalteten Containern entwickelt. Wenn Sie einen neuen Docker-Container mit --volumes-from <container>erstellen, erhält der neue Container mit den aktualisierten Images den gemeinsamen Besitz von Docker-verwalteten Volumes.

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

Wenn Sie das Original my_mysql_containernoch nicht sofort entfernen , können Sie zum bekannten Arbeitscontainer zurückkehren, wenn der aktualisierte Container nicht über die richtigen Daten verfügt oder einen Integritätstest nicht besteht.

An diesem Punkt führe ich normalerweise alle Sicherungsskripte aus, die ich für den Container habe, um mir ein Sicherheitsnetz zu geben, falls etwas schief geht

docker stop my_mysql_container
docker start my_mysql_container_tmp

Jetzt haben Sie die Möglichkeit, sicherzustellen, dass die Daten, die sich voraussichtlich im neuen Container befinden, vorhanden sind, und eine Überprüfung der Integrität durchzuführen.

docker rm my_mysql_container
docker rename my_mysql_container_tmp my_mysql_container

Die Docker-Volumes bleiben so lange erhalten, wie sie von einem Container verwendet werden, sodass Sie den ursprünglichen Container sicher löschen können. Sobald der ursprüngliche Container entfernt wurde, kann der neue Container den Namensvetter des Originals annehmen, um alles so hübsch zu machen, wie es ursprünglich war.

Die Verwendung dieses Musters für die Aktualisierung von Docker-Containern bietet zwei wesentliche Vorteile. Erstens entfällt die Notwendigkeit, Volumes in Hostverzeichnisse zu mounten, da Volumes direkt in aktualisierte Container übertragen werden können. Zweitens sind Sie nie in einer Position, in der es keinen funktionierenden Docker-Container gibt. Wenn das Upgrade fehlschlägt, können Sie problemlos zu der vorherigen Funktionsweise zurückkehren, indem Sie den ursprünglichen Docker-Container erneut hochfahren.

kMaiSmith
quelle
3
Warum mounten Sie Host-Volumes nicht gerne in einem Docker-Container? (Ich mache genau das, also bin ich an Argumenten dagegen interessiert: -) Ich habe zB ./postgres-data/:/var/lib/postgres/data./postgres-data/
Folgendes
4
@KajMagnus Ich benutze häufig Docker-Schwärme und schreibe gerne meine Container, um in einem Schwarm gut zu funktionieren. Wenn ich einen Container in einem Schwarm hochfahre, habe ich keine Ahnung, auf welchem ​​Schwarmknoten der Container leben wird, daher kann ich mich nicht auf den Hostpfad verlassen, der die gewünschten Daten enthält. Da Docker 1.9-Volumes (glaube ich) von mehreren Hosts gemeinsam genutzt werden können, ist das Aktualisieren und Migrieren von Containern mit der von mir beschriebenen Methode ein Kinderspiel. Eine Alternative wäre, sicherzustellen, dass auf allen Schwarmknoten ein gewisses Netzwerkvolumen bereitgestellt wird, aber das klingt nach einem großen Aufwand.
kMaiSmith
Vielen Dank! Ok, das Mounten von Host-Volumes scheint etwas zu sein, das ich jetzt ebenfalls vermeiden möchte. Zumindest etwas später, wenn meine App populär wird und auf mehr als einen Server
skaliert
32

Nur um eine allgemeinere (nicht MySQL-spezifische) Antwort zu geben ...

  1. Zusamenfassend

Mit der Service-Image-Registrierung synchronisieren ( https://docs.docker.com/compose/compose-file/#image ):

docker-compose pull 

Erstellen Sie den Container neu, wenn sich die Docker-Compose-Datei oder das Docker-Image geändert hat:

docker-compose up -d
  1. Hintergrund

Die Verwaltung von Container-Images ist einer der Gründe für die Verwendung von Docker-Compose (siehe https://docs.docker.com/compose/reference/up/ ).

Wenn für einen Dienst bereits Container vorhanden sind und die Konfiguration oder das Image des Dienstes nach der Erstellung des Containers geändert wurde, übernimmt Docker-Compose die Änderungen, indem die Container gestoppt und neu erstellt werden (unter Beibehaltung der bereitgestellten Volumes). Verwenden Sie das Flag --no-recreate, um zu verhindern, dass Compose Änderungen aufnimmt.

Der Datenverwaltungsaspekt wird auch durch Docker-Compose über bereitgestellte externe "Volumes" (siehe https://docs.docker.com/compose/compose-file/#volumes ) oder Datencontainer abgedeckt .

Dies lässt potenzielle Abwärtskompatibilitäts- und Datenmigrationsprobleme unberührt, aber dies sind "anwendungsbezogene" Probleme, die nicht Docker-spezifisch sind und anhand von Versionshinweisen und Tests überprüft werden müssen ...

Ronan Fauglas
quelle
Wie geht das mit der Versionierung? Beispiel: Das neue Bild ist foo / image: 2 und docker-compose.yml hat image: foo / image: 1?
Dman
VIELEN DANK. Beste Antwort!
Mick
Dies ist sicherlich der richtige Weg, aber man sollte sich bewusst sein, dass alle im Container vorgenommenen Änderungen nach der Neuerstellung des Containers immer noch verloren gehen. Daher ist es weiterhin erforderlich, Containerwechsel nur innerhalb der bereitgestellten Volumes zu halten.
Petr Bodnár
23

Ich möchte hinzufügen, dass Sie WatchTower verwenden können, wenn Sie diesen Vorgang automatisch ausführen möchten (Herunterladen, Stoppen und Neustarten eines neuen Containers mit denselben Einstellungen wie von @Yaroslav beschrieben). Ein Programm, das Ihre Container automatisch aktualisiert, wenn sie geändert werden. Https://github.com/v2tec/watchtower

Ricardo Polo Jaramillo
quelle
20

Beachten Sie für diese Antworten:

  • Der Datenbankname lautet app_schema
  • Der Containername lautet app_db
  • Das Root-Passwort lautet root123

So aktualisieren Sie MySQL beim Speichern von Anwendungsdaten im Container

Dies wird als schlechte Vorgehensweise angesehen , da Sie die Daten verlieren, wenn Sie den Container verlieren. Obwohl es eine schlechte Praxis ist, gibt es hier eine Möglichkeit, dies zu tun:

1) Führen Sie einen Datenbankspeicherauszug als SQL durch:

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

2) Aktualisieren Sie das Bild:

docker pull mysql:5.6

3) Aktualisieren Sie den Container:

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

4) Stellen Sie den Datenbankspeicherauszug wieder her:

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

So aktualisieren Sie den MySQL-Container mithilfe eines externen Volumes

Die Verwendung eines externen Volumes ist eine bessere Methode zum Verwalten von Daten und erleichtert das Aktualisieren von MySQL. Durch das Verlieren des Containers gehen keine Daten verloren. Mit Docker-Compose können Sie Docker-Anwendungen mit mehreren Containern auf einem einzigen Host verwalten:

1) Erstellen Sie die docker-compose.ymlDatei, um Ihre Anwendungen zu verwalten:

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) Aktualisieren Sie MySQL (aus demselben Ordner wie die docker-compose.ymlDatei):

docker-compose pull
docker-compose up -d

Hinweis: Der letzte Befehl oben aktualisiert das MySQL-Image, erstellt den Container neu und startet ihn mit dem neuen Image.

Alexandre V.
quelle
Angenommen, ich habe eine riesige Datenbank (mehrere GB). Sind meine Daten erst verfügbar, wenn die gesamte Datenbank importiert wurde? Das könnte eine große "Ausfallzeit" sein
hellimac
docker-composeWird das funktionieren, seit Sie es erwähnt haben? stackoverflow.com/a/31485685/65313
sivabudh
1
volumes_fromDer Schlüssel ist jetzt veraltet (sogar in Version 3 der Erstellungsdatei entfernt) und wird zugunsten des neuen volumesSchlüssels verwendet.
Franklin Yu
docker pull image_uri:tag && docker restart container_running_that_imagearbeitete für mich. Keine Notwendigkeit für docker-compose pull && docker-compose up -d.
Yuriy Pozniak
16

Ähnliche Antwort wie oben

docker images | awk '{print $1}' | grep -v 'none' | grep -iv 'repo' | xargs -n1 docker pull
Eddie Jaoude
quelle
1
Brillant! Sehr überrascht, dass es nicht mehr Stimmen gab. Jetzt fehlt nur noch ein Neustart aller aktualisierten Container.
Sorin
7
Leider wird der vorhandene Container dadurch nicht aktualisiert. Dadurch wird nur das gezogene Bild aktualisiert, aber der vorhandene Container ist unveränderlich und verwendet weiterhin das Originalbild, mit dem es erstellt wurde. Dies funktioniert nur, wenn Sie aus dem Bild einen neuen Container erstellen, der vorhandene Container jedoch weiterhin auf dem Originalbild basiert.
Eric B.
Tolle. Wenn Sie die spezifische Version des Containers abrufen müssen, gehen Sie folgendermaßen vor: Docker-Images | awk '{print $ 1 ":" $ 2}' | grep -v 'none' | grep -iv 'repo' | Xargs -n1 Docker ziehen
Rogervila
11

So sieht es docker-composebeim Erstellen eines benutzerdefinierten Objekts aus Dockerfile.

  1. Erstellen Sie zuerst Ihre benutzerdefinierte Docker-Datei und fügen Sie zur Unterscheidung eine nächste Versionsnummer hinzu. Ex:docker build -t imagename:version . Dadurch wird Ihre neue Version lokal gespeichert.
  2. Lauf docker-compose down
  3. Bearbeiten Sie Ihre docker-compose.yml Datei so, dass sie den neuen Bildnamen widerspiegelt, den Sie in Schritt 1 festgelegt haben.
  4. Ausführen docker-compose up -d. Es wird lokal nach dem Bild suchen und Ihr aktualisiertes verwenden.

-BEARBEITEN-

Meine obigen Schritte sind ausführlicher als sie sein müssen. Ich habe meinen Workflow optimiert, indem ich den build: .Parameter in meine Docker-Compose-Datei aufgenommen habe. Die Schritte sehen jetzt so aus:

  1. Stellen Sie sicher, dass meine Docker-Datei so ist, wie sie aussehen soll.
  2. Legen Sie die Versionsnummer meines Bildnamens in meiner Docker-Compose-Datei fest.
  3. Wenn mein Image noch nicht erstellt wurde: Ausführen docker-compose build
  4. Lauf docker-compose up -d

Ich wusste es damals noch nicht, aber Docker-Compose ist klug genug, um meinen Container einfach mit dem einen Befehl auf das neue Image zu aktualisieren, anstatt ihn zuerst herunterfahren zu müssen.

gdbj
quelle
In einer realen Situation können Sie Ihre eigenen Hände nicht benutzen und diese Änderungen vornehmen. Ihre Lösung unterstützt keine automatischen Methoden zur Lösung des Problems.
Carlos Vázquez Losada
7
Sie sagen also, dass meine Lösung nicht gültig ist, weil sie nicht automatisiert ist? Ist das eine Anforderung des OP? Und implizieren die anderen Antworten Automatisierung? Wirklich verwirrt. Und ich denke, die Abstimmungen tun anderen, die hierher kommen, einen schlechten Dienst. Meine Antwort ist 100% gültig für die gestellte Frage.
GDBJ
danke für diese antwort, ich wusste auch nicht, dass du einfach rennen kannst, docker-compose up -dohne zuerst alles stoppen zu müssen.
Radicand
4

Wenn Sie Docker Compose nicht verwenden möchten, kann ich portainer empfehlen . Es verfügt über eine Neuerstellungsfunktion, mit der Sie einen Container neu erstellen können, während Sie das neueste Bild abrufen.

berühmt
quelle
2

Sie müssen entweder alle Images neu erstellen und alle Container neu starten oder die Software irgendwie aktualisieren und die Datenbank neu starten. Es gibt keinen Upgrade-Pfad, den Sie jedoch selbst entwerfen.

seanmcl
quelle
Was genau meinen Sie mit einem Neustart der Container? Es gibt einen docker restartBefehl, aber ich bin nicht sicher, ob er Bildänderungen aufnimmt. Und was passiert mit meinen Daten in Containern?
Jaroslaw Stavnichiy
1
Entschuldigung, ich wollte nicht Docker neu starten. Ich meine Docker rm -f CONTANER; Docker führen NEW_IMAGE aus. Die Daten in Ihrem SQL-Container verschwinden. Aus diesem Grund verwenden Benutzer im Allgemeinen Volumes, um die Daten zu speichern.
Seanmcl
Wenn Sie alle Ihre Daten in Volumes in separaten Containern oder auf einem Host-Computer bereitgestellt haben, sagte nas @seanmcl, erstellen Sie einfach neue Container mit neuem MySQL, das mit denselben Daten verbunden ist. Wenn Sie dies nicht getan haben (sollten Sie es tun), können Sie den in Docker 1.3 verfügbaren Befehl docker exec verwenden, um MySQL zu aktualisieren und im Container neu zu starten.
Usman Ismail
2

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

Sie können alle vorhandenen Bilder mit der folgenden Befehlspipeline aktualisieren:

docker images | awk '/^REPOSITORY|\<none\>/ {next} {print $1}' | xargs -n 1 docker pull
gvlx
quelle
6
Dadurch werden die Bilder aktualisiert, nicht jedoch der Container. Der Container ist unveränderlich und sein Basisimage kann nicht geändert werden, ohne aus einem aktualisierten Image einen neuen Container zu erstellen.
Eric B.
2

Stellen Sie sicher, dass Sie Volumes für alle persistenten Daten (Konfiguration, Protokolle oder Anwendungsdaten) verwenden, die Sie in den Containern speichern, bezogen auf den Status der Prozesse in diesem Container. Aktualisieren Sie Ihre Docker-Datei, erstellen Sie das Image mit den gewünschten Änderungen neu und starten Sie die Container neu, wobei Ihre Volumes an der entsprechenden Stelle bereitgestellt werden.

Daniel Dinnyes
quelle
1

Damit habe ich auch um meine eigenen Bilder gekämpft. Ich habe eine Serverumgebung, aus der ich ein Docker-Image erstelle. Wenn ich den Server aktualisiere, möchte ich, dass alle Benutzer, die Container basierend auf meinem Docker-Image ausführen, auf den neuesten Server aktualisieren können.

Im Idealfall würde ich es vorziehen, eine neue Version des Docker-Images zu generieren und alle Container, die auf einer früheren Version dieses Images basieren, automatisch auf das neue Image "an Ort und Stelle" zu aktualisieren. Dieser Mechanismus scheint jedoch nicht zu existieren.

Das nächstbeste Design, das ich bisher entwickeln konnte, ist die Bereitstellung einer Möglichkeit, den Container selbst zu aktualisieren - ähnlich wie eine Desktop-Anwendung nach Updates sucht und sich dann selbst aktualisiert. In meinem Fall bedeutet dies wahrscheinlich, dass ein Skript erstellt wird, bei dem Git-Pulls von einem bekannten Tag stammen.

Das Bild / der Container ändert sich nicht wirklich, aber die "Interna" dieses Containers ändern sich. Sie können sich vorstellen, dasselbe mit apt-get, yum oder was auch immer für Ihre Umgebung angemessen ist. Zusammen mit diesem würde ich das myserver: neueste Image in der Registrierung aktualisieren, sodass alle neuen Container auf dem neuesten Image basieren würden.

Es würde mich interessieren, ob es einen Stand der Technik gibt, der sich mit diesem Szenario befasst.

bjlevine
quelle
7
Es widerspricht dem Konzept der unveränderlichen Infrastruktur und einigen ihrer Vorteile. Sie können Ihre Anwendung / Umgebung testen, um festzustellen, ob sie funktioniert, und es kann nicht garantiert werden, wenn Sie Komponenten im Inneren aktualisieren. Durch das Aufteilen von Containercode von Daten aus der Konfiguration können Sie aktualisieren, testen, ob wir gerade arbeiten, und in der Produktion bereitstellen, da Sie wissen, dass es keine andere Codezeile vom getesteten Image zum Produktionsimage gibt. Wie auch immer, das System lässt Sie es verwalten, wie Sie auch sagen, es ist Ihre Wahl.
Gmuslera
Sehr guter Punkt gmuslera. Einverstanden, dass es ein Anti-Pattern ist, die 'Interna' eines vorhandenen Docker-Containers zu aktualisieren.
Bjlevine
Was ist also die beste Lösung, um den Docker-Container basierend auf Docker-Image-Updates automatisch zu aktualisieren und die Updates mühelos an alle Container zu liefern?
Tarek Salem
1

Aktualisieren

Dies dient hauptsächlich dazu, den Container abzufragen, der nicht aktualisiert werden soll, da das Erstellen von Images der Weg ist

Ich hatte das gleiche Problem, also habe ich Docker-Run erstellt , ein sehr einfaches Befehlszeilentool, das in einem Docker-Container ausgeführt wird , um Pakete in anderen laufenden Containern zu aktualisieren.

Es verwendet Docker-Py , um mit laufenden Docker-Containern zu kommunizieren und Pakete zu aktualisieren oder einen beliebigen einzelnen Befehl auszuführen

Beispiele:

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

Standardmäßig wird der dateBefehl in allen laufenden Containern ausgeführt und es werden Ergebnisse zurückgegeben. Sie können jedoch einen beliebigen Befehl ausgeben, zdocker-run exec "uname -a"

So aktualisieren Sie Pakete (derzeit nur mit apt-get):

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

Sie können einen Alias ​​erstellen und als reguläre Befehlszeile verwenden, z

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

iTech
quelle
Ist das eine gute Idee? (Wenn Sie dies tun apt update; apt upgrade, wird das Bild wachsen.)
Strg-Alt-Delor
@yaroslavs Bild ist eine bessere Lösung für dieses Problem. Das Obige ist nicht wirklich die Docker-Methode, um Dinge zu tun.
Joost van der Laan