Як встановити health check для контейнера?
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, у Composedepends_on: service_healthy, у Swarm для рішень про заміну реплік. - Поширена команда:
curl -f http://localhost:<port>/health. Тонкий момент: команда має існувати всередині container.
Швидкий приклад
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
EXPOSE 3000
HEALTHCHECK \
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]$ 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-синтаксис
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-команди
# Форма 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-баг:
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
# Поточний статус у 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 падає під час повільного старту
# НЕПРАВИЛЬНО: застосунок потребує 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, що залежить від залежності
# НЕПРАВИЛЬНО: 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
HEALTHCHECK CMD curl -f https://api.example.com/health || exit 1Тепер health твого container залежить від чужого uptime. Не роби.
Вимкнути healthcheck не помітивши
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 стартом
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$ docker compose up -d
[+] Running 2/2
✔ Container db Healthy 1.4s
✔ Container api Healthy 12.3sCompose чекає, поки db healthy, перед стартом api. Без race condition, без «connection refused» на першому запуску.
Node-застосунок з вбудованим healthcheck (без extra-пакетів)
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
EXPOSE 3000
HEALTHCHECK \
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-логів
$ 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?» без подальшого розслідування.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.
Коментарі
Ще немає коментарів