DigitalOcean에서 Nginx와 Gunicorn 오류 해결

2020. 03. 16 IT/컴퓨터 > 네트워크

이전 글들:

DigitalOcean에 Django 프로젝트 배포하기 (1)

DigitalOcean에 Django 프로젝트 배포하기 (2)

DigitalOcean에 Django 프로젝트 배포하기 (3)

 

이전의 DigitalOcean 에서 배포하기 순서대로 했다면 웬만하면 문제없이 배포가 되었을 것이다. 그러나 여러 가지 이유로 인해서 에러가 난다면 다음의 내용을 참고해 보자. 내가 겪은 에러 위주로 몇가지만 정리해 보았다.

 

#1. Nginx 가 Django 프로젝트 대신 디폴트 화면을 보여주는 경우

 

이 경우에는 외부에서 정상적으로 Droplet 의 IP에 접근은 가능하지만, Nginx에서 프로젝트의 디렉토리를 찾지 못하는 경우이다. 이전에 만들어주었던 서버 블록에서 잘못된 내용이 있는지 확인하고 수정해 준다.

​$ sudo nano /etc/nginx/sites-available/프로젝트
[ /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;
    }
}

대개는 IP주소나 디렉토리 경로에 오타가 있는 경우일 것이다. 확인하고 수정해 주자.

 

#2. Nginx에서 413 오류가 나는 경우.

이 블로그를 처음 가동할때 내가 겪었던 에러는 파일 크기 관련한 에러였다. 

블로그에 연동된 CK 에디터를 통해서 글을 작성하면서 이미지를 업로드하는데, pythonanywhere에서는 분명히 정상적으로 작동했는데 DigitalOcean 에서는 계속 413 에러가 나면서 안 되는 것이다. CK에디터 창에서는 413 에러라고만 나오니 왜 저런 에러가 나는지 알기가 어려웠는데, 검색해 보니 파일 크기 관련된 문제인 것 같았다. 우선

$ sudo less /var/log/nginx/error.log

를 통해서 에러의 내용을 파악해볼 수 있다. 에러가 난 경우 마지막 에러가 난 시점의 기록을 잘 보면

'client intended to send too large body'

라는 내용을 볼 수 있다. 이것은 Nginx 서버의 업로드 제한 용량 디폴트 값이 1MB 라서 발생하는 일이다. 역시 위에서와 같이 서버 블럭에 파일 용량 제한 옵션을 명시해 주어야 한다. 꼭 서버 블럭이 꼭 아니더라도 특정 디렉토리 블럭에 명시해줄 수도 있지만, 사이트 전체적으로 적용하기 위해서는 서버 블럭에 적어주면 편한것 같다.

[ /etc/nginx/sites-available/프로젝트 ]
server {
    listen 80;
    server_name 128.199.144.73;

    client_body_buffer_size 10M;
    client_max_body_size 10M;

...이하 동일

 

위에서 client_body_buffer_size는 request 가 들어왔을 때 처리 가능한 용량이고, client_max_body_size 는 실제로 업로드될 수 있는 파일 크기의 상한선을 지정해 주는 것이다. client_max_body_size 는 실제 업로드를 허용하고 싶은 만큼 지정해 주어야 하지만 client_body_buffer_size는 꼭 그럴 필요는 없는것같다.

정상적으로는 들어온 요청을 메모리에서 처리하지만, buffer 보다 큰 요청이 들어오는 경우 경고를 띄우고 나서 요청이 다 처리될 때까지 디스크에 임시 파일을 만들어 처리한다고 한다. 실제로는 다량의 이미지를 지속적으로 업로드하는 사이트가 아닌 경우에는 동작에 느껴지는 차이는 거의 없다고 한다.

client_body_buffer_size를 필요 이상으로 크게 설정해 주는 경우 DDoS 공격에 취약해질 수 있는 약점이 있다고 하므로 너무 크게 잡아줄 필요는 없는것같다. 일단 이미지 등 올리기에는 10MB 정도면 적당해서 저렇게 설정해 주었는데, 사이트 사정에 맞게 조절해주면 될 것 같다.

 

#3. Django 앱 동작 중 Nginx에서 500 Internal server error 오류가 나는 경우

500 에러는 상황이 워낙 다양해서 원인을 한 가지로 정리하기는 쉽지 않다. 다만 이 경우는 Nginx 자체보다는 거의 대부분 소스 코드의 동작 오류라고 한다. Nginx 와 gunicorn 설정이 잘 되었는데도 오류가 난다면 Django 세팅이나 코드에 문제 있는 부분이 없는지 살펴보자.

처음 이 블로그를 설치하고 배포하니, 로컬 환경에서는 분명히 정상적으로 모든 기능이 동작하는데 서버에서는 첫 화면은 잘 보이다가 글을 작성하고 저장하려고 하면 꼭 internal server error 500가 나오는것이다.

소스 코드는 잘 작동하던것을 그대로 배포한 것이라서 static 파일 문제인지, 디렉토리 퍼미션 문제인지, 자바스크립트 문제인지, 온갖 설정을 다 바꾸고 유저를 새로 만들었다가 없앴다가 퍼미션을 풀었다 잠궜다가 삽질을 수없이 하고, 그후에 도저히 안돼서 Droplet을 삭제하고 처음부터 다시 해 보기도 했는데..

거의 미쳐버릴뻔 했는데 알고보니 허무하게도 로컬 환경에만 설치돼있고 서버에 설치하지 않은 패키지를 Django 에서 불러들이려고 해서 발생하는 문제였다. 디렉토리 퍼미션은 원래 주어진 대로 하면 아무 문제 없으니 특별한 경우 아니면 안 건드리는게 좋겠다.

구체적으로는 프로젝트에 포함된 CK 에디터의 image_backend에 pillow 가 설정돼있는데 pillow 패키지를 설치하지 않은것 (포스트의 썸네일 생성용으로 CK에디터가 자체적으로 pillow를 이용해 이미지를 처리한다). 

(deploy_env)$ pip install pillow

로 설치해 주고 나니 거짓말처럼 정상 작동하는 것을 보고 거의 기절할뻔 했다. 

사실 pillow 는 이미지 처리할때 필요해서 안 그래도 설치하기는 했어야 하는데..이 블로그 프로젝트 할 때 로컬환경에서 가상환경을 구성해놓지 않으니 이런 사단이 난다. 가상환경을 꼭 먼저 구성해놓고 프로젝트를 하자.

 

#4. Nginx가 502 bad gateway 오류를 내보낼때

502 오류는 Nginx가 들어온 요청을 제대로 처리를 못 해 주는 경우에 발생하는데, 대개 소켓 등 설정 관련된 경우가 많다고 한다. 

$ sudo tail -F /var/log/nginx/error.log

를 이용해 원인을 파악해볼 수 있다. 

 

connect() to unix:/run/gunicorn.sock failed (2: No such file or directory)

라고 나오는 경우 gunicorn.sock 을 찾을 수 없다는 뜻인데,

처음에 

[ /etc/systemd/system/gunicorn.socket ]

파일 안에 

[Socket]
ListenStream=/run/gunicorn.sock

라고 적어주어야 하는 것을 gunicorn.sock 이 아닌 gunicorn.socket 이라고 적었다가 엉뚱한 파일이 만들어져서 이런 오류가 난 적이 있다. 시스템의 핵심부에 이상한 파일이 한번 만들어지면 수작업으로 지우기도 어렵고 뭔가 알수 없는 연계된 오류를 연달아서 뱉어내므로 가급적이면 처음 설정할때 실수하지 않도록 하자. 이것때문에도 Droplet 을 갈아 엎었다. 도대체 몇번을 갈아 엎은건지..ㅎㅎ

 

connect() to unix:/run/gunicorn.sock failed (13: Permission denied)

이런 오류가 나기도 한다는데, 이것은 개인적으로 겪어 보지는 못했다. systemd가 Gunicorn 의 소켓은 만들어 놓았는데, 퍼미션 문제로 Nginx가 그 소켓에 접근을 못 하는 등의 문제라고 한다. 접속한 유저가 sudo 권한이 있는지, 명령어를 내릴 때 sudo 를 붙여서 내렸는지 등을 확인해 보아야 한다.

 

$ namei -l /run/gunicorn.sock

를 이용하면 해당하는 파일과 그 상위 디렉토리의 퍼미션 현황을 쭉 보여 주는데, 적절하게 설정되었는지 확인해보고 퍼미션을 조정해주면 된다. (다만 DigitalOcean에서 정상적으로 배포된 경우라면 퍼미션을 손댈 필요가 있는 경우는 없었다)

 

 

기타 문제 해결에 도움이 되는 명령어들

$ sudo journalctl -u nginx
$ sudo less /var/log/nginx/access.log
$ sudo less /var/log/nginx/error.log
$ sudo journalctl -u gunicorn
$ sudo journalctl -u gunicorn.socket

위의 명령어들을 이용해서 Nginx 또는 gunicorn 관련된 로그들을 확인해볼 수 있다.

 

뭔가 설정을 바꿔 주었다면 다음 명령들을 이용해서 nginx 또는 gunicorn 을 재기동해 주어야 한다. 특히 Django 프로젝트의 소스 코드를 바꾼 경우에도 gunicorn 을 다시 기동해 주지 않으면 변경사항이 반영되지 않으므로 꼭 restart gunicon 을 해 주자.

$ sudo systemctl restart gunicorn
$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn.socket gunicorn.service
$ sudo nginx -t
$ sudo systemctl restart nginx

 

이외에도 크고작은 에러 메세지가 많았는데 다 정리하지는 못했다. 사실 웬만한 에러 메세지는 구글에 물어 보면 거의 다 찾을 수 있다. 그나마 프로그래밍은 비교적 독학하기가 쉬운 점이 구글에 물어보면 비슷한 사례를 많이 찾을수 있다는 것이다. 이런식으로 하나씩 하면 체계는 좀 없는 것이 문제지만..

어쨌든 DigitalOcean 은 비교적 친절하게 튜토리얼도 잘 되어 있고 설정이 크게 어려운 부분은 없는 것 같다. 로컬에서 잘 작동하는것을 확인한 프로젝트라면, 내가 오타 등 실수만 하지 않으면 깔끔하게 배포가 가능한 것 같다.



최근 글 목록