본문 바로가기

프로그래밍/배포

[배포] 도커에서 uwsgi, nginx연동 확인하기

들어가기 앞서

OS : Ubuntu 16.04 LTS
Django (1.11.3)
Python : 3.6.1
Nginx : 1.10.3(ubuntu)

굉장히 부족하고, 굉장히 저급 수준의 실력을 가진 유저가 쓴 글입니다. 진행하시다가 오류를 발견하시거나, 진행사항에 문제가 있는 경우 적극적으로 알려주세요. 감사합니다.

Nginx - uWSGI - Django 연동하기

본 포스팅은 도커(Docker) 상에서 NginxuWSGI를 연동을 확인하는 포스팅입니다. 도커 스크립트를 통해 컨테이너에는 이미 NginxuWSGI가 설치된 가정하에 글의 순서는 다음과 같습니다.

  1. Web Client - 장고 App

  2. Web Client - uWSGI - 장고 App

  3. Web Client - Nginx - uWSGI - 장고 App

으로 진행합니다.

Nginx, uWSGI란

nginx&uWSGI

Web Client는 웹 브라우저다. 웹 서버에게 HTTP 요청을 보낸다.
Nginx는 웹 서버다. HTTP 요청을 받는다. 그런데 Python 언어를 모른다.
uWSGIHTTP 요청을 Python으로 변환해준다.

컨테이너 연결하기

해당 예제는 도커 스크립트(Dockerfiles)를 이용해 로컬 프로젝트 파일을 컨테이너로 복사하고, 해당 프로젝트를 /srv/(프로젝트 이름)에 위치시키고, 워킹 디렉토리를 프로젝트 경로로 이동한 상태입니다.

도커 이미지 파일 생성후 컨테이너로 들어가기

sudo docker run --rm -it -p [외부포트]:[내부포트] [이미지 태그 이름] /bin/zsh
$ sudo docker run --rm -it -p 9000:8000 eb /bin/zsh

run --rm : 컨테이너 안의 프로세스가 종료되면 컨테이너를 자동으로 삭제(remove)한다.
-it : 컨테이너를 연결해 터미널을 사용하고 싶다면 필수적으로 지정해줘야 한다.

  • -i : 컨테이너가 STDIN을 오픈해서 유지하도록 지정. 이걸 빼놓고 -t만 입력하면, 터미널은 실행되지만 입력이 불가능하다.

  • -t : 컨테이너에 pesudo-tty(터미널)를 할당한다. 이걸 빼놓고 -i만 입력하면 터미널이 열리지 않는다.

-p : 컨테이너의 어떤 네트워크 포트를 오픈할지를 지정한다. 외부포트는 외부에서 컨테이너와 통신할 포트를 의미하며 내부포트는 컨테이너 내부에서 통신할 포트를 의미한다.

만약, 로컬 환경의 웹 브라우저에서 컨테이너의 웹서버로 접속하려고 한다면 외부포트(예제에서 9000번)를 이용해야 한다. 반면에, 컨테이너 안에서 지들끼리(uWSGI, Nginx 등) 통신하고자 할때는 내부포트(예제에서 8000번)를 이용하면 된다.

3-1 Django App Runserver

request
response
Web Client
Django App

아무것도 실행하지 않고, 웹 클라이언트와 Djano가 서버를 담당하는 예제를 보여줍니다.



Runserver (컨테이너 연결 후)
manage.py가 있는 곳으로 이동해 runserver를 실시하자.

$ python manage.py runserver 0:8000 --settings=config.settings.debug

원래 Django에서 런서버 명령어는 python manage.py runserver가 아닌가? debug는 뭐고 deploy는 뭔지 처음에 당황스러웠다. 갑w자기 runserver를 할 때 --settings도 붙이고 여러모로 당황스럽다.

settings 원본
스크린샷, 2017-07-05 01-19-16
보통 프로젝트를 시작하면 settings.py 하나만 던져준다. 하지만 실무에서는 로컬(개발용, 디버그용)배포(서비스용, 릴리즈 버전)로 환경설정을 나눈다. 이것은 상용화되는 모든 소프트웨어의 공통적인 특징이다. 우리가 흔히 하는 게임도 개발용과 배포용으로 나뉜다.

  • 로컬용은 아무래도 오류를 검출하기 위한 환경세팅을 가져간다.

    • 로컬용은 development, debug로 표현하기도 한다.

  • 배포용은 로그 기록이나 오류를 숨겨 사용자 편의성을 도모하고 악의적인 해커로부터 보호하는 환경세팅을 가져간다.

    • 배포용은 deploy, production으로 표현하기도 한다.

settings 모듈화
스크린샷, 2017-07-05 00-57-19
위와 같은 이유로 settings의 설정들을 개발용(debug)용과 배포용(deploy)으로 모듈화(분리)했다. 다만, 모든 환경에서 공통적으로 사용되는 설정들은 base.py에 두었다.

이제 runserver를 하려면 우리는 두 가지 옵션이 생겼다. 배포용런서버를 할 것인지 개발용 런서버를 할 것인지 선택할 수 있게 되었다. 그렇다면 코드는 이렇게 된다.

$ ./manage.py runserver --settings=config.settings.debug # 개발용 
$ ./manage.py runserver --settings=config.settings.deploy # 배포용 

settings.py에 대한 자세한 내용은 여기를 참조하도록 하자. 굉장히 쉽고 자세하게 설명되어있다.

$ python manage.py runserver 0:8000 --settings=config.settings.debug

다만, 우리는 도커의 컨테이너 상에서 로컬용(debug) 런서버를 실행할 것이다. 따라서 우리 웹 클라이언트(크롬)에서 접속하려면 localhost:9000 으로 들어가면 장고가 띄워주는 서버를 만날 수 있다 왜 8000번 포트가 아니라 9000번 포트냐고 묻는다면, 컨테이너를 실행할 때 외부포트를 9000번으로 설정했기 때문이다.

$ sudo docker run --rm -it -p 9000:8000 eb /bin/zsh (도커 컨테이너)

성공화면
스크린샷, 2017-07-05 01-36-25

3-2 uWSGI

nginx&uWSGI
uWSGI는 웹서버(Nginx)가 보낸 요청을 파이썬 언어로 해석하도록 도와준다. 그래서 프로젝트를 시작하면 기본적으로 wsgi.py가 존재한다. 파이썬 언어로 해석해주는 유용한 툴이니, 당연히 Django 프로젝트를 시작하면 wsgi.py 파일을 제공한다.

request
request
response
response
Web Client
uWSGI
Django App

3-1 Django 예제에서는 Django 애플리케이션이 직접 웹 클라이언트 요청을 받는 예제를 보였다.

3-2 uWSGI 파트에서는 웹 클라이언트에서 요청을 uWSGI가 받도록 할 것이다. 3-1 Django에서 설명했듯이, wsgi.py도 개발용(debug)과 배포용(deploy)을 나눠서 설정했다.

wsgi의 분리

스크린샷, 2017-07-05 01-48-42
이제 wsgi 모듈을 이용할 차례다. settings.py를 분리했던 것처럼, wsgi 역시 로컬용과 배포용을 구분했다. 3-1 Django Runserver에서 debug 파일을 사용했듯이, 여기서는 uWSGI용 debug 모듈을 사용할것이다.

1. 풀(Full) 명령어를 입력하는 방법

옵션에 대해서는 여기에 자세히 설명되어 있다.

uwsgi --http :[포트번호] --home [가상환경 디렉토리] --chdir [장고 프로젝트 디렉토리] --module [모듈 이름]
$ uwsgi --http :8000 --home /root/.pyenv/versions/deploy_eb_docker --chdir /srv/deploy_eb_docker/django_app -w config.wsgi.debug

위 명령어를 대충 해석하면 이렇다. 위에서 설명은 안했지만 uwsgi는 가상환경 위에서 돌아가기 때문에, 번역가 역할을 하기 위해서 manage.py와 연결해준다.

  1. --http :8000 : 8000번 포트를 이용해 http 통신을 한다.

  2. --home : 가상환경 디렉토리를 지정한다.

    • 가상환경 위치를 모르겠다면 which uwsgi로 검색해보자.

  3. --chdir : manage.py가 들어있는 장고 프로젝트 디렉토리를 지정한다.

  4. --module : 모듈을 사용한다는 옵션. -w와 같은 의미이다.

    • -w : --wsgi-file의 준말로, 모듈을 사용한다는 옵션

성공화면
스크린샷, 2017-07-05 01-36-25
localhost:9000으로 접속했을 때 이 역시 위와 같은 화면이 떠야 한다. 그리고 새로고침을 누를 때마다 아래와 같이 갱신되어야 한다. req : 3/3을 보면 3번 통신을 시도해서 3번 성공(HTTP/1.1 200)했다는 메시지를 힘겹게 찾아볼 수 있다.
스크린샷, 2017-07-05 02-30-35

2. .ini로 파라미터를 전달하기

Full 명령어를 입력하면 번거롭기 때문에 프로젝트 내에 .ini 파일을 만든뒤 이를 실행시켜서 파라미터를 전달할 수도 있다.

디렉토리 구조
스크린샷, 2017-07-04 16-57-57

uwsgi-app.init 파일 세부 내용
스크린샷, 2017-07-04 16-57-25

uwsgi --ini [모듈 경로]
 
$ uwsgi --ini .config/uwsgi/uwsgi-app.ini
$ uwsgi --http :8000 .config/uwsgi/uwsgi-app.ini # 파라미터에 http가 없는 경우

파일 맨 상단에 보면 http = :8000이 있는데, 이는 곧 uWSGI가 직접 8000번 통신을 한다는 이야기다. 만약, 이 http가 없다면, 명시적으로 --http :8000을 써줘야 한다.

그러나, 3-3 Nginx 부터는 웹 요청을 Nginx가 담당하도록 할 것이기 때문에 .ini 파일에서 http 파라미터를 지우도록 하자. (이때 다시 도커를 build해줘야 한다.)

3-3 Nginx 실행하기

nginx&uWSGI
스크린샷, 2017-07-03 12-54-13

request
request
request
request
response
response
response
response
Web Client
Nginx
socket
uWSGI
Django App

드디어 웹서버인 Nginx까지 왔다. 3-3 Nginx의 연동을 테스트하기 위해서는 추가적인 설명이 더 필요하다. 소켓(socket)의 개념이 추가될뿐만 아니라, uWSGI를 가동시킨 상태로 웹 서버도 가동시켜야 하기 때문에 새로운 명령어도 하나 배워야 한다. 이 부분에 대해서는 하단에 넣고, 먼저 실행되는 것을 살펴보도록 하자.

uWSGI(컨테이너) 먼저 실행

1. 컨테이너 실행
  $ sudo docker run --rm -it -p 9000:8000 [이미지 태그명] /bin/zsh
 
2. uWSGI 실행(프로젝트 앱에서)
  $ ./manage.py

nginx 실행(컨테이너)

실행 중인 컨테이너 쉘 하나 더열기
$ sudo docker exec -it [컨테이너 id] [/bin/bash]

3-3-1 소켓

NginxuWSGI의 접점을 소켓이라고 일단은 이해하자. 그러면 NginxuWSGI 모두 소켓에 관한 설정이 어딘가 들어있음을 유추할 수 있다.

uWSGI.ini
스크린샷, 2017-07-05 02-48-51
이때, 소켓파일의 권한을 nginx가 사용할 수 있도록 소켓을 666(읽고, 쓰기) 권한으로 바꿔주는 것을 확인할 수 있다.

nginx.conf
스크린샷, 2017-07-05 02-48-32

uWSGI에도, nginx에도 tmp/eb.sock이라는 걸 찾아볼 수 있다.

3-3-2 새로운 도커 명령어의 등장

실행 중인 컨테이너에 명령 넣기

$ sudo docker exec [컨테이너 id] [실행할 명령]

실행 중인 컨테이너 쉘 하나 더열기

$ sudk docker exec -it [컨테이너 id] [/bin/bash]

이미 실행되고 있는 컨테이너 안에 명령을 넣고 싶을 때 사용한다. uWSGI를 가동시킨 상태로 Nginx의 연동 테스트를 실시해야 한다. 즉, 쉘을 두 개 켜야 테스트가 가능하다.

명령어를 보면 컨테이너 ID가 있으니까 이 컨테이너에 명령을 넣는다는 의미이다. 컨테이너의 ID를 얻으려면 sudo docker ps 명령어를 통해 얻어내야 한다. 여기서는 아이디를 통해 컨테이너로 들어가기 때문에 이미지 태그가 필요 없다.

출처

실행중 발생했던 에러

&#x#xC2A4;크린샷, 2017-07-04 15-07-32

포트가 이미 할당되어있는 문제
sudo docker ps로 실행중인 컨테이너 확인

스크린샷, 2017-07-04 15-08-51
현재 실행중인 컨테이너를 중단시켜야 함
sudo docker stop [Container ID] 입력하면 수초 후에 중단됨.
다시 접속하면 잘된다.

참고한 글

  1. 가장 빨리 만나는 도커 http://pyrasis.com/book/DockerForTheReallyImpatient/Chapter20/28

  2. docker run과 관련된 옵션들 http://pyrasis.com/book/DockerForTheReallyImpatient/Chapter20/28

  3. settings.py를 분리해야하는가? https://cjh5414.github.io/django-settings-separate/

    • 굉장히 쉽고, 자세하게 설명되어있다.

  4. Nginx와 uWSGI 연동 https://wikidocs.net/7387

    • Nginx보다는 uWSGI의 구동테스트 옵션에 관한 내용들을 배웠다.


  • 초보자 2018.12.06 10:26

    안녕하세요 좋은 내용의 글 잘 읽었습니다.
    한가지 궁금한점이 있어서 그런데 맨 처음 이미지 빌드할때 뭐로 빌드하였는지 알 수 있을까요?
    처음에 무엇으로 베이스를 깔아야하는지 잘 모르겠어서 그렇습니다. ㅠ