Kontrole stanu
OxPHP uruchamia wewnętrzny serwer HTTP na osobnym porcie na potrzeby monitorowania stanu, zbierania metryk i inspekcji konfiguracji. Pozostaje odizolowany od ruchu aplikacji, dzięki czemu monitoring nigdy nie konkuruje z żądaniami użytkowników.
Konfiguracja
Ustaw INTERNAL_ADDR, aby włączyć serwer wewnętrzny:
INTERNAL_ADDR=127.0.0.1:9090Gdy INTERNAL_ADDR nie jest ustawiony, serwer wewnętrzny się nie uruchamia i żadne endpointy kontroli stanu nie są dostępne.
INTERNAL_ADDR zawierający tylko port (:9090) wiąże się z 127.0.0.1; użyj jawnego 0.0.0.0:9090, aby udostępnić serwer wewnętrzny poza hostem. Gdy jest osiągalny spoza hosta, ogranicz dostęp za pomocą INTERNAL_ALLOW_IPS (lista dozwolonych CIDR/IP — ścieżki /metrics, /config oraz ścieżki wtyczek zwracają 403 klientom spoza niej, natomiast sondy kontroli stanu pozostają osiągalne); pętla zwrotna nie jest domyślna, więc wpisz 127.0.0.1/32, aby zachować dostęp z localhost. Serwer ostrzega przy starcie, jeśli listener jest udostępniony bez ustawionej listy dozwolonych.
Sondy Kubernetes
OxPHP udostępnia dedykowane endpointy dla każdego typu sondy Kubernetes. Każdy endpoint jest również dostępny pod krótkim aliasem (/healthz, /readyz, /startupz).
| Endpoint | Alias | Sprawdzenia | 200 | 503 |
|---|---|---|---|---|
/health/liveness |
/healthz |
Brak (żywy, jeśli odpowiada) | Zawsze | Nigdy |
/health/readiness |
/readyz |
Nie w trakcie zamykania, wątki workerów działają, pula nie jest zaklinowana, brak nieudanych wtyczek | Gotowy | Niegotowy |
/health/startup |
/startupz |
Wątki workerów działają | Gotowy | Niegotowy |
Liveness zawsze zwraca 200 OK. Jeśli proces jest w stanie odpowiedzieć na żądanie HTTP, jest żywy. Nie są wykonywane żadne sprawdzenia executora ani wtyczek — zapobiega to restartowaniu podów przez Kubernetes z powodu przejściowych problemów z pulą workerów.
Readiness zwraca 503 Service Unavailable, gdy:
- serwer jest w trakcie zamykania (trwa łagodne zamknięcie)
- zakończył się każdy wątek workera PHP (pula nie ma już komu przekazać żądania)
- pula jest zaklinowana: żądania czekają na workery, które są bezczynne i nic nie robią. Ten stan jest wykrywany tylko w trybie worker (zobacz Sygnał zaklinowanej puli)
- którakolwiek wtyczka zgłasza awarię
Podczas łagodnego zamknięcia readiness natychmiast zwraca 503, co powoduje, że Kubernetes usuwa pod z endpointów Service, zanim zakończy się drenaż połączeń.
Startup zwraca 503 Service Unavailable, dopóki nie działa żaden wątek workera. Użyj tej sondy, aby zapobiec przedwczesnemu zabijaniu przez liveness podczas wolnej inicjalizacji. Raportuje stan wątków, które istnieją od chwili utworzenia puli. Nie raportuje natomiast bootstrapu samej aplikacji, który w nich działa.
Sygnał zaklinowanej puli
Pula może przestać zdejmować pracę ze swojej kolejki, choć każdy należący do niej wątek żyje: żądania się piętrzą, żaden worker nie jest zajęty, a nic z kolejki nigdy nie zostaje wykonane. Każde żądanie i tak dostaje odpowiedź, gdy wyczerpie własny budżet oczekiwania (529 albo 499, jeśli to klient pierwszy przestał czekać), więc z zewnątrz nie wygląda to na zawieszenie. Żądania wciąż dostają odpowiedzi, ale żadne nie zostaje wykonane, a kolejka i tak się zapełnia, bo wpis zwalnia tylko jego pobranie. Każdy inny sygnał wygląda przy tym na zdrowy: wątki są wszystkie na miejscu, wtyczki działają, pliki statyczne są serwowane normalnie. Pula, która po minucie wciąż jest w tym stanie, zwykle nie wraca do siebie sama, dlatego readiness to zgłasza.
Serwer wypatruje kombinacji, której obsługująca ruch pula nie utrzymuje długo: praca czeka (żądania w kolejce albo rosnąca liczba odmów przyjęcia), a jednocześnie co najmniej jeden worker jest bezczynny i pula nie ukończyła niczego od poprzedniego skanu.
- Dwa kolejne skany co sekundę wystarczą do wpisu w logu.
- Readiness czeka jeszcze minutę. Pula, której wszystkie workery zatrzymały się naraz (najczęściej pula z jednym workerem), ma dokładnie ten kształt, gdy zastępczy worker ponownie uruchamia bootstrap aplikacji. Nowy worker obsługuje ruch na długo przed upływem minuty, a przedwczesne wycofanie repliki odebrałoby mu ruch, którego potrzebuje, żeby to pokazać.
- Readiness wraca do normy, gdy worker faktycznie ukończy żądanie (chwila spokoju nie jest powrotem do normy), albo po minucie, w której pula ani razu nie wyglądała na zaklinowaną. Drugie wyjście nie wymaga ruchu: zaklinowana pula trzyma swoją kolejkę, więc nie może spełnić tego warunku, a pula błędnie uznana za zaklinowaną — może. Bez niego stan byłby trwały, bo
503zatrzymuje właśnie te żądania, których ukończenie jest drugą drogą wyjścia.
Sygnał potrzebuje liczby żądań, które workery obsłużyły, a prowadzi ją tylko tryb worker (WORKER_MODE_ENABLED=true), więc istnieje wyłącznie w trybie worker. Poza nim readiness odpowiada na podstawie pozostałych trzech warunków, a oxphp_pool_stalled nie jest eksportowana wcale, zamiast jako stałe 0.
Czego sondy nie raportują
Obciążenia. Przeciążony serwer pozostaje Ready. Gdy kolejka przyjęć jest pełna, a budżet oczekiwania wyczerpany, żądania są odrzucane z 529 Site is overloaded, a replika, która tak robi, wciąż odpowiada, i to szybko, na działającej puli. Wycofanie jej z rotacji przerzuciłoby jej ruch na sąsiadów, a przy równomiernym przeciążeniu usuwałoby po kolei każdą replikę, aż Service nie miałby już żadnych endpointów: zdegradowana strona staje się nieosiągalna, a obciążenie nadal jest. Zamiast tego obserwuj oxphp_admission_refused_total{reason=...} i dodaj mocy. Czytaj jednak oxphp_admission_wait_wasted_total razem z oxphp_admission_wait_budget_us: odmowa wait_timeout sama w sobie nie oznacza puli, która nadąża z pracą, a tam, gdzie pula nie zwalnia żadnego workera przez cały czas oczekiwania żądania, lekarstwo jest odwrotne: skróć budżet oczekiwania zamiast powiększać pulę. Ten licznik liczy tylko wtedy, gdy budżet jest na swoim pułapie. Serwer mówi o tym także w logu: WARN przy skanie, w którym zaczyna zrzucać żądania, powtarzane mniej więcej raz na minutę, dopóki to trwa, oraz INFO, gdy minie pełna minuta bez odmowy. Zobacz Metryki Prometheus.
Wyczerpanego budżetu połączeń. Gdy wyczerpią się zezwolenia MAX_CONNECTIONS, pętla akceptująca staje, a nowi klienci nie dostają żadnej odpowiedzi, ale listener wewnętrzny nie przechodzi przez ten budżet, więc sondy odpowiadają dalej. To także obciążenie, a nie awaria, i nie zmienia readiness; raportuje je oxphp_accept_stalled.
Wolnych odpowiedzi. Nic tutaj nie mierzy opóźnień. Pula, która na wszystko odpowiada z opóźnieniem, jest Ready.
Wszystkie endpointy sond zwracają Content-Type: text/plain z nazwą sondy w treści (np. readiness). Kubernetes analizuje jedynie kod statusu HTTP.
# Quick check
curl -s -o /dev/null -w '%{http_code}' http://localhost:9090/health/readinessGET /health
Zwraca pełny stan serwera w formacie JSON. Używaj tego endpointu w dashboardach i systemach monitorowania, a nie do sond Kubernetes.
curl http://localhost:9090/healthOdpowiedź w stanie sprawnym (200 OK):
{
"active_connections": 7,
"executor_healthy": true,
"plugins": {},
"pool_stalled": false,
"status": "ok",
"total_requests": 48203,
"uptime_secs": 3612
}Odpowiedź w stanie pogorszonym (503 Service Unavailable):
{
"active_connections": 7,
"executor_healthy": false,
"plugins": {},
"pool_stalled": false,
"status": "degraded",
"total_requests": 48203,
"uptime_secs": 3612
}| Pole | Typ | Opis |
|---|---|---|
status |
string | "ok", gdy wszystkie podsystemy są sprawne, w przeciwnym razie "degraded". Obciążenie nie jest awarią podsystemu: serwer zrzucający 529 przy przeciążeniu raportuje "ok" |
uptime_secs |
integer | Liczba sekund od uruchomienia serwera |
total_requests |
integer | Łączna liczba żądań HTTP przetworzonych na porcie głównym |
active_connections |
integer | Aktualnie otwarte połączenia na porcie głównym |
executor_healthy |
boolean | Czy działa jeszcze jakikolwiek wątek workera PHP. false oznacza, że pula nie ma już wątku, któremu mogłaby przekazać żądanie |
pool_stalled |
boolean | Czy pula jest zaklinowana: żądania czekają na workery, które są bezczynne i nic nie robią. Zawsze false poza trybem worker, gdzie ten stan nie jest wykrywany (zobacz Sygnał zaklinowanej puli). true przełącza status HTTP na 503 |
plugins |
object<string, string> |
Stan poszczególnych wtyczek: klucze to nazwy wtyczek, a wartości to "ok", "degraded" lub "failed". Puste {}, gdy żadna wtyczka nie zgłasza stanu. Wtyczka "failed" powoduje przełączenie statusu HTTP na 503; "degraded" pojawia się tutaj, ale utrzymuje status na 200. |
GET /metrics
Zwraca metryki zgodne z Prometheusem w tekstowym formacie ekspozycji. Pełny wykaz metryk znajdziesz w Metryki Prometheus.
curl http://localhost:9090/metricsGET /config
Zwraca aktywną konfigurację serwera w formacie JSON. Ścieżki do certyfikatu i klucza TLS, internal_addr oraz error_pages_dir są usuwane z odpowiedzi ze względów bezpieczeństwa.
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
}Ścieżki do certyfikatu i klucza TLS nigdy nie są emitowane (tls_enabled wskazuje, czy TLS jest aktywny), a internal_addr oraz error_pages_dir są usuwane z serwowanej odpowiedzi.
Integracja z Kubernetes
Użyj dedykowanych endpointów sond dla każdego typu sondy:
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| Sonda | Efekt przy niepowodzeniu |
|---|---|
| Startup | Kubernetes czeka — nie zabija poda podczas inicjalizacji |
| Liveness | Kubernetes restartuje pod |
| Readiness | Kubernetes usuwa pod z endpointów Service (bez restartu) |
Krótkie aliasy (/healthz, /readyz, /startupz) są w pełni równoważne i można ich użyć zamiennie.
Kontrola stanu w Docker Compose
Ten przykład wiąże serwer wewnętrzny z 127.0.0.1, a przykład dla Kubernetes z 0.0.0.0: Docker uruchamia polecenie healthcheck wewnątrz własnej przestrzeni nazw sieci kontenera, gdzie pętla zwrotna dociera do listenera, natomiast kubelet wysyła swoje sondy httpGet spoza przestrzeni nazw sieci poda, gdzie pętla zwrotna do niego nie dociera.
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 oznacza kontener jako unhealthy po nieudanej skonfigurowanej liczbie ponownych prób, co może wyzwolić polityki restartu lub usunięcie z load balancera.
Prompty dla Twojego agenta
Te prompty zakładają agenta z podłączonym serwerem MCP dokumentacji OxPHP — sam przeczyta strony, których potrzebuje.
Skonfiguruj kontrole stanu OxPHP dla mojego projektu. Przeczytaj stronę `operations/health-checks` przez serwer MCP dokumentacji OxPHP.
Dodaj healthcheck do mojego docker-compose.yml oraz sondy liveness i readiness do mojego Deploymentu Kubernetes, opierając się na usługach i portach, które to repozytorium już definiuje.Mój kontener OxPHP jest restartowany przez orkiestrator i nie wiem dlaczego. Przeczytaj stronę `operations/health-checks` przez serwer MCP dokumentacji OxPHP.
Skonfrontuj moją konfigurację kontroli stanu z tym, co faktycznie udostępnia serwer wewnętrzny, i powiedz mi, która z tych dwóch rzeczy jest błędna.Zobacz też
- Metryki Prometheus — pełny wykaz wszystkich udostępnianych metryk
- Łagodne zamknięcie — jak sondy kontroli stanu współdziałają z drenażem podczas zamykania
- Dokumentacja konfiguracji — wszystkie zmienne środowiskowe, w tym
INTERNAL_ADDR