Skip to main content

Як оновити контейнер без втрати даних?

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-патерн

bash
# Оригінал 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: чистіша версія

yaml
services: api: image: myapp:1.0 volumes: - api_data:/var/lib/myapp ports: ["80:80"] restart: unless-stopped volumes: api_data:
bash
# 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.

bash
# Це впаде або корумпує: 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:

  1. Спочатку backup. Завжди.
  2. Бери pg_dumpall зі старого container, restore у новий container зі свіжим volume.
  3. АБО бери pg_upgrade у transitional-container, що має обидві версії.
  4. Тестуй у staging перед прод.

Те саме застосовується для MySQL, Mongo, Elasticsearch, major-version bump потребують migration-логіки, не просто зміни tag.

Zero-downtime updates

Базовий stop+rm+run flow має короткий outage (кілька секунд, поки новий container стартує). Для zero-downtime:

Compose з health-gating

yaml
services: api: image: myapp:1.0 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 5s
bash
# 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

bash
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.

bash
# Старе: 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

bash
# НЕПРАВИЛЬНО: новий 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

bash
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

bash
# НЕПРАВИЛЬНО під час прод-трафіку 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.

Забути мережу

bash
# Старий 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-сервісом

yaml
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:
bash
# 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

bash
# Крок 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

bash
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.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів