惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
Vercel News
Vercel News
F
Fortinet All Blogs
月光博客
月光博客
G
Google Developers Blog
博客园 - Franky
GbyAI
GbyAI
The Cloudflare Blog
I
InfoQ
雷峰网
雷峰网
WordPress大学
WordPress大学
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
T
The Blog of Author Tim Ferriss
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
小众软件
小众软件
腾讯CDC
B
Blog
量子位
V
V2EX
S
SegmentFault 最新的问题
Google DeepMind News
Google DeepMind News

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Healthchecks в Docker Compose для Laravel: как сделать та...
Илья Лящук · 2026-05-27 · via Все публикации подряд на Хабре

3 мин

6.9K

Если вы хоть раз поднимали Laravel-проект в Docker Compose, наверняка сталкивались с ситуацией: контейнер с приложением стартует раньше, чем база данных успевает принять соединения, и миграции падают с ошибкой SQLSTATE[08006] или Connection refused. Перезапустишь — всё работает. На локалке терпимо, но в продакшене — это в падающие деплои.

По умолчанию Docker считает контейнер «живым», если его процесс запущен. Но это не всегда означает, что сервис внутри готов к работе.

Решение — правильно настроенные healthcheck’и и условие depends_on с параметром condition: service_healthy. В этой статье разберём, как это сделать для типичного стека Laravel: PHP-FPM, PostgreSQL, Redis и Nginx.

Почему depends_on без healthcheck не работает

Многие думают, что depends_on: [db] заставит контейнер с приложением ждать, пока база данных будет готова. На самом деле Docker Compose ждёт только запуска контейнера — то есть момента, когда процесс внутри стартовал. Между «процесс запустился» и «база готова принимать запросы» может пройти 5–15 секунд, особенно при первой инициализации.

Чтобы Compose действительно ждал готовности сервиса, нужно:

  1. Определить healthcheck у зависимого сервиса (БД, Redis и т. д.).

  2. В сервисе-потребителе указать depends_on в расширенной форме с condition: service_healthy.

Healthcheck для PostgreSQL

В официальный образ Postgres встроена утилита pg_isready — она и есть самая надёжная проверка готовности:

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 10
      start_period: 10s

Важный момент — параметр start_period. Он задаёт «грейс-период»: в течение этого времени неуспешные проверки не считаются провалами. Для Postgres это критично, потому что при первом запуске инициализируется PGDATA, и pg_isready временно отвечает «not accepting connections».

Healthcheck для Redis

redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
      start_period: 5s

Команда redis-cli ping возвращает PONG, когда сервер готов. Если у вас включена авторизация, добавьте -a $REDIS_PASSWORD или используйте переменную окружения REDISCLI_AUTH, чтобы пароль не светился в docker ps.

Healthcheck для PHP-FPM

С PHP-FPM сложнее: в стандартный образ php:8.3-fpm-alpine не входит ни curl, ни wget. Самый универсальный способ — использовать встроенный в PHP-FPM статус-пинг. Включаем его в конфиге пула:

  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000 | grep -q pong"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 15s

Не забудьте установить fcgi в Dockerfile:

RUN apk add --no-cache fcgi

Healthcheck для Nginx

Для Nginx достаточно простой проверки через wget (он есть в alpine-образе):

  nginx:
    image: nginx:alpine
    depends_on:
      app:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s
    ports:
      - "8080:80"

В конфиге Nginx добавьте отдельный location, который не идёт в PHP:

location = /health {
    access_log off;
    add_header Content-Type text/plain;
    return 200 "ok";
}

Полезные флаги

docker compose up --wait — поднимает все сервисы и блокирует консоль до тех пор, пока они не станут healthy (или не упадут). Идеально подходит для CI.

docker compose ps покажет столбец STATUS с пометкой (healthy) или (unhealthy) — удобно для быстрой диагностики на сервере.

Подводные камни

  • Слишком короткий interval создаёт лишнюю нагрузку. 5–10 секунд — обычно достаточно.

  • retries × interval определяет максимальное время ожидания. Если БД восстанавливается из бэкапа 2 минуты, а вы поставили retries: 3 и interval: 5s — контейнер пометится как unhealthy раньше, чем база реально упадёт.

  • Healthcheck должен проверять реальную готовность, а не просто наличие процесса. Например, pgrep postgres вернёт успех ещё до того, как Postgres примет первое соединение.

  • Не злоупотребляйте condition: service_healthy в проде на одном узле: если зависимый сервис упадёт и перезапустится, потребитель не «переподпишется» автоматически. Эта механика работает только при старте.

Итог

Несколько строчек YAML экономят часы отладки и убирают целый класс «магических» проблем: миграции, которые иногда падают; тесты, которые иногда красные; деплои, которые иногда обрываются. Если у вас в проекте до сих пор стоит голый depends_on без условий — самое время это починить.

Больше Healthcheck конструкций для docker compose вы можете найти здесь.