Docker Compose로 Django 프로젝트 셋업
최근에 많은 Django 프로젝트가 Docker를 이용해서 컨테이너(container) 기반으로 개발되고 테스트, 배포되고 있습니다. 이번 포스팅에서는 Docker Compose이용해서 PostgreSQL를 데이터베이스로 사용하는 Django 애플리케이션을 셋업해보도록 하겠습니다.
Docker를 처음 접하시는 분들은 관련 포스팅를 참고하시어 먼저 본인 컴퓨터에서 Docker에 설치하시기 바랍니다.
requirements.txt
파이썬에서는 보통 프로젝트에서 필요한 패키지를 requirements.txt
파일에 등록해놓고, 패키지 매니저인 pip를 이용해서 패키지를 설치합니다.
아래와 같이 원하는 위치에 디렉터리를 생성하고, 그 안에 requirements.txt
파일을 작성합니다.
$ mkdir django-app && cd django-app
- requirements.txt
Django
psycopg2
Django 패키지 외에도 psycopg2 패키지를 추가로 등록하였는데요. 이 패키지는 PostgreSQL을 데이터베이스로 사용하기 위해서 필요합니다.
Dockerfile
컨테이너화된 소프트웨어 프로젝트에서는 모든 개발 작업이 Docker 컨테이너 안에서 이루어집니다. 따라서 개발자의 로컬 컴퓨터에서 파이썬을 설치하거나 프로젝트에 필요한 패키지를 설치할 필요가 없습니다.
대신에 Docker 컨테이너 안에서 해당 애플리케이션이 돌아갈 수 있는 환경을 이미지(image)를 떠 놓어야 합니다. 이렇게 이미지를 떠 놓으면 개발자들은 번거로운 사전 세팅을 생략하고 바로 해당 이미지를 컨테이너 안에서 실행할 수 있습니다.
Dockerfile
은 Docker 이미지가 빌드(build)될 때 거쳐야하는 단계를 정의하고 있는 텍스트 파일인데요.
다음과 같이 프로젝트 최상위 디렉터리에 Dockerfile
을 생성하고, 그 안에 다음과 같이 작성합니다.
- Dockerfile
FROM python:3
ENV PYTHONUNBUFFERED 1
WORKDIR /web
COPY . .
RUN pip install -r requirements.txt
위 Dockerfile
파일은 python:3
이미지를 기반으로 다음 일련의 작업들을 순차적으로 명시하고 있습니다.
- 이미지 내
PYTHONUNBUFFERED
환경 변수를1
로 설정 - 이미지 내 작업 디렉터리를
/web
으로 변경 - 현재 디렉터리의 모든 파일을 이미지 내의 작업 디렉터리로 복사
pip
를 이용해서 이미지 안에 패키지 설치
Dockerfile을 작성하는 구체적인 방법에 대한 자세한 설명은 관련 포스팅을 참고바랍니다.
이제, 작성한 Dockerfile
를 이용해서 이미지를 빌드해보겠습니다.
$ docker build .
Sending build context to Docker daemon 5.632kB
Step 1/5 : FROM python:3
---> 4f7cd4269fa9
Step 2/5 : ENV PYTHONUNBUFFERED 1
---> Using cache
---> 876aab098e56
Step 3/5 : WORKDIR /web
---> Using cache
---> c60b0a18dc24
Step 4/5 : COPY . .
---> 33d85bf96191
Step 5/5 : RUN pip install -r requirements.txt
---> Running in f06ba6efc0ba
Collecting Django
Downloading Django-3.0.6-py3-none-any.whl (7.5 MB)
Collecting psycopg2
Downloading psycopg2-2.8.5.tar.gz (380 kB)
Collecting sqlparse>=0.2.2
Downloading sqlparse-0.3.1-py2.py3-none-any.whl (40 kB)
Collecting pytz
Downloading pytz-2020.1-py2.py3-none-any.whl (510 kB)
Collecting asgiref~=3.2
Downloading asgiref-3.2.7-py2.py3-none-any.whl (19 kB)
Building wheels for collected packages: psycopg2
Building wheel for psycopg2 (setup.py): started
Building wheel for psycopg2 (setup.py): finished with status 'done'
Created wheel for psycopg2: filename=psycopg2-2.8.5-cp38-cp38-linux_x86_64.whl size=500463 sha256=bc9f1dc1d5b8f986a31f72daf1ff1a2605e733a561e064e05d84e7e954f550c6
Stored in directory: /root/.cache/pip/wheels/35/64/21/9c9e2c1bb9cd6bca3c1b97b955615e37fd309f8e8b0b9fdf1a
Successfully built psycopg2
Installing collected packages: sqlparse, pytz, asgiref, Django, psycopg2
Successfully installed Django-3.0.6 asgiref-3.2.7 psycopg2-2.8.5 pytz-2020.1 sqlparse-0.3.1
Removing intermediate container f06ba6efc0ba
---> c614bec34623
Successfully built c614bec34623
마지막에 Successfully built <이미지ID>
가 출력되었다면 제대로 이미지가 빌드가 된 것입니다.
위에서 얻은 이미지ID를 이용하여 이미지를 컨테이너로 실행한 후, Django 설치가 잘 되었는지 파이썬 인터프리터에서 확인할 수 있습니다.
$ docker run -it c614bec34623
Python 3.8.2 (default, Apr 23 2020, 14:22:33)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> print(django.get_version())
3.0.6
docker-compose.yml
Docker Compose는 여러 개의 서비스를 이뤄진 애플리케이션을 하나의 YAML 파일에 정의할 수 있도록 해줍니다. 우리가 셋업하려는 Django 프로젝트는 Django 애플리케이션과 PostgreSQL 데이터베이스로 이루어집니다.
다음과 같이 프로젝트 최상위 디렉터리에 docker-compose.yml
을 생성하고, web
서비스로 Django 애플리케이션을 db
서비스로 PostgreSQL 데이터베이스를 정의해줍니다.
- docker-compose.yml
version: "3"
services:
web:
build: .
command: python manage.py runserver 0:8000
ports:
- "8000:8000"
volumes:
- .:/web
depends_on:
- db
db:
image: postgres
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
web
서비스를 보시면 현재 디렉터리에 있는 Dockerfile
을 이용해서 이미지를 빌드하고, Django 애플리케이션을 구동하기 위해서 python manage.py runserver 0:8000
커맨드를 실행하게 되어 있습니다.
또한, 호스트의 8000 포트와 컨테이너의 8000 포트를 바인드(bind)시키고, 현재 디렉터리를 컨테이너의 /web
디렉터리로 마운트(mount)하고 있습니다.
마지막으로 web
서비스가 돌아가기 전에 db
서비스가 반드시 먼저 돌아갈 수 있도록 depends-on
설정이 되어 있습니다.
db
서비스는 postgres
외부 이미지를 사용하도록 되어 있으며, POSTGRES_USER
와 POSTGRES_PASSWORD
환경변수를 설정해주고 있습니다.
Docker Compose 설정 방법에 대한 좀 더 자세한 설명은 관련 포스팅를 참고 바랍니다.
Django 프로젝트 생성
지금부터 본격적으로 컨테이너 상에서 Django 프로젝트 셋업해보도록 하겠습니다.
터미널에서 다음 django-admin
명령어를 실행하여 our_project
라는 프로젝트를 생성합니다.
$ docker-compose run web django-admin startproject our_project .
이 명령어를 실행하면 다음과 같이 manage.py
가 생성되고, our_project
디렉터리에 Django 관련 파일들이 생성될 것입니다.
$ tree
.
├── Dockerfile
├── docker-compose.yml
├── manage.py
├── our_project
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── requirements.txt
Django 프로젝트 실행
터미널에서 docker-compose up
명령어를 실행해보면 다음과 같이 PostgreSQL 데이터베이스와 Django 애플리케이션이 모두 컨테이너 안에서 구동되는 것을 볼 수 있습니다.
$ docker-compose up
Starting django-app_db_1 ... done
Recreating django-app_web_1 ... done
Attaching to django-app_db_1, django-app_web_1
db_1 |
db_1 | PostgreSQL Database directory appears to contain a database; Skipping initialization
db_1 |
db_1 | 2020-05-14 02:15:02.745 UTC [1] LOG: starting PostgreSQL 12.2 (Debian 12.2-2.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
db_1 | 2020-05-14 02:15:02.745 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
db_1 | 2020-05-14 02:15:02.745 UTC [1] LOG: listening on IPv6 address "::", port 5432
db_1 | 2020-05-14 02:15:02.748 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
db_1 | 2020-05-14 02:15:02.764 UTC [25] LOG: database system was shut down at 2020-05-14 02:12:56 UTC
db_1 | 2020-05-14 02:15:02.769 UTC [1] LOG: database system is ready to accept connections
web_1 | Watching for file changes with StatReloader
web_1 | Performing system checks...
web_1 |
web_1 | System check identified no issues (0 silenced).
web_1 |
web_1 | You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
web_1 | Run 'python manage.py migrate' to apply them.
web_1 | May 14, 2020 - 02:15:03
web_1 | Django version 3.0.6, using settings 'our_project.settings'
web_1 | Starting development server at http://0:8000/
web_1 | Quit the server with CONTROL-C.
이제 브라우저에서 http://localhost:8000
를 열면, 로켓 모양이 올라가는 Django 초기 화면을 보실 수 있으실 겁니다. 🚀
Django 프로젝트 설정
Django 애플리케이션과 PostgreSQL 데이터베이스가 모두 컨테이너로 실행은 되고 있지만 Django 애플리케이션은 디플트로 SQLite 데이터베이스를 사용하기 때문에 PostgreSQL 데이터베이스 사용하도록 설정이 필요합니다.
our_project/settings.py
을 열고 DATABASES
부분을 아래와 같이 수정해줍니다.
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "postgres",
"USER": "postgres",
"PASSWORD": "postgres",
"HOST": "db",
"PORT": 5432,
}
}
이번에는 터미널에서 docker-compose up
명령어를 -d
옵션으로 실행하여 백그라운드에서 두 개의 서비스가 돌아가도록 해보겠습니다.
$ docker-compose up -d
Starting django-app_db_1 ... done
Starting django-app_web_1 ... done
DB 마이그레이션
Django 애플리케이션을 정상적으로 사용하려면 먼저 DB 마이그레이션(migration)을 실행하여 관련 테이블과 생성하고 필요한 데이터를 적재해줘야 합니다.
다음과 같이 python manage.py migrate
명령어를 현재 백그라운에서 돌아가고 있는 web
서비스를 대상으로 날려줍니다.
$ docker-compose exec web python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying sessions.0001_initial... OK
DB 테이블 확인
DB 마이그레이션이 잘 되었는지 확인하려면, PostgreSQL 데이터베이스에 접속하여 테이블을 확인해봐야 합니다.
db
서비스를 대상으로 psql
커맨드를 실행한 후 \z
를 입력해보면 다음과 같이 생성된 테이블들이 확인될 것입니다.
$ docker-compose exec db psql postgres postgres
psql (12.2 (Debian 12.2-2.pgdg100+1))
Type "help" for help.
postgres=# \z
Access privileges
Schema | Name | Type | Access privileges | Column privileges | Policies
--------+-----------------------------------+----------+-------------------+-------------------+----------
public | auth_group | table | | |
public | auth_group_id_seq | sequence | | |
public | auth_group_permissions | table | | |
public | auth_group_permissions_id_seq | sequence | | |
public | auth_permission | table | | |
public | auth_permission_id_seq | sequence | | |
public | auth_user | table | | |
public | auth_user_groups | table | | |
public | auth_user_groups_id_seq | sequence | | |
public | auth_user_id_seq | sequence | | |
public | auth_user_user_permissions | table | | |
public | auth_user_user_permissions_id_seq | sequence | | |
public | django_admin_log | table | | |
public | django_admin_log_id_seq | sequence | | |
public | django_content_type | table | | |
public | django_content_type_id_seq | sequence | | |
public | django_migrations | table | | |
public | django_migrations_id_seq | sequence | | |
public | django_session | table | | |
(19 rows)
Django 프로젝트 종료
마지막으로 Docker Compose를 이용해서 모든 서비스를 한 번에 종료하고 컨테이너와 네트워크를 삭제할 수 있습니다.
$ docker-compose down
Stopping django-app_web_1 ... done
Stopping django-app_db_1 ... done
Removing django-app_web_1 ... done
Removing django-app_db_1 ... done
Removing network django-app_default
마치면서
이상으로 Docker Compose를 이용하여 Django 애플리케이션과 PostgreSQL 데이터베이스로 구성된 프로젝트를 셋업하는 방법을 매우 대략적으로 살펴보았습니다. 본 포스팅의 내용이 따라오기가 조금 버거우셨다면 제 블로그의 다른 포스팅에서 Docker나 Django에 대한 기본 지식을 다루고 있으니 참고바랍니다.
이렇게 개발 환경을 컨테이너화(containerization)해놓고 해당 프로젝트를 Github와 같은 코드 저장소에 올려두면 개발자들은 Docker만 설치하면 바로 해당 애플리케이션을 띄우고 개발을 시작할 수 있어서 매우 편리합니다. 왜냐하면, 파이썬 런타임 설치부터 Django 패키지 설치, 데이터베이스 셋업 등이 모두 container 안에서 완료된 상태이기 때문입니다. 또한 모든 개발자가 동일한 개발 환경에서 작업하는 것을 보장받을 수 있어서 개개인 간의 미묘한 세팅 차이로 인한 황당한 상황도 피할 수 있습니다.