Django:제로부터 시작하는 인스타그램 만들기 - DevOps #2 - AWS EC2에 nginx, uwsgi 사용하여 서비스 하기.

     

2021.09.15 - [Course/django : 제로부터 시작하는 인스타그램] - Django:제로부터 시작하는 인스타그램 만들기 - clone instagram 목차

 

 

 

 

시작하기 전에

지난 포스팅에서는 내 컴퓨터에서만 실행하던 django 프로젝트를 AWS에 EC2 위에 올렸습니다. 소스를 github을 이용해서 이동시키고, 서버에 설치된 파이썬을 이용해 python manage.py runserver를 실행했고, 외부에서 접속을 위해 방화벽 설정 및 환경설정을 건드렸습니다. 이걸로 외부에서 여러분이 만든 인스타그램(혹은 다른 django 프로젝트)이 외부에서 접속할 수 있게 설정되었습니다.

 

하지만 이전 포스팅에서 말했듯이 runserver라는 명령어는 개발자가 개발할 때 디버깅 해보기 위해 제공하는 django의 서비스 중에 하나입니다. 즉, 실제 django로 만들어진 서비스들은 runserver라는 명령어로 실행 중이지 않습니다. 여러분이 익히 들어본 아파치 톰캣, nginx가 바로 django로 만들 프로젝트를 웹서비스로 만들어주는 녀석들인데요, 이런 녀석들을 '웹서버'라고 부릅니다. 

 

이번 포스팅에서는 웹서버중에 하나인 nginx를 설치해서 우리가 만든 인스타그램을 서비스해 보겠습니다. 웹서버에 대한 자세한 설명은 아래 포스팅으로 대체하겠습니다.

서버개발자가 되는법 [2] - 서버 개발환경 셋팅, Nginx와 uWSGI 설치해서 연결하기 + docker로 mysql 띄우기 (tistory.com)

 

 

uwsgi 설치

django로 만든 인스타그램과 nginx를 연결시키려면 중간에 인터페이스가 하나 필요합니다. django에서 웹서버와 인터페이스를 해주는 패키지가 여러 개 존재하는데요, 대표적으로는 gunicon과 uwsgi가 있습니다. uwsgi가 좀 더 대중적으로 사용되고 있는데 성능은 gunicon이 더 좋다는 평가도 있습니다. 여기서는 uwsgi를 사용하도록 하겠습니다.

 

uwsgi는 위에 링크된 블로그 글을 보셨다면 아시겠지만 Web Server Gateway Interface의 약자인 WSGI 앞에 u를 붙인 패키지 이름입니다. u는 왜 붙었는지 모르겠네요 ㅋㅋ 

 

일단 장고에 설치되는 패키지이기 때문에 pip로 설치할 수 있습니다. 지난 시간에 만들었던 EC2에 putty를 이용해서 접속한 다음 pip3 install uwsgi를 해봅시다.

 

pip3 install uwsgi

뭔가 설치되네요 ㅎㅎ 이게 설치가 생각보다 오래 걸립니다. 서버가 꾸져서 그런 걸 수 있어요 ㅠ

 

설치가 완료되었으면 uwsgi를 입력해 봅니다. 

uwsgi 입력

 

입력하면 뭐라 뭐라 뜨는데 단순히 uwsgi만 입력해서는 실행이 되지 않습니다. uwsgi를 실행하면서 뭔가 우리가 만든 장고 프로젝트를 연결시켜 줘야 동작을 하게 되는데요. 일단 프로젝트가 설치된 폴더로 이동합니다.

 

프로젝트 폴더로 이동

 

그럼 이제 장고 프로젝트 이름이랑 똑같은 폴더가 있을 텐데요 (혹은 configs라는 폴더)

그 안에 wsgi.py라는 파일이 있을 겁니다. (아니면 이 파일이 있는 폴더이름을 찾으세요~!)

 

wsgi.py 파일 찾기

요 파일이 있는 폴더를 uwsgi에 연결시켜 실행하면 되는데, 다시 프로젝트 폴더로 이동한다음 아래와 같이 입력합니다.

uwsgi --http :8000 --module Jinstagram.wsgi​

 

그럼 아까와 다르게 뜨는 것을 확인할 수 있습니다.

 

uwsgi 실행

 

이제 uwsgi를 통해 우리는 장고 어플리케이션을 실행했습니다. runserver를 하지 않고 uwsgi를 통해 실행한 것이죠. 브라우저를 이용해 접속해서 확인해 봅시다.

 

돼쥬?

잘되는 것을 확인할 수 있죠? 

 

 

 

nginx 설치

uwsgi를 설치해서 실행을 했지만 끝이 아닙니다. 사실 우리는 nginx를 설치하기 위해 uwsgi를 설치한 거예요. nginx는 많이 들어보셨다시피 웹서버입니다. 웹서버의 역할은 처음에 링크한 포스팅에 있으니 참고하세요~!

 

uwsgi는 pip를 이용해서 설치했는데 nginx는 아닙니다. uwsgi는 python 프레임워크를 위한 인터페이스이고 nginx는 더 광범위한 솔루션입니다. 따라서 설치하는 법이 엄청 많은데, 설치파일을 다운받아도 되고 패키지 관리자(apt, brew 등)를 이용하셔도 됩니다. 여기서는 apt를 이용해서 설치해 보도록 하겠습니다~! 

 

sudo apt-get install nginx

 

우분투에서는 apt-get을 이용해 패키지를 다운 받을 수 있습니다. sudo는 관리자 권한으로 실행한다는 의미이고요. 

 

명령어를 실행하면 설치할 것인지 묻습니다.

 

정말 설치하시겠습니까?

상콤하게 y를 눌러주면 uwsgi 설치할 때와 비슷하게 진행되면서 nginx가 설치됩니다.

 

설치가 완료되면 nginx -v를 입력해 봐서 정말 설치가 되었는지 확인합니다.

 

nginx -v

 

 

uwsgi 설정

 

uwsgi와 nginx 설정이 끝났습니다. 이제 nginx -> uwsgi -> django로 통신이 되게끔 설정을 해주면 되는데요, 일단 uwsgi 설정을 위해 파일을 하나 만들어보도록 하겠습니다. 프로젝트 폴더에 uwsgi.ini파일을 추가합니다.

 

uwsgi.ini파일 추가

 

여기에는 옵션을 이것저것 넣을 수 있는데 제일 중요한 것은 socket입니다. 위에서 uwsgi를 실행할 때는 http를 사용해서 포트번호를 입력해서 실행했는데, 여기서는 socket이라는 놈을 통해서 실행할 예정입니다. 즉 이전에는 uwsgi안으로 들어오기 위해서 http프로토콜로 특정 포트로 들어왔어야 하는데 지금은 uwsgi.sock이라는 파일을 통해서 통신을 하게 됩니다. http는 네트워크 프로토콜이기 대문에 네트워크영역을 한번 거쳐서 통신이 되지만, 파일을 이용한 소켓통신은 네트워크영역까지 가지 않기 때문에 속도가 더 빠르게 됩니다. 물론 이게 가능한 이유는 nginx와 uwsgi가 하나의 물리적 서버에서 동작하기 때문입니다. 만약 uwsgi가 실행되는 서버와 nginx가 실행되는 서버가 물리적으로 다르다면 이 방법은 사용할 수 없습니다.

 

그 밖에 옵션들은 거의 디폴트인데 아래 공식 문서를 보시면 쉽게 확인이 가능합니다.

Configuring uWSGI — uWSGI 2.0 documentation (uwsgi-docs.readthedocs.io)

 

processes와 threads는 서버의 물리적인 스펙에 따라 결정되는데, 저희는 AWS에서 가장 안 좋은 서버이기 때문에 낮게 설정합니다. ㅎㅎ 나중에 좋은 서버에서 할 때는 많이 올려야 해요. module은 위에서 uwsgi를 실행할 때 사용했던 Jinstagram.wsgi를 입력합니다. 그 아래 옵션들은 사실 안 써도 되는 옵션입니다 :)

 

파일을 추가했으면 git에 push 해서 github에 올리고, ec2서버에서 git pull을 해서 uwsgi.ini파일을 서버에 배포합니다.

 

서버에 올라온 uwsgi.ini

 

이제 이 ini설정파일을 가지고 uwsgi를 실행해 봅니다.

 

# 절대 경로로 실행
uwsgi -i ~/django_zero_to_instagram_devops/uwsgi.ini

# 프로젝트 폴더로 이동해서 상대 경로 실행
uwsgi -i uwsgi.ini

 

실행한 모습

실행하면 위와 같이 어쩌구 저쩌구가 뜨면서 uwsgi가 실행됩니다. 이전에는 브라우저에서 접속해서 확인할 수 있었지만, 이번엔 소켓으로 실행했기 때문에 외부에서 실행할 수가 없습니다 ㅎㅎ 

 

이제 이 uwsgi가 백그라운드에서 항상 실행되게 설정합니다. 요건 리눅스의 systemctl 기능을 이용할 건데요, 리눅스에서 시스템 데몬들을 관리하는 기능입니다. 요기에 uwsgi를 등록해서 항상 실행되도록 만드는 거죠. 윈도에서 작업관리자 같은 거라고 생각하면 편합니다. ㅎㅎ

 

일단 /etc/systemd/system 경로로 이동합니다. 

 

cd /lib/systemd/system

 

해당 경로로 가면 여러 파일이 보일 텐데 ***.service라고 되어있는 애들도 발견할 수 있습니다.

*.service들이 많다

요런 애들처럼 uwsgi.service를 만들어서 systemctl에 등록하면 됩니다. 일단 service파일을 생성합니다.

 

sudo vi uwsgi.service

 

sudo 권한은 필수입니다.

 

[Unit]
Description=Django Zero uWSGI service
After=syslog.target

[Service]
ExecStart=/home/ubuntu/.local/bin/uwsgi -i /home/ubuntu/django_zero_to_instagram_devops/uwsgi.ini

Restart=always
KillSignal=SIGNOUT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

 

service 생성 완료

 

여기서 제일 중요한 건 [Service] 부분입니다.

ExecStart가 서비스가 실행될 때 실행하는 명령어인데, uwsgi를 절대경로로 입력해줘야 합니다. uwsgi가 어디 있는지 찾기 위해서는 터미널에서 아래 명령어를 치면 됩니다. 

 

find / -name uwsgi 2>&1 | grep -v Permission

파일이 어디있는지 나옴

 

결과를 보니 nginx경로에 있는 건 뭔가 nginx에서 사용하는 거 같으니 아래있는 .local/bin/uwsgi를 지정합니다. 

 

Restart=always의 경우 서버가 재실행돼도 자동으로 해당 서비스가 시작되는 옵션으로 넣어주면 ec2를 죽였다 살려도 실행됩니다. 

 

상세 옵션에 대한 설명은 아래 manpage를 참고하세요.

Ubuntu Manpage: systemd.service - Service unit configuration

 

Ubuntu Manpage: systemd.service - Service unit configuration

Powered by the Ubuntu Manpage Repository, file bugs in Launchpad © 2019 Canonical Ltd. Ubuntu and Canonical are registered trademarks of Canonical Ltd.

manpages.ubuntu.com

 

요롷게 uwsgi.service를 생성했다면, systemctl에 등록하면 됩니다. 그전에! 우리가 lib/systemd/system경로에 만들어줬는데, 실제로 실행할 때는 /etc/systemd/system에 등록하고 실행하게 됩니다. 

 

cd /etc/systemd/system

해당 경로에 가면 마찬가지로 *.service파일이 몇 개 보이는데 자세히 보면 심볼릭 링크로 걸려있습니다. 

 

링크가 걸려있다.

여기서 심볼릭 링크는 윈도우로 치면 바로가기라고 생각하시면 됩니다. 실제 파일은 /lib/*에 있는데 요기에 바로가기를 생성해서 사용하는 거죠. /etc/*경로가 뭔가 관리자가 사용하는 영역이라서 여기서 하나 봅니다. /lib/*은 사용자가 사용하는 공간 느낌? ㅎㅎ 저도 궁금해서 찾아봤는데 아래 페이지를 한번 가보세요.

 

debian - What's the difference between /usr/lib/systemd/system and /etc/systemd/system? - Unix & Linux Stack Exchange

 

자 이제 여기에 링크(바로가기)를 생성하면 되는데 생성하는 방법은 ln이란 명령어를 이용합니다. 

 

 

sudo ln -s {원본파일경로} {링크할파일경로}

sudo ln -s /lib/systemd/system/uwsgi.service uwsgi.service

위 명령어를 입력하면 짠~하고 파일이 생깁니다.

쉽쥬?

 

이제 드디어 등록을 하면 되는데요, 등록은 아주 간단합니다.

 

sudo systemctl daemon-reload
sudo systemctl eanable uwsgi
sudo systemctl restart uwsgi

쭈르르륵

systemctl daemon-reload를 하면 /lib/systemd/system경로에 있는 service들을 갱신합니다. 그리고 uwsgi를 사용하겠다고 등록(enable)하면 끝. restart uwsgi를 하게 되면 uwsgi가 실행된 걸 확인할 수 있습니다.

 

확인

systyemctl을 입력해서 구동 중인 데몬으로 확인해도 되고, ps grep을 이용해서 프로세스로 확인해도 됩니다. 

 

만약 uwsgi가 제대로 실행 중이라면 우리가 정의한 소켓파일 역시 생성되었을 텐데요, 홈경로에 가보면 아래 파일을 볼 수 있습니다.

 

uwsgi.sock 확인

이제 nginx가 저 소켓을 물고 실행하면 완료입니다.

 

 

nginx 설정

nginx도 마찬가지로 설정파일을 만들어야 합니다. uwsgi와 다른 점이라면, nginx는 설치하자마자 systemctl 데몬에 등록되어 실행되고 있다는 점이죠. 

 

nginx 짠

 

 

nginx설정은 /etc/nginx 폴더에 가면 됩니다. 우리가 설정 파일을 만드는 곳은 바로 /etc/nginx/sites-available 경로입니다.

 

cd /etc/nginx/sites-available
sudo vi django_zero
upstream django {
    server unix:///home/ubuntu/uwsgi.sock;
}

server {
    listen      80;
    server_name localhost;
    charset     utf-8;

    client_max_body_size 80M;

    location / {
        uwsgi_pass django;
        include    /etc/nginx/uwsgi_params;
    }
}

 

위에처럼 django_zero라는 설정파일을 만들어줍니다. 해당값에 대한 설명은 아래 블로그를 보시면 알 수 있습니다.

 

서버개발자가 되는법 [2] - 서버 개발환경 셋팅, Nginx와 uWSGI 설치해서 연결하기 + docker로 mysql 띄우기 (tistory.com)

 

생성완료

 

sites-available에 파일이 두 개인데, default는 지웁니다. 파일을 열어보시면 listen이 80이라서 저희가 만든 것과 겹칩니다 ㅎㅎ 

 

sudo rm -f default

 

자 이제 준비 끝입니다. 떠있는 nginx.service를 재실행하면 우리가 설정한 django_zero설정을 적용한 nginx가 실행됩니다.

 

에러!

역시 호락호락하지 않네요. 바로 에러 발생!!

 

에러 메시지에 찍힌 것처럼 systemctl status nginx.service를 해봅시다.

에러 메시지

에러 로그가 찍히는데, 딱 보니 default 파일이 없다고 에러가 나네요. 괜히 지웠나... ㅎㅎ 

일단 nginx가 default를 잊지 못하는 것 같으니 우리가 만든 django_zero를 default라고 바꿔줍니다.

이름 변경

이름 변경은 sudo mv {기존파일} {변경파일} 하시면 됩니다.

 

이제 다시 nginx를 재실행해볼까요?

 

sudo systemctl restart nginx

 

에러가 안 나네요!

 

자 그럼 한번,, 브라우저에서 접속해 봅시다.

 

 

에러

 

음.. 에러가 나네요.. ㅋㅋㅋ 

저희가 nginx를 사용하면서 포트가 80으로 바뀌었을 텐데.. 방화벽을 안 뚫어놓은 느낌입니다.

 

aws 콘솔 네트워크 인바운드 규칙

 

역시나 8000 포트만 뚫려있네요. 80 포트도 추가해 줍시다.

 

http 포트 추가

자 이제 다시 접속해 볼까요??

 

빠밤!

 

이제 500 에러가 나는군요 ㅋㅋ 접속은 성공했습니다만 서버 안에서 에러가 나고 있습니다. 이건 nginx 문제일 수도 있고 uwsgi 문제일 수도 있습니다. 먼저 nginx 로그를 좀 볼까요? nginx로그 경로는 /var/log/nginx입니다.

 

nginx로그

보통 에러가 나면 error.log가 생기는데 안생 긴 거 보니 nginx 쪽 오류는 아닌 것처럼 보이네요. 일단 access.log를 봅니다.

 

500 에러

500 에러가 찍히긴 했네요. 흠 근데 왜 발생한 건지는 모르겠네요. uwsgi 쪽으로 통신을 못한 것 같기도 하고요. 

 

그럼 uwsgi 로그를 봐야 하는데, 로그를 생성하는 설정을 하지 않았었습니다. 다시 uwsgi.ini파일을 열어서 아래 한 줄을 추가합니다.

 

[uwsgi]
socket = /home/ubuntu/uwsgi.sock
master = true
processes = 1
threads = 2
module = Jinstagram.wsgi
chmod-socket = 666
vacuum = true
die-on-term = true

logger = file:/tmp/uwsgi.log

 

그리고 systemctl restart uwsgi를 해줍니다. 

 

로그파일이 생김

 

자 이제 로그가 생기니까, 다시 한번 브라우저에 접속해 줍니다.

이제 로그를 열어보는데, sudo권한으로 열어야 합니다. sudo vi uwsgi.log를 해줍니다.

 

로그가 찍힘

여기 로그가 찍힌 걸 보니 nginx->uwsgi까지는 통신이 완료입니다. 그런데 no python application이라고 뜨는군요. 요건 uwsgi에서 python애플리케이션을 못 찾은 건데,, uwsgi.ini파일에서 module을 잘못입력한 것 같습니다.

 

아주 힘겨운 트러블슈팅 끝에 uwsgi.ini를 아래와 같이 바꾸니 실행이 되는군요

 

[uwsgi]
socket = /home/ubuntu/uwsgi.sock
master = true
processes = 1
threads = 2
chdir = /home/ubuntu/django_zero_to_instagram_devops
wsgi-file = Jinstagram/wsgi.py
chmod-socket = 666
vacuum = true
die-on-term = true

logger = file:/tmp/uwsgi.log
pythonpath = /home/ubuntu/.local/lib/python3.8/site-packages

 

chdir의 경우 프로젝트의 경로, module대신 wsgi-file경로를 적었습니다. 그리고 가장 핵심은 pythonpath를 추가한 건데, 아마 따로 설치한 python3의 경로를 못 찾나 봅니다. 참 특이하쥬.. 아무튼 django가 깔린 python의 site-packages경로를 설정해 주면 되는데, 아까와 같이 grep을 이용해 경로를 찾아줍니다.

 

find / -name site-packages 2>&1 | grep -v Permission

저는 /home/ubuntu/.local/lib/python3.8/site-packages 요기에 설치되어 있었고, 실제로 경로에 가보니 장고가 깔려있었습니다.

 

장고 설치

가상환경을 사용하면 ini설정에 home경로로 venv경로를 설정해 주면 돼서 더 간단하게 진행했을 텐데 말이죠. 

 

아무튼 ini를 바꾸고 uwsgi를 재실행합니다.

 

접속 완료
uwsgi 로그, 에러 없쥬

 

완료~!

 

 

마치며

이번 포스팅에서는 서버에 nginx와 uwsgi를 설치해서 django를 웹서버를 통해 서비스하는 방법을 다루었습니다. 이제 서버가 죽었다 살아나도 계속 재실행되는 어플리케이션을 만들었습니다. 

 

다음시간에는 DB를 연동해 볼 건데, 현재 sqlite3으로 파일형태로 관리되는 DB를 사용하고 있지만, docker를 이용한 DB연결, aws에 rds를 활용한 db연결 두 가지를 진행해 보겠습니다. 추가로 도메인 서비스도 한번 사용해 보면 좋겠네요. 

 

그럼 다음 포스팅에서 뵙겠습니다 :)

 

 

 

 

 

반응형

댓글

Designed by JB FACTORY