터칭 데이터

voting application의 docker-compose.yml 개선하기 본문

Docker & K8S

voting application의 docker-compose.yml 개선하기

터칭 데이터 2023. 12. 21. 18:58

 

 

 

앞서 매뉴얼하게 실행해본 voting application를 docker-compose를 통해 실행해보자

 

1. Docker 명령 정리와 2장 퀴즈


2. Docker Volume이란?


3. 다수의 Container로 구성된 소프트웨어 실행


4. Docker-Compose로 다수 Container로 실행해보기


5. Airflow Docker docker-compose.yml 리뷰

 

지난 시간 voting application을 메뉴얼하게 구성하는 실습으로 1~3번을 진행했습니다.

 

그리고 바로 이전 게시물에서 같은 프로그램을 docker-compose 방식으로 실습하며 4번 과정도 진행했습니다.

 

이제는 5번 과정에 앞서 4번 실습에서 사용한 yaml 파일 리뷰를 진행하겠습니다.

 

 

 

 

 

 

 

 

 

 

아까 사용한 docker-compose.mac.yml에 networks와 volumes를 추가해 1차 개선하기

(실제로 docker-compose.mac.yml을 수정하지는 않지만 쉬운 이해를 위해 위의 소제목과 같이 yaml 파일을 수정 보완한다는 상상을 하며 과정을 따라와주세요.)

 

 

 

 

 

 

 

 

 

 

 

docker-compose.yml로 포팅: networks 정의

 

 

yml 파일에서 networks: 라는 탑 레벨 key를 지정하지 않으면 같은 docker-compose안에 기술이 된 Docker Container들은 자동으로 서비스 이름(vote, redis, worker, db, result)을 기준으로 DNS resolution이 됩니다. 그래서 vote라는 이름이 내부 IP어드레스(host)가 되어 연결이 된다고 했습니다.

 

그런데 하나의 네트워크로 일괄적으로 연결하지 않고 위와 같이 front-tier, back-tier와 같이 2개의 네트워크로 분리해 연결하고 싶으면 어떻게 해야할까요? 사용자들은 front-tier에 속한 vote와 result 서비스에만 접근하도록해 보안을 강화하려 합니다.

 

좌측 상단의 그림이 설명하듯이

networks:
  back-tier: redis, worker, db, vote, result
  front-tier: vote, result

 

back-tier에는 5개 모든 서비스가

front-tier에는 vote, result 2개의 서비스가 속해야 합니다.

 

즉, vote, result는 back와 front에 모두 속해야 합니다.

 

그 이유는 front-end인 vote와 result는 우측의 흐름도를 보시면 각각 redis에 접근하고 db에서 접근을 받기 때문입니다. 반면 back-end에 속한 redis, worker, db는 front-end에 접근할 필요가 없습니다.

 

 

 

 

 

 

 

 

 

 

 

그러면 그러기 위해서는 yaml 파일을 어떻게 고쳐야 할까요?

 

docker-compose.yml로 포팅: services들에 네트워크 지정

 

 

기존에는 network key를 사용하지 않았는데 이제 개선된 yaml 파일에는 모든 service들이 network를 사용하고 있습니다.

 

설명드렸듯이 vote, result는 back-tier와 front-tier에 동시에 소속되고 나머지 redis, db, worker는 back-tier에만 소속됩니다.

 

지면상의 문제로 우측으로 빼두었습니다만 networks key는 services의 각 서비스안에만 존재하지 않고 최고 레벌 services 키와 동등하게 아래와 같이 적혀있어야 합니다.

 

networks:
  back-tier:
  front-tier:

services:
  redis:
  ...

 

위와 같이 보완 작성하면 네트워크가 분리되어 조금 더 보안상으로 서비스가 개선될 것입니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

networks를 보완했으니 이제는 volumes를 추가해 Data Persistency를 보완하겠습니다.

 

docker-compose.yml로 포팅: volumes 정의

 

서비스들이 stop, remove, restart에도 데이터들이 계속 보관되도록 volume을 정의할 것입니다.

 

이번에는 모든 서비스에 volume을 추가하지 않고 db 서비스에만 db-data라는 docker volume을 만들어 할당(assign)해보겠습니다. (이후 과정에서 모든 서비스에 volume을 추가하겠습니다.)

 

역시 yaml 파일의 최상위 레벨에 volumes 키를 둡니다. 그리고 volumes key 아래에 들여쓰기로 우리가 앞으로 사용할 docker volume의 이름인 db-data를 기록합니다. 이렇게 최상위에 작성해주면 db-data라는 볼륨이 앞으로 서비스(services)들 중에서 하나 이상에서 사용된다는 뜻이 됩니다.

 

그리고 그 docker volume을 사용하는 서비스의 내용들은 persistent하게 유지 될 것입니다.

 

 

 

 

 

 

 

 

 

 

그러기 위해서는 yaml 파일을 어떻게 수정해야 할까요?

 

docker-compose.yml로 포팅: services들에 볼륨 지정

 

 

우측 하단과 같이 volumes key를 최고 레벨로 두고 db-data volume을 value로 주고

서비스 중에서 db에 named volumes 방식(가장 선호되는 방식이라고 했었죠?)으로 콜론을 두고 volume의 이름과 volume이 attach(마운트, mount)되는 도커 컨테이너 파일시스템을 적습니다.

 

db라는 Docker Container의 /var/lib/postgresql/data의 내용들이 db-data volume에 실시간으로 보관될 것입니다.

 

직접 데모를 해보지는 않겠습니다. 관심이 있다면 한번 해보세요.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

지금까지가 간단한 1차 개선이었습니다.

 

이제 voting application의 docker-compose.yml을 최종 개선하겠습니다.

 

 

 

 

 

 

docker-compose.yml로 포팅: vote 서비스 개선

 

vote부터 서비스들 하나하나씩 살피며 개선해보겠습니다.

 

 

우측 이미지의 파란색 박스로 적힌 command

 

build:키의 ./vote 디렉토리에 존재하는 Dockerfile에 적힌 

 

(생략..)
FROM base AS dev
RUN pip install watchdog
ENV FLASK_ENV=development
CMD ["python", "app.py"]
(생략..)

 

CMD의 내용을 docker-compose를 이용해 오버라이드(override)할 때 사용합니다.

즉 Dockerfile 내부에 있는 'CMD' 명령보다 docker-compose.yml 파일에 있는 'command:' 명령이 더 우선시 됩니다.

 

 

 

 

그리고 빨간색 박스의 depends_on은 5개의 컨테이너가 실행될 때 이 컨테이너들 사이의 이상적인 순서가 존재합니다.

 

 

voting-app의 경우는 먼저 redis가 실행되고 실행되어야 더 안정적입니다. 이와 같은 의존성, 순서를 정해주지 않으면 서비스 실행이 운에 맡겨져 불안정한 상태가 되게 됩니다.

 

빨간색 박스 depends_on의 내용은 vote 서비스는 docker container로 실행되려면 redis가 먼저 docker container로 서비스되어야 한다는 뜻입니다. condition:을 추가하면 redis가 먼저 실행되는 것은 물론 redis의 실행 상태가 건강(healthy)할 때만 vote라는 서비스가 실행되도록 해줄 수 있습니다.

 

이런 conditon 체크를 위해서는 redis service에 redis가 자신의 건강을 체크하는 healthcheck 키가 기술되어야 합니다. 

 

vote 역시 healthcheck key가 있는데 다른 서비스가 vote의 condition을 체크하기 때문에 기술할 수도 있고 혹은 Docker 엔진이 vote 컨테이너의 상태를 체크하는 용도로 기술했을 수도 있습니다.

 

안의 내용들은 test로 주어진 명령을 15초마다 실행해서 응답이 5초 이상 걸리면 실패로 처리하고 실패했을 시 최대 3번 더 재시도하고 그래도 실패한다면 vote의 건강상태를 비정상으로 처리한다는 뜻입니다. start_period는 healthcheck를 vote 컨테이너각 시작하고 10초 뒤에 시작하라는 뜻입니다.

 

command 설명때와 마찬가지로 healthcheck 역시 Dockerfile에 존재할 수 있고 이를 docker-compose.yml파일이 override할 수 있습니다.

 

volume:로 ./vote 폴더와 Docker container 쪽의 /app 폴더를 volume화 시켰습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

docker-compose.yml의 command와 entrypoint

 

command: 이미지 Dockerfile의 CMD를 덮어쓰는데 사용

entrypoint: 이미지 Dockerfile의 ENTRYPOINT를 덮어쓰는데 사용

 

 

 

 

 

 

 

 

docker-compose.yml의 healthcheck

 

역시 Dockerfile에서 기술가능한 기능이며 docker-compose에서 덮어쓰기 가능

 

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"] # 0 for success 1 for failure
  interval: 1m30s
  timeout: 10s
  retries: 3
  start_period: 40s

 

 

 

 

 

 

 

 

 

docker-compose.yml의 depends_on

 

해당 서비스가 실행되기 위해서 먼저 실행되어야 하는 서비스들을 여기 기술

 

short form:

depends_on:
  - db
  - redis

 

long form:

depends_on:
  db:
    condition: service_healthy
  redis:
    condition: service_started

 

 

 

condition으로 가능한 값

 

service_started:

a. is an equivalent of the short syntax described above

 

service_healthy:

a. specifies that a dependency is expected to be "healthy" (as indicated by healthcheck) before starting a dependent service.

 

service_completed_successfully: 

a. specifies that a dependency is expected to run to successful completion before starting a dependent service.

이 옵션은 의존하는 서비스가 성공적으로 완료되어야 합니다. 이는 의존하는 서비스가 완전히 실행되고 성공적으로 종료된 후에만 의존 대상 서비스가 시작됩니다.

 

이후 Airflow를 다룰 때 Docker Image를 실행할 때 초기화 작업이 실행되고 끝난 뒤에야 다른 컨테이너들이 실행되도록 해야하는 경우가 있습니다. 초기화 작업을 실행하는 컨테이너를 다른 모든 컨테이너들이 depends_on:의 값으로 적고 condition:을 service_completed_successfully로 적어줍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

vote 서비스를 개선해보았습니다.

docker-compose.yml로 포팅: db 서비스 개선

db 서비스를 개선하겠습니다.

 

 

environment는 docker-compose.mac.yml 파일로 실습할 때 이미 살펴보았습니다.

 

volumes는 host volume과 named volume 모두 가능합니다. (이 둘에 대한 설명은 여기를 참고)

host volume은 top level volumes:에 적을 필요가 없습니다.

 

 

 

 

 

 

 

 

 

docker-compose.yml의 environment

 

해당 서비스가 컨테이너 안에서 실행될 때 환경변수들을 지정 (Dockerfile의 ENV)

 

Map 문법:

environment:
  RACK_ENV: development
  SHOW: "true"
  USER_INPUT:

 

쭉 나열합니다. 위에서 이미지로 본 기술 방식이 Map 문법입니다.

 

 

Array 문법:

environment:
  - RACK_ENV=development
  - SHOW=true
  - USER_INPUT

 

리스트 형태로 적어줍니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

docker-compose.yml로 포팅: redis 서비스 개선

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

docker-compose.yml로 포팅: worker 서비스 개선

 

 

worker는 redis의 투표 정보를 읽어 db에 적재시키는 일을 하기 때문에 redis와 db 모두 healthy한 상황이어야 합니다.

 

worker는 갑자기 들여쓰기 이후 context:가 등장합니다. 만약 내가 미리 지정된 Dockerfile만 그대로 쓰고 싶지 않고 Dockerfile의 이름을 바꾸거나 Dockerfile을 만들 때 특정 변수를 바꾸고 싶다면 build 키워드 아래에 다른 정보를 적어줄 수 있습니다.

 

우측의 하이라이트된 예시를 보면 context로 ./myapp에 있는 Dockerfile을 사용하겠다고 말하고 Dockerfile.dev처럼 기존의 Dockerfile과 달리 확장자가 붙은 파일을 Dockerfile로 사용한다는 뜻입니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

docker-compose.yml로 포팅: result 서비스 개선

 

 

result 서비스는 ./result 디렉토리의 Dockerfile을 쓸건데 사용할 Dockerfile에는 entrypoint라는 변수가 있고 그 entrypoint를 새로운 값 nodemon server.js로 override 하겠다는 뜻입니다.

 

 

 

 

 

 

 

 

 

 

 

 

최종 파일 리뷰

지금까지 example-voting-app을 두번의 단계를 거쳐 최종 버전이 어떤 모양인지 살펴보았습니다.

그 버전을 아래에서 살펴볼 수 있습니다.

https://github.com/learndataeng/example-voting-app/blob/main/docker-compose.yml

 

단 차이점이 딱 하나 있는데 포트 5000번을 수정해야합니다.

 

다시 한번 Monterey Mac에서 포트 5000번을 사용할 수 없기에 vote 앱의 포트번호를 수정해야함
vote와 result의 포트번호를 각각 5001과 5002로 수정한 버전이 위의 버전임 (원래는 각각 5000과 5001이었음)