Як оновити контейнер без втрати даних?
Update Docker container без втрати даних це щоденний workflow, що ловить новачків. Трюк у розумінні, що container задумано як замінний, а volume це частина, яку ти захищаєш.
Теорія
TL;DR
- Writable-шар container це disposable. Стан, що важить, живе у volume (або external-сервісах).
- Update-flow:
stopстарого →rmстарого →runнового з тими ж volume і новим image-tag. - Для Compose:
docker compose pull && docker compose up -dперестворює лише змінені сервіси. - Для zero-downtime: blue-green, rolling-update (Swarm/K8s) або
--update-configз health-gating. - DB version-bump потребує додаткової обережності: Postgres 16 → 17 потребує
pg_upgradeабоpg_dumpall. Самого збереження volume недостатньо через major-версії.
Простий update-патерн
# Оригінал
docker run -d --name api \
-v api_data:/var/lib/myapp \
-p 80:80 \
--restart=unless-stopped \
myapp:1.0
# Update до 1.1
docker pull myapp:1.1
docker stop api && docker rm api
docker run -d --name api \
-v api_data:/var/lib/myapp \ # ТОЙ САМИЙ volume
-p 80:80 \
--restart=unless-stopped \
myapp:1.1 # НОВИЙ imageДані у api_data переживають, бо volume окремі від container. Container це лише «runtime», що показує на дані.
Compose: чистіша версія
services:
api:
image: myapp:1.0
volumes:
- api_data:/var/lib/myapp
ports: ["80:80"]
restart: unless-stopped
volumes:
api_data:# Update workflow
sed -i 's/myapp:1.0/myapp:1.1/' compose.yaml
docker compose pull
docker compose up -d
# Compose помічає зміну image, stop + recreate ЛИШЕ api,
# лишає volume не зачепленим, лишає інші сервіси running.Без ручних stop/rm/run. Compose обробляє recreate, зберігаючи volume.
Що переживає, а що ні
При docker rm:
| Переживає? | |
|---|---|
| Named volumes | ТАК (окремо від container) |
Anonymous volumes (без --rm -v) | ТАК |
Anonymous volumes (з --rm -v) | НІ (видалено) |
| Bind mount (host-шляхи) | ТАК (живуть на host) |
| Writable-шар container | НІ (видалено разом з container) |
| Network-alias container | НІ (перестворюється для нового container) |
Логи у /var/lib/docker/containers/<id>/ | НІ (видалено) |
Правило: усе у volume або bind mount переживає. Усе інше помирає з container.
Caveat DB version-bump
Зберігання volume через docker rm нормально для сумісних image-версій. Postgres 16 → 16.5 = той самий data-формат, той самий volume працює. Postgres 16 → 17 = інший on-disk-формат, інший volume-layout.
# Це впаде або корумпує:
docker stop pg && docker rm pg
docker run -d --name pg -v pgdata:/var/lib/postgresql/data postgres:17
# postgres:17 бачить /var/lib/postgresql/data, форматований версією 16; відмовляє стартувати.Для major DB-version upgrade:
- Спочатку backup. Завжди.
- Бери
pg_dumpallзі старого container, restore у новий container зі свіжим volume. - АБО бери
pg_upgradeу transitional-container, що має обидві версії. - Тестуй у staging перед прод.
Те саме застосовується для MySQL, Mongo, Elasticsearch, major-version bump потребують migration-логіки, не просто зміни tag.
Zero-downtime updates
Базовий stop+rm+run flow має короткий outage (кілька секунд, поки новий container стартує). Для zero-downtime:
Compose з health-gating
services:
api:
image: myapp:1.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 5s# Rolling-style update з двома репліками за load-balancer
docker compose up -d --no-deps --scale api=2 api # підняти новий поряд зі старим
# Чекай health
# Видали старі реплікиДля надійного zero-downtime у Compose зазвичай додаєш reverse proxy (Traefik/nginx), що стежить за healthcheck.
Swarm rolling update
docker service update --image myapp:1.1 \
--update-parallelism 1 \
--update-delay 10s \
--update-failure-action rollback \
api
# Update одну репліку за раз, чекає 10с, перевіряє health, продовжує або rollback.Swarm обробляє rolling update нативно. Якщо healthcheck падає, автоматичний rollback.
Blue-green
Крути green (новий) поряд з blue (старим). Перемикай load-balancer, коли green healthy. Знеси blue.
# Старе: myapp:1.0 слухає 8081, lb route'ить сюди
# Стартуємо новий: myapp:1.1 на 8082
docker run -d --name api-green -p 8082:3000 -v api_data:/data myapp:1.1
# Чекай /health = 200
# Оновлюємо config load-balancer: route на 8082
# Drain старого: docker stop api-blue; docker rm api-blueПодвоєні ресурси під час переходу; миттєвий cutover; легкий rollback (флипни LB назад).
Типові помилки
Забути той самий volume-mount на новому container
# НЕПРАВИЛЬНО: новий container без volume; дані «зникли»
docker run -d --name api myapp:1.1
# ПРАВИЛЬНО: той самий volume-mount як раніше
docker run -d --name api -v api_data:/var/lib/myapp myapp:1.1Дані ще у volume; новий container просто не приєднано. Перезапусти з правильним -v.
Використання anonymous volume
docker run -d --name api -v /var/lib/myapp myapp:1.0
# Anonymous volume (авто-UUID-ім'я)
docker rm -fv api # -v стирає anonymous volume → DATA LOSSЗавжди бери named volume (-v api_data:/var/lib/myapp). Вони переживають rm -v; anonymous ні, залежно від флагів.
Major DB version-upgrade без migration
Згадано вище. Фікс це pg_dump+restore або pg_upgrade, не просто бамп tag.
Update під час peak-трафіку без health-gating
# НЕПРАВИЛЬНО під час прод-трафіку
docker stop api && docker rm api
# 5-30 секунд вікно 502
docker run -d --name api -v api_data:/data myapp:1.1Для user-facing сервісів бери Compose recreate, Swarm rolling update або blue-green. Базовий stop+run нормально лише для off-hours update.
Забути мережу
# Старий container був на кастомній мережі
docker run -d --name api --network appnet -v api_data:/data myapp:1.0
# Перестворено без --network → на дефолтному bridge → не дістає db, redis тощо.
docker run -d --name api -v api_data:/data myapp:1.1
# api тепер ізольований від решти стеку.Відтворюй УСІ флаги оригінального docker run, не тільки volume.
Реальне застосування
- Single-container сервіс:
docker stop && docker rm && docker runз правильним-v. Короткий outage, просто. - Compose-стеки:
docker compose pull && docker compose up -d. Compose рахує diff і перестворює лише те, що змінилося. - Прод з трафіком: Compose + reverse proxy з health-checks, АБО Swarm/K8s з нативним rolling update.
- DB-upgrade: dump → свіжий volume → restore. Плануй downtime, тестуй у staging.
Питання для поглиблення
Q: Що, якщо новий image потребує іншого volume-mount-шляху?
A: Змонтуй той самий volume на новому шляху: -v api_data:/new/path/in/v1.1/myapp. Або запусти one-time cp-job всередині тимчасового container, щоб перемістити дані всередині volume.
Q: Чи docker update змінює image?
A: Ні. docker update модифікує runtime-параметри (пам'ять, CPU, restart-policy) існуючого container БЕЗ зміни image. Щоб використати новий image, треба перестворити.
Q: Чи достатньо docker compose restart для застосування image-змін?
A: Ні. restart лише stop+start ТОГО САМОГО container. Щоб застосувати image-зміни, бери docker compose up -d.
Q: Як rollback'нути поганий update?
A: З Compose, відредагуй назад на старий tag і docker compose up -d. Зі Swarm, docker service rollback <name>. З ручним docker run маєш зберегти команду старого container спочатку, потім re-run.
Q: (Senior) Як обробляти stateful-update, що потребує schema-migration?
A: Three-step патерн: (1) деплой migration-job (окремий container, крутиться проти volume, exit 0 при успіху). (2) деплой нової app-версії, що очікує новий schema. (3) тримай migration ідемпотентною, тож reruns безпечні. У Compose бери one-shot migrate-сервіс з restart: "no" і depends_on: db: service_healthy. У K8s бери Job + readiness-gate на deployment. Складна частина: робити migration backward-compatible (старий застосунок має працювати під час rolling-update); це потребує expand-then-contract schema-змін.
Приклади
Compose-update з одним bumped-сервісом
services:
web:
image: nginx:1.27-alpine
api:
image: myorg/api:1.0 # бамп цього
volumes: [api_data:/var/lib/myapp]
depends_on: [db]
db:
image: postgres:16
volumes: [pgdata:/var/lib/postgresql/data]
volumes:
api_data:
pgdata:# Edit compose.yaml: api стає myorg/api:1.1
sed -i 's/myorg\/api:1.0/myorg\/api:1.1/' compose.yaml
docker compose pull
docker compose up -d
[+] Running 4/4
✔ Container web Running (без змін)
✔ Container db Running (без змін)
✔ Container api Recreated (image змінено)Лише api перестворено. db і web не зачеплено. api_data переживає.
Postgres major version-upgrade
# Крок 1: backup
$ docker exec pg pg_dumpall -U postgres > backup.sql
# Крок 2: stop і remove старого
$ docker stop pg && docker rm pg
# Крок 3: перейменуй старий volume (тримай як fallback)
$ docker volume create pgdata-pg17
$ docker run --rm \
-v pgdata:/old \
-v pgdata-pg17:/new \
alpine sh -c 'cp -a /old/. /new/' # initial copy, якщо хочеш incremental
# Крок 4: стартуємо свіжий pg17 з порожнім volume
$ docker volume rm pgdata-pg17 && docker volume create pgdata-pg17
$ docker run -d --name pg \
-v pgdata-pg17:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=devpass \
postgres:17
# Крок 5: restore
$ cat backup.sql | docker exec -i pg psql -U postgres
# Крок 6: верифікуй, потім видали старий volume `pgdata`Major-version bump не може просто «вказати на volume». Dump + restore це безпечний шлях. Або інструкції pg_upgrade офіційного postgres:17 image.
Swarm rolling update з rollback на failure
docker service update \
--image myorg/api:1.1 \
--update-parallelism 1 \
--update-delay 30s \
--update-failure-action rollback \
--update-monitor 30s \
--rollback-parallelism 2 \
apiОдна репліка за раз, 30с monitor, автоматичний rollback, якщо healthcheck падає. Production-ready zero-downtime update для Swarm.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів