본문 바로가기

Back-End

Mod_wsgi를 이용해 장고 프로젝트를 리눅스 우분투, Apache 에 Deploy하기

728x90

Preview

리눅스 우분투 OS에 대한 이해가 많이 부족한 상황에서 프로젝트의 도메인이 나와 더 이상 DJango runserver로 테스트할 수 없는 상황이 되어 mod_wsgi를 이용해 Deploy 하기로 했습니다. 이 과정에서 제가 겪은 다이내믹(?) 한 일들을 기록해보고자 합니다.

 

WSGI

우선 wsgi는 Web Server Gateway Interface라고 해서 웹서버(아파치와 NginX)와 파이썬 애플리케이션을 연결시키기 위한 인터페이스 입니다. 쉽게 말해 제가 작성한 장고 프로젝트는 파이썬으로 짜여있기 때문에 장고 자체 서버인 runserver를 돌리는 데는 무리가 없지만 정적 웹 서버인 아파치는 이를 해석할 수 있는 방법이 없습니다. 아파치는 사용자로부터 request를 받으면 Wsgi 인터페이스를 통해 장고 애플리케이션에 받은 요청을 보내는 것이죠. 

출처 https://medium.com/analytics-vidhya/what-is-wsgi-web-server-gateway-interface-ed2d290449e

 

Wsgi는 대표적으로 mod_wsgi, uWSGI, Gunicorn 이 세 가지로 나뉘어집니다. 이 셋 중 자기에게 맞는 인터페이스를 선택하면 되는데 mod_wsgi는 Apache 웹서버와의 호환성이 좋고 Gunicorn은 NginX와의 호환성이 좋다고 합니다. uWSGI는 두루두루 잘 되긴 하지만 조금 무거운 특성이 있다고 합니다. 저는 Graham Dumpleton이 만든 mod_wsgi를 선택했습니다. 이유는 그냥 자료가 많이 나와서..였습니다. 하지만 저는 좋았던 점은 Graham Dumpleton 님이 Document 정리도 잘해놓았고 Stackoverflow나 깃허브에 남긴 질문에 댓글을 성심성의껏 달아주어서 많은 도움을 받았습니다.

 

https://modwsgi.readthedocs.io/en/master/user-guides/quick-installation-guide.html

 

Quick Installation Guide — mod_wsgi 4.8.0 documentation

This document describes the steps for installing mod_wsgi on a UNIX system from the original source code. Restart Apache Web Server Having adding the required directives you should perform a restart of Apache to check everything is okay. If you are using a

modwsgi.readthedocs.io

이제부터 제가 겪은 문제들과 해결방법들입니다.  

 

파이썬 버전이 맞지 않다?

mod wsgi를 설치하는 방법이나 Apache 설정 파일인 000-default.conf, apache2.conf 파일을 수정하는 방법 등은 이 블로그가 가장 정리가 잘되어 있기 때문에 참고하시길 바랍니다. https://blog.boxcorea.com/wp/archives/2298

 

ubuntu server 18.04 LTS 에 apache2, django, mod_wsgi 설치

ubuntu server 18.04 LTS 에 apache2, django, mod_wsgi 설치 우분투서버는 최초 설치한 상태. 1. ubuntu 18.04에는 python3.6.5가 설치되어 있다. 이 버전을 그냥 사용하기위해 아래 명령어로 아파치웹서버와 mod_wsgi를

blog.boxcorea.com

 

위의 블로그를 참고하여 설치와 세팅을 맞췄는데 500 Internal Server error 가 떠서 /var/log/apache2/error.log (여기에 아파치 에러 로그가 쌓입니다. ) 파일을 확인해보니 

ImportError: No module named 'encodings'

이 에러가 계속 뜨고 있었습니다. 이 에러말고 No module named 'django' 에러가 뜰 때도 있는데 이는 /etc/apache2/sites-available에 있는 000-default.conf 파일에서 파이썬 가상 환경 위치를 잘못 적었거나 파이썬 버전 문제 혹은 실제로 가상 환경에 django가 안 깔려있어서 그럴 수도 있습니다. 아무튼 이 encoding 모듈이 없다는 에러는 검색해보면 알겠지만 대부분 파이썬 버전이 맞지 않아서 그렇다고 나옵니다. 처음 mod_wsgi를 설치할 때 본인 가상 환경에 맞는 파이썬 버전을 설치하였으면 상관없지만 저는 global python Version(초기에 가상 환경 없이 설치한 파이썬)이랑 가상 환경의 파이썬 버전도 맞지 않았고 mod_wsgi의 버전도 맞지 않았습니다. 연구실 컴퓨터가 예전 선배들 때부터 있었기 때문에 선배들이 설치해놓은 파이썬 버전과 제 가상 환경의 버전이 달랐습니다. 버전 업그레이드도 해보고 다운그레이드도 해보고 했는데 해결되지가 않아서 "아...그럼 그냥 예전 버전은 다 지우고 파이썬 3.7만 남기자"

라는 생각으로 완전히 예전 파이썬 버전을 지우는 것을 선택했습니다. 재앙의 시작이었습니다...

 

** mod_wsgi의 버전 확인하는 방법은 아래와 같습니다.

ldd /usr/lib/apache2/modules/mod_wsgi.so

 

 

지우면서 갑자기 하나둘씩 우분투 애플리케이션이 사라지더니 느낌이 싸해서 재부팅을 하였고 우분투 UI는 보이지 않은 채 덩그러니 Shell 만 보였습니다. 찾아보니 우분투 애플리케이션 중에 파이썬으로 작성된 것들이 있었고 그중에 UI를 표시하는 Ubuntu-desktop 패키지도 파이썬으로 작성된 것이어서 이전 파이썬 버전을 밀면서 같이 날아간 것이었습니다. 절대 절대... 무턱대고 파이썬 지우지 마세요... 구글에 파이썬 완전히 삭제하는 법.. 이런 거 절대 안 됩니다..

 

처음엔 우분투를 잘 다룰 줄도 몰라서 그냥 에러 로그를 구글에 쳐서 나온 대로 막 했더니 네트워크 설정 파일도 날려먹고 해서 인터넷도 연결이 안 되기 시작했습니다. 네트워크 설정하는 법도 몰라서 하루를 꼬박 까먹고 겨우 설정한 다음 ubuntu-desktop을 설치하고 다시 처음으로 돌아갔습니다. 머.. 다행히도 파이썬 버전은 다 통일이 되긴 했습니다.

이때 알게 된 것은
1) 리눅스는 패키지를 apt-get으로 다운로드할 때 mirror 서버를 통해서 다운로드를 하고 국내에는 kakao, kaist 등에서 서비스하고 있다고 합니다. 처음 리눅스 우분투를 한국지역에서 설치하면 kr.archive.ubuntu.com으로 미러서버가 설정되어 있을 텐데 카카오나 kaist가 더 빠르다고 합니다.
2) 우분투 17.10 이상부터는 네트워크 설정 파일이 /etc/network/interface 에서 /etc/netplan/50-cloud-init.yaml로 바뀌었다고 합니다.  

 

용량 과부하

파이썬 통일도 됐겠다.. 이제 문제를 본격적으로 찾으려고 보니 갑자기 컴퓨터의 용량이 부족하다고 하며 꺼지더니 다시 켜도 이번엔 Shell도 안 뜨고 아예 부팅 화면에서 멈췄습니다. 제가 찍은 사진은 아니지만 구글에 비슷한 사진이 있어서 첨부합니다.

 

일단 grub 부팅 모드로 진입하기 위해 본체 부팅 모드 누르자마자 shift키를 누르고 있었더니 grub 부트 메뉴로 진입 성공했습니다. ubuntu recovery mode로 들어가서 메모리도 지우고 fsck 등등 있는 메뉴는 다 눌렀습니다. 

 

그래도 안되길래 위에 root 메뉴를 들어가서 shell 창으로 진입한 다음 df -h 명령어와 du -sh * 명령어로 용량 상태를 보니까 /var/log에 있는 cups 패키지의 에러 로그가 컴퓨터 용량 100%를 잡아먹고 있었습니다. 심지어 에러 로그를 비우고 확인해보니 1초에 100메가씩 쌓였습니다. 처음엔 괜히 cups 패키지도 지웠다가 컴퓨터 먹통 되면 어떡하지 싶었는데 그냥 눈 딱 감고 지웠더니 해결됐습니다. 이렇게 또 하나 배웠습니다ㅠ  다행히 용량이 생기니까 부팅도 정상적으로 됐습니다. 

 

PERMISSION?

이젠 파이썬 버전도 똑같고 더 이상 장애요소도 없으니 되겠지 했는데 이놈의 

ImportError: No module named 'encodings'

에러는 계속 떴습니다. 또 이것저것 찾아보고 Graham Dumbleton 님의 댓글들도 보니, 디렉터리의 Permission도 맞춰줘야 한다더군요. 그래서 처음엔 /home 디렉터리에 있던걸 /var/www 밑으로 프로젝트 폴더를 옮겨보기도 하고 권한을 777(read-write-execute)로 줘보기도 했지만 역시나.. 해결되진 않았습니다.

sudo chmod -R 777 /directory_PATH

위 명령어는 보안상 위험한 명령어지만,,(모두가 읽고 쓰고 실행시키는 것이 가능하기 때문) 어쩔 수 없이 했습니다. 나중에 안 사실이지만 대부분 777 자리에 755만 써도 된다고 합니다. 여기서 간단하게 저 숫자들이 의미하는 것이 무엇인지 소개해 드리자면 read는 4, write는 2, execute는 1이란 숫자로 표현합니다. 그리고 첫 번째 자리는 user, 두 번째 자리는 group, 세 번째 자리는 other의 접근 권한을 의미하는 것이죠. 따라서 777의 7은 4+2+1로 user와 group, other까지 모두에게 읽을 수 있고, 쓸 수도 있으며 실행시키기 까지 가능하게 한다는 의미입니다. 또한 755는 user에게는 모든 권한을 부여하고 group에게는 write(4)+exe(1)만, other에게도 동일하게 권한을 부여한다는 것이죠. 또한 -R은 하위 디렉터리에 모든 권한을 동일하게 부여한다는 뜻입니다.

 

추가로 디렉토리 혹은 파일의 소유자를 정해주는 방법도 있는데 

sudo chown -R user:www-data /directory_PATH

이러면 user 소유권은 user에게, group 소유권은 www-data에게 부여가 됩니다. 여기서 www-data는 우분투의 아파치 실행 권한을 의미합니다. 즉, 아파치 서버에 directory_PATH 소유권을 부여하는 것이죠.

 

 

처음으로 돌아가자

파이썬 버전, 소유권, 퍼미션 다 해줬는데도 왜 에러가 나는 것일까... 이번에는

RuntimeError: populate() isn't reentrant

이런 에러가 발생했습니다. 

reentrant는 재진 입성을 의미하는데 어떤 함수가 재진입이 가능하면 여러 번 호출이 동시에 이뤄져도 되지만 그렇지 않으면 에러를 띄우는 뭐.. 그런 건가 보다 했습니다.

https://stackoverflow.com/questions/27093746/django-stops-working-with-runtimeerror-populate-isnt-reentrant

 

Django stops working with RuntimeError: populate() isn't reentrant

I've been developing a Django web application deployed on an Apache server with WSGI, and everything has been going smoothly. Today, I made some minor changes to my app's admin.py in an attempt to

stackoverflow.com

stackoverflow의 말대로 django/apps/registry.py (이 경로는 에러 로그를 보면 나옵니다) 에서 raise RuntimeError 부분을 self.app_configs = {} 로 바꿨더니 에러를 가르키는 부분이 상세해졌습니다. 에러로그를 따라 에러 지점 부분을 다 확인했는데 App config가 loading이 다 되지 않은 상태에서 populate() 함수가 다시 호출됐고 populate 함수는 재진입이 불가능하기 때문에 에러가 났다...라고 생각이 드는데 저도 이 부분은 확실히 모르겠습니다만, App에 대한 언급이 있는 거 보니 django app에서 문제가 발생하는 건가 싶어 이번에는 settings.py의 INSTALLED APPS 부분에 프로젝트 앱을 지운 다음에 했더니 웬걸 장고 화면이 처음으로 떴습니다. 

 

하지만 App을 지우면 아무 의미가 없어지는 것이므로 이 app을 적는 방법을 찾아봤더니 좀 다르더군요. 제 앱 이름은 FElab_app인데 이를 FElab_app.apps.FelabAppConfig로 바꿔서 써야 했습니다. 

 

DEBUG = False 

앱을 맞게 써줘도 저 장고의 로켓 날아가는 화면이 없어지지 않았습니다.. 불현듯 예전에 봤던 글이 떠올라 settings.py 에 DEBUG = False로 바꿔줘야 한다는 것이 떠올라 바꿔줬더니 이제는 404 Not Found 에러가 뜹니다. (404 에러가 가장 양반입니다..) 우선 DEBUG에 대해 말씀드리면 DEBUG가 True일 때는 장고 내에서 static, media 같은 외부 파일들을 자체적으로 관리하지만 아파치나 NginX와 같이 다른 웹서버를 통해 production 하는 것이라면 DEBUG = false로 해주어 관리를 웹서버에게 넘겨줘야 합니다. 보안상 이렇게 해주는 것이 좋다고 합니다. 

 

404 에러의 원인은 다름 아닌 /etc/apache2/sites-available/000-default.conf 파일에 있었습니다. 우선 terminal에서 프로젝트의 manage.py가 있는 디렉터리로 이동한 다음 

python manage.py collectstatic

이 명령어를 통해 static 파일들을 한데 모아준 다음 이 static 폴더의 경로를 000-default.conf 파일에서 

Alias /static /static_PATH를 통해 지정해주어야 합니다. 그래야 아파치가 아! static 파일이 저기 있구나 하고 찾아서 갈 수 있습니다. 000-default.conf 파일이나 아파치 파일 설정하는 방법은 처음에 소개해드린 블로그에 잘 적혀있습니다. 제 설정 파일은 이렇습니다.

 

/etc/apache2/sites-available/000-default.conf

저처럼 가상 환경 내에서 앱을 실행시키는 것이라면 WSGIDaemonProcess와 WSGIScriptAlias를 사용해야 합니다. 구글에 WSGIPythonPATH 설정하라고 나오는데 그건 가상 환경이 아닐 때 하는 것이고 virtualhost 라면 이 두 가지만 설정해주면 됩니다.

 

 

마무리..

아파치에게 이제 static 파일 관리도 시켰고, 모든 설정이 마무리돼서 성공적으로 Deploy를 했습니다. 처음 하는 거라 꽤 오랜 시간 삽질을 했지만 그래도 하고 나니 배우는 건 많아서 도움이 되는 시간이었습니다. 다음에는 이런 OS에 구애받지 않는 도커를 사용해보는 게 목표입니다.