Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як оновити контейнер без втрати даних?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Патерн: зупини старий container, видали його, запусти новий з ТИМИ САМИМИ volume-mount і новим image.** Стан живе у volume, не в container, тому переживає. ```bash docker stop pg && docker rm pg docker run -d --name pg \ -v pgdata:/var/lib/postgresql/data \ postgres:17 # бамп з 16 на 17 # Старі дані ще у volume `pgdata`; новий container їх підхоплює. ``` **Головне:** container замінні; volume ні. Update по суті це «заміни container». Для zero-downtime бери Compose `up -d` (перестворює лише змінені сервіси), Swarm rolling update або blue-green.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.