Ten istý compose súbor na notebooku a na swarme
Predchádzajúci článok rozdelil compose súbor na zdieľané služby, ktoré môže
používať akýkoľvek stack. Tento je o samotnom súbore. Compose súbor napísaný pre
docker compose up nie je ten istý artefakt ako ten, ktorý dostane
docker stack deploy. Syntax vyzerá rovnako, správanie nie: Swarm
nemá poradie štartu, nečíta .env, nepozná cesty špecifické pre hostiteľa ani
krok buildu. Väčšina týchto rozdielov zlyháva potichu, a práve preto sa oplatí
poznať ich skôr, než sa súbor dostane na klaster.
Dva príkazy, dve pravidlá
docker compose up spustí kontajnery na uzle, kde ho spustíte. Na manažérovi to
povie aj nahlas: „Compose nepoužíva režim swarm na nasadenie služieb na viac
uzlov.“
docker stack deploy odovzdá ten istý súbor orchestrátoru, ktorý vytvorí služby,
rozmiestni repliky, postaví overlay sieť a nechá každý uzol stiahnuť image z
registra. Z toho hneď vyplývajú tri dôsledky:
- Neexistuje lokálny build.
build:sa ignoruje, takže image musí skončiť v registri. - Používa starší formát compose verzie 3, nie Compose špecifikáciu.
- Interpolácia prebieha v shelli, z ktorého nasadzujete, nie v adresári projektu.
Čo súbor nesmie obsahovať
Kľúč name: na najvyššej úrovni je odmietnutý hneď s „additional property ‘name’
is not allowed”. Názov stacku prichádza z príkazového riadka.
Neexistuje prepínač --env-file a .env sa nikdy nečíta. Každá ${VARIABLE} sa
nahrádza z prostredia shellu, ktorý deploy spúšťa. Ak ju necháte nenastavenú,
stane sa z nej prázdny reťazec, nie chyba — a pri odkaze na image z toho vznikne
holé dvojbodkové zakončenie a invalid reference format.
env_file: funguje, ale vyhodnocuje sa na klientovi: súbor musí ležať vedľa
compose súboru na stroji, ktorý deploy spúšťa, nie na uzloch.
Čo Swarm potichu zahodí
Docker vypíše „Ignoring unsupported options: …“ a ide ďalej:
build,restart,links,network_mode,privileged,devicesshm_size,security_opt,ipc,pid,mac_addresscgroup_parent,cgroupns_mode,domainname,external_links,userns_mode
restart je ten, ktorý ľudí prekvapí. Na swarme žije politika reštartu pod
deploy.restart_policy, a ak chýba, predvolená hodnota je condition: any —
kontajner, ktorý padá v slučke, bude reštartovaný donekonečna. links nahradzujú
siete: služby na tej istej overlay sieti sa nájdu podľa názvu. container_name je
deprecated a ignorovaný, pretože Swarm generuje názvy taskov.
depends_on nemá ekvivalent
Na swarme neexistuje poradie štartu ani čakanie na healthcheck. depends_on
zlyháva dvoma spôsobmi podľa toho, ako je napísaný.
Krátka podoba ako zoznam sa načíta a potom bez varovania zmizne. Dlhá podoba — tá
s condition: service_healthy — neprejde validáciou vôbec:
services.n8n.depends_on: must be a list
Tak či tak sa aplikácia musí vyrovnať s tým, že databáza ešte nebeží, čo v praxi znamená opakovať pripojenie namiesto spoliehania sa na naštartovanú závislosť.
Skutočný súbor s poznámkami
Compose súbor n8n je dobrý exemplár, pretože používa takmer všetky tieto voľby:
volumes:
db_storage:
n8n_storage:
services:
postgres:
image: postgres:18
restart: always # ignored by swarm
environment:
- POSTGRES_USER # read from the shell at deploy time
- POSTGRES_PASSWORD
- POSTGRES_DB
- POSTGRES_NON_ROOT_USER
- POSTGRES_NON_ROOT_PASSWORD
volumes:
- db_storage:/var/lib/postgresql/data
- ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}",
]
n8n:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
restart: always # ignored by swarm
ports:
- 5678:5678
links:
- postgres # ignored by swarm
volumes:
- n8n_storage:/home/node/.n8n
depends_on: # rejected by the v3 schema: must be a list
postgres:
condition: service_healthy
Dva riadky v ňom zlyhávajú bez toho, aby to povedali. ${POSTGRES_USER} nahrádza
Compose, nie shell vnútri kontajnera; s nenastavenou premennou sa kontrola zmení
na pg_isready -h localhost -U -d a nikdy neprejde. Potrebuje $$POSTGRES_USER,
aby ju rozbalil shell kontajnera. A passthrough environment: - POSTGRES_USER sa
vyhodnotí raz, pri nasadení, z vášho shellu — nie z .env, ktoré mal projekt na
notebooku.
Porty, cesty a volumy
Publikované porty fungujú, s jedným rozdielom: IP adresa hostiteľa sa zahodí.
127.0.0.1:8000:8000 vyprodukuje varovanie — „ignoring IP-address
(127.0.0.1:8000:8000/tcp) service will listen on ‘0.0.0.0’“ — a služba je
dostupná cez routing mesh na každom uzle. Keď potrebujete port na jednom
konkrétnom uzle, použite dlhú syntax s mode: host.
Cesty sú druhý tichý rozdiel. Bind mount sa vyhodnocuje na tom uzle, kde pristane
task, a Docker tam vytvorí prázdny adresár namiesto toho, aby zlyhal. Pomenovaný
volume s predvoleným lokálnym driverom nie je o nič lepší: žije len na jednom
uzle, takže presunutá databáza začne s prázdnym volume a vyzerá to ako strata
dát. Buď službu pripnite cez deploy.placement.constraints, alebo dajte volume na
zdieľané úložisko. Sprievodný článok o zdieľaných službách ukazuje verziu s NFS.
Skontrolujte pred nasadením
docker stack config -c paperless.yml
Zlúči a nahrádza premenné v súbore bez toho, aby sa dotkol swarmu, čo odchytí
chýbajúcu premennú pred nasadením. Nepovie vám však, ktoré kľúče sa práve
ignorujú — to vypíše len docker stack deploy. Prečítať si výstup nasadenia raz,
na čerstvom stacku, je cennejšie než akákoľvek kontrola schémy.
Pravidlo, ktoré to celé pokrýva: na swarme compose súbor popisuje služby, nie stroj. Čokoľvek, čo predpokladalo jedného hostiteľa — poradie procesov, cesty na hostiteľovi, lokálny Docker build — treba nahradiť niečím, čomu orchestrátor rozumie.