본문 바로가기
카테고리 없음

마이크로서비스 개발 2

by ^..^v 2022. 3. 17.
728x90
반응형

3 마이크로서비스 코드 구현

각 기술 스택의 검증된 템플릿을 사용해 마이크로서비스를 빠르게 시작 

. 항공편 마이크로서비스 ⇒ node.js 부트스트래퍼인 노드 부트스트랩(node bootstrap)을 사용해 구현
. 예약 마이크로시스템 ⇒ 파이썬 플라스크 보일러플레이트가 작성된 깃허브 저장소를 사용 

 

3.1 항공편 마이크로서비스 코드

노드부트스트랩을 사용해 프로젝트를 생성(구성)하고, 노드부트스랩에서 제공하는 데이터 마이그레이션 기능을 이용해 MySQL 데이터 저장소를 구현

 

방법1. nodebootstrap 설치 후 ms-flights 프로젝트 생성

c:\msur> npm install -g nodebootstrap
c:\msur> nodebootstrap -h

Node/Express.js project bootstrapper loaded with best-practices

Usage:
  nodebootstrap <project_name> [options]

Options:
  -m, [--mode]           build mode to use.
                           Possible values: "webapp", "api", "cli" or "microservice" (default).
  -p, [--path=PATH]      output to the specified folder. Default is: <project_name>.
                           You can also indicate "." to build directly inside the current folder.
  -h, [--help]           output usage information
  -v, [-V], [--version]  output the version number

Description:
  The 'nodebootstrap ' command creates a new application with a default
  structure and configuration at the path you specify.

Examples:
  # create a skeleton of a containerized microservice:
  > nodebootstrap ms-first
  # create a skeleton of an express MVC webapp:
  > nodebootstrap -m webapp nodeapp-first
  # create a skeleton of a console or client Node application
  > nodebootstrap -m cli client-first

See more information at: http://www.nodebootstrap.io

c:\msur> nodebootstrap ms-flights
Building 'ms-flights' in microservice mode.
 ATTENTION: microservice mode requires properly set-up Docker environment.
 Do you have proper Docker setup (y/n)? y
        Starting installation…
        Downloading zip archive to C:\Users\myanj\AppData\Local\Temp\1647301073018-nodebootstrap.zip
        Zip archive downloaded
        Extraction complete
Will launch on port: 7507
Setup.sh created
Error: Command failed: chmod u+x ms-flights/setup.sh
'chmod'��(��) ���� �Ǵ� �ܺ� ����, ������ �� �ִ� ���α׷�, �Ǵ�
��ġ ������ �ƴմϴ�.

 

방법2. 깃 템플릿 복제 방식으로 코드를 가져와서 사용

https://github.com/inadarei/nodebootstrap-microservice 

 

GitHub - inadarei/nodebootstrap-microservice: Microservice Template for NodeBootstrap

Microservice Template for NodeBootstrap. Contribute to inadarei/nodebootstrap-microservice development by creating an account on GitHub.

github.com

 



 

 

로컬 레포지토리로 복사

c:\msur> git clone https://github.com/myanjini/ms-flights.git
Cloning into 'ms-flights'...
remote: Enumerating objects: 70, done.
remote: Counting objects: 100% (70/70), done.
remote: Compressing objects: 100% (56/56), done.
Receiving objects:  52% (37/70)sed 63 (delta 2), pack-reused 0
Receiving objects: 100% (70/70), 272.64 KiB | 1.38 MiB/s, done.
Resolving deltas: 100% (2/2), done.

 

 

프로젝트 이름 및 설정 변경

파일이름 변경 전 변경 후
C:\msur\ms-flights\docker-compose.yml ms-nodebootstrap-example ms-flights
ms-nodebootstrap-example-db ms-flights-db
5501:5501 7507:5501
C:\msur\ms-flights\package.json ms-nodebootstrap-example ms-flights
C:\msur\ms-flights\Makefile ms-nodebootstrap-example ms-flights
C:\msur\ms-flights\database.env ms-nodebootstrap-example-db ms-flights-db
C:\msur\ms-flights\node_modules   디렉터리 삭제



항공편 마이크로서비스 OAS(OpenAPI Spec) 확인

노드부트스트랩에서 제공하는 기능을 이용해서 항공편 마이크로서비스 OAS 구성을 확인

 

OAS 업데이트

C:\msur\ms-flights\docs\api.yml 파일의 내용을 앞에서 설계한 내용(https://github.com/implementing-microservices/ms-flights/blob/master/docs/api.yml)으로 업데이트 

 

redoc을 이용해서 OAS 문서를 읽어서 디플로이

c:\msur\ms-flights\docs> make start PWD=c:/msur/ms-flights/docs
docker run -d --rm --name ms-nb-docs -p 3939:80 -v c:/msur/ms-flights/docs/api.yml:/usr/share/nginx/html/swagger.yaml -e SPEC_URL=swagger.yaml redocly/redoc:v2.0.0-rc.8-1
6f55ab54754de5d5d999411fec146fa7c6791c3f86338e22fb4c6e23a9318068
"server started at: http://0.0.0.0:3939"		⇐ 0.0.0.0 대신 127.127.127.127로 접속해야 함

 

HTML 템플릿으로 렌더링된 결과 확인

http://127.127.127.127:3939

오류 원인 조치 후에도 오류가 발생하는 경우, swagger.yaml 파일에 대한 브라우저 캐시를 삭제해야 함

 

 

불필요한 모듈 삭제 및 마이크로서비스 모듈 추가

모듈 추가

C:\msur\ms-flights> npm install

 

불필요한 모듈 삭제 (기본적으로 제공되는 모듈의 디렉터리 삭제)

C:\msur\ms-flights\lib\users
C:\msur\ms-flights\lib\homdoc

 

flights 모듈 추가 (디렉터리 생성)

C:\msur\ms-flights\lib\flights

 

appConfig 설정 변경

C:\msur\ms-flights\appConfig.js

          … (생략) …

// Add all routes and route-handlers for your service/app here:
function serviceRoutes(app) {
  // Add advanced healthcheck middleware (incl. database check)
  const check = healthcheck();
  const AdvancedHealthcheckers = require('healthchecks-advanced');
  const advCheckers = new AdvancedHealthcheckers();
  // Database health check is cached for 10000ms = 10 seconds!
  check.addCheck('db', 'usersQuery', advCheckers.dbUsersCheck, {
    minCacheMs: 10000,
  });
  app.use(check.express());

  /* eslint-disable global-require */

  /*
  app.use('/'     , require('homedoc')); // attach to root route
  app.use('/users', require('users')); // attach to sub-route
  */

  app.use('/flights', require('flights'));


  /* eslint-enable global-require */
}

          … (생략) …

 

입력 유효성 검사 코드 추가 및 API 엔드포인트에 대해 호출할 마이크로서비스 actions 모듈의 함수를 지정

C:\msur\ms-flights\lib\flights\controllers\mappings.js

// https://www.npmjs.com/package/spieler
const { spieler, check, matchedData, sanitize } = require('spieler')();

const router = require('express').Router({
  mergeParams: true,
});

const actions = require('./actions');

const log = require('metalogger')();

const flightNoValidation = check(
  'flight_no',
  'flight_no must be at least 3 chars long and contain letters and numbers',
)
  .exists()
  .isLength({
    min: 3,
  })
  .matches(/[a-zA-Z]{1,4}\d+/);

const dateTimeValidation = check(
  'departure_date_time',
  'departure_date_time must be in YYYY-MM-ddThh:mm format',
)
  .exists()
  .matches(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/);

const flightsValidator = spieler([flightNoValidation, dateTimeValidation]);

const seatmapsValidator = spieler([flightNoValidation]);

router.get('/', flightsValidator, actions.getFlightInfo);
router.get('/:flight_no/seat_map', seatmapsValidator, actions.getSeatMap);

module.exports = router;

 

actions 모듈 구현

C:\msur\ms-flights\lib\flights\controllers\actions.js

/* jshint -W079 */
const Promise = require('bluebird'),
  config = require('config'),
  log = require('metalogger')(),
  representor = require('kokua'),
  _ = require('lodash');

const actions = {};

const responseMediaType = 'application/hal+json';

actions.getSeatMap = async function (req, res, next) {
  const response = { status: 'ok' };
  response.req = req.body;
  res.status(200).json(response);
};

actions.getFlightInfo = async function (req, res, next) {
  const response = {
    flight_id: 'edcc03a4-7f4e-40d1-898d-bf84a266f1b9',
    origin_code: 'LAX',
    destination_code: 'DCA',
  };
  //response.req = req.body;
  res.status(200).json(response);
};

module.exports = actions;

 

mapping 모듈 추가

C:\msur\ms-flights\lib\flights\index.js

module.exports = require('./controllers/mappings');

 

 

데이터베이스 마이그레이션

노드 부트스트랩은 데이터베이스 수정을 코드화하여 모든 환경에 적용할 수 있도록 데이터베이스 마이그레이션을 위한 간단한 솔루션을 제공

 

데이터 마이그레이션을 위한 코드 템플릿 생성

C:\msur\ms-flights> make migration-create name=seat-maps
docker-compose -p nb-demo up -d
Creating ms-nodebootstrap-example-db ... done
Creating ms-nodebootstrap-example    ... done
docker-compose -p nb-demo exec ms-nodebootstrap-example node_modules/db-migrate/bin/db-migrate create seat-maps --sql-file
[INFO] Created migration at /opt/app/migrations/20220315000644-seat-maps.js
[INFO] Created migration up sql file at /opt/app/migrations/sqls/20220315000644-seat-maps-up.sql
[INFO] Created migration down sql file at /opt/app/migrations/sqls/20220315000644-seat-maps-down.sql

C:\msur\ms-flights> make migration-create name=flights
docker-compose -p nb-demo up -d
ms-nodebootstrap-example-db is up-to-date
ms-nodebootstrap-example is up-to-date
docker-compose -p nb-demo exec ms-nodebootstrap-example node_modules/db-migrate/bin/db-migrate create flights --sql-file
[INFO] Created migration at /opt/app/migrations/20220315000811-flights.js
[INFO] Created migration up sql file at /opt/app/migrations/sqls/20220315000811-flights-up.sql
[INFO] Created migration down sql file at /opt/app/migrations/sqls/20220315000811-flights-down.sql

C:\msur\ms-flights> make migration-create name=sample-data
docker-compose -p nb-demo up -d
ms-nodebootstrap-example-db is up-to-date
ms-nodebootstrap-example is up-to-date
docker-compose -p nb-demo exec ms-nodebootstrap-example node_modules/db-migrate/bin/db-migrate create sample-data --sql-file
[INFO] Created migration at /opt/app/migrations/20220315000858-sample-data.js
[INFO] Created migration up sql file at /opt/app/migrations/sqls/20220315000858-sample-data-up.sql
[INFO] Created migration down sql file at /opt/app/migrations/sqls/20220315000858-sample-data-down.sql

 

 

데이터 마이그레이션 코드 추가

C:\msur\ms-flights\migrations\sqls\DATETIME-seat-maps-up.sql

CREATE TABLE IF NOT EXISTS `seat_maps` (
 `flight_no` varchar(10) NOT NULL,
 `seat_map` json NOT NULL,
 `origin_code` varchar(10) NOT NULL,
 `destination_code` varchar(10) NOT NULL,
 PRIMARY KEY (`flight_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

C:\msur\ms-flights\migrations\sqls\DATETIME-seat-maps-down.sql

DROP TABLE `seat_maps`;

 

C:\msur\ms-flights\migrations\sqls\DATETIME-flights-up.sql

CREATE TABLE IF NOT EXISTS `flights` (
 `flight_id` varchar(36) NOT NULL,
 `flight_no` varchar(10) NOT NULL,
 `flight_date` datetime(0) NULL,
 PRIMARY KEY (`flight_id`),
 FOREIGN KEY(`flight_no`)
 REFERENCES seat_maps(`flight_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

C:\msur\ms-flights\migrations\sqls\DATETIME-flights-down.sql

INSERT INTO `seat_maps`
VALUES ('AA2532', '{"Cabin": [{"Row": [{"Seat": [{"Number": "A",
 "Facilities": [{"Detail": {"content": "LegSpaceSeat"}}],
 "exitRowInd": false, "premiumInd": false, "noInfantInd": false,
 "restrictedReclineInd": false}], "RowNumber": 8}],
 "Wing": {"lastRow": 22, "firstRow": 14},
 "Column": [{"Column": "A", "Characteristics": ["Window"]}],
 "lastRow": 23, "firstRow": 8,
 "CabinClass": {"CabinType": "Economy"}}]}', 'LAX', 'DCA')
ON DUPLICATE KEY UPDATE
 flight_no = 'AA2532' ;

 

C:\msur\ms-flights\migrations\sqls\DATETIME-sample-data-down.sql

TRUNCATE TABLE `seat_maps`;

 

데이터 마이그레이션

make restart로 프로젝트를 다시 시작해 마이그레이션이 자동으로 적용되도록 하거나, make migrate로 명시적으로 마이그레이션 작업을 수행

C:\msur\ms-flights> make migrate

 

API 테스트 ⇒ DB 연동이 되지 않은 상태이므로 mapping에 정의된 검증 로직과 action에 정의된 데이터를 반환하는지를 확인

c:\msur\ms-flights> make start

 

http://127.127.127.127:7507/flights?flight_no=AA34&departure_date_time=2020-05-17T13:20

 

http://127.127.127.127:7507/flights/AA2532/seat_map



헬스 체크

컨테이너로 배포되는 앱의 수명 주기를 관리하기 위해 컨테이너의 상태를 확인할 수 있는 엔드포인트 서비스가 필요

쿠버네티스의 경우 활성 프로브(liveness probe)와 준비성 프로브(readiness probe)를 위한 엔드포인트를 제공해야 함

 

헬스 체크 코드 수정

C:\msur\ms-flights\appConfig.js

function serviceRoutes(app) {
  // 활성 프로브(Liveness Probe) - 컨테이너가 동작하는지 확인
  const livenessCheck = healthcheck({ path: '/ping' });
  app.use(livenessCheck.express());

  // 준비성 프로브(Readiness Probe) - 데이터베이스와 앱이 실제로 동작할 준비가 되었는지 확인
  const check = healthcheck();
  const AdvancedHealthcheckers = require('healthchecks-advanced');
  const advCheckers = new AdvancedHealthcheckers();
  check.addCheck('db', 'dbQuery', advCheckers.dbCheck, {
    minCacheMs: 10000,       // 헬스 체크 엔드포인트가 자주 호출되더라도 10초 동안은 캐시 값을 제공하게 하여, 
  });                        // 데이터베이스와 같은 다운스트림 시스템에 대한 부하를 줄임
  app.use(check.express());

  /* eslint-disable global-require */

  /*
  app.use('/',      require('homedoc')); // attach to root route
  app.use('/users', require('users')); // attach to sub-route
  */
  app.use('/flights', require('flights'));

  /* eslint-enable global-require */
}

 

헬스 체크 쿼리 수정

C:\msur\ms-flights\lib\healthchecks-advanced\index.js

const Duration = require('duration');
const db = require('datastore');
const log = require('metalogger')();

class Checks {
  // async dbUsersCheck() {
  //   const start = new Date();
  //   const conn = await db.conn();
  //   const query = 'select `email`, `uuid`, `last_updated` from users LIMIT 1';

  async dbCheck() {
    const start = new Date();
    const conn = await db.conn();
    const query = 'select count(1) from seat_maps';

    let errMsg = '';
    const response = {};
    try {
      const users = await conn.query(query);
    } catch (err) {
      errMsg = err;
    } finally {
      const elapsed = new Duration(start, new Date());
      const status = errMsg ? 'fail' : 'pass';

      response.status = status;
      response.metricValue = elapsed.milliseconds;
      response.metricUnit = 'ms';

      if (errMsg) {
        response.output = errMsg;
      }
    }

    return response;
  }
}

module.exports = Checks;

 

헬스 체크 동작 확인

추가한 맵핑 정보가 동작(반영)하지 않는다면 make restart 명령으로 컨테이너를 새로 실행

 

http://127.127.127.127:7507/health

 

http://127.127.127.127:7507/ping

 

 

서비스 중지 및 변경 사항 깃허브 등록

c:\msur\ms-flights> make stop

c:\msur\ms-flights> git add .

c:\msur\ms-flights> git commit -m "항공편 마이크로서비스 구현"
[master 4ab094e] 항공편 마이크로서비스 구현
 4 files changed, 12 insertions(+), 12 deletions(-)

c:\msur\ms-flights> git push origin master
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 659 bytes | 329.00 KiB/s, done.
Total 6 (delta 5), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (5/5), completed with 5 local objects.
To https://github.com/naanjini/ms-flights.git
   2e5a6e8..4ab094e  master -> master

 

728x90
반응형

댓글