이번 글에서는 docker에서 django + nginx + uwsgi 조합으로 돌아가고 있는 웹사이트의 구성을 실행해보려고 합니다.
AWS ec2에서 우분투 환경으로 돌아가던 서버를 docker로 관리 및 운영하기 위해서 docker를 위한 배포를 연습중입니다. 설치 및 container 기초에 대해서 궁금하시다면 이전 글을 참고 해주세요!
우선 저번 글에서 django 서버를 manage.py runsever를 통해서 로컬호스트로 구현을 해보았습니다. 하지만 실제 배포를 위해서는 uwsgi및 nginx를 이용해야합니다.
uwsgi를 쓰는 이유
django의 공식 문서에의 runserver의 글입니다.
django에서는 runserver를 통해 개발 및 테스트를 합니다.
(https://docs.djangoproject.com/en/2.2/ref/django-admin/#runserver)
nginx를 쓰는 이유
uwsgi만 이용하게 된다면 https를 위한 인증서 관리가 힘들뿐더러 static file에 대한 요청을 처리하기 힘듭니다. 또한 nginx를 통한 요청으로 성능이 더 좋아진다고 합니다.
출처: https://uiandwe.tistory.com/1268 [조아하는모든것]
django + uwsgi + nginx 조합으로 docker로 배포를 하기 위해서는 우선
django 컨테이너 및 이미지가 있다는 가정하에 nginx의 이미지를 생성해야한다.
프로젝트 directory에 nginx라는 폴더를 하나 새로 생성하고
FROM nginx:latest
COPY nginx.conf /etc/nginx/nginx.conf
COPY letter.conf /etc/nginx/sites-enabled/
CMD ["nginx", "-g", "daemon off;"]
user root;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 256M;
proxy_connect_timeout 10;
proxy_send_timeout 15;
proxy_read_timeout 20;
# server_tokens off;
server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /log/access.log;
error_log /log/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/sites-enabled/*.conf;
}
다음으로 nginx.conf를 만들어주었다. 기존에 사용하던 설정이며, docker로 전환하면서 log파일을 container에 저장할 수 없기 때문에 경로를 다시 지정해주었다.
server {
listen 80;
server_name localhost;
location / {
uwsgi_pass unix:///srv/letter_web/letter.sock;
include uwsgi_params;
}
location /static/ {
alias /static/;
}
}
다음과 같이 letter.conf 파일을 만들어주고 해당 글은 localhost에서 배포를 실험해보는 것으로 sever_name이 localhost이지만 실제 배포를 할때는 도메인이 들어가는 것이 맞다. 해당 서버네임으로 요청이 들어온다면 uwsgi를 통해서 파이썬 코드를 실행하는 과정이다.
해당 파일을 nginx의 /etc/nginx/sites-enabled/ 로 복사해주고 nginx의 설정에서 include /etc/nginx/sites-enabled/*.conf; 다음과 같이 추가하여 서버설정을 완료했다.
[uwsgi]
chdir = /srv/letter_web
module = letterproject.wsgi:application
socket = /srv/letter_web/letter.sock
enable-threads = true
master = true
vacuum = true
logto = /log/@(exec://date +%%Y-%%m-%%d).log
log-reopen = true
ignore-sigpipe = true
ignore-write-errors = true
disable-write-exception = true
post-buffering = 8192
processes = 1
threads = 2
no-orphans = 1
thunder-lock = true
서버 프로젝트 폴더 안에 uwsgi라는 폴더를 생성 후 uwsgi.ini 파일을 다음과 같이 만들어 주었다. 여기서 중요하게 보아야할 것은 socket의 경로설정이다!! 실제로 해당 글을 작성하면서 연습을 해볼때 이 경로 설정이 안맞아 socket 파일을 찾지 못해 웹사이트에 접속을 못하는 에러를 계속 겪었다...
django의 Dockerfile 도 수정이 필요하다. 원래는 python3 manage.py runserver로 서버를 실행했지만 이제는 uwsgi를 통해서 돌려야하기 때문이다.
우선 pip3 install uwsgi를 통해서 패키지를 설치해주고 requirement.txt에 저장해준다.
FROM python:3.7.7
# python 3.7.7 버전의 컨테이너 이미지를 base이미지
ENV PYTHONUNBUFFERED 1
LABEL yh Bang <yh20studio@gmail.com>
# Docker의 컨테이너를 생성 및 관리 하는 사람의 정보를 기입해줍니다.
RUN pip3 install django
# python:3.7.7 이미지 상에 django를 pip를 통해 설치합니다.
RUN mkdir /srv/letter_web
WORKDIR /srv/letter_web
# CMD에서 설정한 실행 파일이 실행될 디렉터리를 지정해주어야 한다.
COPY ./requirements.txt ./
RUN pip3 install -r requirements.txt
COPY . .
CMD ["uwsgi", "--ini", "uwsgi/uwsgi.ini"]
# uwsgi로 실행할 수 있도록 cmd 교체
EXPOSE 8000
uwsgi --ini uwsgi/uwsgi.ini 해당 코드로 uwsgi 폴더에 있는 uwsgi.ini 파일로 uwsgi 패키지로 서버를 실행시켜줍니다.
지금까지 django, nginx, uwsgi 의 준비가 끝났다면 해당 이미지와 컨테이너들을 한번에 묶어서 실행할 수 잇는 docker-compose가 필요합니다.
docker-compose 가 무엇이냐? 하면 쉽게 말해서 여러가지 컨테이너를 묶어서 한번에 실행을 할 수 있도록 도와주는 툴이라고 생각하시면 됩니다. docker를 설치했다면 따로 부가적인 설치가 없이 사용할 수 있는 툴입니다.
# docker-compose.yml
version: "3"
services:
django-web:
container_name: django-web
image: 20studio/letterproject-test:1.0.0 # 이미지, 태그
ports:
- "8000:8000"
build: ./letterproject
volumes:
- ./letterproject:/srv/letter_web
- ./log/uwsgi:/log
nginx:
container_name: nginx
image: nginx
ports:
- "80:80"
build: ./nginx
volumes:
- ./letterproject:/srv/letter_web
- ./log/nginx:/log
- ./.static_root:/static
depends_on :
- django-web
폴더 내에 docker-compose.yml 파일을 생성해줍니다.
이는 django-web 컨테이너와 nginx 컨테이너를 한번에 묶어서 실행시켜주는 역할을 합니다. 이때 이미지 및 포트 설정은 원하시는 대로 하면 됩니다. 중요한 것은 volumes 설정인데 상대경로를 지정할 수 있도록 해서 편합니다.
예를들어서 ./letterproject:/srv/letter_web 라고 작성한 코드에서
django-web 컨테이너에서는 /srv/letter_web이라고 작성한 경로들이 실제로는 ./letterproject의 경로에서 파일을 찾으면 된다는 것입니다!!
이를 이용해서 nginx에서 static file의 root를 편하게 관리해줄수 있다는 점이 좋은 것 같습니다 --> django는 서버 배포시에 static file을 찾지 못하기 때문에 경로 지정을 해주어야합니다!!
python3 manage.py collectstatic
해당 코드를 이용해서 staticfile을 지정한 곳에 모은 후
nginx에 요청이 들어온 경우 사이트에 대한 요청은 uwsgi의 socket으로, static file에 대한 요청은 특정 static_root로 보내주어야 웹사이트에서 static 파일을 인식할 수 있게 됩니다. 해당 사항에 대해서는 더 자세하게 다른 글에서 다뤄보도록 하겠습니다!!
설정이 다 끝났다면
docker compose up
를 입력하면 image와 container를 생성하는 시간이 지난 후
localhost로 접속하면 화면이 정상적으로 뜨는 것을 확인할 수 있을 겁니다!!
기존에 배포되고 있는 웹사이트를 docker를 통해서 다시 관리를 하려고하니 힘든점이 많지만 그래도 docker내에 정상적으로 작동하는 image들이 쌓이는 것을 보니 뿌듯한 마음도 있습니다!!
안정적으로 정상화가 되면 단위 테스트 및 버전 관리가 더 쉬워질거라 믿습니다 ㅎㅎ
모두들 화이팅입니다!!