Mundarija (31)
- Bu darsda
- 1. Nega bu kerak?
- 2. Uch savol
- 2.1 Jadval
- 2.2 Har biri qaysi xatoni tuzatadi
- 3. Probe arifmetikasi
- 3.1 Sozlamalar
- 3.2 Model
- 3.3 Timeout tuzog'i
- 4. Nimani tekshirish kerak
- 4.1 Readiness: faqat "usiz ishlay olmayman"
- 4.2 Yengil va tez
- 4.3 Xavfsizlik
- 5. Nomlar va formatlar
- 5.1 /healthz va boshqalar
- 5.2 Javob formati
- 5.3 Docker HEALTHCHECK
- 6. Ko'p uchraydigan xatolar
- 7. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): probe byudjeti
- 3-mashq (qiyin): o'z health agregatoringiz
- 8. Loyiha qadami: /health/startup
- 8.1 Holat: boshlandi
- 8.2 Controller
- 8.3 Testlar o'z ishini qildi
- 8.4 Kuzatuv testi
- 8.5 Tekshiruv
- 9. Real ishda
- Xulosa
- Manbalar
Health check endpoint'lari: liveness, readiness, startup va probe sozlamalari
Qisqacha: Orkestrator ilovaga uch xil savol beradi. Startup — "ishga tushib bo'ldingmi?" (sekin startda jarayonni o'ldirmaslik uchun). Liveness (tiriklik tekshiruvi) — "tirikmisan?" (qotib qolsa, qayta ishga tushirish uchun). Readiness (tayyorlik tekshiruvi) — "so'rov qabul qila olasanmi?" (yo'q bo'lsa, trafikni boshqa nusxaga burish uchun). Health endpoint yengil va tez bo'lsin, faqat o'zi tuzata olmaydigan narsani tekshirsin. Kanon o'lchovida bir xil
vazifalar-api2.5 soniyada ham, 12.9 soniyada ham ishga tushdi — shuning uchun loyihaga/health/startupqo'shiladi.
Bu darsda
- Uch tekshiruvning farqini va har biri yiqilganda orkestrator nima qilishini tushuntirasiz.
- Probe sozlamalaridan (davr, chegara) jarayon qachon o'ldirilishini hisoblaysiz.
- Readiness'ga qaysi bog'liqlik kiradi, qaysi biri kirmasligini asoslaysiz.
- Docker
HEALTHCHECKva/healthznomlash odatlarini bilasiz. vazifalar-apiga/health/startupqo'shib, marshrut testlari uni qanday ushlaganini ko'rasiz.
Oldin bilishingiz kerak: Health check'lar: @nestjs/terminus, Graceful shutdown to'liq, Event loop'ni bloklash, Reflector va DiscoveryService.
30-qismda /health/live va /health/ready ni terminus bilan qurgan edik. O'tgan darsda readiness yangi vazifa oldi: yopilish boshlanganda 503 qaytarish. Bu darsda tekshiruvlarning uchinchisini qo'shamiz va eng muhimini o'rganamiz — orkestrator ularga qanday qaror chiqaradi.
1. Nega bu kerak?
Kanon 31-qism davomida vazifalar-api ni 15 marta jonli ishga tushirdi. Har safar jarayon boshlanganidan /health/live birinchi marta 200 qaytargunicha vaqt o'lchandi. Natija bir xil kod uchun keng tarqaldi:
- eng tez100 %
- mediana (15 tadan)106 %
- Prisma + kesh232 %
- mashina band514 %
Manba: IlmHamroh kanon o'lchovi, 2026-11-02/03: vazifalar-api (NestJS 12.2.0, Node 24.21.0), 15 jonli ishga tushirish, jarayon boshidan /health/live 200 gacha: eng tez 2 509 ms, mediana 2 655 ms, Prisma + kesh 5 829 ms, mashina band (boshqa og'ir jarayonlar) 12 899 ms; bitta mashina
Odatda ~2.6 soniya. Lekin mashina band bo'lganda — 12.9 soniya, besh baravar ko'p. Endi tasavvur qiling: Kubernetes'da liveness har 3 soniyada so'raydi va 3 marta ketma-ket javob olmasa, jarayonni o'ldiradi. Jarayon ~9 soniyada o'ldiriladi — u hali ishga tushib ulgurmagan. Qayta ishga tushadi, yana 9 soniyada o'ldiriladi. Bu cheksiz aylanish — crash loop. Ilova hech qachon ishlamaydi, loglarda esa xato yo'q.
Kubernetes'da bu holatning o'z nomi bor — CrashLoopBackOff. kubectl get pods buyrug'i pod holati ustunida aynan shu so'zni ko'rsatadi. "Back-off" — "orqaga chekinish": har yangi qayta ishga tushirishdan oldin kutish ikki baravar oshadi — 10 s, 20 s, 40 s, 80 s … eng ko'pi 5 daqiqa. Konteyner 10 daqiqa muammosiz ishlasa, hisob noldan boshlanadi (Kubernetes hujjatiga ko'ra, 2026-11-03 da tekshirildi). Demak crash loop'dagi ilova vaqt o'tgan sari tobora kamroq urinadi — ishga tushib ulgurish imkoni esa baribir yo'q.
Bu — bitta misol. Health tekshiruvlarini noto'g'ri sozlash ilovani sog'lom paytida ham o'ldiradi. Orkestrator aqlli emas: u siz bergan savolga siz bergan javobni oladi va qoidaga ko'ra harakat qiladi.
O'xshatish: tez yordam brigadasi. "Nafas olyaptimi?" — liveness. "Ishga chiqa oladimi?" — readiness. "Narkozdan uyg'ondimi?" — startup. Narkozdan endi uyg'onayotgan bemorga "nafas olyaptimi" savolini juda qattiq qo'ysangiz, uni behuda reanimatsiyaga olib ketasiz.
2. Uch savol
2.1 Jadval
| Startup | Liveness | Readiness | |
|---|---|---|---|
| Savol | ishga tushib bo'ldingmi? | tirikmisan? | so'rov ola olasanmi? |
| Qachon | faqat start paytida | doim | doim |
| Yiqilsa | (chegarada) qayta ishga tushirish | qayta ishga tushirish | trafikdan chiqarish |
| Nimani tekshiradi | start tugadimi | hech narsa — javob berdimi | baza, xotira, yopilish |
Kubernetes'da startup probe sozlangan bo'lsa, u muvaffaqiyatli o'tguncha liveness va readiness ishlamaydi. Startup bir marta o'tgach, boshqa so'ralmaydi — nazoratni liveness oladi. Hujjatda shunday yozilgan: "startup probe muvaffaqiyatli o'tguncha Kubernetes liveness va readiness tekshiruvlarini bajarmaydi" (2026-11-03 da tekshirildi; klasterda sinalmagan).
Jadvaldagi "Yiqilsa" qatoriga qarang: liveness va startup — konteynerni qayta ishga tushiradi (pod o'sha node'da qoladi), readiness esa hech narsani o'ldirmaydi, faqat nusxani balanser ro'yxatidan vaqtincha chiqaradi. Readiness yana 200 qaytarsa, nusxa ro'yxatga o'zi qaytadi.
2.2 Har biri qaysi xatoni tuzatadi
Tekshiruvni tanlashning eng foydali savoli: "bu yiqilsa, orkestratorning harakati muammoni tuzatadimi?" Diagrammada uchala tekshiruv va kubelet'ning javobi — yuqoridan pastga o'qing:
flowchart TD
S["Konteyner ishga tushdi"] --> P{"startup 200?"}
P -->|"chegaragacha yo'q"| R["qayta ishga tushirish"]
P -->|"ha"| L{"liveness 200?"}
L -->|"chegaragacha yo'q"| R
L -->|"ha"| D{"readiness 200?"}
D -->|"yo'q"| C["trafikdan chiqarish"]
D -->|"ha"| T["trafik keladi"]
C -.->|"yana 200"| TDiqqat qiling: o'ldiradigan o'q faqat startup va liveness'dan chiqadi. Readiness'dan chiqqan o'q konteynerga tegmaydi. Diagramma soddalashtirilgan: startup o'tgach, liveness va readiness bir-biridan mustaqil, parallel so'raladi.
- Event loop cheksiz siklda qotdi (Event loop'ni bloklash) — qayta ishga tushirish tuzatadi. Demak — liveness.
- Baza uzildi — qayta ishga tushirish tuzatmaydi (yangi jarayon ham bazaga ulana olmaydi). Lekin trafikni boshqa nusxaga burish yordam berishi mumkin. Demak — readiness.
- Start sekin — kutish tuzatadi. Demak — startup.
Shuning uchun liveness'da tashqi bog'liqlik yo'q: baza uzilsa, hamma nusxa birdaniga qayta ishga tushib ketadi va baza qaytganda hammasi sovuq startda bo'ladi (terminus darsida ko'rgandik).
Tekshirib ko'ring: Ilova xotirasi sizib (memory leak) asta-sekin 2 GB ga yetdi va GC to'xtovsiz ishlayapti. Bu qaysi tekshiruvga tegishli?
Javob
Readiness va (oxir-oqibat) liveness. Kanon readiness'i heap 512 MB dan oshsa 503 qaytaradi — nusxa trafikdan chiqadi va yangi so'rov olmaydi. Agar xotira shunchalik ko'p bo'lsaki, jarayon umuman javob bera olmay qolsa — liveness yiqiladi va qayta ishga tushirish xotirani tozalaydi. Qayta ishga tushirish sizishni tuzatmaydi, lekin vaqt yutadi — sababini heap snapshot bilan qidirish kerak (Memory leak'ni topish).
3. Probe arifmetikasi
3.1 Sozlamalar
Kubernetes probe sozlamalari (rasmiy hujjat bo'yicha, 2026-11-03 da tekshirildi):
| Sozlama | Ma'nosi | Sukut |
|---|---|---|
periodSeconds |
tekshiruvlar oralig'i (davr) | 10 s |
failureThreshold |
ketma-ket nechta xato — yiqildi (chegara) | 3 |
timeoutSeconds |
bitta tekshiruv javobini kutish | 1 s |
initialDelaySeconds |
birinchi tekshiruvgacha kutish | 0 s |
successThreshold |
yiqilgandan keyin nechta muvaffaqiyat — tiklandi | 1 |
successThreshold liveness va startup uchun doim 1 bo'lishi shart; readiness'da kattaroq qo'yish mumkin — nusxa trafikka shoshilmasdan qaytsin.
Eng muhim formula: davr × chegara — muammo boshlangandan orkestrator harakat qilguncha ketadigan eng ko'p vaqt. Sukut qiymatlarda — 30 soniya.
3.2 Model
Soddalashtirilgan kubelet (Kubernetes'ning node'dagi agenti). Ilova tayyorMs da javob bera boshlaydi:
// Soddalashtirilgan kubelet. Ilova `tayyorMs` da javob beradi.
// Startup probe bo'lsa — u muvaffaqiyatli o'tguncha liveness kutadi.
function kubelet({ tayyorMs, live, startup }) {
let boshi = 0;
if (startup) {
let yiqildi = 0;
for (let t = startup.davr; ; t += startup.davr) {
if (t >= tayyorMs) { boshi = t; break; }
yiqildi++;
if (yiqildi === startup.chegara) {
return `o'ldirildi ${t} ms da (startup)`;
}
}
}
let yiqildi = 0;
for (let t = boshi + live.davr; ; t += live.davr) {
if (t >= tayyorMs) return `tirik, birinchi 200 — ${t} ms`;
yiqildi++;
if (yiqildi === live.chegara) {
return `o'ldirildi ${t} ms da (liveness)`;
}
}
}
const live = { davr: 10_000, chegara: 3 };
const startup = { davr: 5000, chegara: 30 };
console.log("3 s, startupsiz:", kubelet({ tayyorMs: 3066, live }));
console.log("45 s, startupsiz:", kubelet({ tayyorMs: 45_000, live }));
console.log("45 s, startup bilan:",
kubelet({ tayyorMs: 45_000, live, startup }));
console.log("200 s, startup bilan:",
kubelet({ tayyorMs: 200_000, live, startup }));Konsolda:
3 s, startupsiz: tirik, birinchi 200 — 10000 ms
45 s, startupsiz: o'ldirildi 30000 ms da (liveness)
45 s, startup bilan: tirik, birinchi 200 — 55000 ms
200 s, startup bilan: o'ldirildi 150000 ms da (startup)To'rt qator — to'rt xulosa:
- Kanondagi oddiy start (3 s) — hech qanday muammo yo'q.
- Katta bazada migratsiya tekshiruvi 45 soniya olsa — liveness 30-soniyada jarayonni o'ldiradi. Crash loop.
- Startup probe (5 s × 30 = 150 s byudjet) bilan — jarayon kutib olinadi, keyin liveness odatdagidek ishlaydi.
- 150 soniyada ham ishga tushmasa — startup uni o'ldiradi. Bu to'g'ri: ilova haqiqatan buzilgan.
Startup'ning qulayligi shunda: liveness'ni qattiq qoldirish mumkin (qotishni tez sezsin), start uchun esa alohida, keng byudjet beriladi. Startupsiz ikkalasini bitta sozlamada murosaga keltirish kerak edi — liveness'ni sekinlashtirish orqali.
3.3 Timeout tuzog'i
timeoutSeconds sukuti — 1 soniya. Kanon readiness'idagi ombor tekshiruvi esa bazani 2 soniya kutadi (kutishMs = 2000). Baza sekinlashsa, Kubernetes 1-soniyada "javob yo'q" deb hisoblaydi va indikatorning chiroyli down javobini hech qachon ko'rmaydi. Natija baribir to'g'ri (nusxa trafikdan chiqadi), lekin sabab logda yo'qoladi. Qoida: ichki timeout'lar yig'indisi < probe timeout'i. 33–34-qismda probe yozganda timeoutSeconds: 3 qo'yamiz.
Tekshirib ko'ring: Liveness
periodSeconds: 5,failureThreshold: 2. Event loop 8 soniyaga bloklandi (og'ir JSON parse). Jarayon o'ldiriladimi?
Javob
Ehtimol, ha. Davr × chegara = 10 soniya — bu "eng ko'pi". Agar bloklanish tekshiruvdan sal oldin boshlangan bo'lsa, ikkita tekshiruv 8 soniyalik oynaga tushadi va jarayon o'ldiriladi. Bitta og'ir so'rov butun nusxani qayta ishga tushirdi — ichidagi boshqa so'rovlar ham uzildi. Yechim ikki tomonlama: liveness'ni yumshatish (masalan 10 s × 3) va og'ir ishni event loop'dan chiqarish (worker yoki navbat).
4. Nimani tekshirish kerak
4.1 Readiness: faqat "usiz ishlay olmayman"
Readiness'ga bog'liqlik qo'shishdan oldin savol: "usiz birorta ham foydali so'rovga javob bera olmaymanmi?"
| Bog'liqlik | Readiness'ga | Nega |
|---|---|---|
| Asosiy baza | ha | usiz deyarli hamma yo'l 500 |
| Heap chegarasi | ha | xotira to'lgan nusxa hammani sekinlashtiradi |
| Yopilish holati | ha | O'tgan darsdagi drain |
| Redis kesh | yo'q | keshsiz ham ishlaydi, sekinroq |
| Gemini (AI) | yo'q | faqat AI yo'llari ishlamaydi |
| S3, SMTP, Telegram | yo'q | xususiyat buziladi, API emas |
Gemini qatoriga e'tibor bering. Agar readiness AI provayderini tekshirsa, Google tomonda 5 daqiqalik uzilish hamma nusxalarni trafikdan chiqaradi. Vazifalar ro'yxati, kirish, eksport — hammasi ishlamay qoladi, vaholanki faqat "qadamlarga bo'lish" tugmasi buzilgan edi. Bu — kaskadli yiqilish (cascading failure): kichik tashqi xato butun tizimni ag'daradi. Tashqi xizmatlar uchun to'g'ri himoya — Resilience naqshlari darsida (circuit breaker), health'da emas.
4.2 Yengil va tez
Health endpoint'ni doim kimdir so'rab turadi: orkestrator, balanser, monitoring. 10 nusxa × 3 tekshiruv × har 5 soniya — bu daqiqasiga 360 so'rov, faqat "sog'misan?" uchun. Shuning uchun:
- Arzon so'rov: kanonda
ombor.soni({})— indeks bo'yichacount,SELECT 1ga yaqin. Og'ir hisobot yoki to'liq jadval skani — yo'q. - O'z timeout'i: osilgan baza health'ni ham osiltirmasin (kanonda
Promise.raceva 2 s). - Kesh: tekshiruv natijasini 1–2 soniya saqlash mumkin (terminus darsi 3-mashqi).
- Log: har health so'rovini
infodarajasida yozish loglarni ko'mib yuboradi.
4.3 Xavfsizlik
Health yo'llari kirishsiz (@Ochiq()) — orkestrator cookie yubormaydi. Demak javobni har kim ko'ra oladi. Qoidalar:
- Versiya, kutubxonalar ro'yxati, ichki IP — yo'q.
- Xato matni ehtiyotkorlik bilan: kanon readiness'i baza xatosining
messageinitekshiruvlarichida qaytaradi (masalan"ulanish uzildi"). Ba'zi drayver xabarlarida xost nomi bo'ladi. Ochiq internetga chiqadigan bo'lsa, tafsilotni faqat ichki tarmoqqa bering. - Kesh sarlavhasi: terminus
Cache-Control: no-cache, no-store, must-revalidateqo'yadi — proksi eski "sog'lom" javobni saqlab qolmasin.
5. Nomlar va formatlar
5.1 /healthz va boshqalar
Bir nechta odat bor, hammasi ishlaydi:
| Uslub | Misol | Qayerdan |
|---|---|---|
z qo'shimchali |
/healthz, /livez, /readyz |
Google ichki odati; Kubernetes API server |
| Ierarxik | /health/live, /health/ready |
Spring Boot, NestJS misollari |
| Bitta yo'l | /health |
oddiy servislar, uptime monitoring |
Kubernetes'ning o'z API serveri /healthz ni v1.16 dan beri eskirgan deb belgilab, /livez va /readyz ga o'tgan (hujjatda 2026-11-03 da tekshirildi). z — oddiy foydalanuvchi yo'li bilan to'qnashmaslik uchun. Kanon ierarxik uslubni tanladi: /health/live, /health/ready, /health/startup — versiyasiz (VERSION_NEUTRAL), inglizcha (orkestrator konventsiyasi).
5.2 Javob formati
Orkestrator faqat status kodga qaraydi: 200–399 — sog'lom, qolgani — yiqilgan. Tana — odam va monitoring uchun. Terminus formati: status (ok / error), info (sog'lomlar), error (yiqilganlar), details (hammasi). IETF'da health javobi uchun alohida format qoralamasi ham bor (application/health+json) — u standartga aylanmagan (2026-11).
5.3 Docker HEALTHCHECK
Kubernetes'siz, oddiy Docker'da ham tekshiruv bor — Dockerfile'da. Hujjat bo'yicha namuna (biz sinamadik, 33-qismda Dockerfile yozamiz):
HEALTHCHECK --interval=10s --timeout=3s --start-period=20s \
--retries=3 CMD node -e \
"fetch('http://127.0.0.1:3000/health/ready').then(\
r => process.exit(r.ok ? 0 : 1), () => process.exit(1))"Uch detal. --start-period — startup probe'ning soddaroq o'xshashi: shu vaqt ichidagi xatolar hisobga olinmaydi. curl o'rniga node -e — kichik Node image'larida curl bo'lmaydi. Va eng muhimi: oddiy Docker unhealthy konteynerni qayta ishga tushirmaydi — faqat belgilaydi. Undan Compose'dagi depends_on: condition: service_healthy yoki orkestrator foydalanadi.
Sukut qiymatlarni ham biling (Docker hujjatiga ko'ra, 2026-11-03): --interval 30 s, --timeout 30 s, --retries 3, --start-period 0 s. Ya'ni bayroqsiz HEALTHCHECK muammoni 1.5 daqiqada sezadi. Buyruq chiqish kodi bilan javob beradi: 0 — sog'lom, 1 — kasal (2 — band qilingan, ishlatilmaydi). Shuning uchun namunada process.exit(r.ok ? 0 : 1).
Tekshirib ko'ring: Namunadagi
HEALTHCHECKda ilova 15-soniyada ishga tushdi, 60-soniyada esa baza uzilib readiness 503 qaytara boshladi. Konteyner holati qachonunhealthybo'ladi?
Javob
Taxminan 80–90-soniyada (tekshiruvlar soatiga qarab). Start davrida (20 s) xatolar hisobga olinmaydi, lekin u allaqachon tugagan. 60-soniyadan keyingi uchta ketma-ket tekshiruv (har 10 s da) yiqilgach — --retries=3 — holat unhealthy ga o'tadi. Konteyner esa ishlashda davom etadi: oddiy Docker uni o'zi qayta ishga tushirmaydi.
6. Ko'p uchraydigan xatolar
| Xato | Oqibat | To'g'risi |
|---|---|---|
| Liveness'da baza | baza uzilsa hamma nusxa qayta ishga tushadi | liveness — tekshiruvsiz |
| Startup yo'q, liveness qattiq | sekin startda crash loop | /health/startup + keng byudjet |
| Readiness'da tashqi API | uning uzilishi butun API'ni o'chiradi | breaker, readiness'da emas |
| Health'da og'ir so'rov | o'z-o'zini DoS qilish | SELECT 1 darajasi + kesh |
| Ichki timeout > probe timeout | sabab logda yo'qoladi | ichki < timeoutSeconds |
| Health javobida sirlar | ma'lumot oqishi | faqat holat, tafsilot ichkarida |
| Yangi yo'l testlarga qo'shilmagan | ruxsatsiz yo'l jim paydo bo'ladi | marshrutlar ro'yxati testi |
Oxirgi qatorni loyiha qadamida jonli ko'ramiz.
7. Mashqlar
Mashqlar kurs/mashqlar/31/27-health/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).
1-mashq (oson): bo'shliqlarni to'ldiring
- Sekin ishga tushayotgan jarayonni liveness'dan himoya qiladigan tekshiruv —
- Readiness yiqilsa orkestrator nusxani qayta ishga tushiradimi? (ha/yo'q) —
- Kubernetes
failureThresholdsukuti — - Probe muvaffaqiyatli hisoblanadigan eng katta status kod —
- Kichik tashqi xato butun tizimni ag'darishi — kaskadli
Yechim
- startup — u o'tguncha liveness ishlamaydi.
- yo'q — faqat trafikdan chiqaradi; qayta ishga tushirish — liveness ishi.
- 3 —
periodSecondssukuti 10 bilan birga — 30 soniya. - 399 — 200–399 oralig'i sog'lom hisoblanadi.
- yiqilish — inglizchasi cascading failure.
2-mashq (o'rta): probe byudjeti
byudjet(probe) funksiyasini yozing. Kirish — { davr, chegara, timeout } (soniyada). Natija — aniqlash (muammo boshlanganidan harakatgacha eng ko'p vaqt: davr × chegara + timeout) va xulosa: aniqlash 60 dan katta bo'lsa "sekin", 5 dan kichik bo'lsa "asabiy", aks holda "me'yorda". Uch sozlamani tekshiring: Kubernetes sukuti (10, 3, 1), juda qattiq (1, 2, 1) va juda yumshoq (30, 5, 3). Ishora: if/else if zanjiri yoki ternar.
Yechim
function byudjet({ davr, chegara, timeout }) {
const aniqlash = davr * chegara + timeout;
let xulosa = "me'yorda";
if (aniqlash > 60) xulosa = "sekin";
else if (aniqlash < 5) xulosa = "asabiy";
return { aniqlash, xulosa };
}
console.log("sukut:", byudjet({ davr: 10, chegara: 3, timeout: 1 }));
console.log("qattiq:", byudjet({ davr: 1, chegara: 2, timeout: 1 }));
console.log("yumshoq:",
byudjet({ davr: 30, chegara: 5, timeout: 3 }));Konsolda:
sukut: { aniqlash: 31, xulosa: "me'yorda" }
qattiq: { aniqlash: 3, xulosa: 'asabiy' }
yumshoq: { aniqlash: 153, xulosa: 'sekin' }"Asabiy" sozlama bitta GC to'xtashi yoki bitta sekin so'rovda nusxani o'ldiradi. "Sekin" sozlama qotib qolgan nusxani 2.5 daqiqa trafikda ushlab turadi. Sukut — o'rtacha murosa. xulosa birinchi qatorda qo'shtirnoqda — satr ichida apostrof bor.
3-mashq (qiyin): o'z health agregatoringiz
Terminus'siz kichik agregator yozing: salomatlik(tekshiruvlar, kutishMs). Har tekshiruv — { nom, fn, muhim }; fn — Promise qaytaradi. Har biriga kutishMs timeout qo'ying. Muhim tekshiruv yiqilsa yoki kechiksa — status 503, aks holda 200 (muhim bo'lmaganlari faqat degraded deb belgilanadi). Natija: { status, natija: { nom: holat } }. Ishora: Promise.race va Promise.all (Promise kombinatorlari).
Yechim
const kut = (ms, qiymat) =>
new Promise((ok) => setTimeout(() => ok(qiymat), ms));
async function birTekshiruv({ fn, muhim }, kutishMs) {
try {
const javob = await Promise.race([
fn().then(() => "up"),
kut(kutishMs, "timeout"),
]);
if (javob === "up") return "up";
return muhim ? "down (timeout)" : "degraded (timeout)";
} catch {
return muhim ? "down" : "degraded";
}
}
async function salomatlik(tekshiruvlar, kutishMs) {
const holatlar = await Promise.all(
tekshiruvlar.map((t) => birTekshiruv(t, kutishMs)));
const natija = {};
tekshiruvlar.forEach((t, i) => { natija[t.nom] = holatlar[i]; });
const yiqildi = holatlar.some((h) => h.startsWith("down"));
return { status: yiqildi ? 503 : 200, natija };
}
const baza = { nom: "baza", muhim: true, fn: () => kut(5) };
const redis = {
nom: "redis", muhim: false,
fn: () => Promise.reject(new Error("ECONNREFUSED")),
};
const sekinBaza = { nom: "baza", muhim: true, fn: () => kut(300) };
console.log(await salomatlik([baza, redis], 100));
console.log(await salomatlik([sekinBaza, redis], 100));Konsolda:
{ status: 200, natija: { baza: 'up', redis: 'degraded' } }
{ status: 503, natija: { baza: 'down (timeout)', redis: 'degraded' } }Redis uzilgan — lekin u muhim emas, ilova sog'lom (200). Baza 300 ms javob bermadi — 100 ms timeout uni down qildi va butun javob 503. Ikkinchi chaqiruvda sekinBaza ning va'dasi baribir 300 ms da tugaydi — Promise.race uni to'xtatmaydi, faqat kutmaydi (Asinxron naqshlar darsidagi tuzoq). Haqiqiy ilovada so'rovni AbortSignal bilan bekor qilish ham kerak.
8. Loyiha qadami: /health/startup
8.1 Holat: boshlandi
O'tgan darsdagi IlovaHolati ga ikkinchi maydon. src/tokenlar.ts:
/** 31 #26: jarayon holati — yopilishni readiness biladi */
export const ILOVA_HOLATI = Symbol("ILOVA_HOLATI");
export class IlovaHolati {
/** ishga tushish tugadi (listen) — /health/startup */
boshlandi = false;
/** SIGTERM keldi — /health/ready 503, yangi trafik kelmasin */
yopilmoqda = false;
}src/main.ts — holatni olgan qatordan keyin bitta qator:
const holat = app.get<IlovaHolati>(ILOVA_HOLATI);
holat.boshlandi = true; // 31 #27: /health/startupNega aynan shu yerda? Bu qatorgacha ishgaTushir() tugagan: config tekshirildi, DI konteyner qurildi, baza va migratsiya holati tekshirildi, server listen qildi. Undan oldin javob berish mumkin bo'lsa ham, ilova hali tayyor emas.
8.2 Controller
src/salomatlik/salomatlik.module.ts — tirik() va tayyor() orasiga:
/** 31 #27: startup probe — ishga tushish tugadimi; sekin startda
* liveness jarayonni o'ldirmasin */
@Get("startup")
@HealthCheck()
boshlandi(): Promise<HealthCheckResult> {
if (!this.holat.boshlandi) {
throw new ServiceUnavailableException({
status: "error", info: {},
error: { boshlanish: { status: "down" } }, details: {},
});
}
return this.salomatlik.check([]);
}Shakl tayyor() dagi yopilish tekshiruvi bilan bir xil, faqat kalit — boshlanish. this.salomatlik.check([]) — tekshiruvsiz muvaffaqiyat, @HealthCheck() esa kesh sarlavhasini qo'yadi.
8.3 Testlar o'z ishini qildi
Kodni qo'shib, testlarni yurgizdik — va bitta test yiqildi. Biz loyiha nusxasida ishga tushirdik (Node 24.21.0, bazasiz; Vitest chiqishining sarlavha va kod parchasi qatorlarini qisqartirdik):
↓ |asosiy| test/nest-auth.test.ts (1 test | 1 skipped)
❯ |asosiy| test/marshrutlar.test.ts (2 tests | 1 failed) 206ms
× DiscoveryService: marshrut jadvali (controller + handler metama'lumoti) 9ms
✓ har @All() handler'ining Allow sarlavhasi haqiqiy metodlar bilan bir xil 46ms
AssertionError: expected { '/ichki/tekshiruv': 'POST', …(11) } to deeply equal { …(11) }
- Expected
+ Received
@@ -2,10 +2,11 @@
"/eksportlar": "POST",
"/eksportlar/:id": "GET, HEAD",
"/eksportlar/:id/fayl": "GET, HEAD",
"/health/live": "GET, HEAD",
"/health/ready": "GET, HEAD",
+ "/health/startup": "GET, HEAD",
"/ichki/tekshiruv": "POST",Tarjimasi: "kutilgan jadvalga chuqur teng bo'lishi kerak edi". Farq (+ qatori) — jadvalda yangi yo'l bor, kutilganda yo'q. Bu — Reflector va DiscoveryService darsida yozilgan test: ilovaning hamma marshrutlarini ro'yxat bilan solishtiradi. Yangi yo'l kimdir bilmagan holda paydo bo'lmasin. Bu yerda yo'l ataylab qo'shilgan — ro'yxatni yangilaymiz.
Ikkinchi shunday test — nest-auth.test.ts dagi OCHIQ ro'yxati: kirishsiz ochiq yo'llar. U bazani talab qiladi, shuning uchun bizda o'tkazildi (↓ … skipped), kanon esa Postgres bilan yurgizdi va u ham yiqildi. Bu test yana ham muhim: @Ochiq() yo'l — har kim uchun ochiq eshik. Yangi ochiq eshik ko'rib chiqilmasdan qo'shilmasin.
test/marshrutlar.test.ts — jadvalga:
"/health/live": "GET, HEAD",
"/health/ready": "GET, HEAD",
"/health/startup": "GET, HEAD", // 31 #27test/nest-auth.test.ts — OCHIQ ro'yxatining birinchi qatori:
"GET /health/live", "GET /health/ready", "GET /health/startup",8.4 Kuzatuv testi
test/kuzatuv.test.ts dagi o'tgan darsdagi testni kanon shakliga keltiramiz — startup va readiness bitta stsenariyda:
test("#27 /health/startup: boshlanmaguncha 503, keyin 200; "
+ "#26 yopilmoqda — /health/ready 503, /health/live 200",
async () => {
const app = await ilova();
const h = app.get<IlovaHolati>(ILOVA_HOLATI);
const http = app.getHttpServer();
await request(http).get("/health/startup").expect(503);
h.boshlandi = true;
await request(http).get("/health/startup").expect(200);
await request(http).get("/health/ready").expect(200);
h.yopilmoqda = true;
const r = await request(http).get("/health/ready").expect(503);
assert.match(JSON.stringify(r.body), /yopilish/);
await request(http).get("/health/live").expect(200);
await app.close();
});Test butun hayotni bir chiziqda o'tadi: ishga tushmagan → ishga tushgan → tayyor → yopilmoqda (lekin tirik). Fayl boshidagi izohga "#27 /health/startup" ni qo'shing.
8.5 Tekshiruv
npm run tip
npx vitest run test/kuzatuv.test.ts test/marshrutlar.test.tsBiz loyiha nusxasida (Node 24.21.0, bazasiz): tsc --noEmit xatosiz,
✓ |asosiy| test/marshrutlar.test.ts (2 tests) 550ms
✓ |asosiy| test/kuzatuv.test.ts (1 test) 553ms
✓ #27 /health/startup: boshlanmaguncha 503, keyin 200; #26 yopilmoqda — /health/ready 503, /health/live 200 550ms
Test Files 2 passed (2)
Tests 3 passed (3)Jonli ham ko'rdik: loyiha nusxasini SAQLASH=xotira bilan ishga tushirib, curl qildik (sarlavhalardan keraklilarini qoldirdik):
$ curl -i http://127.0.0.1:5594/health/startup
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Content-Type: application/json; charset=utf-8
{"status":"ok","info":{},"error":{},"details":{}}
$ curl http://127.0.0.1:5594/health/ready
{"status":"ok","info":{"ombor":{"ms":1,"status":"up"},"xotira":{"status":"up"}},"error":{},"details":{"ombor":{"ms":1,"status":"up"},"xotira":{"status":"up"}}}Nima bo'ldi? /health/startup 200 qaytardi — main.ts dagi holat.boshlandi = true qatori ishladi. Cache-Control sarlavhasini terminus o'zi qo'ydi. Readiness ikkala tekshiruvi (ombor, xotira) up. Xotira rejimida ombor 1 ms da javob berdi. Kanon esa Postgres bilan jonli tekshiruvda /health/startup: 200; /health/ready: 200 ni yozib oldi.
Hali commit qilmang — bu bosqichda yana uchta dars bor.
9. Real ishda
- Kubernetes: har Deployment'da uch probe —
startupProbe,livenessProbe,readinessProbe. Noto'g'ri liveness — prodakshndagi "sababsiz" restartlarning eng ko'p uchraydigan sababi. - Bulut balanserlari: AWS ALB, Google Cloud Load Balancing, Yandex Cloud — "target health check" yo'li va oralig'ini so'raydi; odatda readiness'ni berasiz.
- Uptime monitoring: UptimeRobot, Uptime Kuma kabi vositalar tashqaridan
/health/readyni so'raydi va yiqilsa xabar yuboradi (34-qism). - Intervyu savollari: "Liveness, readiness, startup farqi?", "Readiness'ga tashqi API qo'shish nega xavfli?", "Crash loop'ni qanday tashxislaysiz?", "Probe timeout'i va ichki timeout qanday bog'liq?"
Xulosa
- Uch tekshiruv — uch harakat: startup (kutish), liveness (qayta ishga tushirish), readiness (trafikdan chiqarish).
- Har tekshiruvga savol: "orkestratorning harakati bu muammoni tuzatadimi?"
- Davr × chegara — muammodan harakatgacha vaqt; startup liveness'ni qattiq qoldirishga imkon beradi.
- Readiness'ga faqat "usiz ishlay olmayman" bog'liqliklari kiradi; tashqi API — yo'q.
- Health yengil, tez, o'z timeout'i bilan va ochiq bo'lgani uchun sirsiz bo'lsin.
- Marshrutlar va ochiq yo'llar ro'yxati testlari yangi
/health/startupni darhol ushladi.
Keyingi dars: Observability: metrikalar, trace va xatolarni kuzatish — /metrics endpoint'i, Prometheus metrikalari va OpenTelemetry trace'lari bilan ilova ichida nima bo'layotganini ko'ramiz.
Manbalar
- Kubernetes hujjati — Liveness, Readiness and Startup Probes: https://kubernetes.io/docs/concepts/workloads/pods/probes/ (2026-11-03)
- Kubernetes hujjati — Configure Liveness, Readiness and Startup Probes (sozlamalar va sukutlar): https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ (2026-11-03)
- Kubernetes hujjati — Kubernetes API health endpoints (
/livez,/readyz): https://kubernetes.io/docs/reference/using-api/health-checks/ (2026-11-03) - Docker hujjati — Dockerfile
HEALTHCHECK: https://docs.docker.com/reference/dockerfile/#healthcheck (2026-11-03) - IETF qoralamasi — Health Check Response Format for HTTP APIs: https://datatracker.ietf.org/doc/html/draft-inadarei-api-health-check (2026-11-03)
- NestJS hujjati — Health checks (Terminus): https://docs.nestjs.com/recipes/terminus
- Kanon (IlmHamroh, 2026-11-02/03): 15 ta jonli ishga tushirish vaqti,
/health/startupva/health/readyjonli tekshiruvi (PostgreSQL 18.6). Kubelet modeli, mashq yechimlari, loyiha nusxasidagi testlar (marshrut testining yiqilishi bilan) va jonlicurl— biz ishga tushirdik (2026-11-03, Node 24.21.0, NestJS 12.2.0,@nestjs/terminus12.1.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!