Docker을 이용한 백엔드 개발 (1)

yongun·2022년 11월 27일
1

원티드의 프리온보딩이란 프로그램을 진행하면서 다른 사람들의 깃허브를 봤는데, docker를 사용하는 사람이 별로 아예 없고, poetry나 pipenv 같은 가상환경을 많이 사용했다.

무엇보다 프리온보딩 과정 중에서 대부분의 기업 과제가 Docker을 이용하면 가산점이 있었다. 그래서 이 기회에 docker 사용하는 이유와 방법들을 좀 정리해서 올려놓으려고 한다.

Docker를 사용하는 이유

사용해보면 굉장히 편하다. requirements.txt, dockerfile, docker-compose.yml 파일을 작성해놓으면 나중에 개발할 때 가상환경을 새로 만드는 시간이나 패키지 설정하는 시간이 굉장히 줄어들 것이다.

새로운 앱을 개발할 때마다 그냥 저 3개의 파일을 복사해서 컨테이너 이름이랑 환경변수 정도만 바꿔주면 된다. 가장 큰 장점은 배포할 때 서버에 패키지를 또 설치하고 세팅할 필요없이 내가 만들어 놓은 컨테이너를 이용하여 배포하면 된다는 것이다.

예시로 CVAT를 한번 클론해서 실행시켜보는 것을 추천한다. 문서 설명도 아주 자세히 되어있어서 따라하기 어렵지 않을 것이다.

Docker가 무엇인지에 관한 설명은 찾아보면 많기에 굳이 여기서 설명은 하지 않겠다. 참고를 보면 자세한 설명이 되어있다. 단순하게 설명하면 컴퓨터 속에서 새로운 컴퓨터(컨테이너)를 만드는 것이라 보면 된다.

물론 pipenv 같은 패키지도 사용해봤지만 편하다. 하지만 sqlite가 아닌 PostgreSQL이나 MySQL같은 것을 함께 사용하려면 설치를 해야되고, DB설정도 매번 새로 해줘야한다. 하지만 docker를 사용하면 처음 수고만 하면 다음 개발엔 docker-compose.yml up --build 명령어로 끝난다.

Dockerfile 작성

먼저 dockerfile이다. dockerfile을 간단하게 말하면 어떤 os를 설치하고 어떤 폴더를 작업 폴더로 정할건지, 어떤 패키지를 설치하여 컨테이너를 만들 것인지 작성하는 부분이다. 말보다는 작성된 파일을 보며 설명하겠다. 아래는 내가 작성한 DRF 환경 dockerfile이다. 디렉터리 구조는 다음과 같다.
project
├──app
├──requirements.txt
├──Dockerfile
└──docker-compose.yml

Django

FROM python:3.9-alpine3.13
# docker hub에 등록된 이미지를 가져와서 
# os (linux alpine) 와 python 환경을 설정
# 이미지는 이미 만들어진 환경파일이라고 생각하면 편하다.
# 설명 참고 
# - https://velog.io/@ragnarok_code/도커-컨테이너Container와-이미지Image란
# FROM 명령어를 이용하여 어떤 이미지(환경)를 기본 이미지로 사용할지 설정

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYHONUNBUFFERED 1
# 환경 변수를 설정하는 부분이다. 
# 해당 환경 변수를 설정하면 (값은 크게 상관없다.) 
# .pyc파일을 쓰지 않고, 파이썬 출력 버퍼링 비활성화(로그가 바로 출력됨)

COPY ./requirements.txt /tmp/requirements.txt
# dockerfile을 작성하는 현재 디렉토리의 requirements.txt 파일을 
# 컨테이너 내의 /tmp 경로로 복사
COPY ./app /app
#현재 디렉터리의 app 폴더를 컨테이너 내의 /app 경로에 복사
WORKDIR /app
# 모든 명령어를 컨테이너 내의 /app 경로에서 실행
EXPOSE 8000
# 컨테이너의 8000번 포트 개방


RUN python -m venv /py && \
    /py/bin/pip install --upgrade pip && \
	# 컨테이너 내에 venv 가상환경을 만들고 pip을 upgrade한다.
    apk add --update --no-cache postgresql-client && \
    apk add --update --no-cache --virtual .tmp-build-deps \
    # postgresql에 필요한 패키지들을 설치할 virtual dependecy 패키지
    build-base postgresql-dev musl-dev zlib zlib-dev && \
    # postgresql에 필요한 패키지 설치
    /py/bin/pip install -r /tmp/requirements.txt && \
    # 파이썬에 필요한 requirements.txt 패키지를 설치
    rm -rf /tmp && \
    apk del .tmp-build-deps && \
    # tmp 파일들 및 가상 패키지 삭제
    adduser \
    --disabled-password \
    --no-create-home \
    django-user && \
    # 유저 생성
    mkdir -p /vol/web/media && \
    mkdir -p /vol/web/static && \
    chown -R django-user:django-user /vol && \
    chmod -R 755 /vol
    # static, media 파일들을 위한 경로 생성 및 유저 권한 설정

ENV PATH="/py/bin:$PATH"
# 파이썬을 실행할 기본 디렉터리 위치 설정

USER django-user
# user 변경 (root 유저로 남겨두면 보안 문제)

requirements.txt

Django>=3.2.4,<3.3
djangorestframework>=3.12.4,<3.13
psycopg2>=2.8.6,<2.9
drf-spectacular>=0.15.1,<0.16

지금 설명한 dockerfile은 django restframework와 postgresql을 사용하기 위한 파일이다. 각 프레임워크별 필요한 패키지들은 docker hub에서 찾아볼 수도 있고, docker drf 혹은 docker fastapi등 필요한 프레임워크와 함께 docker을 붙여서 검색해보면 된다.

FastAPI

fastapi와 postgresql을 사용한 dockerfile은 다음과 같다.

FROM python:3.9-slim
LABEL maintainer='Omognuni'

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYHONUNBUFFERED 1

COPY ./requirements.txt /tmp/requirements.txt
COPY ./app /app
WORKDIR /app
EXPOSE 8000

RUN python -m venv /py && \
   /py/bin/pip install --upgrade pip && \
   /py/bin/pip install -r /tmp/requirements.txt && \
   rm -rf /tmp && \
   adduser \
   --disabled-password \
   --no-create-home \
   fastapi-user && \
   mkdir -p /vol/web/media && \
   mkdir -p /vol/web/static && \
   chown -R fastapi-user:fastapi-user /vol && \
   chmod -R 755 /vol

ENV PATH="/py/bin:$PATH"
ENV PYTHONPATH="/py/bin:$PYTHONPATH"
# pytest를 하기 위해서 파이썬 경로 지정

USER fastapi-user

requirements.txt

fastapi
uvicorn
pydantic
pytest
requests
psycopg2-binary
SQLAlchemy
asyncpg
httpx

docker-compose.yml 작성

docker-compose는 쉽게 말해 여러 컨테이너들을 작성된 yaml파일에 기반해 한번에 만들어주고 연결해주는 것이라고 보면 편하다. docker 명령어로도 할 수 있지만 yaml파일에 한번에 작성하여 실행시키는 것이 보기 쉽고 훨씬 편하다. 먼저 DRF를 설정하는 docker-compose.yml 파일을 보겠다.

Django

version: "3.9"
# 사용하는 docker-compose 버전

services:
# 사용할 컨테이너들
 app:
 # app 컨테이너
   build:
     context: .
	# 현재 디렉토리에 있는 dockerfile로 빌드
   
   ports:
     - "8000:8000"
   # 내 컴퓨터의 localhost:8000번 포트와 컨테이너 8000번 포트 연결

   volumes:
   # 사용할 볼륨들
     - ./app:/app
     # 현재 디렉터리의 app 폴더와 컨테이너 내의 app 폴더 연결
     - dev-static-data:/vol/web
     # 컨테이너 내의 /vol/web 폴더를 dev-static-data 볼륨으로 생성
     # persistent data는 볼륨으로 빼주는 게 것이 좋다. (참고1에 설명)
   
	
   command: >
     sh -c "python manage.py migrate &&
            python manage.py runserver 0.0.0.0:8000"
   # 이 컨테이너가 실행될 때 자동으로 실행할 명령어들이다.
   # django의 경우 migrate와 서버를 실행시키는 명령어를 넣어놓았다.
   
   environment:
     - DB_HOST=db
     - DB_NAME=devdb
     - DB_USER=devuser
     - DB_PASS=changeme
     - DEBUG=1
	# DB에 연결하기 위해 설정한 환경 변수이다.
   # .env 파일에 값들을 입력해놓고 DB_NAME=${DB_NAME}과 같이 입력해도 된다.
   
   depends_on:
     - db
   # db 컨테이너가 실행한 후 이 컨테이너를 실행한다.

 db:
 # postgresql을 사용할 컨테이너
   image: postgres:13-alpine
   # postgresql 이미지를 가져온다.
   volumes:
     - dev-db-data:/var/lib/postgresql/data
   # 데이터를 위한 볼륨 생성
   environment:
     - POSTGRES_DB=devdb
     - POSTGRES_USER=devuser
     - POSTGRES_PASSWORD=changeme
	
volumes:
 dev-db-data:
 dev-static-data:
# 반드시 생성한 볼륨들은 마지막에 선언해주어야 한다.

참고1

FastAPI

version: "3.9"

services:
  app:
    build:
      context: .

    ports:
      - "8000:8000"

    volumes:
      - ./app:/app
      - dev-static-data:/vol/web

    command: >
      bash -c 'while !</dev/tcp/db/5432; do sleep 1; done; uvicorn main:app --reload --host 0.0.0.0'
    # fastapi는 uvicorn으로 실행한다.
    # while문으로 db 연결이 될 때까지 sleep 1을 실행한다.

    environment:
      - DATABASE_URL=postgresql://db_user:testpass@db:5432/devdb

    depends_on:
      - db

  db:
    image: postgres:13-alpine
    volumes:
      - dev-db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=devdb
      - POSTGRES_USER=db_user
      - POSTGRES_PASSWORD=testpass
    ports:
      - "5432:5432"

volumes:
  dev-db-data:
  dev-static-data:

이제 여기까지 작성했으면 다음 명령어로 실행시키면 된다.

docker-compose up --build

1개의 댓글

comment-user-thumbnail
2022년 11월 29일

좋은 글이네요. 도움이 많이 되었습니다

답글 달기