Zum Ausführen einer ASP.NET Core-Anwendung habe ich eine Docker-Datei generiert, die die Anwendung erstellt und den Quellcode in den Container kopiert, der von Git mit Jenkins abgerufen wird. In meinem Arbeitsbereich mache ich also Folgendes in der Docker-Datei:
WORKDIR /app
COPY src src
Während Jenkins die Dateien auf meinem Host korrekt mit Git aktualisiert, wendet Docker dies nicht auf mein Image an.
Mein grundlegendes Skript zum Erstellen:
#!/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
Ich habe verschiedene Dinge wie --rm
und --no-cache
Parameter für docker run
und auch das Stoppen / Entfernen des Containers ausprobiert, bevor der neue erstellt wird. Ich bin mir nicht sicher, was ich hier falsch mache. Es scheint, dass Docker das Image korrekt aktualisiert, da der Aufruf von COPY src src
zu einer Layer-ID und keinem Cache-Aufruf führen würde:
Step 6 : COPY src src
---> 382ef210d8fd
Was ist die empfohlene Methode zum Aktualisieren eines Containers?
Mein typisches Szenario wäre: Die Anwendung wird auf dem Server in einem Docker-Container ausgeführt. Jetzt werden Teile der App aktualisiert, z. B. durch Ändern einer Datei. Jetzt sollte der Container die neue Version ausführen. Docker scheint zu empfehlen, ein neues Image zu erstellen, anstatt einen vorhandenen Container zu ändern. Daher halte ich die allgemeine Art der Neuerstellung für richtig, aber einige Details in der Implementierung müssen verbessert werden.
Antworten:
Nach einigen Recherchen und Tests stellte ich fest, dass ich einige Missverständnisse über die Lebensdauer von Docker-Containern hatte. Ein einfacher Neustart eines Containers führt nicht dazu, dass Docker ein neues Image verwendet, wenn das Image in der Zwischenzeit neu erstellt wurde. Stattdessen ruft Docker das Bild erst ab, bevor der Container erstellt wird. Der Status nach dem Ausführen eines Containers bleibt also bestehen.
Warum ist das Entfernen erforderlich?
Daher reicht das Neuerstellen und Neustarten nicht aus. Ich dachte, Container funktionieren wie ein Dienst: Stoppen Sie den Dienst, nehmen Sie Ihre Änderungen vor, starten Sie ihn neu und sie würden angewendet. Das war mein größter Fehler.
Da Container permanent sind, müssen Sie sie zuerst mit entfernen
docker rm <ContainerName>
. Nachdem ein Container entfernt wurde, können Sie ihn nicht einfach startendocker start
. Dies muss mit erfolgendocker run
, das selbst das neueste Image zum Erstellen einer neuen Container-Instanz verwendet.Container sollten so unabhängig wie möglich sein
Mit diesem Wissen ist es verständlich, warum das Speichern von Daten in Containern als schlechte Praxis eingestuft wird, und Docker empfiehlt stattdessen Datenmengen / Hosting-Direktoren : Da ein Container zerstört werden muss, um Anwendungen zu aktualisieren, gehen auch die darin gespeicherten Daten verloren. Dies führt zu zusätzlicher Arbeit beim Herunterfahren von Diensten, Sichern von Daten usw.
Es ist also eine clevere Lösung, diese Daten vollständig aus dem Container auszuschließen: Wir müssen uns keine Sorgen um unsere Daten machen, wenn sie sicher auf dem Host gespeichert sind und der Container nur die Anwendung selbst enthält.
Warum
-rf
kann Ihnen nicht wirklich helfenDer
docker run
Befehl hat einen Bereinigungsschalter namens-rf
. Dadurch wird das Verhalten der dauerhaften Aufbewahrung von Docker-Containern gestoppt. Mit-rf
Docker wird der Container nach dem Verlassen zerstört. Dieser Schalter hat jedoch zwei Probleme:-d
switch im Hintergrund auszuführenWährend der
-rf
Switch eine gute Option ist, um Arbeit während der Entwicklung für schnelle Tests zu sparen, ist er in der Produktion weniger geeignet. Insbesondere wegen der fehlenden Option, einen Container im Hintergrund auszuführen, was meistens erforderlich wäre.So entfernen Sie einen Behälter
Wir können diese Einschränkungen umgehen, indem wir einfach den Container entfernen:
Der
--force
(oder-f
) Schalter, der SIGKILL zum Ausführen von Containern verwendet. Stattdessen können Sie den Container auch vorher anhalten:Beide sind gleich.
docker stop
verwendet auch SIGTERM . Die Verwendung von--force
switch verkürzt jedoch Ihr Skript, insbesondere bei Verwendung von CI-Servern:docker stop
Gibt einen Fehler aus, wenn der Container nicht ausgeführt wird. Dies würde dazu führen, dass Jenkins und viele andere CI-Server den Build fälschlicherweise als fehlgeschlagen betrachten. Um dies zu beheben, müssen Sie zuerst überprüfen, ob der Container wie in der Frage ausgeführt ausgeführt wird (siehecontainerRunning
Variable).Vollständiges Skript zum Wiederherstellen eines Docker-Containers
Nach diesem neuen Wissen habe ich mein Skript folgendermaßen repariert:
Das funktioniert perfekt :)
quelle
--force-recreate
Option für Docker Compose der hier beschriebenen? Und wenn ja, wäre es nicht wert, stattdessen diese Lösung zu verwenden (sorry, wenn diese Frage dumm ist, aber ich bin ein Docker Noob ^^)docker-compose
Ist aber schlauer als die einfachendocker
Befehle. Ich arbeite regelmäßig mitdocker-compose
und die Änderungserkennung funktioniert gut, daher verwende ich sie--force-recreate
sehr selten. Diesdocker-compose up --build
ist nur wichtig, wenn Sie ein benutzerdefiniertes Image (build
Direktive in Compose-Datei) erstellen, anstatt ein Image vom z. B. Docker-Hub zu verwenden.Wenn Änderungen in Dockerfile oder Compose oder Anforderungen vorgenommen werden, führen Sie sie erneut mit aus
docker-compose up --build
. Damit Bilder neu erstellt und aktualisiert werdenquelle
/opt/mysql/data:/var/lib/mysql
?--build
in lokalen Entwicklungsumgebungen zu arbeiten. Die Geschwindigkeit, mit der Docker die Dateien erneut kopiert, von denen sonst angenommen wird, dass sie nicht kopiert werden müssen, dauert nur ein paar Millisekunden und spart eine große Anzahl von WTF-Momenten.Sie können
build
für einen bestimmten Dienst ausgeführt werden, indem Sie dort ausführen ,docker-compose up --build <service name>
wo der Dienstname mit dem Aufruf in Ihrer Docker-Compose-Datei übereinstimmen muss.Beispiel Nehmen wir an, dass Ihre Docker-Compose-Datei viele Dienste enthält (.net-App - Datenbank - Verschlüsseln ... usw.) und Sie nur die .net-App aktualisieren möchten, die wie
application
in der Docker-Compose-Datei benannt ist. Sie können dann einfach laufendocker-compose up --build application
Zusätzliche Parameter Wenn Sie Ihrem Befehl zusätzliche Parameter hinzufügen möchten, z. B.
-d
zum Ausführen im Hintergrund, muss der Parameter vor dem Dienstnamen stehen:docker-compose up --build -d application
quelle