NestJS에서 Consul-KV 사용

Consul 키/값 저장소로 쉽게 작업

연결



https://developer.hashicorp.com/consul/docs/dynamic-app-config/kv - 키/값(KV) 저장소

https://www.npmjs.com/package/ilink-console-tools - 콘솔. env 파일을 consul-kv로/에서 업로드/다운로드하는 유틸리티

https://www.npmjs.com/package/nestjs-consul-kv-realtime - Consul-KV와 실시간 작업을 위한 NestJS 모듈

https://github.com/EndyKaufman/nestjs-consul-example - 이 게시물의 코드가 포함된 프로젝트

nest 및 consul을 사용하여 프로젝트를 생성하는 단계



1. NestJS 프로젝트 생성



a) @nestjs/cli를 사용하여 새로운 NestJS 애플리케이션 생성




npm i -g @nestjs/cli
nest new nestjs-consul-example
cd nestjs-consul-example


b) 종속성 설치




npm i --save consul nestjs-consul-kv-realtime


c) devDependencies 설치




npm i --save-dev ilink-console-tools @types/consul


2. Docker-compose 작업 추가



a) docker-compose 설치



https://docs.docker.com/compose/install

b) nestjs-consul-example/docker-compose.yml 생성




version: "3"
networks:
  nestjs-consul-example-network:
    driver: bridge

services:
  nestjs-consul-example:
    image: bitnami/consul:latest
    container_name: "nestjs-consul-example"
    environment:
      - CONSUL_HTTP_TOKEN=${CONSUL_TOKEN_MASTER}
    networks:
      - nestjs-consul-example-network
    ports:
      - "8300:8300"
      - "8301:8301"
      - "8301:8301/udp"
      - "8500:8500"
      - "8600:8600"
      - "8600:8600/udp"


c) nestjs-consul-example/env.docker-compose 생성




CONSUL_HTTP_TOKEN=e2999fc6-1fc1-4345-a56e-e9d27b34c1c1


d) nestjs-consul-example/package.json에 새 스크립트 추가




{
  "scripts": {
    "__________dev infra__________": "__________dev infra__________",
    "docker:dev:restart": "npm run docker:dev:down && npm run docker:dev:up",
    "docker:dev:up": "set -a && . ./env.docker-compose && set +a && export COMPOSE_INTERACTIVE_NO_CLI=1 && docker-compose -f ./docker-compose.yml --compatibility up -d",
    "docker:dev:down": "set -a && . ./env.docker-compose && set +a && export COMPOSE_INTERACTIVE_NO_CLI=1 && docker-compose -f ./docker-compose.yml down"
  }
}


e) docker-compose 시작




npm run docker:dev:restart


3. 기본 환경 변수를 consul에 추가



a) nestjs-consul-example/env.default 생성




HELLO_MESSAGE="Hello from ENV file!"


b) 새 스크립트를 추가하고 nestjs-consul-example/package.json에 업데이트가 있음




{
  "scripts": {
    "__________dev infra__________": "__________dev infra__________",
    "docker:dev:restart": "npm run docker:dev:down && npm run docker:dev:up && npm run docker:dev:fill-default-data",
    "docker:dev:up": "set -a && . ./env.docker-compose && set +a && export COMPOSE_INTERACTIVE_NO_CLI=1 && docker-compose -f ./docker-compose.yml --compatibility up -d",
    "docker:dev:down": "set -a && . ./env.docker-compose && set +a && export COMPOSE_INTERACTIVE_NO_CLI=1 && docker-compose -f ./docker-compose.yml down",
    "docker:dev:fill-default-data": "set -a && . ./env.docker-compose && set +a && ilink-console-tools env-to-consul --path=./env.default --consul-token=$CONSUL_HTTP_TOKEN --consul-clear=true"
  }
}


c) docker-compose를 다시 시작합니다.




npm run docker:dev:restart


d) http://localhost:8500/ui/dc1/kv/env/로 이동하여 UI에서 데이터 확인





4. 애플리케이션에 NestjsConsulKvRealtimeModule 추가



a) nestjs-consul-example/src/app.module.ts 업데이트




import { Module } from '@nestjs/common';
import { NestjsConsulKvRealtimeModule } from 'nestjs-consul-kv-realtime';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    NestjsConsulKvRealtimeModule.forRootAsync({
      useFactory: async () => ({
        port: '8500',
        host: 'localhost',
        defaults: {
          token: process.env.CONSUL_HTTP_TOKEN,
        },
      }),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}


b) nestjs-consul-example/src/app.controller.ts 업데이트




import { Controller, Get } from '@nestjs/common';
import { ConsulKeyValue } from 'nestjs-consul-kv-realtime';
import { AppService } from './app.service';

@Controller()
export class AppController {
  @ConsulKeyValue({
    key: 'env',
  })
  consulEnvironments!: { HELLO_MESSAGE: string };

  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Get('consul')
  getConsulHello(): string {
    return this.consulEnvironments.HELLO_MESSAGE;
  }
}


c) nestjs-consul-example/tsconfig.json 업데이트




{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2017",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,
    "esModuleInterop": true
  }
}


d) nestjs-consul-example/package.json에 새 스크립트 추가




{
  "scripts": {
    "start:dev": "set -a && . ./env.docker-compose && set +a && nest start --watch",
    "test": "npm run docker:dev:fill-default-data && set -a && . ./env.docker-compose && set +a && jest --forceExit",
  }
}


5. 테스트 업데이트 및 테스트



a) 기존 테스트 nestjs-consul-example/src/app.controller.spec.ts 업데이트




import { Test, TestingModule } from '@nestjs/testing';
import Consul from 'consul';

import { NestjsConsulKvRealtimeModule } from 'nestjs-consul-kv-realtime';
import { AppController } from './app.controller';
import { AppService } from './app.service';

describe('AppController', () => {
  let appController: AppController;

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      imports: [
        NestjsConsulKvRealtimeModule.forRootAsync({
          useFactory: async () => ({
            port: '8500',
            host: 'localhost',
            defaults: {
              token: process.env.CONSUL_HTTP_TOKEN,
            },
          }),
        }),
      ],
      controllers: [AppController],
      providers: [AppService],
    }).compile();
    await app.init();

    appController = app.get<AppController>(AppController);
  });

  describe('root', () => {
    it('should return "Hello World!"', () => {
      expect(appController.getHello()).toBe('Hello World!');
    });

    it('should return "Hello from ENV file!"', async () => {
      expect(appController.getConsulHello()).toBe('Hello from ENV file!');
    });

    it('should return "Hello from TEST!"', async () => {
      const consul = new Consul({
        port: '8500',
        host: 'localhost',
        defaults: {
          token: process.env.CONSUL_HTTP_TOKEN,
        },
      });

      await consul.kv.set('env/HELLO_MESSAGE', 'Hello from TEST!');

      expect(appController.getConsulHello()).not.toBe('Hello from TEST!');

      await new Promise((resolve) => setTimeout(resolve, 1500));

      expect(appController.getConsulHello()).toBe('Hello from TEST!');
    });
  });
});



b) 테스트 실행




npm run test


좋은 웹페이지 즐겨찾기