Docker-шлюз API имеет статус healthy, но не работает: проверки здоровья, готовности и зависимостей
Разделите живость контейнера, готовность шлюза и бизнес-проверку. Проверьте коды выхода, порог последовательных ошибок и зависимости Compose: healthy, доступность базы и политика перезапуска не доказывают полную работоспособность.
Контейнер имеет статус healthy, но шлюз API всё равно отдаёт 502, тайм-аут или не видит модель. Обычно Docker не «сломался»: проверяется не тот слой. Одна команда может подтвердить, что процесс жив, но не то, что шлюз подключён к базе, загрузил маршруты и доступен upstream.
Статья разделяет живость, готовность и бизнес-проверку и предназначена для Docker Engine и Compose. Примеры проверены локально как конфигурация и функции; к реальному шлюзу, модели или платному API они не подключались.
1. Разделите три значения «нормы»
Живость отвечает, работает ли процесс; готовность — может ли экземпляр принимать трафик; бизнес-проверка — проходит ли контролируемый запрос ключевой путь. Docker HEALTHCHECK меняет состояние контейнера по коду возврата указанной команды и сам не понимает базу, маршруты или upstream API.
Поэтому healthy означает, что проверка ранее прошла и порог последовательных ошибок ещё не достигнут; самая последняя попытка уже могла завершиться ошибкой. Смотрите также время проверки, код выхода и число последовательных ошибок. Это не доказывает:
- что аккаунты, баланс и маршруты загружены;
- что DNS, TLS, права и сопоставление модели у upstream работают;
- что потоковый ответ корректно завершается;
- что настоящий запрос не получит лимит, тайм-аут или повторное списание.
Сначала прочитайте практику резервирования и восстановления шлюза, чтобы проверка не принимала восстанавливаемую базу или старую конфигурацию за готовую. Проверка должна быть короткой, повторяемой и без побочных эффектов; API Key нельзя помещать в URL, журналы или текст ошибки.
2. Проверьте существующий HEALTHCHECK, а не угадывайте по статусу
В нужной среде выполните только чтение и замените имя контейнера:
docker inspect --type container --format '{{json .State.Health}}' gateway-container
docker inspect --type container --format '{{json .Config.Healthcheck}}' gateway-container
State.Health показывает последние состояния, число ошибок и вывод; Config.Healthcheck отражает команду, интервал, тайм-аут, число попыток и период запуска, принятые при создании контейнера. Если проверка не настроена, поля могут отсутствовать или быть пустыми: это не подтверждает здоровье. Вывод может содержать секреты, напечатанные проверкой; перед передачей удалите их. Одна колонка из docker ps скрывает самый полезный контекст сбоя.
Типичные ложные сигналы:
| Что видно | Что фактически проверено | Что не проверено |
|---|---|---|
Успешен curl 127.0.0.1:8080/health | Процесс принимает локальное соединение | База, upstream и авторизация |
Выполняется только test -f config | Файл существует | Парсинг, действующий ключ и маршрут |
| Проверка делает настоящий POST | Возможно, проходит полный путь | Может создать расход, запись или инструментальное действие |
Нет start_period | Контейнер быстро считается сломанным | Время миграции и загрузки кэша |
Значения Dockerfile, переопределение Compose и параметры запуска могут находиться в разных файлах. После изменения убедитесь, что контейнер действительно пересоздан: простой restart старого экземпляра не означает применение новой проверки.
3. Спроектируйте многоуровневую проверку без побочных эффектов
Первый слой может проверять только прослушивание процесса, второй — загруженное шлюзом состояние только для чтения, третий — фиксированный безопасный запрос в изолированной среде. Не помещайте в периодическую проверку чтение баланса, создание сессии или настоящий пользовательский prompt.
Это фрагмент для объединения с существующим сервисом Compose, а не полное развёртывание. Сохраните образ, тома, сети и прочие настройки. Предполагается, что образ уже содержит Node.js и реализованный и проверенный вами /app/healthcheck.js; здесь этот скрипт не предоставляется. Локальная функция из раздела 5 не заменяет его: она не делает сетевой запрос и не задаёт код завершения процесса. Числа служат примером:
services:
gateway:
healthcheck:
test: ["CMD", "node", "/app/healthcheck.js"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
Ошибки внутри start_period обычно не входят в порог последовательных сбоев. Но после первой успешной проверки в этом периоде последующие ошибки уже учитываются: это не безусловное ожидание до конца периода. Скрипт должен завершаться с кодом 0 при успехе и 1 при ошибке; код 2 зарезервирован Docker, не используйте его намеренно. Записывайте короткую обезличенную причину в stderr. Задайте предел соединения и полного времени внутри скрипта, оставив запас до Docker timeout. Не следуйте на неизвестный хост и не ждите бесконечно.
Для обычного контейнера Docker Engine unhealthy — состояние здоровья. Политика вроде restart: always реагирует на выход процесса или остановку контейнера, а не автоматически перезапускает его из-за неудачного healthcheck. Исключение из трафика, перезапуск и оповещение требуют явной настройки прокси, оркестратора или мониторинга. Временная ошибка внешнего upstream не должна вызывать бесконечные локальные перезапуски. В зависимости от того, затронуты ли все запросы, помечайте неготовым весь экземпляр либо отключайте только проблемный маршрут.
Если шлюз зависит от базы или локальной модели, сначала используйте существующий read-only endpoint версии или соединения. Границы адресов между хостом, контейнером шлюза и контейнером модели описаны в проверке подключения локальной модели к единому шлюзу, но доступность порта не равна успешной генерации.
4. Не объявляйте шлюз healthy до запуска зависимостей
В Compose depends_on можно сочетать с condition: service_healthy, чтобы зависимый сервис сначала достиг своего состояния здоровья. Это только порядок запуска: правильный смысл проверки всё равно задаёте вы. Если проверка базы подтверждает только TCP-порт, шлюз может стартовать до завершения миграций.
Это фрагмент для существующих сервисов PostgreSQL db и шлюза, а не самостоятельно запускаемое развёртывание базы:
services:
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d gateway -t 2 || exit 1"]
interval: 5s
timeout: 3s
retries: 10
gateway:
depends_on:
db:
condition: service_healthy
app и gateway — примеры имени пользователя и базы, замените их своими. Образ должен содержать pg_isready и оболочку для этой команды. Конструкция || exit 1 приводит ненулевые коды утилиты к коду ошибки healthcheck Docker. Эта утилита проверяет, принимает ли PostgreSQL соединения, но не доказывает успешную авторизацию приложения или завершение миграций. По документации статус сервера можно получить даже с неверным пользователем, паролем или именем базы; неверные значения могут создавать записи об ошибках соединения.[6] Сохраните фактическую версию образа, пароли, сети и тома без обновления или удаления. Если база перестанет работать позже, depends_on не будет постоянно исключать шлюз из трафика: приложению по-прежнему нужны переподключение и изменение готовности.
Перед запуском проверьте переменные среды, лимиты, журналы и предупреждения о стоимости по контрольному списку готовности API к production. Healthcheck — одно свидетельство, а не замена всему списку.
5. Проверьте границы результата локальной функцией
Функция Node.js ниже не обращается к сети, а классифицирует собранное состояние проверки. Она отклоняет пустой объект, ненулевой код и недоверенное перенаправление; ready означает только соответствие локальному правилу.
function classifyProbe(result) {
if (result === null || typeof result !== 'object' || Array.isArray(result)) {
return 'invalid-input'
}
if (result.redirected === true) return 'reject-redirect'
if (result.redirected !== false) return 'invalid-redirect-flag'
if (!Number.isInteger(result.exitCode)) return 'missing-exit-code'
if (result.exitCode !== 0) return 'probe-failed'
if (!Number.isFinite(result.elapsedMs) || result.elapsedMs < 0 ||
result.elapsedMs > 5000) return 'timeout-window'
if (result.httpStatus !== 200) return 'unexpected-status'
if (result.body !== 'gateway-ready') return 'unexpected-body'
return 'ready'
}
const samples = [
{ exitCode: 0, httpStatus: 200, elapsedMs: 120, body: 'gateway-ready', redirected: false },
{ exitCode: 1, httpStatus: 200, elapsedMs: 80, body: 'gateway-ready', redirected: false },
{ exitCode: 0, httpStatus: 200, elapsedMs: 80, body: 'gateway-ready', redirected: true }
]
console.log(samples.map(classifyProbe))
Нормальный пример даёт три результата: ready, probe-failed, reject-redirect. Строка gateway-ready — условное тело ответа этой статьи, а не endpoint любого шлюза. Статус 200 и точное тело нужно определить по контракту настоящего интерфейса. Отсутствующее время, NaN, время строкой, неверный HTTP-статус и отсутствующий признак перенаправления не дают готовность. Даже при redirected: false запрос должен запрещать переходы заранее: отметка после ответа не предотвращает уже совершённую пересылку. Код не моделирует Docker, сеть или списания upstream, а проверяет только классификацию. Настоящая проверка должна записывать время, ID контейнера, версии зависимостей и обезличенную причину сбоя, чтобы состояния реплик не смешивались.
6. Проведите полную приёмку от healthy до доступного сервиса
После изменения проверки действуйте так:
- В изолированной среде поочерёдно вызовите состояние без прослушивания, неготовую базу, отказавший upstream и нормальный ответ; сравните переходы статуса с ожиданиями.
- Проверьте число попыток и окно повторов; если дополнительно настроено автоматическое восстановление, короткая задержка запуска не должна вызывать лишний restart.
- Верните фиксированный безопасный бизнес-результат и проверьте завершение ответа, код ошибки и связь с журналом.
- Перед production убедитесь, что частая проверка живости и готовности не тратит баланс, не пишет бизнес-данные и не раскрывает внутренние ошибки. Отдельная приёмка настоящей модели может быть платной: ей нужны отдельный бюджет, низкая частота и условия остановки. Не смешивайте её с healthcheck контейнера; в этой статье таких вызовов не было.
- После пересоздания сервиса перечитайте
State.HealthиConfig.Healthcheck, сохраните ID и время создания.
Если страница показывает healthy, а запрос падает, разделите процесс, внутреннюю логику, зависимость, upstream и клиентский слой. Не перезапускайте бесконечно и не принимайте редкий успех за восстановление. Здоровье — сигнал в окне времени; оно не заменяет журналы запросов, сверку счёта и план отката.
7. Официальные источники и дата проверки
Источники проверены 2026-11-03. Это официальная документация Docker; значения параметров зависят от установленной версии.
- Dockerfile HEALTHCHECK: команда, коды выхода, интервалы, тайм-ауты, повторы и период запуска.
- Порядок запуска Compose: границы
depends_onиservice_healthy. - docker inspect: чтение состояния и конфигурации контейнера.
- Автоматический запуск контейнеров: политика перезапуска не равна health status.
- Compose healthcheck: переопределения сервиса, CMD и CMD-SHELL.
- PostgreSQL pg_isready: статус сервера, коды выхода и границы подтверждения авторизации.