💡 오픈 소스 데이터베이스로, Oracle DB, MySQL 등 상용 라이센스를 가지고 있는 데이터베이스와는 다르게 무료로 사용 가능하다.
Oracle DB, Mysql, Microsoft SQL에 이어 네번째로 사용량이 많은 데이터베이스이다.
또한 장고에서는 기본 데이터베이스로 postgresql을 사용하는 것을 권장하고 있다.
💡 docker에서 사용 가능한 이미지들은 https://hub.docker.com 에서 제공
사이트 접속 후 원하는 이미지를 검색하면 해당 이미지의 정보를 확인할 수 있다.
version: '3.8'
volumes:
postgres: {} # postgresql에서 사용 할 볼륨 지정
services:
postgres:
container_name: postgres
image: postgres:14.5
volumes:
- postgres:/var/lib/postgresql/data/
environment: # postgresql 컨테이너에서 사용할 환경변수 지정해주기
- POSTGRES_USER=user # 데이터베이스 사용자 지정
- POSTGRES_PASSWORD=P@ssw0rd # 사용자 비밀번호 지정
- POSTGRES_DB=django # 데이터베이스 이름 지정
restart: always
sudo docker compose up -d
: 컨테이너 실행
sudo docker compose logs
: 컨테이너 로그 찍어보기
💡 django 프로젝트를 실행할 때 사용되는 runserver와 같이, 사용자의 요청을 받아 django에 작성한 코드를 실행시켜 주도록 하는 역할
=> 배포용으로 사용되는 runserver라고 생각하자!
💡 기본적으로 runserver는 배포용이 아닌 개발용으로 사용되는 명령어이며, 공식 문서에서도 runserver로 배포하는 것을 권장하지 않고 있다.
또한 runserver는 기본적으로 싱글 스레드에서 동작하지만, gunicorn은 멀티 스레드로 동작하도록 설정할 수 있기 때문에 많은 요청을 더 효율적으로 처리할 수 있다.
이외에도 runserver에 비해 속도, 안정성 등 다양한 장점을 가지고 있기 때문에 배포 환경에서는 gunicorn을 사용하는 것을 권장하고 있습니다.
git clone https://github.com/sparta-course/drf-project.git ./django
ALLOWED_HOSTS = ['*'] # 빈 리스트는 아무 호스트도 허용하지 않겠다는 뜻, *은 모든 호스트를 대상으로 허용하겠다.
STATIC_ROOT = BASE_DIR / "static"
💡 Linux를 처음 설치하면 대부분 표준 시간대가 협정 세계시(UTC)로 설정되어 있다.
timezone을 한국 시간대(KST)로 변경해주자!!
리눅스에서 date
명령어를 사용하면 현재 적용된 timezone 정보를 확인할 수 있다.
timezone을 변경해주기 위해, 기존 timezone 설정을 Asia/Seoul로 덮어씌워야 한다.
sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
설정 후 다시 date 명령어를 확인해보면 KST로 바뀐 것을 확인할 수 있다.
# python 3.10.8버전 이미지를 사용해 빌드
FROM python:3.10.8
# .pyc 파일을 생성하지 않도록 설정
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정
WORKDIR /app/
# requirments.txt를 작업 디렉토리(/app/) 경로로 복사
COPY ./django/requirements.txt .
# 프로젝트 실행에 필요한 패키지들을 설치
RUN pip install --no-cache-dir -r requirements.txt
# gunicorn을 사용하기 위한 패키지를 설치
RUN pip install gunicorn
version: '3.8'
services:
backend:
container_name: backend
build: ./backend/
# drf_project.wsgi는 프로젝트 경로에 맞게 지정해야 함
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"
ports:
- 80:8000
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro # host의 timezone 설정을 컨테이너에 적용
# ro 은 읽기 전용(read only) 속성으로 볼륨을 설정하는 것을 의미
restart: always
💡 위에서 docker-compose.yml데 작성했던 entrypoint에서 사용한 명령어들을 살펴보자!
sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn project_name.wsgi --workers=5 -b 0.0.0.0:8000"
sh -c
: 컨테이너에서 뒤에 작성한 명령어를 실행시킬 수 있도록 해준다.
&&
: 특정 명령어를 실행한 이후 다음 명령어를 실행시켜 준다.
python manage.py collectstatic --no-input
: 배포를 위해 static 파일을 모아준다. 해당 명령어를 실행시키기 위해서는 settings.py에 STATIC_ROOT가 정의되어 있어야 한다.
python manage.py migrate
: django에 연결된 db를 migrate 해준다.
gunicorn project_name.wsgi --workers=5 -b 0.0.0.0:8000
: gunicorn을 사용해 django 프로젝트를 실행시킨다.
project_name
: django 프로젝트 이름을 입력한다. 이름을 다르게 입력할 경우 에러가 발생한다.--workers=5
: django를 실행시킬 process 갯수를 입력한다. 일반적으로 cpu 코어 갯수 2 + 1만큼 지정해준다.(ex - 2코어라면 22+1=5)-b 0.0.0.0:8000
: 8000번 포트로 실행시킨다.sudo docker compose up --build -d
sudo docker compose logs
웹브라우저에서 접속해보기
💡 nginx는 클라이언트의 request 요청을 처리해주는 웹 서버(web server)이다.
reverse proxy, 로드밸런싱, 캐싱 등의 기능을 지원하며, 클라이언트의 요청을 nginx가 받은 후 service(django) 데이터를 넘겨주는 역할을 해준다.
version: '3.8'
services:
nginx:
container_name : nginx
image: nginx:1.23.2
ports:
- "80:80" # http 포트포워딩
- "443:443" # https 포트포워딩
restart: always
sudo docker compose up -d
sudo docker compose logs
웹브라우저에서 접속해보기
💡 nginx 디렉토리 내 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/ 경로에 있는 파일들을 보여줍니다.
}
}
# python 3.10.8버전 이미지를 사용해 빌드
FROM python:3.10.8
# .pyc 파일을 생성하지 않도록 설정합니다.
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정합니다.
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성합니다.
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정합니다.
WORKDIR /app/
# requirments.txt를 작업 디렉토리(/app/) 경로로 복사합니다.
COPY ./django/requirements.txt .
# 프로젝트 실행에 필요한 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# gunicorn과 postgresql을 사용하기 위한 패키지를 설치합니다.
RUN pip install gunicorn psycopg2
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
path : /home/ubuntu/
├── backend
│ ├── Dockerfile
│ └── django # project directory
├── docker-compose.yml
└── nginx
└── default.conf
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5
volumes:
- postgres:/var/lib/postgresql/data/
environment:
**** - POSTGRES_USER=user
- POSTGRES_PASSWORD=P@ssw0rd
- POSTGRES_DB=django
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=django
- POSTGRES_USER=user
- POSTGRES_PASSWORD=P@ssw0rd
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2
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
sudo docker compose up -d --build
하여 실행
sudo docker compose logs -f
하여 로그 확인
이미지는 기존에 다운로드 받은 이미지가 있기 때문에 다운받지 않아도 되고, 이전 명령어들이 cashed가 되어있는데 이건 Dockerfile에서 명령어를 작성하고 build할 때 속도의 최적화를 위해 기존에 한번 실행한 명령어는 캐싱을 해두어 빠른 속도로 실행할 수 있다.
1. 먼저 사용자가 EC2의 nginx에 request 요청
nginx에서 사용자의 요청을 받아 .conf 파일에서 설정한 서버로 요청을 전달
이후 gunicorn에서는 django로, django에서는 필요에 따라 데이터베이스에 쿼리를 날려 개발자가 작성한 코드를 실행
DEBUG=1
POSTGRES_DB=django
POSTGRES_USER=user
POSTGRES_PASSWORD=P@ssw0rd
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5
# 만약 .env 파일을 다른 이름으로 사용할 경우 env_file 옵션을 사용해 불러올 수 있습니다.
# env_file:
# - prod.env
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/
- django_static:/app/static/
environment: #
- DEBUG
- POSTGRES_DB
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_HOST
- POSTGRES_PORT
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2
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
environment 부분에서 =뒤에 부분을 모두 지워주면 된다!!
만약 .env 파일을 다른 이름으로 사용할 경우 env_file 옵션을 사용하여 불러올 수 있다!
env_file:
- prod.env
아래 예제는 python 이미지 기준이며, 이미지마다 지원하는 버전은 차이가 있다.
동일한 docker 이미지라 하더라도 태그에 따라 이미지의 용량이 달라지는데, 가령 python 이미지의 경우 가장 큰 이미지와 가장 작은 이미지의 용량이 300mb 이상 차이가 난다.
<기본 python 이미지>
<가장 작은 용량의 python alpine 이미지>
다만, 용량이 작은 이미지를 사용할 때는 외부 패키지를 설치할 때 필요한 의존성 파일들이 존재하지 않아 에러가 발생할 수 있다.
내가 사용하려는 프로젝트에 맞는 이미지를 사용하는것이 중요!
내가 사용하는 이미지에 어떤 종류의 태그가 존재하는지 확인이 필요할 때는 docker hub 이미지 페이지의 tags에서 확인 가능하다.
buster, jessie, stretch
slim
alpine
# python 3.10.8-slim버전 이미지를 사용해 빌드
FROM python:3.10.8-slim
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN mkdir /app/
WORKDIR /app/
# slim 이미지에서 postgresql 패키지를 설치하기 위해 필요 명령어 추가
RUN apt update && apt install libpq-dev gcc -y
COPY ./django/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn psycopg2
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5-alpine # 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/
- django_static:/app/static/
environment:
- 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 # alpine 이미지를 지정해줍니다.
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- django_media:/media/
- django_static:/static/
depends_on:
- backend
restart: always
이미지 부분만 수정해주었다!!
sudo docker images
: 이미지 용량 확인
sudo docker images prune -a
: 사용하지 않는 이미지 삭제
sudo docker rmi 이미지 이름
: 이미지 지정해서 삭제(rmi는 remove image의 약자)
<전>
<후>