우선 nginx 컨테이너를 생성할 때 bind mount 방식으로 volume을 잡기 위해 nginx 디렉터리를 생성하고 nginx 디렉터리 내 default.conf 파일을 작성한다.
# default.conf
server {
listen 80;
server_name _; # 모든 도메인 혹은 ip로 들어오는 요청에 대해 처리해 줍니다.
location / { # nginx로 요청이 들어왔을 때
proxy_pass http://backend:8000/; # backend 컨테이너의 8000번 포트로 전달합니다.
}
location /static/ { # 브라우저에서 /static/ 경로로 요청이 들어왔을 때
alias /static/; # /static/ 경로에 있는 파일들을 보여줍니다.
}
location /media/ { # 브라우저에서 /media/ 경로로 요청이 들어왔을 때
alias /media/; # /media/ 경로에 있는 파일들을 보여줍니다.
}
}
설정한 내용은 80포트(http) 요청이 들어왔을 때 처리를 하도록 하였고,
server_name의 _는 모든 도메인 or ip의 요청에 처리해주는 뜻이다.
그리고 location은 root에 대한 모든 처리는 http://backend:8000/; 를 입력하여 django로 보낸다.
단, static과 media는 docker-compose에서 volume을 잡을 것이기 때문에 django에서 처리하지 않고 컨테이너에 있는 각 directory 경로로 설정해주어 nginx에서 바로 사용자에게 파일을 보여주도록 설정함.
해당 설정은 굳이 vi 편집기로 하지 않아도 되고, django 프로젝트에서 설정한 후 repositoy에 push하여 docker에서 pull 받으면 된다.
먼저 DEBUG 모드는 기본값으로 0(False)로 설정해두고, 환경변수를 1(True)로 받으면 DEBUG모드가 True가 된다.
그리고 nginx에서 django로 request를 보낼 때 host가 backend로 들어가기 때문에 ALLOWED_HOSTS를 backend로 설정한다.
환경변수를 가져와 POSTGRES_DB 이름의 변수에 담고 조건문으로 환경 변수가 있을 경우 postgres db에 연결하고 없을 경우 sqlite3 db를 사용하도록 설정한다.
CORS 허용 목록에 ec2 public ip를 추가하여 ec2 ip에서 접속하더라도 CORS 에러가 발생하지 않도록 설정해준다.
import os
# 환경변수에 따라 DEBUG모드 여부를 결정합니다.
DEBUG = os.environ.get('DEBUG', '0') == '1'
# 접속을 허용할 host를 설정합니다.
ALLOWED_HOSTS = ['backend', ]
# postgres 환경변수가 존재 할 경우에 postgres db에 연결을 시도합니다.
POSTGRES_DB = os.environ.get('POSTGRES_DB', '')
if POSTGRES_DB:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': POSTGRES_DB,
'USER': os.environ.get('POSTGRES_USER', ''),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', ''),
'HOST': os.environ.get('POSTGRES_HOST', ''),
'PORT': os.environ.get('POSTGRES_PORT', ''),
}
}
# 환경변수가 존재하지 않을 경우 sqlite3을 사용합니다.
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# CORS 허용 목록에 ec2 ip를 추가합니다.
CORS_ORIGIN_WHITELIST = ['http://$ec2_public_ip']
# ex) CORS_ORIGIN_WHITELIST = ['http://43.201.72.190']
# CSRF 허용 목록을 CORS와 동일하게 설정합니다.
CSRF_TRUSTED_ORIGINS = CORS_ORIGIN_WHITELIST
gunicorn 컨테이너를 생성할 때 필요한 imgae를 build 하기 위해 Dockerfile을 작성한다.
django 프로젝트를 실행하기 위해 기본적으로 python 이미지를 받아오고 컨테이너가 생성될 때 /app/ 디렉토리를 생성 및 작업 디렉토리로 설정 후 프로젝트 실행에 필요한 패키지들을 설치한다.
이렇게 Dockerfile을 작성후 docker-compose의 image로 잡아주면 컨테이너가 생성될 때 Dockerfile에 작성한대로 동작한다.
# python 3.10.8버전의 slim 이미지를 사용해 빌드
FROM python:3.10.8-slim
# .pyc 파일을 생성하지 않도록 설정합니다.
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정합니다.
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성합니다.
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정합니다.
WORKDIR /app/
# slim 이미지에서 postgresql 패키지를 설치하기 위해 필요 명령어 추가
# slim 이미지에서는 postresql이 제공되지 않아 컨테이너에 미리 설치
RUN apt update && apt install libpq-dev gcc -y
# requirments.txt를 작업 디렉토리(/app/) 경로로 복사합니다.
COPY ./django/requirements.txt .
# 프로젝트 실행에 필요한 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# gunicorn과 postgresql을 사용하기 위한 패키지를 설치합니다.
RUN pip install gunicorn psycopg2
nginx 설정과 Dockerfile 작성을 마친 후 docker-compose를 작성하여 실행하면 각각의 컨테이너가 생성되고 설정한대로 동작한다.
여기서 depends_on으로 postgres(db) - backend(service-project) - nginx(web server) 순서에 맞게 실행되도록 설정해야한다.
backend(django)컨테이너의 경우 image를 Dockerfile의 경로로 설정하여 이미지를 받아오지 않고 Dockerfile로 build한 이미지를 사용한다.
nginx(서버)와 postgres(db)는 이미지를 직접 build 하지 않고 보안에 유리한 alpine 태그의 이미지를 받아 사용한다.
volumes는 postgres(db) 컨테이너의 경우 docker volume 방식이기에 host의 /var/lib/docker/volumes/ 경로에 저장되고 backend(django)와 nginx(webserver)의 경우에는 bind mount 방식으로 컨테이너에 사용자가 지정한 경로로 데이터가 저장된다.
backend 컨테이너는 /backend/django/ 경로의 파일이 컨테이너에 생성된 /app/ 경로에 mount 되고, app 경로에 있는 media와 static을 django_media, django_static volume에 담아 nginx 컨테이너에서 받아 /media/, /static/으로 사용할 수 있게 설정해준다.
그리고 nginx 컨테이너에서 작성한 ./nginx/default.conf 파일을 컨테이너의 /etc/nginx/conf.d/default.conf 경로로 마운트한다.
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5-alpine
volumes:
- postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
restart: always
backend:
container_name: backend
build: ./backend/
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro
- django_media:/app/media/ # nginx에서 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/app/static/ # nginx에서 static을 사용할 수 있도록 volume을 지정해줍니다.
environment: # django에서 사용할 설정들을 지정해줍니다.
- DEBUG=1
- POSTGRES_DB
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_HOST
- POSTGRES_PORT
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- django_media:/media/ # django의 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/static/ # django의 static 사용할 수 있도록 volume을 지정해줍니다.
depends_on:
- backend
restart: always
docker-compose 파일에서 db(postgres)에 들어가는 value는 외부에 노출되면 보안상 좋지 않기 때문에 지워주고 .env 파일을 만들어 별도로 숨김파일로 관리한다.
아래와 같이 env 파일을 작성해주면 컨테이너가 생성될 때 env 파일이 숨겨져있더라도 탐색을 하고 생성하기 때문에 외부에 노출되지 않고 환경변수를 적용할 수 있다.
# .env
DEBUG=1
POSTGRES_DB=django
POSTGRES_USER=user
POSTGRES_PASSWORD=P@ssw0rd
POSTGRES_HOST=postgres
POSTGRES_PORT=5432