Перевірки стану
OxPHP запускає внутрішній HTTP-сервер на окремому порту для моніторингу стану, збору метрик і перегляду конфігурації. Він ізольований від трафіку застосунку, тож моніторинг ніколи не конкурує із запитами користувачів.
Налаштування
Щоб увімкнути внутрішній сервер, задайте INTERNAL_ADDR:
INTERNAL_ADDR=127.0.0.1:9090Якщо INTERNAL_ADDR не задано, внутрішній сервер не запускається і жодні ендпоінти стану не доступні.
INTERNAL_ADDR лише з портом (:9090) прив’язується до 127.0.0.1; щоб зробити внутрішній сервер доступним ззовні хоста, вкажіть явно 0.0.0.0:9090. Коли він доступний ззовні, обмежте доступ через INTERNAL_ALLOW_IPS (список дозволених CIDR/IP — /metrics, /config і шляхи плагінів повертають 403 клієнтам поза ним, а проби стану лишаються доступними); loopback не додається неявно, тож щоб зберегти доступ із localhost, додайте до списку 127.0.0.1/32. Під час запуску сервер попереджає, якщо слухач відкритий назовні без заданого списку дозволених адрес.
Проби Kubernetes
OxPHP надає окремий ендпоінт для кожного типу проб Kubernetes. Кожен ендпоінт також доступний під коротким псевдонімом (/healthz, /readyz, /startupz).
| Ендпоінт | Псевдонім | Що перевіряє | 200 | 503 |
|---|---|---|---|---|
/health/liveness |
/healthz |
Нічого (живий, якщо відповідає) | Завжди | Ніколи |
/health/readiness |
/readyz |
Сервер не завершує роботу, робочі потоки запущені, пул не заклинило, немає збійних плагінів | Готовий | Не готовий |
/health/startup |
/startupz |
Робочі потоки запущені | Готовий | Не готовий |
Liveness завжди повертає 200 OK. Якщо процес здатен відповісти на HTTP-запит, він живий. Жодних перевірок виконавця чи плагінів не виконується — це не дає Kubernetes перезапускати поди через тимчасові проблеми пулу воркерів.
Readiness повертає 503 Service Unavailable, коли:
- Сервер завершує роботу (триває плавне завершення)
- Усі робочі потоки PHP завершилися (пулу більше нікому передати запит)
- Пул заклинило: запити чекають на воркерів, які простоюють і нічого не виконують. Це виявляється лише в режимі воркерів (див. Сигнал заклиненого пулу)
- Будь-який плагін повідомляє про збій
Під час плавного завершення readiness одразу повертає 503, і Kubernetes вилучає под з ендпоінтів Service ще до того, як завершиться дренування.
Startup повертає 503 Service Unavailable, поки не запущено жодного робочого потоку. Використовуйте цю пробу, щоб liveness не вбивала под передчасно під час повільної ініціалізації. Вона звітує про потоки, які існують, щойно створено пул. Про власний бутстрап застосунку, що виконується всередині них, вона не звітує.
Сигнал заклиненого пулу
Пул може перестати забирати роботу зі своєї черги, хоча всі його потоки живі: запити накопичуються, жоден воркер не зайнятий, і нічого з черги так і не виконується. Кожен запит усе одно отримує відповідь, щойно вичерпає власний бюджет очікування (529 або 499, якщо клієнт перестав чекати раніше), тож ззовні це не схоже на зависання. Запити й далі отримують відповіді, але жоден із них не виконується, а черга все одно заповнюється, бо звільняє місце в ній лише взяття запиту в роботу. Усі інші сигнали при цьому показують норму: потоки на місці, плагіни в порядку, статичні файли віддаються як завжди. Пул, що й за хвилину лишається в такому стані, зазвичай сам не відновлюється, тому readiness про це й повідомляє.
Сервер стежить за поєднанням, якого пул, що обслуговує запити, довго не тримає: є робота, що чекає (запити в черзі або зростання відмов у допуску), і водночас щонайменше один воркер простоює, а пул не завершив нічого від попереднього сканування.
- Двох поспіль односекундних сканувань достатньо для рядка в лозі.
- Readiness чекає ще хвилину. Пул, у якого всі воркери зупинилися одночасно (найчастіше пул з одного воркера), має саме таку картину, поки заміна заново виконує бутстрап застосунку. Новий воркер знову обслуговує запити задовго до кінця хвилини, а передчасне вилучення репліки позбавило б її трафіку, потрібного, щоб це показати.
- Readiness відновлюється, коли воркер справді завершує запит (затишшя відновленням не вважається), або після хвилини, протягом якої пул жодного разу не виглядав заклиненим. Другий вихід не потребує трафіку: заклинений пул тримає свою чергу, тож цієї умови виконати не може, а пул, який помилково сприйняли за заклинений, — може. Без нього стан був би постійним, бо
503зупиняє саме ті запити, завершення яких є іншим виходом.
Сигналу потрібна кількість запитів, які опрацювали воркери, а її веде лише режим воркерів (WORKER_MODE_ENABLED=true), тож існує він тільки в режимі воркерів. В інших режимах readiness відповідає за трьома іншими умовами, а oxphp_pool_stalled відсутня зовсім, а не експортується як стала 0.
Про що проби не повідомляють
Навантаження. Перевантажений сервер лишається Ready. Коли черга допуску заповнена, а бюджет очікування вичерпано, запити відхиляються з 529 Site is overloaded, і репліка, що так робить, усе ще відповідає, причому швидко, на пулі, який працює. Вилучення її з ротації перекинуло б її трафік на сусідів, і за рівномірного перевантаження це по черзі вилучить усі репліки, аж поки в Service не залишиться жодного ендпоінта: сайт, що працював із погіршенням, стане недоступним, а навантаження нікуди не дінеться. Натомість стежте за oxphp_admission_refused_total{reason=...} і додавайте потужності. Проте читайте oxphp_admission_wait_wasted_total разом з oxphp_admission_wait_budget_us: відмова wait_timeout сама по собі не означає, що пул справляється з роботою, і там, де пул не звільняє жодного воркера за весь час очікування запиту, рішення протилежне: скоротити бюджет очікування, а не збільшувати пул. Цей лічильник рахує лише тоді, коли бюджет на своїй стелі. Сервер також повідомляє про це в лозі: WARN на скануванні, з якого починається скидання запитів, повторюваний приблизно раз на хвилину, поки воно триває, і INFO, коли мине ціла хвилина без жодної відмови. Див. Метрики Prometheus.
Вичерпаний бюджет з’єднань. Коли дозволи MAX_CONNECTIONS закінчуються, цикл прийому з’єднань призупиняється і нові клієнти не отримують жодної відповіді, але внутрішній слухач через цей бюджет не проходить, тож проби й далі відповідають. Це теж навантаження, а не збій, і на readiness воно не впливає; про нього повідомляє oxphp_accept_stalled.
Повільні відповіді. Ніщо тут не вимірює затримку. Пул, що відповідає на все із запізненням, має стан Ready.
Усі ендпоінти проб повертають Content-Type: text/plain з назвою проби в тілі (наприклад, readiness). Kubernetes перевіряє лише HTTP-код статусу.
# Quick check
curl -s -o /dev/null -w '%{http_code}' http://localhost:9090/health/readinessGET /health
Повертає повний стан сервера у форматі JSON. Використовуйте його для дашбордів і систем моніторингу, а не для проб Kubernetes.
curl http://localhost:9090/healthВідповідь у справному стані (200 OK):
{
"active_connections": 7,
"executor_healthy": true,
"plugins": {},
"pool_stalled": false,
"status": "ok",
"total_requests": 48203,
"uptime_secs": 3612
}Відповідь у погіршеному стані (503 Service Unavailable):
{
"active_connections": 7,
"executor_healthy": false,
"plugins": {},
"pool_stalled": false,
"status": "degraded",
"total_requests": 48203,
"uptime_secs": 3612
}| Поле | Тип | Опис |
|---|---|---|
status |
string | "ok", коли всі підсистеми справні, інакше "degraded". Навантаження не є збоєм підсистеми: сервер, що під перевантаженням відповідає 529, повідомляє "ok" |
uptime_secs |
integer | Кількість секунд від запуску сервера |
total_requests |
integer | Загальна кількість HTTP-запитів, оброблених на основному порту |
active_connections |
integer | Кількість відкритих зараз з’єднань на основному порту |
executor_healthy |
boolean | Чи працює ще хоч один робочий потік PHP. false означає, що в пулі не лишилося потоку, якому можна передати запит |
pool_stalled |
boolean | Чи заклинило пул: запити чекають на воркерів, які простоюють і нічого не виконують. Поза режимом воркерів завжди false, бо там цей стан не виявляється (див. Сигнал заклиненого пулу). true змінює HTTP-статус на 503 |
plugins |
object<string, string> |
Стан кожного плагіна: ключі — назви плагінів, значення — "ok", "degraded" або "failed". Порожній {}, коли жоден плагін не повідомляє про стан. Плагін зі станом "failed" змінює HTTP-статус на 503; "degraded" відображається тут, але статус лишається 200. |
GET /metrics
Повертає метрики, сумісні з Prometheus, у текстовому форматі експозиції. Повний довідник метрик див. у Метриках Prometheus.
curl http://localhost:9090/metricsGET /config
Повертає активну конфігурацію сервера у форматі JSON. Шляхи до TLS-сертифіката й ключа, internal_addr і error_pages_dir з міркувань безпеки вилучаються з відповіді.
curl -s http://localhost:9090/config | jq .{
"access_log": "all",
"async_in_flight_cap": 0,
"async_max_fibers": 256,
"async_queue_capacity": 0,
"async_workers": 0,
"brotli_level": 5,
"deny_file": {"loaded": false},
"document_root": "/var/www/html/public",
"drain_timeout_seconds": 25,
"entry_file": "/var/www/html/public/index.php",
"executor_type": "sapi",
"gzip_level": 6,
"header_timeout_seconds": 5,
"listen_addr": "0.0.0.0:443",
"log_level": "info",
"max_connections": 10000,
"max_query_body": 524288,
"php_workers": "8",
"plugins": {},
"queue_capacity": 1024,
"queue_max_waiting": 1024,
"queue_max_waiting_bytes": 67108864,
"queue_wait_timeout_ms": 1000,
"rate_limit": 100,
"rate_window_seconds": 60,
"runtime_hooks": [],
"static_max_age": 2592000,
"static_revalidate": false,
"superglobals_enabled": true,
"tls_enabled": true,
"tls_min_version": "1.2",
"tokio_workers": 4,
"trace_context": false,
"trusted_proxies": false,
"worker_max_memory_mib": 0,
"worker_mode_enabled": false,
"zstd_level": 6
}Шляхи до TLS-сертифіката й ключа ніколи не виводяться (tls_enabled показує, чи активний TLS), а internal_addr і error_pages_dir вилучаються з відповіді, яку віддає сервер.
Інтеграція з Kubernetes
Використовуйте окремі ендпоінти для кожного типу проб:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: oxphp
image: ghcr.io/oxphp/oxphp:0.13.0
env:
- name: INTERNAL_ADDR
value: "0.0.0.0:9090"
- name: INTERNAL_ALLOW_IPS
# the Prometheus scraper's network, plus loopback
value: "10.0.0.0/8,127.0.0.1/32"
ports:
- containerPort: 80
- containerPort: 9090
startupProbe:
httpGet:
path: /health/startup
port: 9090
initialDelaySeconds: 1
periodSeconds: 2
failureThreshold: 15
livenessProbe:
httpGet:
path: /health/liveness
port: 9090
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/readiness
port: 9090
periodSeconds: 5
failureThreshold: 2| Проба | Наслідок збою |
|---|---|
| Startup | Kubernetes чекає — не вбиває под під час ініціалізації |
| Liveness | Kubernetes перезапускає под |
| Readiness | Kubernetes вилучає под з ендпоінтів Service (без перезапуску) |
Короткі псевдоніми (/healthz, /readyz, /startupz) повністю рівнозначні, і їх можна використовувати замість основних шляхів.
Перевірка стану в Docker Compose
Цей приклад прив’язує внутрішній сервер до 127.0.0.1, а приклад для Kubernetes — до 0.0.0.0: Docker виконує команду healthcheck у власному мережевому просторі імен контейнера, де loopback дістається до слухача, а kubelet надсилає свої проби httpGet ззовні мережевого простору імен пода, де loopback до нього не дістається.
services:
oxphp:
image: ghcr.io/oxphp/oxphp:0.13.0
ports:
- "8080:80"
environment:
INTERNAL_ADDR: "127.0.0.1:9090"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:9090/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5sDocker позначає контейнер як unhealthy, коли не вдається задана кількість повторних спроб, що може запустити політики перезапуску або вилучення з балансувальника навантаження.
Промпти для вашого агента
Ці промпти розраховані на агента з підключеним MCP-сервером документації OxPHP — потрібні сторінки він прочитає сам.
Налаштуй перевірки стану OxPHP для мого проєкту. Прочитай сторінку `operations/health-checks` через MCP-сервер документації OxPHP.
Додай healthcheck до мого docker-compose.yml, а також liveness- і readiness-проби до мого Deployment у Kubernetes, спираючись на сервіси й порти, які вже визначено в цьому репозиторії.Оркестратор перезапускає мій контейнер OxPHP, і я не знаю чому. Прочитай сторінку `operations/health-checks` через MCP-сервер документації OxPHP.
Звір мою конфігурацію перевірок стану з тим, що насправді надає внутрішній сервер, і скажи, що з двох неправильне.Див. також
- Метрики Prometheus — повний довідник усіх метрик, які віддає сервер
- Плавне завершення роботи — як проби стану взаємодіють із дренуванням під час завершення
- Довідник конфігурації — усі змінні середовища, зокрема
INTERNAL_ADDR