우분투 서버에 Django 프로젝트 배포하기 마지막편.
DigitalOcean에 Django 프로젝트 배포하기 (1)
DigitalOcean에 Django 프로젝트 배포하기 (2)
이제 본격적인 서버 구동을 위해 Nginx 및 Gunicorn 세팅을 해 주어야 한다.
다른 세팅을 해 주기 전에 gunicorn 이 정상적으로 작동하는지 확인해본다. 다음 명령어로 서버를 구동하고, 브라우저에서 droplet 의 IP를 입력하고 8000번 포트로 접속했을 때 index 화면이 나와야 한다. 다만 아직 서버에서 정상적으로 static 파일 설정을 하지 않았으므로 css 파일을 찾을 수 없어서 스타일링은 정상적으로 적용되지 않을 것이다.
(deploy_env)$ gunicorn --bind 0.0.0.0:8000 프로젝트.wsgi
위에서 '프로젝트' 는 wsgi.py
파일이 들어있는 Django app 디렉토리 이름이다. 실제 자기가 만들어 준 app 디렉토리 이름으로 적어 준다. 해당 디렉토리 내에 Django가 자동적으로 생성한 wsgi.py
에 설정되어 있는 설정을 적용해서 gunicorn 을 구동하는 것이다.
정상적으로 구동이 된다면 이제 파이썬 가상환경에서 빠져나오기 위해서
(deploy_env)$ deactivate
를 입력한다. 프롬프트 앞의 (deploy_env) 가 사라진 것을 확인할 수 있다.
이제 리눅스 서버에 systemd 소켓과 서비스 파일을 만들어 준다. (systemd는 리눅스의 서비스 관리자 같은 것으로 이 부분에 대해서는 더 공부가 필요할듯)
해당 파일들을 작성하게 되면 시스템 부팅시에 gunicorn 소켓이 생성되고, 외부에서의 connection을 기다리고 있다가 connection 이 발생하면 systemd가 자동으로 gunicorn 에게 해당 처리를 맡기는 동작이 발생한다.
우분투에 내장된 nano 에디터를 이용해 socket 파일을 작성해주기 위해 다음 명령어를 입력한다.
$ sudo nano /etc/systemd/system/gunicorn.socket
에디터가 열리면 아래의 내용을 입력하고 저장해준다. (nano 에디터 내에서 도움말이 나오긴 하는데 저장은 Ctrl + O, 종료는 Ctrl + X 이다)
처음 작성할때 [Socket] 섹션에 gunicorn.sock
부분을 gunicorn.socket
이라고 잘못 입력해놓고 서버 구동이 되지 않아서 어디가 잘못되었는지 못 찾고 한참을 헤맸다. 한번 잘못된 파일이 생성되니 쉽게 복구가 되지 않고 결국 이것저것 건드리다가 완전히 망해서 아예 Droplet 을 삭제하고 처음부터 다시 해야 했다. 오타에 주의하자.
[ /etc/systemd/system/gunicorn.socket ]
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
마찬가지로 nano 에디터를 이용해 systemd 서비스 파일을 만들어 준다.
$ sudo nano /etc/systemd/system/gunicorn.service
[ /etc/systemd/system/gunicorn.service ]
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=sitemaster
Group=www-data
WorkingDirectory=/home/sitemaster/django-deploy-exercise
ExecStart=/home/sitemaster/django-deploy-exercise/deploy_env/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
프로젝트.wsgi:application
[Install]
WantedBy=multi-user.target
여기서도 sitemaster를 자기 id로, django-deploy-exercise 는 자기 프로젝트 root 디렉토리 명으로, '프로젝트' 는 자기 Django app 으로 적절히 적어 주자.
이제 정상적으로 Gunicorn 소켓 파일이 생성되어 구동할수 있다. socket을 구동하면 /run/gunicorn.sock 라는 소켓 파일이 만들어지게 된다. 다시 강조하지만 이걸 gunicorn.sock 이 아니라 gunicorn.socket 이라고 적었다고 droplet 전체를 갈아 엎었으니 조심하자.
다음 명령어로 Gunicorn socket 을 구동하자.
$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket
정상적으로 소켓이 작동하는지 다음 명령어로 확인할 수 있다.
$ sudo systemctl status gunicorn.socket
정상 동작중이라면 active 라는 상태 표시가 보일 것이다.
다음으로 gunicorn.sock 파일이 정상적으로 생성되었는지를 다음 명령어로 확인해 본다. 아래 줄과 같은 출력이 나오면 정상이다.
$ file /run/gunicorn.sock
/run/gunicorn.sock: socket
에러 메세지가 나는 등의 상황에서 자세한 동작 로그를 보려면
$ sudo journalctl -u gunicorn.socket
를 입력해보면 확인할 수 있다.
현 상태에서는 gunicorn.socket 만 활성화되어 있고, 소켓이 아직 아무 연결을 수신하지 않은 상태이므로 gunicorn.service 는 아직 활성화되지 않은 상태이다.
$ sudo systemctl status gunicorn
를 입력해서 확인해볼 수 있다.
위와 같이 inactive(dead) 라고 나올 것이다.
이때 curl 을 이용해서 연결 신호를 보내서 정상 작동하는지 확인해볼 수 있다.
다음 명령어를 입력하면 index 페이지의 html 내용이 수신되어 화면에 출력되는 것을 볼 수 있다.
$ curl --unix-socket /run/gunicorn.sock localhost
이후
$ sudo systemctl status gunicorn
를 다시 확인해 보면
와 같이 active 상태가 되면서 아래에 자세한 동작 로그가 나오는 것을 확인할 수 있다.
에러가 있는 경우에는
$ sudo journalctl -u gunicorn
을 입력해서 세부 내용을 확인해볼 수 있다.
경험상 이 단계에서의 에러는 대부분 gunicorn.socket 혹은 gunicorn.service 파일 내의 사소한 오타에 의한 것이었다. 파일을 잘 확인해 보고 수정해야 할 부분이 있는지 확인해 보자.
파일을 수정한 경우에는
$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn
명령으로 gunicorn 을 재기동해 주어야 변경사항이 제대로 반영된다.
마지막 단계로 Nginx 서버의 설정을 해 준다.
다음 명령으로 Nginx의 서버 블록을 sites-available 디렉토리 아래에 만들어 주어야 한다.
$ sudo nano /etc/nginx/sites-available/프로젝트
server {
listen 80;
server_name 128.199.144.73;
client_body_buffer_size 10M;
client_max_body_size 10M;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
alias /home/sitemaster/django-deploy-exercise/staticfiles/;
}
location /media/ {
root /home/sitemaster/django-deploy-exercise;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
우선 포트는 특별한 일이 없으면 80번으로 설정하고, 아래에 server_name 에는 현재 작업중인 Droplet의 IP 주소를 적어 준다. 나중에 커스텀 도메인을 사용하는 경우는 도메인 이름으로 바꾸어 주어야 한다. 이것은 나중에 다뤄 보겠다.
그 아래의 client_body_buffer_size와 client_max_body_size 는 블로그나 게시판에 글을 올리는 등 사용자가 업로드하는 파일들의 크기에 관한 설정인데, Nginx의 기본 설정은 최대 파일 용량 제한이 1MB로 되어 있어 이보다 큰 파일을 업로드하는 경우 에러가 나게 된다. 프로젝트의 사정에 맞게 적당히 설정해 주자.
아래의 favicon 관련 설정은 인터넷 익스플로러 등 브라우저에서 HTTP 헤더가 로딩되기 전에 /favicon.ico 경로에서 에 파비콘 파일을 요청하는 경우에 Nginx 에 에러를 일으킨다고 하는데 이를 무시하기 위한 설정이다.
같은 서버 블록 아래에 static과 media 파일 경로도 설정해 준다. location 을 명시해 주고 실제 파일시스템 상의 경로를 적어 주는데, static 파일의 경우 현재 Django 프로젝트에서 /static/ 으로 들어온 요청은 실제로는 staticfiles 디렉토리에서 서빙하도록 설정되어 있어 위와 같이 'alias' 를 이용해 설정해 주었다 (이전 편 settings.py 참고).
Media 파일의 경우는 경로 이름이 media로 실제 디렉토리 이름과 같아서 직접 'root' 를 설정해 /media/ 로 들어온 요청을 '/home/sitemaster/django-deploy-exercise/media/' 에서 서빙하도록 한 것이다.
맨 아래 root 경로 ('location /') 요청에 대한 설정도 위와 같이 작업해 준다.
파일을 저장 후 작성한 파일을 sites-enabled 디렉토리에 링크해 준다.
$ sudo ln -s /etc/nginx/sites-available/프로젝트 /etc/nginx/sites-enabled
설정이 제대로 적용되었는지를 다음 명령어로 확인해 본다.
$ sudo nginx -t
위와 같은 메세지가 보이면 잘 설정된 것이다.
에러가 없다면 다음 명령어로 Nginx를 재기동해 준다.
$ sudo systemctl restart nginx
마지막으로 방화벽을 Nginx 에 대해 열어 주기 위해 다음 명령어를 입력해 준다. 동시에 테스트용으로 썼던 8000번 포트는 더이상 사용하지 않을 것이므로 방화벽에서 허용해 주었던 설정을 삭제한다.
$ sudo ufw allow 'Nginx Full'
$ sudo ufw delete allow 8000
이제 Droplet의 IP 주소로 가 보면 Django 프로젝트의 index 화면이 정상적으로 보일 것이다. 고생해서 만든 프로젝트의 첫 화면이 어디서나 접속 가능한 상태로 뜨는 것은 언제 봐도 감동적이다.
여기까지 하면 DigitalOcean 에서의 배포는 정상적으로 끝난 것이다. 커스텀 도메인을 가지고 있는 경우 도메인에 연결해 주는 작업이 필요한데, 그것은 다음 글에서 다루고 있다.