Programming/Docker

[Docker-compose] Refactoring docker-compose(1, dev)

stein 2022. 1. 17. 18:35

서론

때는 11월. 이제 막 docker-compose로 Dapada Stock을 구동시켜보고, 반복 작업을 피하기 위해서 front, back, db, nginx가 모두 구성된 web-compose를 만들어낸 시기.

데브경수님의 인스타툰(문제시 삭제)

출처: https://www.instagram.com/waterglasstoon/

그 때도 내심 코드가 더럽다고는 생각했지만 정말 돌아만 가는 코드를 만들어 놨더라. 무슨 생각으로 깃헙에 올렸던건지, 거참.

계기

회사에서 사용하던 Azure의 Credit이 다 떨어지고, AWS Credit이 넘쳐나고 있어서 운영하던 서비스를 Azure에서 AWS로 옮겨야하는 일이 생겼다. 하지만 기존에 작업하셨던 백엔드 개발자 분이 다른 곳으로 이직하셔서, 결국 옮길 사람은 나 혼자!

기존의 코드는 도커로도 감싸져 있지 않아서 이 참에 docker-compose로 올리기로 했다(정리도 하는겸...).

 

이전 작업은 성공적으로 끝났고, 이때 얻은 지식들을 사용해 과거의 web-compose를 refactoring 해보자.

 

refactoring의 요모조모

poetry

이번 이전 작업을 통해 알게 된 중요한 라이브러리인 poetry.

nodejs에 존재하는 npm과 흡사하게 python 프로젝트에서도 project와 package들을 정리해준다. 물론 pip도 존재한다! 하지만 pip의 requirements.txt로는 package-lock.json과 같은 dependency lock 파일의 기능을 구현할 수 없다. lockf file에 대해서는 이를 잘 정리해놓은 포스트가 있으니 그 개념을 잘 모른다면 한 번 읽어보길 바란다.

 

package-lock.json이란?

package-lock.json을 자주 봤지만 어떤 역할을 하는지 모르고 있었습니다. 오늘은 package-lock.json에 대해서 정리해보려고 합니다.

junwoo45.github.io

어쨌든, 매번 가상환경으로 python library들을 관리하던게 찝찝하던 차에(도커 컨테이너에는 거의 안썼지만), 내겐 익숙한 npm 방식의 패키지 관리툴이 python에도 있는 것을 알고 꽤나 기뻤다.

 

djagno docker file

FROM python:3.9-slim-buster

ENV PYTHONUNBUFFERED=0

RUN apt update && apt -y install libpq-dev gcc git procps
RUN pip install poetry

pip install을 통해 poetry를 빌드파일에 설치해 놓는다. 

 

 

django docker compose

django:
    build: ./django
    volumes:
      - ./django:/container/django
    expose:
      - 8000
    env_file:
      - ./django/.env.dev
    command:
      bash -c '
      cd /container/django/webcompose
      && poetry config virtualenvs.in-project true
      && poetry install --no-interaction --no-root
      && poetry run python manage.py collectstatic --noinput
      && poetry run gunicorn --bind 0.0.0.0:8000 --reload webcompose.wsgi:application
      && tail -F /dev/null'
      # && poetry update
            # Use this command if you want to update dependencies latest 
            # or to fix dependencies version error.
      
    depends_on:
      - postgres
poetry config virtualenvs.in-project true 라는 configure를 주면 poetry가 package를 해당 프로젝트 root에 .venv 폴더밑에 설치하게 된다. 이 옵션을 준 이유는 다음과 같다.
 
  1. package들이 존재한다면 poetry install 과정을 skip
    poetry는 poetry.lock 파일이 존재하면 해당 파일을 참고하여 설치를 진행하는데 이미 설치가 되어있다면 다음과 같은 메세지를 띄워준다.
    django_1    | Installing dependencies from lock file
    django_1    | 
    django_1    | No dependencies to install or update

    project root에 존재하지 않으면 docker volume이 외부에 연결되어있지 않기 때문에 컨테이너를 띄울 때 마다 설치해야하지만, 이 설정으로 시간을 아낄 수 있다.
  2. debugger
    필자는 vscode의 debugger 기능을 아주 잘 쓰고 있다. 그래서 poetry로 설정후에 vscode debugger의 launch.json configure에 기존의 gunicorn debugger를 poetry를 감싼 명령어로 바꾸니 잠시 실행되다가 꺼지는 현상을 만났다. 따라서 poetry가 설치한 gunicorn에 직접 접근하는 방식을 택했다. 현재 project 폴더 하위에 해당 패키지가 존재하기 때문에 직관적으로 configure를 설정할 수 있었다.(이건 poetry가 설치하는 경로가 기존에도 있기 때문에, 그 경로를 이용한다면 가능할 것 같다. 하지만 테스트를 해보지는 못했기 때문에 이 정도만 말하겠다)

이 방식으로 npm 과 더욱 흡사해졌고, 동일한 구조로 프로젝트를 관리하는 느낌이라 기분이(?) 좋다

npm ci

poetry와 마찬가지로 package-lock.json을 사용하기 위해서 npm install -> npm ci로 명령어를 변경했다. 하지만 매번 설치할 필요는 없으므로 주석과 함께 밑으로 빼놓았다.

  nuxt:
    image: node:14.17
    volumes:
      - ./nuxt:/container/nuxt
    expose:
      - 3000
    command: bash -c '
      cd /container/nuxt
      && npm run dev
      && tail -F /dev/null'
      # && npm ci 
            # insert it to before 'npm run dev' when doesn't have node_modules
            # also package-lock.json doesn't exist

경로(volumes)

모든 컨테이너들의 volume을 ./dir:container/dir 로 설정하였다.

따라서 앞에 container가 붙은 경로가 보이면 container 내부의 경로인 것을 쉽게 알 수 있을 것이다.

nginx가 django의 static file을 serving하기 위해서 해당 폴더 경로와 연결 된 점만 특이사항이고, 나머지는 모두 동일하다.

 

ports

nginx와 database를 제외하고 모든 포트를 외부와 연결하지 않았다. nginx로 모두 proxy했다.

 

환경변수(env_file, environment)

모든 환경변수들은 .env파일로 옮겼고 해당 컨테이너 프로젝트 폴더 밑에 위치 시켰다.

 

코드 전문

docker-compose.dev.yml

version: "3.8"

services:
  nuxt:
    image: node:14.17
    volumes:
      - ./nuxt:/container/nuxt
    expose:
      - 3000
    command: bash -c '
      cd /container/nuxt
      && npm run dev
      && tail -F /dev/null'
      # && npm ci 
            # insert it to before 'npm run dev' when doesn't have node_modules
            # also package-lock.json doesn't exist

  django:
    build: ./django
    volumes:
      - ./django:/container/django
    expose:
      - 8000
    env_file:
      - ./django/.env.dev
    command:
      bash -c '
      cd /container/django/webcompose
      && poetry config virtualenvs.in-project true
      && poetry install --no-interaction
      && poetry run python manage.py collectstatic --noinput
      && poetry run gunicorn --bind 0.0.0.0:8000 --reload webcompose.wsgi:application
      && tail -F /dev/null'
      # && poetry update
            # Use this command if you want to update dependencies latest 
            # or to fix dependencies version error.
      
    depends_on:
      - postgres

  postgres:
    image: postgres:13.0-alpine
    volumes:
      - ./postgres:/var/lib/postgresql/data/
    ports:
      - 5432:5432
    env_file:
      - ./.env.postgres

  nginx:
    build: ./nginx
    volumes:
      - ./nginx:/container/nginx
      - ./django/webcompose/static:/container/django/webcompose/static
    ports:
      - 80:80
    depends_on:
      - nuxt
      - django

 

결론

기존 compose file은 주렁주렁 뭐가 많이 달려 있었는데, 일단 시각적으로 깔끔해져서 보기 좋다. 그리고 package install이 compose command에 적혀져 있어서 compose를 올리고 내리는게 꽤나 부담이 되었는데, 이 부분이 해결되어서 앞으로의 개발 시간을 많이 단축시킬거라 기대한다.

현재 개인 서버와 도메인이 없어서 let's encrypt 컨테이너를 못붙인다. 따라서 prod용 compose를 못 만들고 있는데, 조만간 정리되는 대로 prod 버전 compose도 포스팅하겠다.