Health checks (Terminus)
Health checks (Terminus)
terminus๋ ์ ์์ ์ธ ์ข ๋ฃ์ ๋ฐ์ํ๊ธฐ์ํ ํํฌ๋ฅผ ์ ๊ณตํ๋ฉฐ ๋ชจ๋ HTTP ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ํ ์ ์ ํ Kubernetes readiness/liveness ํ์ธ์ ์์ฑํ๋๋ก ์ง์ํฉ๋๋ค. @nestjs/terminus ๋ชจ๋์ ํฐ๋ฏธ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ Nest ์์ฝ ์์คํ ๊ณผ ํตํฉํฉ๋๋ค.
Getting started
@nestjs/terminus
๋ฅผ ์์ํ๋ ค๋ฉด ํ์ํ ์์กด์ฑ์ ์ค์นํด์ผํฉ๋๋ค.
$ npm install --save @nestjs/terminus @godaddy/terminus
Setting up a health check
์ํ ํ์ธ์ ์ํ ํ์๊ธฐ์ ์์ฝ์ ๋ํ๋ ๋๋ค. ์ํ ํ์๊ธฐ๋ ์๋น์ค ์ํ์ ๊ด๊ณ์์ด ์๋น์ค ๊ฒ์ฌ๋ฅผ ์คํํฉ๋๋ค. ํ ๋น๋ ๋ชจ๋ ์ํ ํ์๊ธฐ๊ฐ ์๋๋์ด ์คํ์ค์ธ ๊ฒฝ์ฐ ์ํ ํ์ธ์ ๊ธ์ ์ ์ ๋๋ค. ๋ง์ ์์ฉ ํ๋ก๊ทธ๋จ์ด ์ ์ฌํ ์ํ ํ์๊ธฐ๋ฅผ ํ์๋ก ํ๊ธฐ ๋๋ฌธ์ @nestjs/terminus๋ ๋ค์๊ณผ ๊ฐ์ ๋ฏธ๋ฆฌ ์ ์๋ ์ํ ํ์๊ธฐ ์งํฉ์ ์ ๊ณตํฉ๋๋ค.
DNSHealthIndicator
TypeOrmHealthIndicator
MongooseHealthIndicator
MicroserviceHealthIndicator
MemoryHealthIndicator
DiskHealthIndicator
DNS Health Check
์ฒซ๋ฒ์งธ ์ํ ํ์ธ์ ์์ํ๋ ์ฒซ๋ฒ์งธ ๋จ๊ณ๋ ์ํ ํ์๊ธฐ๋ฅผ ์๋ ํฌ์ธํธ์ ์ฐ๊ฒฐํ๋ ์๋น์ค๋ฅผ ์ค์ ํ๋ ๊ฒ์ ๋๋ค.
@@filename(terminus-options.service)
import {
TerminusEndpoint,
TerminusOptionsFactory,
DNSHealthIndicator,
TerminusModuleOptions
} from '@nestjs/terminus';
import { Injectable } from '@nestjs/common';
@Injectable()
export class TerminusOptionsService implements TerminusOptionsFactory {
constructor(
private readonly dns: DNSHealthIndicator,
) {}
createTerminusOptions(): TerminusModuleOptions {
const healthEndpoint: TerminusEndpoint = {
url: '/health',
healthIndicators: [
async () => this.dns.pingCheck('google', 'https://google.com'),
],
};
return {
endpoints: [healthEndpoint],
};
}
}
@@switch
import { Injectable, Dependencies } from '@nestjs/common';
import { DNSHealthIndicator } from '@nestjs/terminus';
@Injectable()
@Dependencies(DNSHealthIndicator)
export class TerminusOptionsService {
constructor(dns) {
this.dns = dns;
}
createTerminusOptions() {
const healthEndpoint = {
url: '/health',
healthIndicators: [
async () => this.dns.pingCheck('google', 'https://google.com'),
],
};
return {
endpoints: [healthEndpoint],
};
}
}
์ผ๋จ TerminusOptionsService
๋ฅผ ์ค์ ํ๋ฉด, TerminusModule
์ ๋ฃจํธ ApplicationModule
๋ก ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. TerminusOptionsService
๋ ์ค์ ์ ์ ๊ณตํ๋ฉฐ,์ด ์ค์ ์ TerminusModule
์ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
@@filename(app.module)
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { TerminusOptionsService } from './terminus-options.service';
@Module({
imports: [
TerminusModule.forRootAsync({
useClass: TerminusOptionsService,
}),
],
})
export class ApplicationModule { }
info ํํธ ์ฌ๋ฐ๋ฅด๊ฒ ์ํ๋๋ฉด Nest๋ GET ์์ฒญ์ ํตํด ์ ์ ๋ ๊ฒฝ๋ก์ ๋๋ฌ ํ ์์๋ ์ ์ ๋ ์ํ ์ ๊ฒ์ ๋ ธ์ถํฉ๋๋ค. ์๋ฅผ ๋ค์ด
curl -X GET 'http://localhost:3000/health'
Custom health indicator
๊ฒฝ์ฐ์ ๋ฐ๋ผ @nestjs/terminus
์์ ์ ๊ณตํ๋ ์ฌ์ ์ ์๋ ์ํ ํ์๊ธฐ๊ฐ ๋ชจ๋ ์ํ ํ์ธ ์๊ตฌ ์ฌํญ์ ๋ค๋ฃจ์ง ์์ต๋๋ค. ์ด ๊ฒฝ์ฐ ํ์์ ๋ฐ๋ผ ์ฌ์ฉ์ ์ ์ ์ํ ํ์๊ธฐ๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
๋ง์ถคํ ์ํ ํ์ธ์ ๋ํ๋ด๋ ์๋น์ค๋ฅผ ๋ง๋ค์ด ์์ํ๊ฒ ์ต๋๋ค. ์ํ ํ์ธ์ด ์ด๋ป๊ฒ ๊ตฌ์ฑ๋์ด ์๋์ง์ ๋ํ ๊ธฐ๋ณธ ์ง์์ ์ป๊ธฐ ์ํด DogHealthIndicator
์์ ๋ฅผ ๋ง๋ค ๊ฒ์
๋๋ค. ๋ชจ๋ Dog
์ค๋ธ์ ํธ์ goodboy
์ ํ์ด ์๋ ๊ฒฝ์ฐ์ด Health ํ์๊ธฐ์ ์ํ๋ "up"์ด์ด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ณ Health ํ์๊ธฐ๋ "down"์ผ๋ก ํ์๋ฉ๋๋ค.
@@filename(dog.health)
import { Injectable } from '@nestjs/common';
import { HealthCheckError } from '@godaddy/terminus';
import { HealthIndicatorResult } from '@nestjs/terminus';
export interface Dog {
name: string;
type: string;
}
@Injectable()
export class DogHealthIndicator extends HealthIndicator {
private readonly dogs: Dog[] = [
{ name: 'Fido', type: 'goodboy' },
{ name: 'Rex', type: 'badboy' },
];
async isHealthy(key: string): Promise<HealthIndicatorResult> {
const badboys = this.dogs.filter(dog => dog.type === 'badboy');
const isHealthy = badboys.length === 0;
const result = this.getStatus(key, isHealthy, { badboys: badboys.length });
if (isHealthy) {
return result;
}
throw new HealthCheckError('Dogcheck failed', result);
}
}
@@switch
import { Injectable } from '@nestjs/common';
import { HealthCheckError } from '@godaddy/terminus';
@Injectable()
export class DogHealthIndicator extends HealthIndicator {
dogs = [
{ name: 'Fido', type: 'goodboy' },
{ name: 'Rex', type: 'badboy' },
];
async isHealthy(key) {
const badboys = this.dogs.filter(dog => dog.type === 'badboy');
const isHealthy = badboys.length === 0;
const result = this.getStatus(key, isHealthy, { badboys: badboys.length });
if (isHealthy) {
return result;
}
throw new HealthCheckError('Dogcheck failed', result);
}
}
๋ค์์ผ๋ก ํด์ผ ํ ์ผ์ ์ํ ํ์๊ธฐ๋ฅผ ๊ณต๊ธ์๋ก ๋ฑ๋กํ๋ ๊ฒ์ ๋๋ค.
@@filename(app.module)
import { Module } from '@nestjs/common';
import { TerminusModule } from '@nestjs/terminus';
import { TerminusOptions } from './terminus-options.service';
import { DogHealthIndicator } from './dog.health.ts';
@Module({
imports: [
TerminusModule.forRootAsync({
imports: [ApplicationModule],
useClass: TerminusOptionsService,
}),
],
providers: [DogHealthIndicator],
exports: [DogHealthIndicator],
})
export class ApplicationModule { }
info ํํธ ์ค์ ์์ฉ ํ๋ก๊ทธ๋จ์์
DogHealthIndicator
๋ ๋ณ๋์ ๋ชจ๋ (์:DogsModule
)๋ก ์ ๊ณต๋์ด์ผํ๋ฉฐ, ๊ทธ๋ฐ ๋ค์ApplicationModule
์์ ๊ฐ์ ธ์ต๋๋ค. ๊ทธ๋ฌ๋DogHealthIndicator
๋ฅผDogModule
์exports
๋ฐฐ์ด์ ์ถ๊ฐํ๊ณTerminusModule.forRootAsync()
ํ๋ผ๋ฏธํฐ ๊ฐ์ฒด์imports
๋ฐฐ์ด์DogModule
์ ์ถ๊ฐํด์ผํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก ํด์ผ ํ ์ผ์ ํ์ํ ์ํ ์ ๊ฒ ์๋ ํฌ์ธํธ์ ์ฌ์ฉ ๊ฐ๋ฅํ ์ํ ํ์๊ธฐ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์
๋๋ค. ์ด๋ฅผ ์ํด ์ฐ๋ฆฌ๋ TerminusOptionsService
๋ก ๋์๊ฐ์/health
์๋ ํฌ์ธํธ๋ฅผ ๊ตฌํํฉ๋๋ค.
@@filename(terminus-options.service)
import {
TerminusEndpoint,
TerminusOptionsFactory,
DNSHealthIndicator,
TerminusModuleOptions
} from '@nestjs/terminus';
import { Injectable } from '@nestjs/common';
@Injectable()
export class TerminusOptionsService implements TerminusOptionsFactory {
constructor(
private readonly dogHealthIndicator: DogHealthIndicator
) {}
createTerminusOptions(): TerminusModuleOptions {
const healthEndpoint: TerminusEndpoint = {
url: '/health',
healthIndicators: [
async () => this.dogHealthIndicator.isHealthy('dog'),
],
};
return {
endpoints: [healthEndpoint],
};
}
}
@@switch
import { DogHealthIndicator } from '../dog/dog.health';
import { Injectable, Dependencies } from '@nestjs/common';
@Injectable()
@Dependencies(DogHealthIndicator)
export class TerminusOptionsService {
constructor(dogHealthIndicator) {
this.dogHealthIndicator = dogHealthIndicator;
}
createTerminusOptions() {
const healthEndpoint = {
url: '/health',
healthIndicators: [
async () => this.dogHealthIndicator.isHealthy('dog'),
],
};
return {
endpoints: [healthEndpoint],
};
}
}
๋ชจ๋ ๊ฒ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ฃ ๋์๋ค๋ฉด/health
์๋ ํฌ์ธํธ๋ 503
์๋ต ์ฝ๋์ ๋ค์ ๋ฐ์ดํฐ๋ก ์๋ตํด์ผ ํฉ๋๋ค.
{
"status": "error",
"error": {
"dog": {
"status": "down",
"badboys": 1
}
}
}
@nestjs/terminus
์ ์ฅ์์์ ์ค์ ์์ ๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
Custom Logger
Terminus
๋ชจ๋์ ์ํ ํ์ธ ์์ฒญ ๋์ ๋ชจ๋ ์ค๋ฅ๋ฅผ ์๋์ผ๋ก ๊ธฐ๋กํฉ๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ์ ์ญ ์ ์ผ๋ก ์ ์ ๋ Nest ๋ก๊ฑฐ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ธ๋ก๋ฒ ๋ก๊ฑฐ์ ๋ํ ์์ธํ ๋ด์ฉ์ ๋ก๊ฑฐ ์ฑํฐ์์ ํ์ธํ ์ ์์ต๋๋ค. ๊ฒฝ์ฐ์ ๋ฐ๋ผTerminus
์ ๋ก๊ทธ๋ฅผ ๋ช
์์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ค๊ณ ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ TerminusModule.forRoot[Async]
ํจ์๋ ์ปค์คํ
๋ก๊ฑฐ๋ฅผ์ํ ์ต์
์ ์ ๊ณตํฉ๋๋ค.
TerminusModule.forRootAsync({
logger: (message: string, error: Error) => console.error(message, error),
endpoints: [
...
]
})
๋ก๊ฑฐ ์ต์
์ null
๋ก ์ค์ ํ์ฌ ๋ก๊ฑฐ๋ฅผ ๋นํ์ฑํ ํ ์๋ ์์ต๋๋ค.
TerminusModule.forRootAsync({
logger: null,
endpoints: [
...
]
})
Last updated
Was this helpful?