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:

bash
INTERNAL_ADDR=127.0.0.1:9090

Gdy INTERNAL_ADDR nie jest ustawiony, serwer wewnętrzny się nie uruchamia i żadne endpointy kontroli stanu nie są dostępne.

Note

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 503 zatrzymuje właśnie te żądania, których ukończenie jest drugą drogą wyjścia.
Note

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.

bash
# Quick check curl -s -o /dev/null -w '%{http_code}' http://localhost:9090/health/readiness

GET /health

Zwraca pełny stan serwera w formacie JSON. Używaj tego endpointu w dashboardach i systemach monitorowania, a nie do sond Kubernetes.

bash
curl http://localhost:9090/health

Odpowiedź w stanie sprawnym (200 OK):

json
{ "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):

json
{ "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.

bash
curl http://localhost:9090/metrics

GET /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.

bash
curl -s http://localhost:9090/config | jq .
json
{ "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 }
Note

Ś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:

yaml
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.

compose.yaml
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: 5s

Docker 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.

prompt
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.
prompt
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ż

Znalazłeś błąd? Zgłoś go →