Vanilla Kubernetes

Drugi deo: Workload platforma, mreža, podaci i bezbednost
Liveness, readiness i startup probe – provere zdravlja aplikacija
V. Konfiguracija, zdravlje, resursi i raspoređivanje
22. Startup, readiness i liveness probe – zdravlje aplikacije i kontrolisano gašenje

Kubernetes može da utvrdi da li je proces kontejnera pokrenut, ali samo postojanje procesa ne znači da je aplikacija funkcionalna. Proces može da radi dok su njegove radne niti blokirane, interni red zahteva zagušen, keš neinicijalizovan ili HTTP server nesposoban da odgovori. Obrnuto, aplikacija može da bude potpuno ispravna, ali joj je za inicijalizaciju potrebno nekoliko minuta. Probe omogućavaju kubelet-u da razlikuje ova stanja i da na njih reaguje na predvidiv način.

Liveness, readiness i startup probe nisu tri varijante iste kontrole. One odgovaraju na tri različita operativna pitanja: da li kontejner treba restartovati, da li Pod trenutno sme da prima saobraćaj i da li je aplikacija završila početno pokretanje. Pouzdana konfiguracija zato ne počinje izborom URL putanje, već definisanjem preciznog ugovora između aplikacije i Kubernetes platforme.

Uloga proba u životnom ciklusu kontejnera

Probe periodično izvršava kubelet na worker čvoru na kome se Pod nalazi. Kontrola se, zavisno od konfiguracije, obavlja HTTP ili HTTPS zahtevom, pokušajem uspostavljanja TCP veze, pozivanjem standardnog gRPC health interfejsa ili izvršavanjem komande unutar kontejnera. Rezultat pojedinačne provere može biti uspeh, neuspeh ili nepoznato stanje. Kubelet zatim primenjuje pravila definisana parametrima probe i ažurira stanje kontejnera ili pokreće odgovarajuću akciju.

Probe nisu centralni monitoring sistem. API server ne šalje health zahteve aplikaciji, a scheduler ne odlučuje o svakom pojedinačnom testu. Kubelet izvršava provere lokalno i rezultat prenosi u status Pod-a. Zbog toga problem sa kubelet-om, mrežom čvora ili container runtime-om može da izgleda kao problem aplikacije. Administrator mora da posmatra Pod status, događaje, restartovanja, aplikacione logove i sistemske logove čvora kao povezane izvore podataka.

Proba Pitanje na koje odgovara Akcija nakon trajnog neuspeha Tipična namena
Liveness Da li se kontejner nalazi u stanju iz kog se može oporaviti bez restarta? Kubelet zaustavlja kontejner i primenjuje njegovu restart politiku. Detekcija blokade procesa, deadlock-a ili nepovratnog internog kvara.
Readiness Da li instanca sada može bezbedno da obrađuje saobraćaj? Pod ostaje pokrenut, ali njegov endpoint prestaje da bude spreman za Service. Zagrevanje keša, privremena preopterećenost, nedostupna obavezna zavisnost.
Startup Da li je aplikacija završila početnu inicijalizaciju? Kontejner se restartuje ako ne uspe u dozvoljenom vremenskom prozoru. Zaštita sporih aplikacija od prerane liveness provere.

Liveness probe – odluka o restartovanju kontejnera

Liveness probe treba da utvrdi samo stanje koje restart kontejnera realno može da popravi. Ako aplikacija više ne obrađuje događaje zbog deadlock-a, zaglavljenog event loop-a ili nepopravljivo oštećenog internog stanja, restart je razumna automatska reakcija. Ako je uzrok nedostupna baza podataka, DNS servis, spoljni API ili mrežni uređaj, restart aplikacije najčešće ne uklanja uzrok i može da izazove masovno restartovanje svih replika.

Kritična greška je povezivanje liveness probe sa dubokom proverom svih zavisnosti. Kratak prekid baze tada može istovremeno da obori liveness svih Pod-ova. Kubelet počinje da ih restartuje, aplikacije ponavljaju inicijalizaciju, otvaraju nove konekcije i dodatno opterećuju već nestabilnu bazu. Umesto kontrolisanog degradiranog rada nastaje restart oluja i produženi prekid usluge.

Dobar liveness endpoint treba da bude lokalni, brz, determinističan i jeftin. Obično proverava da li glavna izvršna petlja napreduje, da li ključne interne niti rade ili da li aplikacija nije označila sopstveno stanje kao nepovratno oštećeno. Endpoint ne treba da zahteva autentikaciju koja može isteći, poziva udaljene sisteme niti obavlja skupe upite.

Neuspešna liveness proba ne briše Pod objekat niti zahteva da Deployment napravi novi Pod. Kubelet zaustavlja konkretan kontejner i, za uobičajene workload Pod-ove sa politikom Always, pokreće ga ponovo u istom Pod-u. Broj restartovanja raste, a kubectl logs --previous omogućava pregled izlaza prethodne instance kontejnera. Ako se kvar ponavlja, kontejner može preći u stanje CrashLoopBackOff, sa progresivnim odlaganjem novih pokušaja.

Readiness probe – kontrola prijema saobraćaja

Readiness probe ne odlučuje da li proces treba restartovati. Ona određuje da li je kontejner spreman da učestvuje u pružanju usluge. Kada readiness ne uspe, Pod ostaje pokrenut i može nastaviti inicijalizaciju, obradu pozadinskog posla ili oporavak konekcija, ali njegovo stanje Ready postaje netačno. Kontrolni mehanizmi zatim ažuriraju EndpointSlice objekte, pa Service više ne usmerava redovan saobraćaj ka tom Pod-u.

Readiness je prikladna za proveru da li je aplikacija učitala konfiguraciju, završila migraciju lokalnog stanja, popunila obavezni keš, uspostavila minimalan skup konekcija ili ima dovoljno kapaciteta da primi novi zahtev. Ipak, zavisnost treba uključiti samo kada aplikacija bez nje zaista ne može da ispuni ugovor usluge. Ako može da vrati keširane podatke ili kontrolisan odgovor sa smanjenom funkcionalnošću, potpuno uklanjanje iz saobraćaja možda nije najbolja odluka.

Previše osetljiva readiness proba izaziva oscilovanje endpointa. Kratak skok latencije može ukloniti Pod iz Service-a, povećati opterećenje preostalih replika, oboriti njihove provere i izazvati kaskadni gubitak kapaciteta. Pragovi moraju da apsorbuju kratke prolazne smetnje, ali ne smeju toliko da kasne da korisnici dugo dobijaju greške. Izbor je deo SLO i kapacitativnog dizajna aplikacije, a ne samo YAML sintakse.

Readiness takođe učestvuje u rollout-u Deployment-a. Nova replika se ne smatra raspoloživom dok ne postane spremna i ne ispuni eventualni minReadySeconds. Pogrešno pozitivan rezultat pušta saobraćaj prerano, dok pogrešno negativan rezultat može zaustaviti rollout i dovesti do prekoračenja progressDeadlineSeconds.

Startup probe – zaštita sporog pokretanja

Startup probe se koristi kada aplikacija ima dugo ili promenljivo vreme inicijalizacije. Dok ona ne uspe, kubelet ne izvršava liveness i readiness probe za taj kontejner. Time se sprečava da liveness prekine aplikaciju dok učitava veliki model, obnavlja lokalni indeks, kompajlira interne artefakte ili zagreva keš.

Startup probe nije dodatno trajno nadgledanje. Nakon prvog uspeha ona završava svoju ulogu, a kontrolu preuzimaju liveness i readiness. Maksimalno vreme koje aplikacija dobija za pokretanje približno je proizvod failureThreshold i periodSeconds, uz uticaj trajanja samih pokušaja i početnog raspoređivanja provere. Na primer, prag od 30 neuspeha uz period od 10 sekundi daje oko pet minuta za inicijalizaciju.

Veliki initialDelaySeconds na liveness probi može privremeno da zaštiti spor start, ali ne pravi jasnu razliku između inicijalizacije i kasnijeg rada. Ako vreme startovanja varira, fiksno kašnjenje mora biti podešeno prema najsporijem slučaju i nepotrebno odlaže detekciju stvarnog kvara kod brzih startova. Startup probe eksplicitno signalizira trenutak kada je aplikacija prešla u operativnu fazu.

Mehanizmi izvođenja proba

Izbor mehanizma zavisi od protokola aplikacije, kvaliteta njenog health interfejsa i bezbednosnih ograničenja. Mehanizmi nisu potpuno zamenljivi: uspešno otvaranje TCP porta ne dokazuje da aplikacija obrađuje zahteve, dok složena exec komanda može uvoditi zavisnost od alata koji nisu deo minimalne kontejnerske slike.

Za HTTPS probe kubelet uspostavlja TLS vezu, ali standardna proba ne obezbeđuje punu proveru identiteta servera kao namenski aplikacioni klijent. Health endpoint zato ne treba pogrešno tretirati kao zamenu za end-to-end TLS validaciju, autentikaciju ili eksterni sintetički monitoring.

Parametri i proračun vremena reakcije

Parametar Značenje Administratorska posledica
initialDelaySeconds Odlaganje prve provere nakon pokretanja kontejnera. Ne treba ga koristiti kao grubu zamenu za startup probe kod promenljivog startovanja.
periodSeconds Planirani interval između provera. Kraći period ubrzava reakciju, ali povećava broj zahteva i mogućnost reagovanja na prolazne smetnje.
timeoutSeconds Najduže čekanje rezultata pojedinačnog pokušaja. Mora biti iznad normalne gornje granice odziva health handler-a, ali dovoljno kratak za pravovremenu reakciju.
failureThreshold Broj uzastopnih neuspeha potreban da se proba smatra neuspešnom. Određuje toleranciju na prolazne kvarove i približno vreme detekcije.
successThreshold Broj uzastopnih uspeha potreban nakon neuspeha. Za liveness i startup mora biti 1; kod readiness može usporiti prerano vraćanje instance u saobraćaj.
terminationGracePeriodSeconds Vreme dozvoljeno kontejneru da se ugasi nakon neuspešne liveness ili startup probe. Može se definisati na nivou Pod-a, a za probe i preciznije na nivou konkretne probe kada podržana verzija API-ja to dozvoljava.

Ako liveness ima periodSeconds: 10, timeoutSeconds: 2 i failureThreshold: 3, trajni kvar će obično biti potvrđen nakon približno 20 do 30 sekundi od prvog relevantnog pokušaja, a zatim sledi period gašenja i restart. To nije tvrdi real-time rok. Na raspored utiču trenutak nastanka kvara, trajanje pokušaja, opterećenje čvora i izvršavanje kubelet-a.

Pragovi se ne biraju napamet. Administrator treba da prikupi distribuciju vremena pokretanja i odziva health endpointa, uključujući hladan start, vršno opterećenje i oporavak posle prekida zavisnosti. Razuman cilj je da normalni najsporiji slučajevi ostanu unutar dozvoljenog prozora, dok trajni kvar bude detektovan pre nego što prekrši SLO ili preoptereti druge instance.

Dizajn health endpointa

Aplikacioni i platform tim treba da dokumentuju semantiku svakog endpointa. Odgovor 200 mora imati stabilno značenje, a neuspeh mora biti posledica kontrolisanog stanja, ne slučajne implementacione greške. Endpoint treba da bude izuzet od skupih filtera, spoljne autentikacije, velikog zapisivanja logova i rate limiting pravila namenjenih korisničkom saobraćaju.

Health handler mora imati sopstvenu vremensku granicu za interne operacije. Ako čeka beskonačno na zaključavanje ili udaljeni poziv, kubelet će videti timeout, ali veliki broj zaglavljenih zahteva može potrošiti niti aplikacije. Rezultat probe treba evidentirati u metriku ili strukturisani log samo kada to ne stvara visok volumen. Za svakodnevno praćenje pogodnije su metrike broja neuspeha, trajanja proba, restartovanja i promene broja spremnih replika.

Kontrolisano gašenje Pod-a i prekid saobraćaja

Probe opisuju stanje kontejnera dok radi, ali pouzdana usluga mora pravilno da se ponaša i kada se Pod uklanja zbog rollout-a, skaliranja, evikcije, preemption-a ili administratorskog brisanja. Kada započne regularno gašenje, API objekat dobija deletionTimestamp, EndpointSlice endpoint dobija terminating: true, a radi kompatibilnosti njegov uslov ready postaje false. Uslov serving može da ostane true dok aplikacija završava postojeće zahteve; potrošači EndpointSlice API-ja koji razumeju terminating endpoint-e mogu ga koristiti za kontrolisani drain. To nije isto što i neuspeh readiness probe, i aplikacija ne treba da računa na to da će svi eksterni load balancer-i istog trenutka prestati da šalju nove konekcije.

Kubelet pokreće preStop handler, ako je definisan, a zatim procesu 1 u kontejneru šalje signal SIGTERM. Odbrojavanje terminationGracePeriodSeconds počinje pre izvršavanja preStop; handler i aplikaciono gašenje dele isti budžet. Ako proces ne završi do isteka roka, kubelet šalje SIGKILL, pa se prekidaju aktivni zahtevi i neizvršeni flush podataka. Aplikacija zato mora da obrađuje SIGTERM, prestane da prihvata nov rad, dovrši zahteve u letu, zatvori konekcije i izađe sa predvidivim statusom. Shell kao PID 1 koji ne prosleđuje signal, predug preStop ili prekratak grace period česte su greške.

preStop nije obavezan kada aplikacija sama pravilno obrađuje SIGTERM i infrastruktura dovoljno brzo uklanja terminating endpoint iz redovnog saobraćaja. Kratko kontrolisano čekanje može pomoći starijim load balancer-ima da osveže stanje, ali nasumični dugi sleep samo troši raspoloživi rok. Ako se koristi, mora biti zasnovan na izmerenom vremenu propagacije i ostaviti dovoljno vremena aplikaciji za stvarni drain. Isti lifecycle handler može se izvršiti i pri restartu izazvanom liveness ili startup probom, zato ne sme pretpostaviti da se ceo Pod zauvek uklanja.

Faza Signal ili stanje Administratorska provera
Početak terminacije Pod dobija deletionTimestamp; endpoint postaje terminating: true i uobičajeno ready: false. Pratiti Pod i EndpointSlice, ne samo nestanak imena Pod-a.
Lifecycle handler preStop se izvršava unutar već započetog grace perioda. Potvrditi trajanje i izlaz handler-a u aplikacionim logovima.
Graceful stop Runtime zahteva zaustavljanje, a proces tipično prima SIGTERM. Potvrditi da PID 1 obrađuje signal i da aktivni zahtevi završavaju bez 5xx grešaka.
Prinudni stop Po isteku roka sledi SIGKILL. Pojava presečenih zahteva ukazuje na prekratak rok, zaglavljen handler ili neispravno rukovanje signalom.

Najčešće greške i posledice

Administratorska dijagnostika

Prva provera je status workload-a, broj spremnih replika, restartovanja i događaji. Kolona READY prikazuje broj spremnih kontejnera u Pod-u, ali sama ne objašnjava uzrok. kubectl describe pod prikazuje konfiguraciju proba, trenutno stanje kontejnera, prethodni razlog završetka i događaje poput Unhealthy, Killing ili neuspešnog sandbox mrežnog podešavanja.

kubectl -n <namespace> get deployment,pod -o wide
kubectl -n <namespace> describe pod <pod-name>
kubectl -n <namespace> get events --sort-by=.metadata.creationTimestamp
kubectl -n <namespace> logs <pod-name> -c <container-name> --since=15m
kubectl -n <namespace> logs <pod-name> -c <container-name> --previous

Ako readiness ne uspe, administrator proverava da li se Pod nalazi među spremnim endpointima Service-a. EndpointSlice prikazuje adresu Pod-a i uslov ready. Ako je aplikacija spremna, a endpoint nedostaje, proveravaju se Service selektori i Pod labels; ako readiness nije uspešna, istražuje se njena putanja, port, odgovor i zavisnosti.

kubectl -n <namespace> get service <service-name> -o yaml
kubectl -n <namespace> get endpointslice \
  -l kubernetes.io/service-name=<service-name> -o wide
kubectl -n <namespace> get pod <pod-name> \
  -o jsonpath='{.status.containerStatuses[*].ready}{"\n"}'

Kada događaji ukazuju na timeout ili grešku koju aplikacioni log ne objašnjava, sledeća granica je worker čvor. Na systemd Linux čvoru kubelet logovi se uobičajeno čitaju komandom journalctl -u kubelet. Pristup čvoru zahteva posebno operativno ovlašćenje i ne treba ga zameniti privilegovanim debug Pod-om bez odobrenja. Ako postoji sumnja na container runtime, ovlašćeni administrator može koristiti crictl na čvoru za pregled kontejnera i runtime logova.


Integrisani praktikum – pouzdane probe za internu API uslugu

Poslovni i tehnički kontekst. Interna usluga orders-api obrađuje zahteve drugih aplikacija. Pri pokretanju joj je potrebno približno 20 sekundi da učita lokalne referentne podatke. Posle toga može privremeno izgubiti spremnost bez potrebe za restartom, dok nepovratna blokada glavnog procesa treba da izazove restart. Cilj je implementirati tri odvojena health ugovora, dokazati ponašanje Service endpointa i kontrolisano izazvati liveness oporavak.

Referentno okruženje. Koristi se vanilla Kubernetes klaster sa najmanje dva worker čvora, namespace health-lab, Deployment orders-api sa tri replike, Service orders-api i slika python:3.12-slim. Python aplikacija se isporučuje kroz ConfigMap radi transparentnosti laboratorije. U produkciji bi aplikacioni kod bio deo kontrolisane, skenirane i potpisane slike sa nepromenljivim digestom.

Preduslovi i prava. Potrebni su funkcionalan kubectl kontekst, pravo čitanja Pod-ova, događaja, logova, Service i EndpointSlice objekata, kao i pravo kreiranja i izmene Namespace, ConfigMap, Deployment i Service objekata u laboratorijskom prostoru. Za enterprise rad ta prava treba obezbediti namenskom Role i RoleBinding konfiguracijom, a ne trajnim korišćenjem cluster-admin naloga.

  1. Provera konteksta i početnog stanja. Pre promene treba potvrditi identitet klastera i korisnika, jer primena manifesta u pogrešnom klasteru predstavlja ozbiljan operativni incident. Zatim se proverava da li namespace već postoji.
    kubectl config current-context
    kubectl auth can-i create namespaces
    kubectl auth can-i create deployments.apps --namespace=health-lab
    kubectl get namespace health-lab --ignore-not-found
    Validacija: Kontekst mora odgovarati odobrenom laboratorijskom klasteru. Komande auth can-i treba da vrate yes. Ako namespace postoji, pre nastavka proveriti vlasništvo, labels i postojeće resurse; ne prepisivati tuđe okruženje.
  2. Kreiranje namespace-a i aplikacionog koda. Sledeći manifest pravi izolovan namespace i ConfigMap sa jednostavnom HTTP aplikacijom. Aplikacija koristi datoteke u /state: started potvrđuje završen start, ready dozvoljava saobraćaj, a live označava da je proces operativan.
    cat > health-lab-base.yaml <<'EOF'
    apiVersion: v1
    kind: Namespace
    metadata:
      name: health-lab
      labels:
        environment: training
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: orders-api-code
      namespace: health-lab
    data:
      server.py: |
        import os
        import signal
        import sys
        import threading
        import time
        from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
    
    STATE_DIR = "/state"
    server = None
    
    def exists(name):
        return os.path.exists(os.path.join(STATE_DIR, name))
    
    class Handler(BaseHTTPRequestHandler):
        def send_result(self, status, message):
            body = (message + "\n").encode()
            self.send_response(status)
            self.send_header("Content-Type", "text/plain")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
    
        def do_GET(self):
            if self.path == "/startup":
                self.send_result(200 if exists("started") else 503,
                                 "started" if exists("started") else "starting")
            elif self.path == "/ready":
                self.send_result(200 if exists("ready") else 503,
                                 "ready" if exists("ready") else "not ready")
            elif self.path == "/live":
                self.send_result(200 if exists("live") else 500,
                                 "alive" if exists("live") else "unhealthy")
            elif self.path == "/":
                self.send_result(200, "orders-api")
            else:
                self.send_result(404, "not found")
    
        def log_message(self, fmt, *args):
            print("%s %s" % (self.address_string(), fmt % args), flush=True)
    
    def stop(signum, frame):
        ready = os.path.join(STATE_DIR, "ready")
        if os.path.exists(ready):
            os.remove(ready)
        print("SIGTERM received; stopping HTTP server", flush=True)
        server.shutdown()
        sys.exit(0)
    
    os.makedirs(STATE_DIR, exist_ok=True)
    open(os.path.join(STATE_DIR, "live"), "w").close()
    server = ThreadingHTTPServer(("0.0.0.0", 8080), Handler)
    threading.Thread(target=server.serve_forever, daemon=True).start()
    signal.signal(signal.SIGTERM, stop)
    print("orders-api initialization started", flush=True)
    time.sleep(20)
    open(os.path.join(STATE_DIR, "started"), "w").close()
    open(os.path.join(STATE_DIR, "ready"), "w").close()
    print("orders-api is ready", flush=True)
    while True:
        time.sleep(1)
    
    EOF
    
    kubectl apply -f health-lab-base.yaml
    Validacija: ConfigMap mora postojati u namespace-u i sadržati ključ server.py.
    kubectl -n health-lab get configmap orders-api-code
    kubectl -n health-lab get configmap orders-api-code 
    -o jsonpath='{.data.server.py}' | head
  3. Implementacija Deployment-a i Service-a. Deployment koristi tri replike i strategiju postepenog ažuriranja. Startup probe dozvoljava do 90 sekundi za početak rada. Readiness zahteva dva uzastopna uspeha pre vraćanja instance u saobraćaj, dok liveness zahteva tri neuspeha pre restarta. Service bira samo Pod-ove sa labelom app: orders-api.
    cat > health-lab-workload.yaml <<'EOF'
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: orders-api
      namespace: health-lab
    spec:
      replicas: 3
      minReadySeconds: 5
      progressDeadlineSeconds: 180
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 0
          maxSurge: 1
      selector:
        matchLabels:
          app: orders-api
      template:
        metadata:
          labels:
            app: orders-api
        spec:
          terminationGracePeriodSeconds: 30
          automountServiceAccountToken: false
          securityContext:
            fsGroup: 10001
            seccompProfile:
              type: RuntimeDefault
          containers:
            - name: orders-api
              image: python:3.12-slim
              imagePullPolicy: IfNotPresent
              command:
                - python
                - /opt/orders/server.py
              lifecycle:
                preStop:
                  exec:
                    command:
                      - sh
                      - -c
                      - rm -f /state/ready; sleep 5
              ports:
                - name: http
                  containerPort: 8080
              resources:
                requests:
                  cpu: 50m
                  memory: 64Mi
                limits:
                  cpu: 500m
                  memory: 256Mi
              securityContext:
                allowPrivilegeEscalation: false
                capabilities:
                  drop:
                    - ALL
                runAsNonRoot: true
                runAsUser: 10001
                runAsGroup: 10001
              startupProbe:
                httpGet:
                  path: /startup
                  port: http
                periodSeconds: 5
                timeoutSeconds: 2
                failureThreshold: 18
                successThreshold: 1
              readinessProbe:
                httpGet:
                  path: /ready
                  port: http
                periodSeconds: 5
                timeoutSeconds: 2
                failureThreshold: 2
                successThreshold: 2
              livenessProbe:
                httpGet:
                  path: /live
                  port: http
                periodSeconds: 10
                timeoutSeconds: 2
                failureThreshold: 3
                successThreshold: 1
                terminationGracePeriodSeconds: 10
              volumeMounts:
                - name: application-code
                  mountPath: /opt/orders
                  readOnly: true
                - name: runtime-state
                  mountPath: /state
          volumes:
            - name: application-code
              configMap:
                name: orders-api-code
            - name: runtime-state
              emptyDir: {}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: orders-api
      namespace: health-lab
    spec:
      selector:
        app: orders-api
      ports:
        - name: http
          port: 80
          targetPort: http
      type: ClusterIP
    EOF
    
    kubectl apply -f health-lab-workload.yaml
    Validacija: API server treba da prihvati oba objekta. Ako verzija klastera ne podržava terminationGracePeriodSeconds u probi, server će odbiti manifest; polje tada treba ukloniti tek nakon provere verzije i dokumentovanja promene.
    kubectl -n health-lab get deployment orders-api
    kubectl -n health-lab get service orders-api
    kubectl -n health-lab get deployment orders-api 
    -o jsonpath='{.spec.template.spec.containers[0].startupProbe.httpGet.path}{"\n"}'
  4. Praćenje startup faze. Odmah nakon kreiranja Pod-ovi treba da budu pokrenuti, ali još nespremni. Startup endpoint vraća 503 dok aplikacija ne završi simuliranu inicijalizaciju. Po prvom uspehu startup probe počinju readiness i liveness kontrole.
    kubectl -n health-lab get pods -l app=orders-api -w
    Validacija: U prvih približno 20 do 30 sekundi očekuje se 0/1 u koloni READY, bez restartovanja. Nakon inicijalizacije sva tri Pod-a treba da postanu 1/1 Running. Praćenje se prekida kombinacijom Ctrl+C, a rollout se potvrđuje posebno.
    kubectl -n health-lab rollout status deployment/orders-api --timeout=180s
    kubectl -n health-lab get pods -l app=orders-api \
      -o custom-columns=NAME:.metadata.name,READY:.status.containerStatuses[0].ready,RESTARTS:.status.containerStatuses[0].restartCount
  5. Provera aplikacije kroz Service. Privremeni klijentski Pod šalje više zahteva Service-u. Opcija --rm uklanja klijentski Pod po završetku, pa u namespace-u ne ostaje pomoćni workload.
    kubectl -n health-lab run health-client \
      --image=curlimages/curl:8.12.1 \
      --restart=Never --rm -i \
      -- sh -c 'for i in 1 2 3 4 5; do
        curl -fsS http://orders-api/;
      done'
    Validacija: Svaki zahtev treba da vrati orders-api. EndpointSlice treba da sadrži tri adrese sa spremnim stanjem.
    kubectl -n health-lab get endpointslice \
      -l kubernetes.io/service-name=orders-api -o wide
    kubectl -n health-lab get deployment orders-api \
      -o jsonpath='{.status.readyReplicas}{" ready of "}{.status.replicas}{" replicas\n"}'
  6. Negativni readiness test. Iz jedne instance uklanja se datoteka /state/ready. Ovo je kontrolisana laboratorijska promena koja ne restartuje proces. U produkciji se ne menja stanje aplikacije komandom kubectl exec bez odobrenog testa i plana povratka.
    TEST_POD=$(kubectl -n health-lab get pod -l app=orders-api \
      -o jsonpath='{.items[0].metadata.name}')
    echo "$TEST_POD"
    kubectl -n health-lab exec "$TEST_POD" -c orders-api -- \
      rm /state/ready
    Validacija: Posle dva neuspešna readiness pokušaja, približno u okviru 10 do 15 sekundi, testni Pod treba da bude 0/1 Running, dok ostala dva ostaju spremna. Broj restartovanja testnog Pod-a ne sme da poraste.
    sleep 15
    kubectl -n health-lab get pod "$TEST_POD"
    kubectl -n health-lab get pod "$TEST_POD" \
      -o jsonpath='{.status.containerStatuses[0].restartCount}{" restarts\n"}'
    kubectl -n health-lab get endpointslice \
      -l kubernetes.io/service-name=orders-api -o yaml
    U EndpointSlice izlazu adresa testnog Pod-a treba da ima uslov ready: false ili da ne učestvuje među endpointima koji primaju redovan Service saobraćaj, zavisno od prikaza klijenta i verzije API-ja.
  7. Oporavak readiness stanja. Datoteka se vraća, ali je zbog successThreshold: 2 potrebno da dve uzastopne provere uspeju pre nego što se Pod vrati u saobraćaj.
    kubectl -n health-lab exec "$TEST_POD" -c orders-api -- \
      sh -c 'touch /state/ready'
    sleep 15
    kubectl -n health-lab get pod "$TEST_POD"
    kubectl -n health-lab get deployment orders-api
    Validacija: Pod mora ponovo biti 1/1 Running, Deployment mora imati tri spremne replike, a restart count mora ostati nepromenjen. Time je dokazano da readiness kontroliše saobraćaj, a ne životni ciklus procesa.
  8. Negativni liveness test. Uklanjanje datoteke /state/live simulira nepovratno interno stanje. Nakon tri neuspešna pokušaja kubelet treba da zaustavi kontejner. Pošto je /state emptyDir na nivou Pod-a, sadržaj volumena opstaje kroz restart kontejnera, ali Python aplikacija pri svakom novom pokretanju ponovo pravi live datoteku i prolazi inicijalizaciju.
    BEFORE_RESTARTS=$(kubectl -n health-lab get pod "$TEST_POD" \
      -o jsonpath='{.status.containerStatuses[0].restartCount}')
    kubectl -n health-lab exec "$TEST_POD" -c orders-api -- \
      rm /state/live
    echo "Restart count before test: $BEFORE_RESTARTS"
    Validacija: U roku od približno 30 do 50 sekundi očekuje se događaj neuspešne liveness probe i restart kontejnera. Pod će tokom ponovne inicijalizacije privremeno biti nespreman.
    sleep 45
    kubectl -n health-lab get pod "$TEST_POD"
    kubectl -n health-lab describe pod "$TEST_POD"
    kubectl -n health-lab logs "$TEST_POD" -c orders-api --previous
    Broj restartovanja mora biti najmanje za jedan veći od početne vrednosti. U događajima se očekuju poruke koje ukazuju na neuspeh liveness probe i zaustavljanje kontejnera, dok prethodni log treba da sačuva izlaz ugašene instance ako ga runtime još čuva.
  9. Završna funkcionalna provera. Po završetku ponovne inicijalizacije Deployment mora da se vrati na tri spremne replike bez ručne zamene Pod-a.
    kubectl -n health-lab rollout status deployment/orders-api --timeout=180s
    kubectl -n health-lab get pods -l app=orders-api
    kubectl -n health-lab get endpointslice \
      -l kubernetes.io/service-name=orders-api -o wide
    kubectl -n health-lab run final-health-client \
      --image=curlimages/curl:8.12.1 \
      --restart=Never --rm -i \
      -- curl -fsS http://orders-api/
    Kriterijumi prihvatanja: sva tri Pod-a su 1/1 Running; Deployment prikazuje tri spremne i tri raspoložive replike; Service zahtev vraća orders-api; readiness test nije izazvao restart; liveness test jeste povećao restart count tačno testirane instance; rollout je završen unutar 180 sekundi.
  10. Kontrolisani test graceful shutdown-a. Ovaj korak namerno briše samo jedan Pod kojim upravlja Deployment; kontroler će odmah napraviti zamenu. Pre testa potvrđuju se tri spremne replike i pokreće praćenje događaja u posebnom terminalu. Brisanje celog Deployment-a nije potrebno i povećalo bi rizik laboratorije.
    kubectl -n health-lab get deployment orders-api
    TERM_POD=$(kubectl -n health-lab get pod -l app=orders-api \
      -o jsonpath='{.items[0].metadata.name}')
    kubectl -n health-lab get events --watch \
      --field-selector involvedObject.name="$TERM_POD"
    U drugom terminalu izvršava se state-changing komanda sa podrazumevanim grace periodom iz Pod specifikacije, a zatim se prate Pod i EndpointSlice uslovi.
    kubectl -n health-lab delete pod "$TERM_POD" --wait=false
    kubectl -n health-lab get pod "$TERM_POD" -w
    Dok Pod postoji u stanju Terminating, u trećem terminalu treba uhvatiti EndpointSlice prikaz. Na brzom klasteru prozor može biti kratak zbog pet sekundi u preStop handler-u.
    kubectl -n health-lab get endpointslice \
      -l kubernetes.io/service-name=orders-api -o yaml
    kubectl -n health-lab rollout status deployment/orders-api --timeout=180s
    kubectl -n health-lab get pods -l app=orders-api
    Očekivani rezultat i validacija: endpoint testiranog Pod-a privremeno prikazuje terminating: true i ready: false; aplikacioni log sadrži poruku o prijemu SIGTERM ako je log još dostupan u centralnom logging sistemu; Deployment pravi zamenski Pod i vraća se na tri spremne replike. Odsustvo 5xx grešaka tokom kontinuiranog testa saobraćaja potvrđuje funkcionalni cilj. Ako Pod ostane u Terminating do isteka 30 sekundi ili se zahtevi prekidaju, proveravaju se trajanje preStop handler-a, rukovanje signalom i vreme propagacije load balancer-a pre povećavanja grace perioda.
  11. Rollback i cleanup. Ako je manifest deo GitOps repozitorijuma, ispravan rollback je vraćanje potvrđene prethodne revizije u repozitorijumu i pokretanje kontrolisanog usaglašavanja. Za imperative Deployment izmene može se koristiti rollout istorija i povratak na prethodnu reviziju, ali samo ako istorija sadrži odgovarajući ReplicaSet.
    kubectl -n health-lab rollout history deployment/orders-api
    kubectl -n health-lab rollout undo deployment/orders-api
    kubectl -n health-lab rollout status deployment/orders-api --timeout=180s
    Navedeni rollout undo ne treba izvršavati bez potrebe, jer u ovoj laboratoriji možda ne postoji korisna prethodna revizija. Potpuno uklanjanje laboratorije je destruktivno i briše sve objekte u namespace-u.

    Upozorenje: sledeću komandu koristiti samo kada je potvrđeno da namespace pripada isključivo ovoj vežbi.

    kubectl delete namespace health-lab
    rm -f health-lab-base.yaml health-lab-workload.yaml
    Validacija cleanup-a:
    kubectl get namespace health-lab --ignore-not-found
    test ! -e health-lab-base.yaml && test ! -e health-lab-workload.yaml
    Uspešan rezultat je odsustvo namespace-a i lokalnih laboratorijskih manifesta.

Logovi, metrike i razlikovanje uzroka

Aplikacioni logovi u ovom scenariju odlaze na standardni izlaz i dostupni su kroz kubectl logs. Prethodna instanca kontejnera dostupna je opcijom --previous dok je runtime nije uklonio. Kubernetes događaji prikazuju neposredan rezultat probe i akciju kubelet-a, ali nisu trajni audit zapis i imaju ograničeno vreme zadržavanja. Produkciono okruženje zato treba da šalje aplikacione i sistemske logove u cluster-level logging platformu nezavisnu od životnog ciklusa Pod-a i čvora.

Kada health zahtev vraća HTTP 503, aplikacija je dostupna na mrežnom nivou i svesno prijavljuje nespremnost. Connection refused ukazuje da na ciljanom portu nema listener-a ili da proces sluša na pogrešnom interfejsu. Timeout može poticati od zagušenog procesa, CPU throttling-a, blokirane mreže, prekratkog praga ili problema čvora. Greška command not found kod exec probe znači da je pogrešan mehanizam ili sadržaj slike, a ne nužno kvar poslovne aplikacije.

Za produkciono praćenje treba povezati restart count, broj spremnih replika, trajanje rollout-a, stopu HTTP grešaka, latenciju aplikacije, CPU throttling, memoriju, OOM događaje i dostupnost ključnih zavisnosti. Sama poruka „probe failed” opisuje neposredni okidač, ali ne potvrđuje osnovni uzrok.

Administratorske prakse

Kontrolna lista

Zaključak

Administrator nakon ove lekcije treba da razume da probe nisu univerzalni odgovor na pitanje da li je aplikacija „zdrava”, već tri odvojena upravljačka signala. Liveness dozvoljava platformi da restartuje kontejner samo kada restart ima realnu vrednost. Readiness štiti korisnički saobraćaj i rollout od instance koja trenutno ne može da ispuni ugovor usluge. Startup štiti legitimno sporu inicijalizaciju od preuranjene reakcije liveness mehanizma.

Praktična veština nije samo pisanje httpGet bloka, već izbor semantike endpointa, proračun vremenskih pragova, validacija EndpointSlice stanja, analiza događaja i prethodnih logova, kontrolisani negativni test i bezbedan rollback. Pogrešno podešena proba može smanjiti raspoloživost više nego kvar koji pokušava da otkrije: može izazvati restart oluju, izbaciti sve replike iz saobraćaja ili pustiti zahteve aplikaciji koja još nije spremna.

Granica odgovornosti administratora je jasna. Platform tim obezbeđuje ispravnu Kubernetes konfiguraciju, observability, kontrolu promena i zaštitne pragove, ali ne treba samostalno da izmišlja poslovnu semantiku health endpointa. Aplikacioni tim mora da potvrdi koja interna i zavisna stanja predstavljaju živost, spremnost i završen start. Neobjašnjivo pogoršanje svih replika, problem worker čvora, runtime greške, mrežna nedostupnost ili ponašanje koje zahteva promenu aplikacionog koda razlog su za pravovremenu eskalaciju, uz očuvane tehničke dokaze i bez nasumičnog povećavanja pragova.

← Prethodna lekcija Povratak na sadržaj Sledeća lekcija →