Docker Swarm rolling updates
Docker Swarm rolling updates zijn een zeer eenvoudige manier om updates uit te voeren zonder enige downtime
Enige tijd geleden schreef ik dat het het beste zou zijn om over te stappen op een Kubernetes variant en nu gaat deze post over Docker Swarm. Ja, ik gebruik nog steeds Docker Swarm omdat ik een project heb dat deze variant gebruikt. Onlangs heb ik de ontwikkeling verplaatst van Docker naar Docker Swarm, voornamelijk omdat je met Docker Swarm de basis van container orkestratie leert, dus waarom zou je dit niet leren tijdens de ontwikkeling.
In deze post kijken we naar rolling updates: een omgevingsvariabele en een image. Ik ga ervan uit dat je al enige praktische ervaring hebt met Docker Swarm. Zoals altijd doe ik dit op Ubuntu 22.04.
Ons Docker-Compose project
We maken eerst een Docker-Compose project. De projectboom:
.
├── docker-compose.yml
├── .env
├── app
│ └── run.sh
De bestanden:
# file: .env
COMPOSE_PROJECT_NAME=my-project
LOGGER_LEVEL=DEBUG
We gebruiken de busybox image en beginnen met twee replica's. We maken ook enkele Docker Swarm specifieke variabelen beschikbaar:
- X_SERVICE_LABEL_STACK_IMAGE: informatie over de image.
- X_TASK_SLOT: het nummer van de (taak)instantie.
# file: docker-compose.yml
version: "3.7"
x-service_defaults: &service_defaults
env_file:
- ./.env
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
services:
busybox:
<< : *service_defaults
deploy:
mode: replicated
replicas: 2
restart_policy:
condition: on-failure
image: busybox:1.35.0
environment:
# swarm info
X_COMPOSE_PROJECT_NAME: "${COMPOSE_PROJECT_NAME}"
X_SERVICE_LABEL_STACK_IMAGE: '{{index .Service.Labels "com.docker.stack.image"}}'
X_TASK_SLOT: "{{.Task.Slot}}"
ports:
- "127.0.0.1:8280:8280"
volumes:
- "./app:/app"
command: /bin/sh /app/run.sh
networks:
- my-project-network
networks:
my-project-network:
external: true
name: my-project-network
We gebruiken een script 'run.sh', dat wordt aangeroepen wanneer de container start, en dat twee dingen doet:
- Het start een httpd server op de achtergrond.
- Het genereert logregels in een lus, afgedrukt naar stdout.
# file: app/run.sh
echo "Starting httpd server instance ${X_TASK_SLOT} ..."
echo "Hello from httpd server instance ${X_TASK_SLOT}" > /var/www/index.html
/bin/httpd -f -p 8280 -h /var/www/ &
echo "Starting output ..."
while true; do echo "IMAGE: ${X_SERVICE_LABEL_STACK_IMAGE}, LOGGER_LEVEL = ${LOGGER_LEVEL}"; sleep 1; done
Met Docker Swarm maken we meestal geen netwerken in de 'docker-compose.yml' maar gebruiken we externe netwerken, meer bepaald 'overlay' netwerken. Bij het maken van zo'n netwerk kunnen we ook een vlag opgeven waarmee niet-Docker Swarm beheerde containers verbinding kunnen maken met dit netwerk.
Om het netwerk aan te maken:
docker network create -d overlay --attachable my-project-network
Om dit netwerk te zien:
docker network ls
Resultaat:
NETWORK ID NAME DRIVER SCOPE
...
qn7qwhpsooty my-project-network overlay swarm
...
Enkele Docker Swarm commando's
Een opmerking over de commando's. Vaak gebruiken we '--detach=false'. Dit betekent dat de opdracht niet onmiddellijk terugkomt, maar pas na voltooiing. In de tussentijd wordt nuttige informatie getoond in de terminal.
Laten we ons project ophalen, de lelijke constructie wordt gebruikt om de omgevingsvariabelen door te geven:
env $(cat .env | grep ^[A-Z] | xargs) docker stack deploy --detach=false -c docker-compose.yml my-project
Result:
WARN[0000] ignoring IP-address (127.0.0.1:8280:8280/tcp) service will listen on '0.0.0.0'
Creating service my-project_busybox
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service vflo2g4fiybtx0p9b596uk445 converged
Let op de waarschuwing hier. Dit is onverwacht en anders dan Docker en betekent dat we met Docker Swarm een open poort creëren, wees voorzichtig!
Om ons project te verwijderen, kunnen we
docker stack rm --detach=false my-project
Om de stack services te tonen:
docker stack services my-project
Resultaat:
ID NAME MODE REPLICAS IMAGE PORTS
2oz3yg39zuvx my-project_busybox replicated 2/2 busybox:1.35.0 *:8280->8280/tcp
Om de taken van de service 'my-project_busybox' te tonen:
docker service ps my-project_busybox
Resultaat:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
v1pc5p8yb2fx my-project_busybox.1 busybox:1.35.0 myra Running Running about a minute ago
6ozvx31c6isq my-project_busybox.2 busybox:1.35.0 myra Running Running about a minute ago
Bekijk de logs, voor elke taak, elke seconde een nieuwe logregel:
docker service logs -t -f my-project_busybox
Resultaat:
...
2024-07-07T15:42:20.805354434Z my-project_busybox.2.6ozvx31c6isq@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T15:42:21.808005147Z my-project_busybox.2.6ozvx31c6isq@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T15:42:22.807919531Z my-project_busybox.1.v1pc5p8yb2fx@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T15:42:22.809102067Z my-project_busybox.2.6ozvx31c6isq@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T15:42:23.808999822Z my-project_busybox.1.v1pc5p8yb2fx@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T15:42:23.809973729Z my-project_busybox.2.6ozvx31c6isq@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
In de log zien we dat beide taken draaien.
Om httpd server te controleren, draaien we op onze host:
curl 127.0.0.1:8280
Resultaat:
Hello from httpd server instance 1
Als we dit een paar keer herhalen:
cmd="curl 127.0.0.1:8280"; for i in $(seq 1000); do $cmd; sleep 0.5; done
Resultaat :
...
Hello from httpd server instance 2
Hello from httpd server instance 1
Hello from httpd server instance 2
Hello from httpd server instance 1
Hier zien we dat de Docker Swarm load balancer verzoeken afwisselt naar beide instanties.
Laten we tot slot de service inspecteren:
docker service inspect --pretty my-project_busybox
Resultaat:
ID: 2oz3yg39zuvxyl2k4hc77qsic
Name: my-project_busybox
Labels:
com.docker.stack.image=busybox:1.35.0
com.docker.stack.namespace=my-project
Service Mode: Replicated
Replicas: 2
Placement:
UpdateConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Update order: stop-first
RollbackConfig:
Parallelism: 1
On failure: pause
Monitoring Period: 5s
Max failure ratio: 0
Rollback order: stop-first
ContainerSpec:
...
Let op de parameter 'UpdateConfig' 'Parallelism' . De waarde 1 betekent dat het updateproces eerst een enkele taak zal bijwerken en zodra deze update is voltooid een volgende taak zal bijwerken. Dezelfde parameter is ook aanwezig in 'RollbackConfig'.
Schalen door replicas toe te voegen
Tot nu toe heeft onze service twee replica's. Als we een rolling update uitvoeren met slechts één taak aanwezig, dan zal onze dienst tijdelijk niet beschikbaar zijn. Dat is niet wat we willen. Met twee taken kan Docker Swarm één taak bijwerken en zodra die taak is bijgewerkt, kan het de tweede taak bijwerken. Dit betekent dat onze service de hele tijd beschikbaar blijft.
Om meer replicas toe te voegen, bijvoorbeeld 3:
docker service scale my-project_busybox=3
rolling updates controleren
Om te controleren of onze service is bijgewerkt, kunnen we het servicelogboek controleren. Dit laat zien welke taken worden uitgevoerd en wanneer een taak opnieuw is gestart.
docker service logs -t -f my-project_busybox
Om te controleren of onze service niet wordt onderbroken tijdens het updateproces, kunnen we de httpd server controleren in een aparte terminal, in een "eindeloze" lus zoals eerder genoemd. We zouden geen onderbrekingen moeten zien:
cmd="curl 127.0.0.1:8280"; for i in $(seq 1000); do $cmd; sleep 0.5; done
Rolling updates en rollbacks
Waarom noemen we het een 'Rolling update'? Omdat we een update-instructie met nieuwe gegevens doorgeven aan Docker Swarm en deze vragen de update uit te voeren.
Hieronder staan twee scenario's voor het bijwerken van services:
- Een omgevingsvariabele van de service bijwerken
- De afbeelding van de service bijwerken
De updateopdracht is:
docker service update <parameters> my-project_busybox
Het type van de update wordt gespecificeerd door de parameters.
Omdat een update kan mislukken, willen we terug kunnen gaan naar de vorige versie. Het rollback commando in beide gevallen is:
docker service rollback my-project_busybox
1. Rolling update: Omgevingsvariabele
Hier wijzigen we de 'LOGGER_LEVEL' van onze applicatie. De 'LOGGER_LEVEL' wordt in eerste instantie geladen vanuit het '.env bestand' en heeft de waarde 'DEBUG'. We veranderen het in 'WARNING' met het volgende update-commando:
docker service update --env-add LOGGER_LEVEL=WARNING my-project_busybox
Resultaat:
my-project_busybox
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service my-project_busybox converged
Het servicelogboek toont het volgende tijdens de update:
...
2024-07-07T16:35:36.177525585Z my-project_busybox.1.5jwup67xwmbc@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T16:35:37.178678148Z my-project_busybox.1.5jwup67xwmbc@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T16:35:37.528564504Z my-project_busybox.2.yna9ftex6bau@myra | Starting httpd server instance 2 ...
2024-07-07T16:35:37.528847322Z my-project_busybox.2.yna9ftex6bau@myra | Starting output ...
2024-07-07T16:35:37.529281987Z my-project_busybox.2.yna9ftex6bau@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = WARNING
2024-07-07T16:35:38.180094076Z my-project_busybox.1.5jwup67xwmbc@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T16:35:49.542707071Z my-project_busybox.2.yna9ftex6bau@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = WARNING
2024-07-07T16:35:50.194103057Z my-project_busybox.1.5jwup67xwmbc@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = DEBUG
2024-07-07T16:35:52.132182215Z my-project_busybox.1.qrqsbiowltle@myra | Starting httpd server instance 1 ...
2024-07-07T16:35:52.132401060Z my-project_busybox.1.qrqsbiowltle@myra | Starting output ...
2024-07-07T16:35:52.132788443Z my-project_busybox.1.qrqsbiowltle@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = WARNING
2024-07-07T16:35:52.546112046Z my-project_busybox.2.yna9ftex6bau@myra | IMAGE: busybox:1.35.0, LOGGER_LEVEL = WARNING
Laten we nu terugdraaien:
docker service rollback my-project_busybox
Resultaat:
my-project_busybox
rollback: manually requested rollback
overall progress: rolling back update: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service my-project_busybox converged
Na de rollback-bewerking is de 'LOG_LEVEL' terug op 'DEBUG', controleer het servicelogboek.
2. Rolling update: Afbeelding
In een ander scenario hebben we een nieuwe image voor onze applicatie. Hier gaan we van busybox:1.35.0 naar busybox:1.36.0. Het update commando is:
docker service update --image busybox:1.36.0 my-project_busybox
En, nogmaals, het rollback commando is:
docker service rollback my-project_busybox
Wat als er iets misgaat tijdens de update?
Laten we een fout maken en updaten met een niet-bestaande image:
docker service update --image busybox:9.99.0 my-project_busybox
Resultaat:
image busybox:9.99.0 could not be accessed on a registry to record
its digest. Each node will access busybox:9.99.0 independently,
possibly leading to different nodes running different
versions of the image.
my-project_busybox
overall progress: 0 out of 2 tasks
1/2: preparing [=================================> ]
2/2:
service update paused: update paused due to failure or early termination of task n371geu35a4u5xe9oclefv5j9
Wanneer de update van een taak mislukt, wordt het updateproces beëindigd. De andere taak blijft draaien, wat betekent dat onze service nog steeds beschikbaar is. We zien dit ook door de service te inspecteren:
docker service inspect --pretty my-project_busybox
Resultaat:
ID: k4a0vy77wirk1fglso42qxx38
Name: my-project_busybox
Labels:
com.docker.stack.image=busybox:1.35.0
com.docker.stack.namespace=my-project
Service Mode: Replicated
Replicas: 2
UpdateStatus:
State: paused
Started: 2 minutes ago
Message: update paused due to failure or early termination of task n371geu35a4u5xe9oclefv5j9
Placement:
...
Zoals eerder vermeld, kunnen we teruggaan naar de toestand vóór de update door een rollback uit te voeren:
docker service rollback my-project_busybox
De service wordt bijgewerkt met 'docker stack deploy' en 'docker-compose.yml'.
Nu worden de dingen een beetje lelijk. Tot nu toe hebben we onze services bijgewerkt met 'docker service update', en we konden teruggaan naar een vorige versie door 'docker service rollback' uit te voeren.
Maar we gebruiken hier een 'docker-compose.yml' bestand. Het lijkt mogelijk om het bestand 'docker-compose.yml' te wijzigen en opnieuw te implementeren.
Laten we eens kijken wat er gebeurt. Eerst wijzigen we de omgevingsvariabele in het '.env bestand' en de image tag in het 'docker-compose.yml' bestand. Vervolgens voeren we de opdracht deploy opnieuw uit:
env $(cat .env | grep ^[A-Z] | xargs) docker stack deploy --detach=false -c docker-compose.yml my-project
Resultaat:
Updating service my-project_busybox (id: mi27j7jjsz146y4wqqre439io)
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service mi27j7jjsz146y4wqqre439io converged
Merk op dat het resultaat nu 'Updating service' vermeldt, terwijl het de eerste keer 'Creating service' vermeldde.
Hoe dan ook, dit betekent dat Docker Swarm een update uitvoert (zonder downtime) voor elke service in het bestand 'docker-compose.yml', op dezelfde manier waarop we individuele services bijwerken met 'docker service update'. Dit is geweldig!
Maar er kunnen dingen fout gaan en er is geen 'docker stack rollback' commando. Dit betekent dat als het fout gaat, we terug kunnen gaan naar de vorige versie door ons vorige 'docker-compose.yml' bestand terug te zetten en opnieuw te implementeren!
Samenvatting
Het uitvoeren van updates zonder downtime te veroorzaken is erg belangrijk geworden. Docker Swarm maakt het beheren en uitvoeren van updates een fluitje van een cent. Docker Swarm was en blijft een zeer krachtige container orkestratie tool, ook al lijkt de ontwikkeling te zijn stilgevallen.
Links / credits
Docker - Apply rolling updates to a service
https://docs.docker.com/engine/swarm/swarm-tutorial/rolling-update
docker stack deploy in 1.13 doesn't load .env file as docker-compose up does #29133
https://github.com/moby/moby/issues/29133
Lees meer
Docker Docker Swarm Docker-compose
Laat een reactie achter
Reageer anoniem of log in om commentaar te geven.
Opmerkingen (1)
Laat een antwoord achter
Antwoord anoniem of log in om te antwoorden.
One cool thing for you to look into is the use of Docker secrets. I think it may simplify some of your problems with env lists. Glad to read your articles. Interested to know if you are enjoying Swarm vs Kubernetes better!
Recent
Meest bekeken
- Basis taakplanning met APScheduler
- Voorkomen dat dubbele berichten naar een extern systeem worden gestuurd
- LSTM meerstappen-optimalisatie hyperparameter met Keras Tuner
- Documenteren van een Flask RESTful API met OpenAPI (Swagger) met gebruikmaking van APISpec
- Met behulp van Python's pyOpenSSL om SSL-certificaten die van een host zijn gedownload te controleren
- Celery, Redis en het (in)beroemde e-mail taakvoorbeeld