docker를 이용하여 개발과 test를 하는 방법에 대해 알아 보겠습니다.
microservice architecture 로 구현 해보겠습니다.
Docker, Microservice 개념에 대해서 기본적인 이해가 필요한 내용 입니다. .
본 실습은 아래와 같은 구성으로 진행 됩니다.
Contents
- Project Setup
- Docker Config
- Postgres Setup
- Users Service Setup
- Locations Service Setup
- Web Services Setup
- Testing
- Workflow
- Test Setup
-
Next Steps
목표
- Microservice 를 위한 Docker 와 Docker componse 구성
- container에 volume 붙이기
- Docker container 내에서 unit test , integration test수행하기.
- functional test 수행
- running Docker container Debug 하기
-
container 간의 통신 ( AJAX)
Project Setup
아래와 같은 파일 구조를 갖고 있습니다.
├── services |
폴더 구조를 살펴 보면 Service 와 web으로 나뉘어 있고 Service에 location 과 user 가 있습니다.
web ,location, user 3개의 application이 있다고 생각하면 됩니다.
docker로 application을 build 하기 전에 local 환경에서 바로 실행 해보겠습니다.
Users:
1. cd services/users
2. npm install
3. node src/server.js
4. 브라우저에서 http://localhost:3000/users/ping 로 접속
5. pong 이란 결과가 보이면 정상
6. ctrl + c 로 server 중지
Locations:
1. cd services/locations
2. npm install
3. node src/server.js
4. 브라우저에서 http://localhost:3001/locations/ping 로 접속
5. pong 이란 결과가 보이면 정상
6. ctrl + c 로 server 중지
Docker Config
위 application을 docker 환경에서 구동시켜 보겠습니다.
project root에 아래 파일을 추가 합니다.
/docker-compose.yml
/services/locations/.dockerignore
/services/locations/src/db/.dockerignore
/services/users/.dockerignore
/services/users/src/db/.dockerignore
/tests/.dockerignore
/web/.dockerignore
docker-compose.yml 파일에 아래 내용을 추가 합니다.
version: ‘2.1’ |
각 .dockerignore 파일에 아래 내용을 추가 합니다.
.git |
Postgres Setup
Dockerfile을 추가 합니다.
/services/locations/src/db/Dockerfile
/services/users/src/db/Dockerfile
Dockerfile에 아래 내용을 추가 합니다.
FROM postgres # run create.sql on init |
docker-compose.yml 파일을 아래와 같이 수정 합니다.
version: ‘2.1’ services: users-db: locations-db: |
docker compose는 여러 container를 한번에 build , deploy 하는 것입니다.
docker-compose.yml는 그에 대한 내용을 정의 하는 파일 입니다.
user-db와 location-db라는 container를 정의 했습니다.
build 파라미터에 정의한 location에서 Dockerfile을 찾을 것이고 Dockerfile에 정의된 내용으로 build를 할 것입니다.
두 service를 build 해보겠습니다.
$ docker-compose up –build -d |
build가 완료 되고 container가 running 될 것입니다.
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 477a4c536c78 nodedockerapi_users-db “docker-entrypoint…” 9 seconds ago Up 6 seconds (health: starting) 0.0.0.0:5433->5432/tcp users-db 6997e2b6e707 nodedockerapi_locations-db “docker-entrypoint…” 9 seconds ago Up 5 seconds (health: starting) 0.0.0.0:5434->5432/tcp locations-db |
container 상태를 확인하기 위하여 각 container에 shell로 접속 해보겠습니다.
이 단계에서는 container에 접속만 해보 겠습니다.
(container는 stateless 입니다. shell로 접속은 되지만 변경작업을 한다고 하여도 저장되지 않습니다. )
$ docker-compose run users-db bash |
Dockerfile을 추가 합니다.
/services/users/Dockerfile
Dockerfile에 아래 내용을 추가 합니다.
FROM node:latest # set working directory # install app dependencies # start app |
docker-compose.yml 파일에 아래내용을 추가 합니다. (service section에 포함되게 추가 해주세요.)
users-service: |
container_name: users-service 라는 container를 정의 했습니다.
build: build 위치를 지정했습니다.
voluem : volums을 지정했습니다. 실제 code가 있는 경로를 volume으로 지정함으로써 code 변경을 바로 바로 container에 적용 할 수 있습니다. voluem 이 없다면 source code가 바뀔때 마다 새로운 이미지 build를 하고 container를 다시 올려야 합니다. 이런 번거로움을 없애고자 container 에 source경로를 volume으로 붙였습니다.
일반적으로 development 단계에서는 code변화를 빠르게 적용하고 feedback을 보고자 이런 방식을 취합니다.
depends_on: user-service는 user-db 이 올라온 뒤에 running 될 것입니다.
links: user-service에서 user-db에 5423 port로 접근 가능. user-db:5423 형태.
NODE_ENV setup
$ export NODE_ENV=development |
위와같이 완경변수로 NODE_ENV를 설정 하고 code 내에서 process.env.NODE_ENV 형태로 접근 할 수 있습니다.
development 일경우 production 일 경우 test 일 경우 등으로 환경설정을 나누어 하기위한 목적입니다.
새로추가한 service만 build 해보겠습니다.
$ docker-compose up –build -d users-service |
Knex migrate를 위해 project root 경로에 migrate.sh 파일을 만들고 아래 내용을 추가 합니다.
(Knex.js는 Node.js SQL 빌더 및 query기능을 제공하는 라이브러리입니다. MySQL, MariaDB, PostgreSQL, SQLLite, Oracle, MSSQL과 같이 대부분의 RDBMS를 지원합니다.)
#!/bin/sh docker-compose run users-service knex migrate:latest –env development –knexfile app/knexfile.js |
Docker Compose에서 구동한 컨테이너에서 새로운 커맨드를 실행하고자 할 때에는 docker-compose run 커맨드를 사용합니다. 위 커맨드는 user-service 컨테이너의 knex 명령을 수행 합니다. db 정보를 code와 sync하는 migrate command와 기초data를 넣는 seed 커맨드 입니다.
knex에 대한 이해가 필요한 커맨드 이므로 간단하게만 설명하고 넘어가겠습니다.(ORM 방식을 생각하면 쉽습니다)
migrate라는 command로 code로 정의된 내용을 기반으로 실제 db의 table 또는 column 속성등을 update 할 수 있습니다.
seed는 기초data를 넣는 명령입니다. 일반적으로 test를 위해 사용하거나, initial data를 db에 넣기위해 사용됩니다.
본 과정은 docker 에 대해 알아보는 것이므로 구체적인 programming 관련 사항에대해 서는 참고만 하세요.
migrate.sh을 수행 해보겠습니다.
$ sh migrate.sh |
warning이 나올 수 있는데 무시하고 넘어가겠습니다.
Test를 위한 API는 아래와 같이 구성되어 있습니다.
Endpoint |
HTTP Method |
CRUD Method |
Result |
/users/ping |
GET |
READ |
pong |
/users/register |
POST |
CREATE |
add a user |
/users/login |
POST |
CREATE |
log in a user |
/users/user |
GET |
READ |
get user info |
httpie를 이용하여 post action을 테스트 해보겠습니다.(curl 을 사용해도 되고, http request client program이 있다면 그것을 이용해도 됩니다.)
$ http POST http://localhost:3000/users/register |
아래와 같이 success 결과를 보실 수 있습니다.
$ http POST http://localhost:3000/users/register username=michael password=herman HTTP/1.1 200 OK Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: GET, PUT, POST, DELETE Access-Control-Allow-Origin: * Connection: keep-alive Content-Length: 170 Content-Type: application/json; charset=utf-8 Date: Thu, 11 Oct 2018 08:55:35 GMT ETag: W/”aa-U8XkufKkOlzHVt3cVQJUAw” X-Powered-By: Express { “status”: “success”, “token”: “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NDA0NTc3MzUsImlhdCI6MTUzOTI0ODEzNSwic3ViIjoyfQ.mDziK5xtUpVNa7RwiWVdD_ti1e798ZyLknQS6BulyOc” } $ http POST http://localhost:3000/users/login username=michael password=herman HTTP/1.1 200 OK Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: GET, PUT, POST, DELETE Access-Control-Allow-Origin: * Connection: keep-alive Content-Length: 170 Content-Type: application/json; charset=utf-8 Date: Thu, 11 Oct 2018 08:55:54 GMT ETag: W/”aa-1+JRC+koMwYxgq9slz+91w” X-Powered-By: Express { “status”: “success”, “token”: “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NDA0NTc3NTQsImlhdCI6MTUzOTI0ODE1NCwic3ViIjoyfQ.aPWpmmT4SONNwUlgT051MNDhpA5-EU3eNxsqqABQYE8” } |
Locations Service Setup
Dockerfile을 추가 합니다.
/services/locations/Dockerfile
Dockerfile에 아래 내용을 추가 합니다.
FROM node:latest # set working directory # install app dependencies # start app |
docker-compose.yml 파일에 아래내용을 추가 합니다. (service section에 포함되게 추가 해주세요.)
locations-service: |
날씨 api 를 활용할 예정입니다.
IBM CLOUD의 weather company data service 를 사용 해보겠습니다.
- https://bluemix.net 에 가입 합니다.
- Catalog -> Weather Company Data 서비스 생성
- service credential 에서 url 확인, (service credential 이 없다면 파란색 New credential 버튼을 클릭 하여 credential을 생성 합니다.)
- url을 env 로 등록 합니다.
$ export WEATHER_HOST= [ibm cloud weather compay 서비스 url 정보 기입] |
location service를 build 합니다.
$ docker-compose up –build -d locations-service |
migrate.sh 파일에 아래 코드를 추가 합니다. (코드 내용은 이전에 설명한 것과 동일 합니다.)
docker-compose run locations-service knex migrate:latest –env development –knexfile app/knexfile.js |
migrate를 수행 합니다.
$ sh migrate.sh |
Test를 위한 API는 아래와 같이 구성되어 있습니다.
Endpoint |
HTTP Method |
CRUD Method |
Result |
/locations/ping |
GET |
READ |
pong |
/locations |
GET |
READ |
get all locations |
/locations/user |
GET |
READ |
get all locations by user |
/locations/:id |
GET |
READ |
get a single location |
/locations |
POST |
CREATE |
add a single location |
/locations/:id |
PUT |
UPDATE |
update a single location |
/locations/:id |
DELETE |
DELETE |
delete a single location |
curl 또는 httpie를 이용하여 test 할 수 있습니다.
$ http GET http://localhost:3001/locations/ping |
아래와 같은 결과가 나올 것입니다.
$ http GET http://localhost:3001/locations/ping HTTP/1.1 200 OK Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: GET, PUT, POST, DELETE Access-Control-Allow-Origin: * Connection: keep-alive Content-Length: 4 Content-Type: text/html; charset=utf-8 Date: Thu, 11 Oct 2018 11:03:34 GMT ETag: W/”4-b9sIeqP7+8uCh6WToJGeYQ” X-Powered-By: Express pong |
Web Services Setup
Dockerfile을 추가 합니다.
/web/Dockerfile
Dockerfile에 아래 내용을 추가 합니다.
FROM node:latest # set working directory # install app dependencies # start app |
docker-compose.yml 파일에 아래내용을 추가 합니다. (service section에 포함되게 추가 해주세요.)
web: |
web service를 build하겠습니다.
$ docker-compose up –build -d web |
브라우저에서 http://localhost:3003/login 에 접속 합니다.
login 하라는 메뉴가 나오면 register 후에 login 합니다.
아래와 같은 페이지가 나올 것입니다.
본 웹페이지는 location-service 로 부터 (weather api 를 등록한 service) 정보를 받아서 출력하는 역할을 합니다.
web service의 코드를 좀 살펴보겠습니다.
/web/src/routes/index.js 에서 weather api 받아오는 값을 처리 합니다. ajax 방식으로 처리하는데 GET request의 uri 가
아래와 같습니다.
모든 서비스가 local에서 수행됨에서 request uri가 locations-service:3001 입니다. container base로 서비스가 동작 하기 때문입니다. web service에서 location-service:3001 형태로 접근 할 수 있는 이유는 docker-compose.yml 의 web service 정의 부분에서 links에 locations-service를 지정 해줬기 때문입니다.
Testing
docker 환경에서 unit testing 하는 방법을 알아보겠습니다.
test coding은 이미 되어 있습니다. code를 짜는 법이 아니라 docker 환경에서 test 를 구동하는 방법에 대해 알아보겠습니다.
환경변수를 test로 수정 합니다.
$ export NODE_ENV=test |
container를 update 합니다.
새로 지정된 환경변수를 적용한다는 의미 입니다.
$ docker-compose up -d |
test를 수행 합니다.
$ docker-compose run users-service npm test |
test 결과가 console에 나타날 것입니다.
Workflow
container base환경에서 code가 어떻게 동작하는지 간단히 살펴 보겠습니다.
- container에 volume 형태로 source code를 mount 합니다.
- local 환경에서 source code를 수정합니다.
- container의 nodedaemon이 이 변화를 감지하고 app을 restart하여 변경된 code를 적용합니다.
- local 환경에서 debugging을 위해 사용하는 console.log 의 결과를 docker-compose logs -f 명령을 통해 확인 합니다.
Test Setup
functional test service를 만들어 보겠습니다.
Dockerfile을 추가 합니다.
/tests/Dockerfile
Dockerfile에 아래 내용을 추가 합니다.
FROM node:latest # set working directory # install app dependencies |
docker-compose.yml 파일에 아래내용을 추가 합니다. (service section에 포함되게 추가 해주세요.)
tests: |
tests service를 build 합니다.
$ docker-compose up –build -d tests |
아래 명령을 통해 test를 수행 합니다.
$ export NODE_ENV=test |
ETC docker 환경에서 필요한 command
-
postgres psql 사용법
container-id는 docker ps 명령으로 알 수 있습니다.
$ docker exec -ti <container-id> psql -U postgres |
- stop container
$ docker-compose stop |
- bring down container
$ docker-compose down |
- force build
$ docker-compose build –no-cache |
- remove image
$ docker rmi $(docker images -q) |
- unit test
$ export NODE_ENV=test |
- functional test
$ export NODE_ENV=test |
본 문서는 아래 원문을 편집하였습니다.
원문 : https://mherman.org/blog/developing-and-testing-microservices-with-docker/