도커란?
도커에 대한 이해를 돕는 블로그나 유튜브는 굉장히 많이 나와 있습니다. 참고하면 좋을 것 같은 블로그들을 리스트들입니다. 이 외에도 직접 검색해보면서 여러 블로그들을 훑어보며 감을 잡으면 좋을 것 같습니다.
- 초보를 위한 도커 안내서 - 도커란 무엇인가?
- Docker로 협업하기 — Node.js 웹서버를 Docker로 배포하기
- Introduction To Docker and Docker Containers
- docker-compose를 이용한 개발환경 구축하기 ( feat. vagrant )
사실 서버를 다뤄본 적 없거나 가상 머신을 다뤄본 적 없으면 위에 글들을 읽어도 딱히 와 닿지 않을 수 있습니다. 그래서 제가 이해한 바를 아주 쉽게 정리해서 이야기해보겠습니다. 제가 지금 맥북을 쓰고 있는데 만약 도커를 이용하면 리눅스 운영체제의 또 다른 컴퓨터를 제 컴퓨터 안에 생길 수 있는 것입니다. 완전히 다른 운영체제의 환경을 가져와서 쓸 수 있는 것입니다.
도커는 왜 쓰면 좋을까요? 보통 프로젝트를 진행하기 전에 로컬에서도 프로젝트마다 가상 환경을 만들어 줍니다. 가상 환경을 만들어주는 이유를 예를 들면, 제 랩탑에서 작업하고 있는 프로젝트가 2개가 있는데 프로젝트 A에서는 코드가 python3 버전, 프로젝트 B에서는 코드가 python2 버전으로 쓰였다고 합시다. 만약 가상 환경 없이 두 개의 프로젝트를 병행한다고 하면 프로젝트 A에 대한 코딩을 할 때는 python3을 쓰다가 잠깐 프로젝트 B에 대한 코딩을 해야 할 때 python3을 python2로 downgrade 해줘야 하고, 반대의 경우도 마찬가지입니다. 이런 식으로 매번 쓰는 버전에 따라 python을 업그레이드, 다운그레이드 하면 비효율적이겠죠. 그래서 보통 각 프로젝트마다 로컬에서 가상 환경을 만들어주고 각 프로젝트에 필요한 패키지들을 그 가상 환경에 따로 설치해줍니다.
도커도 마찬가지로 이러한 가상 환경과 굉장히 닮았다고 할 수 있습니다. 그러나 혼자 하는 프로젝트가 아닌 여러 사람이 협업하는 프로젝트고 모두 쓰는 os가 다르다면 os 환경까지 sync를 맞추어 주면 좋겠죠. os 뿐만 아니라 프로젝트에 쓰이는 패키지들의 버전을 멤버들이 모두 같게 쓰게 하기에도 좋습니다. 물론 requirements.txt로 그 프로젝트에 어떤 버전의 패키지들이 쓰이는지 명시해서 git에 올려두어 멤버들이 각자 manually 컴퓨터에서 requirements 패키지들을 설치하도록 할 수 있지만, 누군가가 한 명이 대표로 만들어 놓은 도커 이미지를 클라우드에 push 해 놓으면 멤버들은 그 이미지를 pull해와서 쓸 수도 있습니다. (여기서 도커 이미지는 제가 앞서 말한 가상 환경과 비슷한 개념이라고 생각하시면 됩니다.)
장고-도커-헤로쿠 워크플로우
제가 이해한 바로는 팀원들과 깃헙으로 협업하고 있다는 전제하에 도커를 가장 간단하게 이해하고 쉽게 이용하는 방법은 다음과 같은 워크플로우 시나리오입니다.
- 개발자 A, B, C가 있고 함께 프로젝트하는 레파지토리 D가 있습니다. A가 장고 startapp을 해서 새로운 장고 앱, 예를 들면 "MyApp"을 init 하고 그 앱의 root 디렉토리 내부에
Dockerfile
과docker-compose.yml
파일을 작성합니다. A는 그 이미지가 잘 작동하는지 로컬에서 빌드해 본 후 잘 작동하면 그 도커 관련 파일들이 포함된 MyApp을 레파지토리 D에 push 합니다. - 개발자 B와 C는 git clone으로 프로젝트를 시작하게 됩니다. clone 한 후
docker-compose.yml
이 위치해 있는 MyApp의 root 디렉토리 내부에서 docker-decompose build 명령어를 실행하여 도커 이미지를 빌드합니다. - 프로젝트 개발을 본격적으로 들어갑니다. 자신의 프로젝트가 제대로 실행되는지 확인하기 위해서
docker-compose up --build
명령어를 실행시킵니다. 코드를 수정할 때마다 바로바로 자신의 localhost에서 수정된 결과를 확인하고 디버깅하며 개발을 합니다. - (option) 필요에 의해서 MyApp 개발 시 기존 이미지에 없는 새로운 package를 install 해야 할 때
Dockerfile
을 수정해 주고 변경된 사항을 깃헙에 푸시합니다. - 업데이트된 기능들이 있을 때마다 깃으로 잘 업데이트시켜줍니다. (반복)
- 개발을 마치면 헤로쿠에 deploy 합니다.
장고 프로젝트 시작하기
$ mkdir django-docker
$ cd django-docker
$ python3 -m venv env
$ source env/bin/activate
(env)$ pip install django
(env)$ django-admin startproject MyApp .
(env)$ python manage.py migrate
django-docker 디렉토리 내부에 requirements.txt라는 파일을 만들고 그 파일 안에 다음과 같이 작성.
Django
gunicorn
whitenoise
ipdb
도커 이미지 만들기
다음과 같은 방식으로 도커 이미지를 만듭니다.
1. Dockerfile
작성하기
django-docker의 디렉토리 내부에 Dockerfile
이라는 파일을 만들어주고 아래와 같이 작성해 줍니다. 아래는 예시니 필요에 따라서 지우거나 추가해서 작성해주면 되는 파일입니다.
# pull official base image
FROM python:3.8
# set work directory
WORKDIR /app
RUN pip install django gunicorn whitenoise ipdb
# copy project
COPY . /app
Dockerfile은 저희가 도커 이미지를 빌드할 때 쓰이는 레시피 역할을 한다고 생각하시면 됩니다. 여기서 저희는 Linux 베이스의 python3.8 버전의 도커 이미지를 가져와 시작합니다 (FROM). 아까 도커는 자신의 컴퓨터 안에 또 다른 컴퓨터와 같다고 했듯 도커 내부에는 기본적으로 여러 디렉토리가 존재하는데 그중 /app
이라는 디렉토리 안에서 저희 프로젝트 작업을 할 것입니다 (WORKDIR). 그리고 이미지를 구성할 패키지들을 install 하는 명령어를 실행시키도록 합니다 (RUN). 여기서 참고로 django-docker 디렉토리 내부에 requirements.txt를 이용하여 RUN pip install django gunicorn whitenoise ipdb
대신 RUN pip install -r requirements.txt
을 써주어도 됩니다. 물론 둘 다 써주어도 됩니다. 다만 RUN pip install django gunicorn whitenoise ipdb
이런 식으로 명시해주는 것이 이미지가 빌드될 때 시간을 단축시켜줍니다. 처음 build 할 때만 install 하고 그다음에 코드 수정하고 다시 build 하는 경우 이미 install 된 패키지들이 caching 되어 다시 install하지 않기 때문입니다. RUN pip install -r requirements.txt
만 명시해주면 build 할 때마다 requirements에 있는 패키지들을 매번 install 함. 다시 Dockerfile로 돌아와서 COPY . /app
가 Dockerfile에서 가장 중요한 핵심 부분입니다. 자신이 로컬에서 작업하는 프로젝트가 도커 환경 안에 copy 즉, sync가 되도록 해주는 녀석입니다. 그렇게 해야 도커 위에서 자신이 작성한 코드를 실행시켜볼 수 있겠지요.
2. docker-compose.yml
작성하기
Docker를 간결한 command로 실행시키기 위해 작성해주는 파일입니다.
# docker-compose is used only for development.
version: "3"
services:
web:
build:
context: .
volumes:
- .:/app
command: python manage.py runserver 0.0.0.0:8000
ports:
- 8000:8000
3. 마침내 docker 이미지를 build 해 줍니다.
$ docker-compose build
그 결과 아래와 같이 성공적으로 빌드를 마칩니다.
$ docker-compose build
Building web
Step 1/4 : FROM python:3.8
\---> f5041c8ae6b1
Step 2/4 : WORKDIR /app
\---> Using cache
\---> 678d17c67e05
Step 3/4 : RUN pip install django gunicorn whitenoise ipdb
\---> Using cache
\---> f786c4f71da1
Step 4/4 : COPY . /app
\---> Using cache
\---> 88e688278c1c
Successfully built 88e688278c1c
Successfully tagged django-docker\_web:latest
깃헙에서 clone해와서 도커 이미지 빌드해서 사용하기 (깃헙으로 도커 협업하기)
이제 이미지들을 같이 협업하는 분들과 공유하기 위해 여러 가지 방법을 쓸 수 있을 것입니다.
github에 Dockerfile
을 푸시해두면 각 멤버들이 Dockerfile
을 clone해와서 각자 build 해서 쓸 수도 있고, 혹은 만든 사람이 docker hub에 push 해두면 멤버들이 pull해와서 쓸 수도 있을 것입니다. 간단하게 깃헙에 위와 같이 Dockerfile
과 docker-compose.yml
그리고 장고 앱이 올라와 있는 상태라면 그대로 git에서 clone 또는 pull 해와서 처음에만 도커 이미지를 빌드해주고 개발합니다.
- git clone <레파지토리> 또는 git pull <레파지토리>
- docker-compose build
- docker-compose up --build
도커 이용해서 헤로쿠에 deploy 하기
개발이 다 끝난 후, 도커를 이용하여 헤로쿠에 deploy 하는 방법은 Container Registry 방법과 heroku.yml
를 이용한 manifest방법이 있습니다. 아래는 Container Registry 방법으로 헤로쿠에 푸시하는 방법입니다.
-
헤로쿠에 프로젝트를 올릴 웹사이트를 만들어 줍니다.
$ heroku create Creating app... done, ⬢ immense-bastion-69216 https://immense-bastion-69216.herokuapp.com/ | https://git.heroku.com/immense-bastion-69216.git
-
헤로쿠에 container에 로그인합니다.
$ heroku container:login
-
만들어둔 도커 이미지를 아래와 같은 포맷으로 rebuild 해줍니다.
$ docker build -t registry.heroku.com/<app>/<process-type> .
예를 들면,
$ docker build -t registry.heroku.com/immense-bastion-69216/web .
-
헤로쿠 registry에 push 해 줍니다.
$ docker push registry.heroku.com/immense-bastion-69216/web
-
헤로쿠에 release 해 줍니다.
$ heroku container:release -a immense-bastion-69216 web
+ 추가)
Docker 위에서 makemigrations 또는 migrate 하는 법
- 터미널에서 docker ps 커맨드를 입력합니다.
docker ps
- 위에서 얻은 결과로부터 CONTAINER ID 값을 알아낸 후 다음과 같이 실행시켜 줍니다.
docker exec -it [CONTAINER ID] python manage.py makemigrations
docker exec -it [CONTAINER ID] python manage.py migrate
마지막 노트:
- 작성된 Dockerfile은 예시이므로 pip install ipdb 등 패키지 인스톨은 생략해도 됩니다.
- docker-compose.yml은 생략 가능하지만 docker 실행 시 써야 하는 긴 명령어를 생략 가능하게 해 주므로 docker-compose.yml 작성을 권장합니다.