1. gunicorn, nginx
배포환경에서 runserver 명령어를 사용하면 안된다. django는 웹 프레임워크를 만들기 위한 툴이지 서버에 배포하기 위한 툴이 아니기 때문에 performance, 보안 등에 문제가 생길 수 있다고 django 공식문서에서 설명한다. 따라서 대신 서버를 실행해주고, 뒤에서 언급할 Nginx와 연결해주는 Gunicorn 이라는 라이브러리를 django container에 설치할 것이다.
Nginx와 Gunicorn의 개념(참조 2, 3)
그전에 간단하게 Nginx와 gunicorn 등의 개념에 대해서 짚고 넘어가자.
WEB-WAS(WGSI) 관계
자바-스프링에서는정적 자원과 동적 자원을 WEB과 WAS가 나누어서 제공했던 것(각각 아파치, 톰캣)처럼 여기서 진행하는 프로젝트에서는 정적 자원을 제공하는 NIGINX와 동적 자원을 제공하는 django로 나누어서 연결된 것이다. 그 사이에 gunicorn은 CGI(Common Gatewat Interface)의 역할을 하는 middleware의 일종이다. CGI는 여러 프로그래밍 언어 사용자들이 각기 다른 방식의 요청을 해도 이를 이해할 수 있도록 공통된 규칮긍로 변환하는 역할을 한다. 따라서 WAS에서 HttpRequest를 이해하고 처리할 수 있게 되는 것이다.
WGSI:Gunicorn
WGSI는 파이썬 스크립트가 웹서버와 효율적으로 통신하기 위해 만들어진 인터페이스이다. 다른 CGI에 비해서 WSGI는 스크립트 전체를 실행하는 것이 아니라 필요한 로직만 찾아서 실행하고 결과를 응답하기 때문에 좀 더 효율적이라고 한다. WSGI는 파이썬에서도 uWSGI라는 이름으로 제공하지만, 대체로 gunicorn이 빠르고 성능이 좋다고 하기 때문에 gunicorn을 사용한다.
WEB:Nginx
웹 서버 제공자는 많지만, 그 중 Nginx를 사용하는 대표적인 이유는 리버스 프록시(Reverse Proxy) 사용, 로드 밸런싱, 캐싱, 비동기 처리 기능이라고 할 수 있겠다.
프록시는 서버와 사용자 간에 프록시라는 중간 매개체를 두고 통신하는 것이다. 포워드 프록시(Forward Proxy)란 사용자가 어떤 IP나 네트워크 정보를 갖고 서버에 요청하는 지를 서버에 숨기는 방식이고, 리버스 프록시는 이와 반대로 서버가 서버의 구조를 숨기는 것이라고 할 수 있다. 서버에서 제공하는 static 파일의 디렉토리 위치가 어디인지 등 보안에 민감한 정보를 숨길 수 있게 하는 것이다.
로드 밸런싱은 사용자의 요청을 여러 자원들로 잘 분산해서 들어갈 수 있도록하여 부하를 줄이는 방식이다. 그리고 캐싱은 한 번 정해지면 변경이 안되는 정적 파일 등을 미리 저장해놓는 기능이다. 마지막으로 비동기 처리 기능이 Nginx의 특징 중 하나인데, 아파치는 본래 다중 프로세스 방식으로 부하를 처리하고 Nginx는 이벤트 기반으로 부하를 처리한다. 다시 말해 Nginx는 비동기 방식으로 부하를 처리하여 서버가 더 효율적으로 돌아갈 수 있도록해서 Nginx를 많이 사용하게 되었다. 물론 아파치도 이벤트 기반의 처리 방식을 도입했다고 한다.
2. Gunicorn, Nginx 추가
Gunicorn
우선 Gunicorn을 추가한다. 아래 그림에서 볼 수 있듯이 Gunicorn은 django와 직접 연결할 것이기 때문에 django project에서 직접 설치하고 설정해준다.
- 터미널에서 'pip install gunicorn' 실행
- Dockerfile에서 CMD [".... runserver..."] 부분을 gunicorn 양식으로 아래와 같이 변경
- Dockerfile RUN pip install gunicorn 추가
FROM python:3.9.0
WORKDIR /home/
RUN git clone https://github.com/CJ0823/inflearn_study_django_pinterest.git
WORKDIR /home/inflearn_study_django_pinterest/
RUN pip install -r requirements.txt
RUN pip install gunicorn
RUN echo "SECRET_KEY=django-insecure-$ioa*(%5%p#1&c(as*nqzw7xpo(g8l6ovb_&d+x-+(-pdyo&z+" > .env
RUN python manage.py migrate
RUN echo yes | python manage.py collectstatic
EXPOSE 8000
CMD ["gunicorn", "pragmatic.wsgi", "--bind", "0.0.0.0:8000"]
Nginx
nginx는 외부에서 80번 port로 요청을 받고, django의 8000번 포트로 요청을 전달해주는 구조로 만든다. 즉 WEB server가 모든 요청을 받아서 처리하고, django WAS로 전달해줄지 말지를 결정하는 것이다. 이를 위해서 Docker Network가 필요하다.
Docker network가 Nginx ~ Django/Gunicorn을 하나도 묶는다. Docker network는 Container Name 자체로 network를 구성한다. 즉 Nginx에서 django_container_gunicorn:8000으로 요청을 보내면 요청이 전달되는 방식이다.
우선 하나의 Network에 ngix와 Django/Gunicorn을 묶어서 넣을 수 있도록 새로운 Network을 생성한다. portainer에서 Networks - Add Network에서 nginx-django라는 이름으로 네트워크를 만든다.
django container를 만든다. django_container_gunicorn라는 이름으로 만들되, 기존에 따로 설정했던 포트 정보는 굳이 설정하지 않는다. 왜냐하면 이제 django는 외부와 통신하는 것이 아니라 nginx와만 통신할 것이기 때문이다. 또한 Network 탭에서 상기 만들어두었던 nginx-django에 django container가 포함되도록 하여 deploy 해준다.
nginx.conf : gunicorn과 nginx 연결
이제 Gunicorn과 Nginx를 연결해줘야한다. 이 연결정보는 nginx의 설정 정보인 nginx.conf 파일을 만들고 여기에 gunicorn의 정보를 넣어주는 방식으로 한다.
gunicorn 홈페이지 - deployment 페이지에서 코드를 복사해와서 일부를 수정하여 nginx.conf 파일을 만든다. Dockerfile처럼 django project 파일에 넣어둔다.
https://gunicorn.org/#deployment
worker_processes auto;
events {
}
http {
server {
listen 80;
location / {
proxy_pass http://django_container_gunicorn:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
지금 단계에서 설정 정보를 일일이 알기는 어렵다. 다만 listen 80으로 80번 포트로 외부에서 nginx로 오는 요청 정보를 받는다는점, location / 내부에 container name으로 Docker Network로 연결된 gunicorn의 주소로 proxy_pass를 설정한다는 점 등을 알면 될 것 같다.
3. nginx.conf 파일을 docker container에 넣기 : FileZilla
이제 원래는 이 nginx.conf 파일을 docker container 쪽으로 넘겨줘야한다. 이를 위해서 강의에서는 FileZilla라는 ftp 프로그램을 사용한다. FileZilla에서 호스트 정보를 입력하고 리모트 사이트에 연결한 뒤, 로컬 사이트에서 리모트 사이트의 'home/' 디렉토리에 nginx.conf 파일을 넣어주면 된다. 포트는 22번으로 입력한다.
하지만, 나는 로컬에서 container를 띄웠으므로 다음 과정을 거쳤다.
- nginx container 만들기
- cmd에서 docker cp 명령어를 입력하여 nginx.conf 파일을 기존 nginx.conf 파일에 덮어쓰기
- volume 추가하고 설정하여 연동하기
1. nginx container 만들기
위 django_container_gunicorn을 만들 때와 마찬가지로 하되, 포트는 외부에서 80번을 받아와서 nginx 80번에 매칭되도록 해주고 network는 django_container_gunicorn에서 설정한 nginx-django로 설정하여 두 container를 하나의 network로 묶는다.
2. cmd에서 nginx.conf 파일 덮어쓰기
nginx container를 만들고 나면, 기본적으로 /etc/nginx/nginx.conf에 설정 정보가 입력되어 있다. 이것을 상기 작성한 nginx.conf 파일로 덮어쓸 것이다.
cmd에서 'docker ps'를 입력
nginx container의 ID를 확인한다.
'docker exec -it {container ID} /bin/bash' 를 입력
해당 container에 들어갈 수 있다.
cmd에서 쓰는 ls, cd 명령어로 /etc/nginx/ 에 접근해보자. nginx.conf 파일이 있는 것을 볼 수 있다.
'exit' 으로 container를 빠져나온 후, 'docker cp C:\Users\chang\PycharmProjects\pragmatic\nginx\nginx.conf nginx:/etc/nginx' 라고 입력하면, 로컬 pc에 있는 nginx.conf 파일을 nginx라는 이름을 갖는 컨테이너의 nginx.conf에 덮어쓸 수 있다. 명령어 문법을 정리하자면 다음과 같다.
'docker cp {local에 있는 파일 경로 및 이름} {container 이름}:{붙여넣기를 원하는 경로}'
너무 길어진 것 같다. 다음 글에서 Docker Volume에 대해 알아보고 설정하여 마무리할 것이다.
참조
1. 작정하고 장고! Django로 Pinterest 따라만들기 : 바닥부터 배포까지-박형석님 인프런 강의
2. 문돌이가 이해한 인공지능 이야기 - Nginx, Gunicorn이란 무엇인가? feat Django
https://moondol-ai.tistory.com/467
3. Youtube 영상 - 아파치, NginX, 톰캣이 뭔가요? (+ 웹서버, WAS, 로드밸런싱, 프록시)
https://www.youtube.com/watch?v=Zimhvf2B7Es&t=799s
4. [Docker] 도커 파일 복사(로컬 <-> 컨테이너)