Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як встановити health check для контейнера?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Health check** це періодична команда, що Docker запускає всередині container; якщо повертає 0, container `healthy`. Постав у Dockerfile (`HEALTHCHECK`) або run-time (`--health-cmd`) або у Compose (`healthcheck:`). ```dockerfile HEALTHCHECK --interval=30s --timeout=3s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 ``` ```yaml healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 3s retries: 3 start_period: 10s ``` **Головне:** стан з'являється у `docker ps` (`healthy`, `unhealthy`, `starting`). Compose `depends_on: condition: service_healthy` чекає на нього. Оркестратори використовують для рішення, коли роутити трафік і коли рестартити.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Container healthcheck** це як Docker (і Compose, Swarm, K8s) розрізняють «процес up» і «застосунок реально працює». Без healthcheck єдиний сигнал це «чи живий PID 1?», що пропускає кожен цікавий failure mode. ## Теорія ### TL;DR - Healthcheck це команда, що Docker періодично запускає всередині container. Exit 0 = healthy; non-zero = unhealthy. - Три стани: **starting** (ще у `start_period`), **healthy**, **unhealthy** (`retries` разів підряд провалено). - Постав через `HEALTHCHECK` у Dockerfile, `--health-cmd` на `docker run` або `healthcheck:` у Compose. - Використовується у `docker ps`, у Compose `depends_on: service_healthy`, у Swarm для рішень про заміну реплік. - Поширена команда: `curl -f http://localhost:<port>/health`. Тонкий момент: команда має існувати всередині container. ### Швидкий приклад ```dockerfile FROM node:22-alpine WORKDIR /app COPY . . RUN npm ci --omit=dev EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1 CMD ["node", "server.js"] ``` ```bash $ docker run -d --name api myapp $ docker ps CONTAINER ID IMAGE STATUS NAMES a3f9d2b8c1e4 myapp Up 30 seconds (healthy) api # Status тепер включає (healthy) / (unhealthy) / (starting) ``` Через ~30 секунд перша перевірка крутиться. Якщо успішно, статус стає `(healthy)`. ### Чотири флаги, що важать ``` --interval=DURATION # як часто крутити перевірку (дефолт 30s) --timeout=DURATION # макс час, що перевірка має для повернення (дефолт 30s) --retries=N # скільки невдач до unhealthy (дефолт 3) --start-period=DURATION # grace при старті; невдачі тут не рахуються (дефолт 0s) ``` Для типового веб-сервісу: `--interval=30s --timeout=3s --retries=3 --start-period=10s` нормально. Застосунки, що беруть 30+ секунд на прогрів (JVM, великі Python ML-сервіси), потребують довшого `--start-period` (60-120с). ### Compose-синтаксис ```yaml services: api: image: myapp healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 3s retries: 3 start_period: 10s web: image: nginx depends_on: api: condition: service_healthy # чекати, поки api healthy перед стартом ``` `depends_on: condition: service_healthy` це killer-feature, чекає, поки healthcheck deps пройде, перед стартом dependent. Набагато надійніше за просту list-форму. ### Три форми test-команди ```yaml # Форма 1: CMD (краще, без shell) test: ["CMD", "curl", "-f", "http://localhost:3000/health"] # Форма 2: CMD-SHELL (з shell, дозволяє && || env-розгортання) test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"] # Форма 3: NONE (вимкнути успадкований healthcheck з base-image) test: ["NONE"] ``` Форма `CMD` швидша (без shell-процесу). `CMD-SHELL` потрібна для env-розгортання або shell-логіки. ### Поширений провал: команди немає у container Найпоширеніший healthcheck-баг: ```dockerfile HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1 ``` але image це `alpine` без встановленого `curl`. Healthcheck завжди падає. Рішення: - `RUN apk add --no-cache curl` (або для Alpine часто `wget` уже є: `wget -q --spider URL`) - Для Node-застосунків: `node -e "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode===200?0:1))"` (без додаткового пакета) - Distroless image часто потребують вбудованого бінаря: включи `/healthcheck`-бінар у Dockerfile, що виходить 0/1 ### Inspecting health ```bash # Поточний статус у ps docker ps --format 'table {{.Names}}\t{{.Status}}' # Повні деталі health docker inspect api --format '{{json .State.Health}}' | jq # { # "Status": "healthy", # "FailingStreak": 0, # "Log": [ # { "Start": "...", "End": "...", "ExitCode": 0, "Output": "..." }, # ... # ] # } # Live watch 'docker ps --format "table {{.Names}}\t{{.Status}}"' ``` Масив `Log` тримає останні 5 healthcheck-результатів, неоціненно для дебагу «чому unhealthy?». ### Типові помилки **Без `start_period`, healthcheck падає під час повільного старту** ```yaml # НЕПРАВИЛЬНО: застосунок потребує 60с на прогрів; перші 3 fail роблять unhealthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 5s retries: 3 # Через 15с container unhealthy і може рестартуватися # ПРАВИЛЬНО: start_period дає grace-вікно healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s start_period: 60s # невдачі під час цього вікна не рахуються ``` **Healthcheck, що залежить від залежності** ```yaml # НЕПРАВИЛЬНО: api-healthcheck пингує db; якщо db коротко down, api стає unhealthy test: ["CMD", "sh", "-c", "curl -f http://localhost:3000/health && pg_isready -h db"] ``` Мікс залежностей у твою *liveness*-перевірку, і transient db-збій рестартує твій api. Краще: healthcheck перевіряє лише власний readiness container; роби окремий /readiness endpoint, якщо застосунок має gate'ити трафік на основі health залежностей. **Удар у зовнішній URL у healthcheck** ```dockerfile HEALTHCHECK CMD curl -f https://api.example.com/health || exit 1 ``` Тепер health твого container залежить від чужого uptime. Не роби. **Вимкнути healthcheck не помітивши** ```dockerfile FROM postgres:16 # успадковує healthcheck postgres. Якщо твій застосунок не exit 0 там, отримаєш unhealthy. # Або постав свій: HEALTHCHECK CMD pg_isready -U postgres # Або вимкни успадкований: HEALTHCHECK NONE ``` ### Реальне застосування - **Compose з `depends_on: service_healthy`:** чекати, поки db готова, перед стартом api. Найбільший практичний use. - **Swarm-оркестрація:** unhealthy-репліки вбиваються і замінюються. Healthcheck це сигнал. - **Reverse proxy-інтеграція:** Traefik і nginx-proxy можуть inspect'ити Docker healthcheck-стан, щоб роутити лише до healthy. - **Monitoring-дашборди:** скрейпити вивід `docker inspect` для health-статусу, alert на `unhealthy`. ### Питання для поглиблення **Q:** Яка різниця між Docker healthcheck і Kubernetes liveness/readiness probe? **A:** Та сама ідея, інший scope. K8s розділяє liveness (чи я живий?) і readiness (чи я готовий до трафіку?), з різними поведінками (liveness рестартує; readiness прибирає з сервісу). Docker має лише один комбінований healthcheck. K8s не використовує Docker healthcheck, має свій. **Q:** Який сигнал отримує unhealthy container? **A:** Жодного, бути unhealthy не auto-restart саме по собі. З `--restart=on-failure` це не допомагає (немає exit code). Зі Swarm або Compose-з-оркестратором оркестратор вирішує замінити unhealthy task. Зі звичайним `docker run` ти (або твій monitor) дієш. **Q:** Чи можна мати кілька healthcheck? **A:** Лише один на container. Комбінуй логіку всередині однієї CMD-SHELL команди, якщо треба. **Q:** Як вимкнути успадкований healthcheck з base-image? **A:** `HEALTHCHECK NONE` у твоєму Dockerfile або `test: ["NONE"]` у Compose. **Q:** (Senior) Коли healthcheck має робити більше ніж `curl /health`? **A:** Додай розумніший `/health`-endpoint всередині застосунку, що перевіряє readiness downstream (пул з'єднань DB не вичерпаний, черга не накопичена понад поріг). Тримай саму healthcheck-команду простою, інтелект у endpoint, не у shell. Для сервісів з довгим warmup роби `/livez` (завжди OK, як тільки процес up) і `/readyz` (реальний readiness застосунку) окремо, і бери K8s-style підхід у Swarm через два endpoint. ## Приклади ### Compose-стек з healthcheck-gated стартом ```yaml services: api: build: . depends_on: db: condition: service_healthy healthcheck: test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/health || exit 1"] interval: 10s timeout: 3s retries: 3 start_period: 15s db: image: postgres:16 environment: POSTGRES_PASSWORD: dev healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s retries: 5 start_period: 5s ``` ```bash $ docker compose up -d [+] Running 2/2 ✔ Container db Healthy 1.4s ✔ Container api Healthy 12.3s ``` Compose чекає, поки db healthy, перед стартом api. Без race condition, без «connection refused» на першому запуску. ### Node-застосунок з вбудованим healthcheck (без extra-пакетів) ```dockerfile FROM node:22-alpine WORKDIR /app COPY . . RUN npm ci --omit=dev EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD node -e "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))" CMD ["node", "server.js"] ``` Curl не потрібен, використовує Node runtime, що уже є. Менший image, без extra-пакета. ### Перегляд health-логів ```bash $ docker inspect api --format '{{range .State.Health.Log}}{{.End}}: exit={{.ExitCode}} out={{.Output}}\n{{end}}' 2026-04-30T10:00:00Z: exit=0 out=ok 2026-04-30T10:00:30Z: exit=0 out=ok 2026-04-30T10:01:00Z: exit=1 out=connection refused 2026-04-30T10:01:30Z: exit=1 out=connection refused 2026-04-30T10:02:00Z: exit=0 out=ok ``` Останні 5 результатів з exit-кодами і stdout. Часто відповідає на «чому є/був unhealthy?» без подальшого розслідування.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.