Fastify & MongoDB, docker-compose

Fastify & MongoDB docker-compose 동시 실행

Featured image

1. Fastify WAS 서버 생성 / 컨테이너 화


먼저 fastify를 이용해 기본적인 WAS 서버 세팅을 해볼 것이다. fastify가 깔려있지 않을 경우 다음 명령을 통해 fastify를 설치해준 후, 정상적으로 설치가 되었는지 확인해본다.

$ npm i --global fastify-cli
$ fastify
# 다음과 같은 명령이 출력되면 설치가 완료된 것이다.
Fastify command line interface available commands are:

...

Launch 'fastify help [command]' to learn more about each command.

설치 완료 후, 다음 명령을 통해 fastify 프로젝트를 새로 생성해준다.

$ fastify generate <project-name>

나는 을 helloworld-was로 하였고, 폴더와 프로젝트가 정상적으로 생성되었다. ```npm install```을 통해 필요한 모듈을 설치해준다.

기존에는 127.0.0.1에서 fastify를 테스트하지만, 로컬호스트가 배포 시에는 노출이 되지 않는다. 따라서 fastify 주소를 0.0.0.0 으로 세팅해주어야 한다고 fastify 공식 문서에서 권장하고 있다.

이 때 0.0.0.0은 모든 IPv4로부터 수신이 가능하다는 뜻이다.

// package.json
"scripts": {
  "test": "tap \"test/**/*.test.js\"",
  "start": "fastify start -l info app.js --address 0.0.0.0",
  "dev": "fastify start -w -l info -P app.js"
}

위와 같이 start 뒤에 --address 0.0.0.0 옵션을 붙여주어 npm run start를 할 경우 로컬에서 0.0.0.0:3000 으로 볼 수 있게 세팅해준다.

이렇게 세팅을 한 후 서버를 실행시켜보자.

$ npm run start

> helloworld-was@1.0.0 start
> fastify start -l info app.js --address 0.0.0.0

{"level":30,"time":1682905582125,"pid":7346,"hostname":"seay0-fairycode","msg":"Server listening at http://0.0.0.0:3000"}

기존에는 127.0.0.1:3000 에서 열렸던 서버의 주소가 0.0.0.0:3000 로 바뀐 것을 볼 수 있다. curl을 통해 접속해보면 정상적으로 서버가 열린 것이 확인된다.

$ curl http://0.0.0.0:3000
{"root":true}


2. Dockerfile 작성


이제 간단하게 구축된 node.js 애플리케이션인 fastify 서버를 컨테이너화 하기 위해 fastify 최상위 디렉토리에 Dockerfile과 .dockerignore를 만든다.

.dockerignore에는 용량이 큰 node_modules 같은 것을 컨테이너화 하지 않기 위해 컨테이너에 올리지 않을 파일들을 적어주면 된다.

# .dockerignore
node_modules

Docerfile은 Docker docs의 » 공식 문서 «를 참고하여 작성하였다.

# Dockerfile

FROM node:16-alpine
# node 16 버전을 사용한다.

WORKDIR /app
# /app 디렉토리를 작업 디렉토리로 설정한다.

COPY package*.json ./
# package*.json 형식으로 생긴 파일을 WORKDIR 위치에 복사한다.

RUN npm install
# WORKDIR에서 npn install을 통해 node_modules를 설치해준다.

COPY . .
# WORKDIR에 있는 전체 파일을 가져와서 이미지에 소스 코드를 추가한다.

EXPOSE 3000
# 컨테이너가 3000 포트를 노출하도록 설정한다.

CMD ["npm", "run", "start"]
# 컨테이너가 시작되었을 때 실행되는 명령을 설정한다.

Dockerfile을 작성한 후 이미지 빌드를 해주면, 정상적으로 이미지 생성이 완료된다.

$ docker build -t fastify-was:1.0 .

근데 ,,, 오류가 났다.

[+] Building 0.1s (3/3) FINISHED                                                                           
 => [internal] load build definition from Dockerfile                                                  0.0s
 => => transferring dockerfile: 32B                                                                   0.0s
 => [internal] load .dockerignore                                                                     0.0s
 => => transferring context: 34B                                                                      0.0s
 => ERROR [internal] load metadata for docker.io/library/node:16-alpine                               0.0s
------
 > [internal] load metadata for docker.io/library/node:16-alpine:
------
ERROR: failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to create LLB definition: failed to do request: Head "https://registry-1.docker.io/v2/library/node/manifests/16-alpine": dial tcp: lookup registry-1.docker.io: Temporary failure in name resolution

네트워크 연결이 갑자기 끊겼더라구요 ^^ 껐다 키니까 연결되고 빌드도 정상적으로 됨. 제발 억까 ㄴ

$ docker images
REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
fastify-was   1.0       <secret^^>     27 seconds ago   187MB

이런 식으로 이미지를 빌드할 수 있는데, 나는 이제 docker-compose와 yaml 파일을 이용해 was 이미지와 mongo 이미지를 동시에 실행하는 과정을 진행해볼 것이다.


3. docker-compose.yml 작성


먼저 Mongo 이미지를 가져온다.

$ docker pull mongo

이제 docker-compose.yml 파일을 작성해준다.

# docker-compose.yml
version: '3.1'

services:

  mongo:
    image: mongo:latest
    restart: always
    ports: 
      - '27017:27017'

  fastify: 
    image: fastify-was:1.0
    restart: always
    ports: 
      - '3000:3000'
    depends_on:
      - mongo

이렇게 작성한 후 docker-compose up을 실행하면 was 서버와 mongo 서버가 각각 지정한 localhost:포트에서 열린다.

mongo는 mongo compass에서도 확인할 수 있다.


4. GitHub Action을 이용한 이미지 build 자동화


GitHub Action을 이용하기 위해 최상위 디렉토리에 .github/workflows 폴더를 생성한다.

workflows 폴더 안에 docker-push.yml 파일을 작성해준다.

# docker-push.yml
name: Docker Build

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: $
          password: $

      - name: Build and push Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          push: true
          tags: $/fastify-was:1.1

yml 안에 적혀있는 $는 GitHub 세팅에서 설정해줄 수 있다.

Github에서 생성한 레포지토리의 Setting으로 들어간다.

왼쪽 메뉴에 Secrets and variables의 Actions 탭을 선택한다.

New repository secret을 클릭한다.

위 사진과 같이 DOCKER_USERNAME과 그에 해당하는 값을 배치해주고 저장한다. USERNAME에는 아이디가 아닌 도커 우측 상단에 있는 사용자 이름을 입력해주어야 한다.

DOCKER_PASSWORD도 같은 방식으로 생성해준다. PASSWORD는 Docker 계정 비밀번호를 적어주면 된다.

이제 설정을 마쳤으니 깃허브에 push를 해주면 Action 탭에서 액션이 돌아간다.

$ git add .
$ git commit -m 'docker-push'
$ git push

방금 푸시한 커밋 이름으로 돌아가고 있는 것을 볼 수 있다.

이렇게 체크 표시가 뜨면 Docker Hub에 정상적으로 이미지가 푸시 된 것이다. 로그인 해서 확인해보자.

yml에서 지정한 이미지 이름으로 새로 생성된 것이 보인다. 자세히 볼까?

태그 탭으로 가보면 태그도 yml에서 지정한대로 1.1로 생성되어 있다.

나는 이 과정을 진행하며 기존에 helloworld-was로 만들었던 폴더 안의 내용물들을 모두 Github 레포지토리를 생성해서 clone한 뒤에 Github 폴더 안으로 옮겨 주었다.

Error: buildx failed with: ERROR: failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to read dockerfile: open /var/lib/docker/tmp/buildkit-mount1197326096/Dockerfile: no such file or directory

제대로 된 위치로 옮겨 주지 않아서 위와 같은 에러가 발생했다. 깃허브 폴더의 최상위 디렉토리에 .github/workflows 폴더가 존재하고, Dockerfile이 같은 위치에 있어야 Action이 정상적으로 돌아가기 때문이었다.

폴더를 바꾸면서 폴더 이름이 바뀌었기 때문에 package.json 파일의 제일 위에 적혀있는 이름도 fastify-practice로 바꿔주었다 … ㅋ hoxi 모르잖아요

$ docker pull seay0/fastify:1.1

깃헙 액션으로 이미지를 새로 푸시한 후 docker-compose up으로 테스트를 해보았는데, 그 전에 방금 Action으로 새로 푸시한 이미지를 받아와서 docker-compose.yml 파일에 image이름을 수정해주었다.

fastify: 
  image: seay0/fastify-was:1.1
  restart: always
  ports: 
    - '3000:3000'
  depends_on:
    - mongo

위와 같이 이미지 이름을 바꾼 후 다시 docker-compose up을 해보았는데,

ERROR: for fastify-practice_mongo_1  Cannot start service mongo: driver failed programming external connectivity on endpoint fastify-practice_mongo_1 (0c47d94fb29cf79b7b3077a20f0d419ea8ebbe1f8d0bb5f0d7a8b5804a5cef83): Bind for 0.0.0.0:27017 failed: port is already allocated

포트가 이미 사용중이란다. 이전에 실행하던 컨테이너를 다시 지우고 해줘야겠다 ㅎㅎ

$ docker container ps -a
# 모든 컨테이너 조회
$ docker container stop <멈출 컨테이너 ID>
$ docker container rm <지울 컨테이너 ID>

지우고 다시 compose up을 해보면 정상적으로 서버가 작동된다.


5. Mongo 서버 환경 변수 설정


기존에 설정했던 docker-compose.yml 파일을 수정해서 나의 MongoDB에 아무나 접속할 수 없게 설정해보자.

mongo:
  image: mongo:latest
  restart: always
  ports: 
    - '27017:27017'
  environment:
    MONGO_INITDB_ROOT_USERNAME: root
    MONGO_INITDB_ROOT_PASSWORD: example

실행 중이던 mongo 컨테이너를 없애고, mongo 쪽에 environment를 추가해서 다시 컨테이너를 만들어 줄 것이다.

코드를 수정한 후 docker-compose-up을 해준다.

그럼 기존에 포트만 있어도 접속이 가능하던 DB가 위와 같은 오류가 뜨면서 DB를 이용할 수 없다.

방금 설정한 아이디와 비밀번호를 입력하고 다시 접속을 해보면,

이렇게 정상적으로 이용이 가능해진다.


오늘은 여기까지 끝 !!