Retseptlar11 min read

Healthchecks (Terminus)

Terminus integratsiyasi sizga readiness/liveness health check'larni taqdim etadi. Healthcheck'lar murakkab backend setup'larda juda muhim. Qisqacha aytganda, web development'dagi h

Terminus integratsiyasi sizga readiness/liveness health check'larni taqdim etadi. Healthcheck'lar murakkab backend setup'larda juda muhim. Qisqacha aytganda, web development'dagi health check odatda maxsus manzildan iborat bo'ladi, masalan https://my-website.com/health/readiness. Servis yoki infratuzilmangizdagi biror komponent (masalan, Kubernetes) bu manzilni doimiy tekshiradi. Ushbu manzilga yuborilgan GET so'rovidan qaytgan HTTP status code'ga qarab, servis "unhealthy" javob olganda kerakli chorani ko'radi. Siz taqdim etayotgan servis turiga qarab "healthy" yoki "unhealthy" ta'rifi farqlanadi. Shu sabab Terminus integratsiyasi sizga health indicator lar to'plamini beradi.

Masalan, agar web server ma'lumot saqlash uchun MongoDB ishlatsa, MongoDB hali ham ishlayaptimi yoki yo'qmi degan ma'lumot juda muhim bo'ladi. Bunday holatda MongooseHealthIndicator dan foydalanishingiz mumkin. U to'g'ri sozlansa - bu haqda keyinroq - health check manzilingiz MongoDB ishlayotgan yoki ishlamayotganiga qarab healthy yoki unhealthy HTTP status code qaytaradi.

Boshlash

@nestjs/terminus bilan ishlashni boshlash uchun kerakli dependency'ni o'rnatish kerak.

Terminal
1$ npm install --save @nestjs/terminus

Healthcheck sozlash

Health check - bu health indicator lar yig'indisining qisqacha natijasi. Health indicator servisni tekshiradi va uning healthy yoki unhealthy holatda ekanini aniqlaydi. Agar biriktirilgan barcha health indicator'lar ishlayotgan bo'lsa, health check ijobiy hisoblanadi. Ko'plab ilovalarga o'xshash indicator'lar kerak bo'lgani uchun @nestjs/terminus oldindan tayyorlangan indicator'lar to'plamini beradi:

  • HttpHealthIndicator
  • TypeOrmHealthIndicator
  • MongooseHealthIndicator
  • SequelizeHealthIndicator
  • MikroOrmHealthIndicator
  • PrismaHealthIndicator
  • MicroserviceHealthIndicator
  • GRPCHealthIndicator
  • MemoryHealthIndicator
  • DiskHealthIndicator

Birinchi health check'ni boshlash uchun HealthModule yarating va uning imports massiviga TerminusModule ni qo'shing.

Hint

Modulni Nest CLI bilan yaratish uchun $ nest g module health buyrug'ini ishga tushiring.

TypeScript
health.module
1import { Module } from '@nestjs/common';
2import { TerminusModule } from '@nestjs/terminus';
3
4@Module({
5  imports: [TerminusModule]
6})
7export class HealthModule {}

Healthcheck'larni controller orqali ishga tushirish mumkin va uni Nest CLI bilan osongina yaratish mumkin.

Terminal
1$ nest g controller health
Info

Ilovada shutdown hook'larni yoqish qat'iy tavsiya etiladi. Terminus integratsiyasi ushbu lifecycle event'dan foydalanadi. Shutdown hook'lar haqida ko'proq bu yerda o'qing.

HTTP Healthcheck

@nestjs/terminus o'rnatilib, TerminusModule import qilinib va yangi controller yaratilgach, health check yaratishga tayyor bo'lamiz.

HTTPHealthIndicator uchun @nestjs/axios paketi kerak bo'ladi, shuning uchun u o'rnatilganiga ishonch hosil qiling:

Terminal
1$ npm i --save @nestjs/axios axios

Endi HealthController ni sozlashimiz mumkin:

TypeScript
health.controller
1import { Controller, Get } from '@nestjs/common';
2import { HealthCheckService, HttpHealthIndicator, HealthCheck } from '@nestjs/terminus';
3
4@Controller('health')
5export class HealthController {
6  constructor(
7    private health: HealthCheckService,
8    private http: HttpHealthIndicator,
9  ) {}
10
11  @Get()
12  @HealthCheck()
13  check() {
14    return this.health.check([
15      () => this.http.pingCheck('nestjs-docs', 'https://docs.nestjs.com'),
16    ]);
17  }
18}
TypeScript
health.module
1import { Module } from '@nestjs/common';
2import { TerminusModule } from '@nestjs/terminus';
3import { HttpModule } from '@nestjs/axios';
4import { HealthController } from './health.controller';
5
6@Module({
7  imports: [TerminusModule, HttpModule],
8  controllers: [HealthController],
9})
10export class HealthModule {}

Endi health check https://docs.nestjs.com manziliga GET so'rovi yuboradi. Agar u yerdan healthy javob kelsa, http://localhost:3000/health route'i 200 status code bilan quyidagi obyektni qaytaradi.

JSON
1{
2  "status": "ok",
3  "info": {
4    "nestjs-docs": {
5      "status": "up"
6    }
7  },
8  "error": {},
9  "details": {
10    "nestjs-docs": {
11      "status": "up"
12    }
13  }
14}

Ushbu response obyektining interfeysi @nestjs/terminus paketidagi HealthCheckResult interfeysi orqali mavjud.

statusAgar biror health indicator muvaffaqiyatsiz bo'lsa, status 'error' bo'ladi. Agar NestJS ilovasi yopilish jarayonida bo'lsa, ammo hali HTTP so'rovlarni qabul qilayotgan bo'lsa, health check 'shutting_down' holatida bo'ladi.'error' | 'ok' | 'shutting_down'
infoStatus'i 'up', ya'ni healthy bo'lgan health indicator'lar ma'lumotini o'z ichiga olgan obyekt.object
errorStatus'i 'down', ya'ni unhealthy bo'lgan health indicator'lar ma'lumotini o'z ichiga olgan obyekt.object
detailsHar bir health indicator haqidagi barcha ma'lumotlarni o'z ichiga olgan obyekt.object
Muayyan HTTP response code'larni tekshirish

Ba'zi hollarda siz muayyan kriteriyalarni tekshirib, response'ni validatsiya qilmoqchi bo'lasiz. Masalan, https://my-external-service.com manzili 204 response code qaytaradi deb faraz qilaylik. HttpHealthIndicator.responseCheck yordamida aynan shu response code'ni tekshirib, boshqa barcha kodlarni unhealthy deb belgilashingiz mumkin.

204 dan boshqa response code qaytsa, quyidagi misol unhealthy bo'ladi. Uchinchi parametr sifatida response healthy (true) yoki unhealthy (false) ekanini bildiradigan sync yoki async funksiya uzatiladi.

TypeScript
health.controller
1// Within the `HealthController`-class
2
3@Get()
4@HealthCheck()
5check() {
6  return this.health.check([
7    () =>
8      this.http.responseCheck(
9        'my-external-service',
10        'https://my-external-service.com',
11        (res) => res.status === 204,
12      ),
13  ]);
14}

TypeOrm health indicator

Terminus health check'ga database tekshiruvlarini qo'shish imkonini beradi. Ushbu health indicator bilan ishlashni boshlashdan oldin Database chapter ni ko'rib chiqing va ilovangizda database ulanishi o'rnatilganiga ishonch hosil qiling.

Hint

Ichki qatlamda TypeOrmHealthIndicator shunchaki database tirikligini tekshirish uchun ko'p ishlatiladigan SELECT 1 SQL buyruqini bajaradi. Agar Oracle database ishlatilayotgan bo'lsa, SELECT 1 FROM DUAL ishlatiladi.

TypeScript
health.controller
1@Controller('health')
2export class HealthController {
3  constructor(
4    private health: HealthCheckService,
5    private db: TypeOrmHealthIndicator,
6  ) {}
7
8  @Get()
9  @HealthCheck()
10  check() {
11    return this.health.check([
12      () => this.db.pingCheck('database'),
13    ]);
14  }
15}

Agar database'ga ulanib bo'lsa, http://localhost:3000/health manziliga GET so'rovi yuborilganda quyidagi JSON natijani ko'rishingiz kerak:

JSON
1{
2  "status": "ok",
3  "info": {
4    "database": {
5      "status": "up"
6    }
7  },
8  "error": {},
9  "details": {
10    "database": {
11      "status": "up"
12    }
13  }
14}

Agar ilovangiz bir nechta database ishlatsa, har bir connection'ni HealthController ichiga inject qilishingiz kerak. Shundan so'ng connection reference'ni TypeOrmHealthIndicator ga uzatishingiz mumkin.

TypeScript
health.controller
1@Controller('health')
2export class HealthController {
3  constructor(
4    private health: HealthCheckService,
5    private db: TypeOrmHealthIndicator,
6    @InjectConnection('albumsConnection')
7    private albumsConnection: Connection,
8    @InjectConnection()
9    private defaultConnection: Connection,
10  ) {}
11
12  @Get()
13  @HealthCheck()
14  check() {
15    return this.health.check([
16      () => this.db.pingCheck('albums-database', { connection: this.albumsConnection }),
17      () => this.db.pingCheck('database', { connection: this.defaultConnection }),
18    ]);
19  }
20}

Disk health indicator

DiskHealthIndicator yordamida qancha storage ishlatilayotganini tekshirish mumkin. Boshlash uchun DiskHealthIndicator ni HealthController ichiga inject qiling. Quyidagi misol / path'idagi (Windows'da C:\\) ishlatilayotgan storage'ni tekshiradi. Agar u umumiy storage hajmining 50% idan oshsa, unhealthy Health Check qaytariladi.

TypeScript
health.controller
1@Controller('health')
2export class HealthController {
3  constructor(
4    private readonly health: HealthCheckService,
5    private readonly disk: DiskHealthIndicator,
6  ) {}
7
8  @Get()
9  @HealthCheck()
10  check() {
11    return this.health.check([
12      () => this.disk.checkStorage('storage', { path: '/', thresholdPercent: 0.5 }),
13    ]);
14  }
15}

DiskHealthIndicator.checkStorage orqali ma'lum bir qat'iy hajm chegarasini ham tekshirishingiz mumkin. Quyidagi misolda /my-app/ yo'li 250GB dan oshsa, holat unhealthy bo'ladi.

TypeScript
health.controller
1// Within the `HealthController`-class
2
3@Get()
4@HealthCheck()
5check() {
6  return this.health.check([
7    () => this.disk.checkStorage('storage', {  path: '/', threshold: 250 * 1024 * 1024 * 1024, })
8  ]);
9}

Memory health indicator

Jarayon ma'lum memory limitidan oshib ketmasligini tekshirish uchun MemoryHealthIndicator dan foydalanish mumkin. Quyidagi misol process heap'ini tekshiradi.

Hint

Heap - bu dinamik ajratilgan xotira joylashadigan memory qismi (masalan, malloc orqali ajratilgan xotira). Heap'dan ajratilgan xotira quyidagilardan biri sodir bo'lmaguncha band holatda qoladi:

  • The memory is free'd
  • The program terminates
TypeScript
health.controller
1@Controller('health')
2export class HealthController {
3  constructor(
4    private health: HealthCheckService,
5    private memory: MemoryHealthIndicator,
6  ) {}
7
8  @Get()
9  @HealthCheck()
10  check() {
11    return this.health.check([
12      () => this.memory.checkHeap('memory_heap', 150 * 1024 * 1024),
13    ]);
14  }
15}

MemoryHealthIndicator.checkRSS yordamida process RSS memory'sini ham tekshirish mumkin. Quyidagi misolda process 150MB dan ko'p xotira ajratgan bo'lsa, unhealthy response code qaytariladi.

Hint

RSS - bu Resident Set Size bo'lib, process uchun ajratilgan va RAM'da turgan xotira miqdorini ko'rsatadi. U swap qilingan xotirani o'z ichiga olmaydi. Ammo shared library'lardan real memory'da turgan sahifalar hisobga olinadi. Bundan tashqari, barcha stack va heap memory ham kiritiladi.

TypeScript
health.controller
1// Within the `HealthController`-class
2
3@Get()
4@HealthCheck()
5check() {
6  return this.health.check([
7    () => this.memory.checkRSS('memory_rss', 150 * 1024 * 1024),
8  ]);
9}

Custom health indicator

Ba'zi hollarda @nestjs/terminus taqdim etgan oldindan tayyor health indicator'lar barcha talablaringizni qamrab olmaydi. Bunday vaziyatda ehtiyojingizga mos custom health indicator yaratishingiz mumkin.

Boshlash uchun custom indicator'imizni ifodalaydigan service yaratamiz. Indicator qanday tuzilishini sodda tushunish uchun DogHealthIndicator namunasini yaratamiz. Ushbu service har bir Dog obyekti 'goodboy' turiga ega bo'lsa 'up' holatda bo'lishi kerak. Agar bu shart bajarilmasa, u xato tashlashi kerak.

TypeScript
dog.health
1import { Injectable } from '@nestjs/common';
2import { HealthIndicatorService } from '@nestjs/terminus';
3
4export interface Dog {
5  name: string;
6  type: string;
7}
8
9@Injectable()
10export class DogHealthIndicator {
11  constructor(
12    private readonly healthIndicatorService: HealthIndicatorService
13  ) {}
14
15  private dogs: Dog[] = [
16    { name: 'Fido', type: 'goodboy' },
17    { name: 'Rex', type: 'badboy' },
18  ];
19
20  async isHealthy(key: string){
21    const indicator = this.healthIndicatorService.check(key);
22    const badboys = this.dogs.filter(dog => dog.type === 'badboy');
23    const isHealthy = badboys.length === 0;
24
25    if (!isHealthy) {
26      return indicator.down({ badboys: badboys.length });
27    }
28
29    return indicator.up();
30  }
31}

Keyingi qadam - health indicator'ni provider sifatida ro'yxatdan o'tkazish.

TypeScript
health.module
1import { Module } from '@nestjs/common';
2import { TerminusModule } from '@nestjs/terminus';
3import { DogHealthIndicator } from './dog.health';
4
5@Module({
6  controllers: [HealthController],
7  imports: [TerminusModule],
8  providers: [DogHealthIndicator]
9})
10export class HealthModule { }
Hint

Real ilovada DogHealthIndicator alohida modulda, masalan DogModule ichida taqdim etilib, keyin u HealthModule ga import qilinishi kerak.

Oxirgi kerakli qadam - endi mavjud bo'lgan health indicator'ni kerakli health check endpoint'ga qo'shish. Buning uchun HealthController ga qaytib, uni check funksiyasiga qo'shamiz.

TypeScript
health.controller
1import { HealthCheckService, HealthCheck } from '@nestjs/terminus';
2import { Injectable, Dependencies, Get } from '@nestjs/common';
3import { DogHealthIndicator } from './dog.health';
4
5@Injectable()
6export class HealthController {
7  constructor(
8    private health: HealthCheckService,
9    private dogHealthIndicator: DogHealthIndicator
10  ) {}
11
12  @Get()
13  @HealthCheck()
14  healthCheck() {
15    return this.health.check([
16      () => this.dogHealthIndicator.isHealthy('dog'),
17    ])
18  }
19}

Logging

Terminus faqat error xabarlarini log qiladi, masalan Healthcheck muvaffaqiyatsiz bo'lganda. TerminusModule.forRoot() metodi orqali xatolar qanday log qilinishini aniqroq boshqarishingiz, hatto logging'ni to'liq o'z qo'lingizga olishingiz mumkin.

Ushbu bo'limda TerminusLogger nomli custom logger qanday yaratilishini ko'rsatamiz. Bu logger built-in logger'dan meros oladi. Shuning uchun logger'ning qaysi qismini override qilishni xohlasangiz, o'shani tanlashingiz mumkin.

Info

NestJS'dagi custom logger'lar haqida ko'proq bilmoqchi bo'lsangiz, bu yerda o'qing.

TypeScript
terminus-logger.service
1import { Injectable, Scope, ConsoleLogger } from '@nestjs/common';
2
3@Injectable({ scope: Scope.TRANSIENT })
4export class TerminusLogger extends ConsoleLogger {
5  error(message: any, stack?: string, context?: string): void;
6  error(message: any, ...optionalParams: any[]): void;
7  error(
8    message: unknown,
9    stack?: unknown,
10    context?: unknown,
11    ...rest: unknown[]
12  ): void {
13    // Overwrite here how error messages should be logged
14  }
15}

Custom logger yaratilgach, uni TerminusModule.forRoot() ga quyidagicha uzatish kifoya.

TypeScript
health.module
1@Module({
2imports: [
3  TerminusModule.forRoot({
4    logger: TerminusLogger,
5  }),
6],
7})
8export class HealthModule {}

Terminus'dan keladigan barcha log xabarlarini, jumladan error log'larini ham to'liq o'chirish uchun uni quyidagicha sozlang.

TypeScript
health.module
1@Module({
2imports: [
3  TerminusModule.forRoot({
4    logger: false,
5  }),
6],
7})
8export class HealthModule {}

Terminus healthcheck xatolari loglarda qanday ko'rsatilishini sozlash imkonini beradi.

Error Log StyleDescriptionExample
json (default)Xato yuz berganda health check natijasining qisqacha mazmunini JSON obyekt ko'rinishida chiqaradi
prettyXato yuz berganda health check natijasining qisqacha mazmunini formatlangan bloklarda chiqaradi va muvaffaqiyatli/xatoli natijalarni ajratib ko'rsatadi

Log uslubini errorLogStyle konfiguratsiyasi orqali quyidagicha o'zgartirishingiz mumkin.

TypeScript
health.module
1@Module({
2  imports: [
3    TerminusModule.forRoot({
4      errorLogStyle: 'pretty',
5    }),
6  ]
7})
8export class HealthModule {}

Graceful shutdown timeout

Agar ilovangizga shutdown jarayonini biroz kechiktirish kerak bo'lsa, Terminus buni siz uchun boshqarishi mumkin. Bu sozlama, ayniqsa, Kubernetes kabi orchestrator bilan ishlaganda foydali bo'ladi. Readiness check interval'idan biroz uzunroq kechikish belgilash orqali container'larni o'chirish paytida zero downtime'ga erishish mumkin.

TypeScript
health.module
1@Module({
2  imports: [
3    TerminusModule.forRoot({
4      gracefulShutdownTimeoutMs: 1000,
5    }),
6  ]
7})
8export class HealthModule {}

Ko'proq misollar

Ko'proq ishlaydigan misollar bu yerda mavjud.