본문 바로가기

시행착오 노트

django 웹사이트 배포를 하며 겪은 시행착오

Django로 웹 홈페이지를 만들고, 이를 웹에 배포하는 와중에 겪은 시행착오를 적어본다.

글을 쓰기 전에 이미 준비된 상황은 다음과 같았다.

  • 배포할 웹 페이지가 완성돼있음.
  • local과 amazon ec2 에서 각각 django 가 manage.py runserver 로 잘 돌아가는 것을 확인함.
  • python manage.py collectstatic 을 통해 static file을 따로 두었다.
  • aws elastic IP 설정으로, 해당 ec2에 대한 고정 ip를 연결하였다.
  • 도메인 업체 whois에서 사용할 도메인을 구입해두었다.

이후 내가 해야할 상황은 다음과 같았다.

  1. ec2 콘솔을 끄더라도, django server는 계속해서 run 중이게 해야함.
    (기본적으로 ec2 연결을 끊으면, ec2 instance 는 run 중이지만, 실행 중이던 프로세스는 종료되기 때문.)
  2. django 기본 포트인 8000번 포트가 아니라, http 기본 포트인 80 기본 포트로 웹페이지를 볼 수 있어야 함.
    즉, http://13.125.138.52:8000 이 아니라, http://13.125.138.52 로 들어올 수 있어야함.
  3. 구매한 도메인을 http://13.125.138.52 에 연동해야함.

위 해결과정을 하나씩 적어보도록 하겠다. 작업환경은 ec2에서 프리티어로 주는 ubuntu 서버다.


1. Screen 을 이용한 서버 run 유지.

screen 은 서버 연결을 꺼도, 실행 중이던 프로그램을 그대로 유지시키는 프로그램이다. 어렵게는 ''물리적인 터미널을 다중화하여 사용할 수 있는 윈도 매니저" 라고 부르나 보다. 아무튼 나한테는 서버에 ssh 연결을 한 putty 를 꺼도 django 가 돌아갈 수 있게 하는 도구다.

사용법은 매우매우 쉽다. 먼저 설치는 다음과 같이 하면 된다.

$ sudo apt-get install screen

설치가 되면 이제 스크린을 하나 만들어보자. 스크린 이름은 ypc django 로 주겠다.

# screen 생성
$ screen -S ypc django

이러고 뭐라 블라블라 뜨는데 그냥 엔터누르면 된다. 이제 현재 프로세스들을 유지시킬 수 있는 하나의 스크린을 만든 것이다. 한번 확인해보자.

# 현재 생성된 screen list 조회
$ screen -ls

There is a screen on:
        8134.ypc django (09/20/2018 05:10:13 AM)        (Attached)

보면, 8134라는 아이디로 ypc django 라는 스크린이 생성된 것을 볼 수 있다.

Attached 는 현재 저 스크린으로 들어온 상태라는 것이다.

ctrl + a + d 를 눌르면 해당 스크린을 빠져나올 수 있다. 누른 후 다시 스크린 리스트를 조회하면,

# 현재 생성된 screen list 조회
$ screen -ls

There is a screen on:
        8134.ypc django (09/20/2018 05:10:13 AM)        (Dettached)

AttachDettached 로 바뀐 것을 볼 수 있다. 해당 스크린을 빠져나온 것이다.

이제, 저 스크린에 다시 들어가, 우리의 django 서버를 돌리고 빠져나오자. 그러면 서버 연결을 끊어도 해당 스크린에서는 여전히 서버가 돌아가고 있을 것이다.

# 8134 id를 가진 screen으로 다시 attach 한다.
$ screen -r 8134

# 8134.ypc django screen 으로 다시 들어왔다.
$ python3 manage.py runserver

System check identified no issues (0 silenced).
September 30, 2018 - 21:50:28
Django version 2.1.1, using settings 'ypc.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

브라우저에서 ec2 public ip를 통해 들어가보자. 이를테면, http://123.123.123.123:8000 식으로 접속하면 된다.

근데 안된다. 문제를 검색해보니 127.0.0.1 은 localhost 주소이고, 이는 서버 내부에서만 접근할 수 있는 주소라고 한다. 난 당연히 localhost 를 서버에서 띄워놓으면, 서버로 들어오기전 웹서버가 다 처리해서 보내주는 줄 알았는데, 이게 아니였나보다. 127.0.0.1 이 아니라 0.0.0.0 으로 설정해야 어디서든 들어올 수 있다고 한다. runserver를 종료시키고 다시 다음과 같이 입력한다.

$ python3 manage.py runserver 0:8000

System check identified no issues (0 silenced).
September 30, 2018 - 21:50:28
Django version 2.1.1, using settings 'ypc.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

다시 http://123.123.123.123:8000 로 접속하면, 잘 된다. 다시 putty 로 돌아가, ctrl + a + d 를 눌러 django 를 실행시킨 상태에서 빠져나오자. 그리고 putty를 꺼서 서버와 연결을 끊어보자.

이 상태에서 http://123.123.123.123:8000 로 접속하면 여전히 잘 뜬다. 첫 번째 해결해야할 것을 끝냈다.

스크린 사용법 : https://medium.com/@erwinousy/screen-command-%EC%82%AC%EC%9A%A9%EB%B2%95-linux-mac-62bf5dd23110

+. Gunicorn 을 이용하여 서버 run시키기.

2번 문제를 해결하기 전에, 하나 더 추가할 것이 있다. 기본적으로 django 는 manage.py runserver 를 통해 앱서버를 동작시킬 수 있다. 그런데, 이 서버는 local 에서 test server 용이지, 배포용은 아니라고 한다. 즉 django 내장 server 를 사용해서 배포하지 말라는 것이다. 이는 django 공식 doc 에도 적혀있다.

DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through security audits or performance tests. (And that’s how it’s gonna stay.
from manage.py documentation

django 는 단일 쓰레드로, request가 많이 들어올 때 현저히 느려지고, 전반적인 퍼포먼스가 낮다는게 대략 이유다.

그럼 이제 어떻게하느냐? manage.py runserver 대신 서버를 실행시켜줄 것을 찾아야한다.

1. WSGI (Web Server Gateway Interface)

기본적으로 서버는 크게, 웹서버와 앱서버가 있다. nginx 나 apache 같이, 서버로 들어오는 http request 를 앞단 에서 가장 먼저 처리하는 것이 웹서버 다. 그리고 django 내장 서버와 같이 어플리케이션을 위한 서버가 앱서버다. 클라이언트(예를 들어 브라우저)에서 서버 80포트로 http request 가 오면, 웹서버는 이를 해석하여, 적당한 포트로 요청을 보내, 의도한 특정 앱서버에다가 요청을 전달해준다. 이 때, 웹서버와 앱서버간의 요청에는 http와 같은 일종의 인터페이스가 필요한데, python 에서는 wsgi 라는 인터페이스를 사용한다고 한다. 보다 정확하고 자세한 내용은 아래 링크를 참조하길 바란다.

WSGI 란 무엇인가? : http://paphopu.tistory.com/37
WSGI, WAS, CGI 이해 : http://brownbears.tistory.com/350

2. Gunicorn

gunicorn은 manage.py runsever 를 대신하여 실행할 wsgi 서버다. (gunicorn 외에도 uwsgi 등 다른 wsgi 서버도 있는데, 이게 제일 실행시키가 쉬워보여서 써본다.)

기존 django 서버는 단일 프로세스, 쓰레드인데, gunicorn은 이를 여러개로 만들어준다. 즉 request 가 많이와도 이전보다 더 효율적으로 처리할 수 있도록 한 것이다.

설치와 실행방법은 간단하다.

$ sudo apt-get install gunicorn

설치가 완료되면 해당 스크린에서 manage.py runsever 대신 다음과 같이 서버를 실행하자.

$ gunicorn ypc.wsgi:application -- bind 0:8000

[2018-09-30 13:31:36 +0000] [10836] [INFO] Starting gunicorn 19.9.0
[2018-09-30 13:31:36 +0000] [10836] [INFO] Listening at: http://127.0.0.1:8000 (10836)
[2018-09-30 13:31:36 +0000] [10836] [INFO] Using worker: sync
[2018-09-30 13:31:36 +0000] [10839] [INFO] Booting worker with pid: 10839

그리고 다시 스크린을 빠져나오면 된다. 끝!


2. Nginx 를 이용한 포트 설정

Nginx 는 서버 앞단에서 request를 가장 먼저 처리하는 웹 서버다. 비슷한 다른 소프트웨어론 apache가 있다. Nginx 를 이용하여 기본 포트인 80로 들어와도 우리의 웹사이트를 볼 수 있도록 해보자.
먼저 nginx를 설치해준다.

$ sudo apt-get install nginx

nginx 는 /etc/nginx 에 설치된다. /etc/nginx/nginx.conf 에 기본 설정 정보가 담겨있는데, 잘 보면

http {
    ...
    include /etc/nginx/site-enable/*;
    ...
}

와 같은 코드가 보인다. 즉 우리는 /etc/nginx/sites-enable/ 에서 추가적인 설정 파일을 만들면, nginx.conf 에서 이 파일을 가져와 쓴다는 말인 것이다.

/etc/nginx/ 내부 디렉토리를 잘 보면 아래와 같은 두개의 디렉토리가 보인다. 역할은 다음과 같다.

  • /etc/nginx/sites-available
    사용 예정인 사이트 및 서버에 대한 설정파일을 보관한다. 다시 말하지만, nginx 은 웹서버 최 앞단에서, request를 어떤 앱서버로 보낼지 정해주는 역할을 한다. 여기서 어디로 보낼지 등에 대한 설정파일을 여기에 담는 것이다. 하지만 이는 '사용 예정' 에 불과하다.

  • /etc/nginx/sites-enable
    실제로 설정파일에 적용되는 파일은 이 디렉토리에 들어간다. 여기다가 직접 설정파일을 넣어도 되지만, 대게 ln -s 명령어(파일 연결, 일종의 바로가기와 같은 기능)를 이용하여 /sites-available/ 에 있는 설정 파일에 연결한다. 예를 들면 이런식이다.

    $ ln -s /etc/nginx/sites-available/ypc /etc/nginx/sites-enable/ypc

    이렇게 /sites-available/ 에 있는 설정파일을 /sites-enable/로 가져다 둔다.

    이렇게 하는 이유는, site-available에 가능한 사이트를 모두 두고, 실제로 필요에 따라서 ln-s 으로 연결하면 되기 때문이다. 만약 잠깐 특정 설정파일을 안쓰겠다고 하면, site-enable에서만 삭제하면 된다. 나중에 다시 필요하면, 위와 같이 다시 연결하기만 하면 된다. 실제 설정 파일을 안지우고, 연결 혹은 연결해제로 간편하게 설정할 수 있다.

나는 처음에는 /sites-available/ 에 파일을 담아놓다가, 안되서 직접 /sites-enable/에 실제 설정파일을 두었다. ln -s 명령어로 해도 안되는 사람은 이렇게라도 해보길 바란다.

그럼 이제 실제 설정파일을 작성해보자. /etc/nginx/sites-enable/ 에서 django-webiste 파일을 만들어서 다음과 같이 작성하자.

server {
        listen 80;
        server_name 123.123.123.123;

        location / {
                proxy_pass http://127.0.0.1:8000/;
        }
        location /static/ {
               alias /home/ubuntu/django-website/ypc/staticfiles;
       }
}

위 코드는 nginx 문법으로 이루어 져있다.

  • 먼저server {...} 는 nginx 문법 중, server block 이라는 것인데, 우리는 지금 하나의 앱서버로 연결하려고 하는 것이니, 하나의 server block이 필요하다.

  • listen 80 은 80 port로 들어오는 요청에 대해서 처리하겠다는 말이다.

  • server_name 은 http reqeust 내 header에 담긴 server_name 값이다.

    예를들어, 브라우저에서 123.123.123.123 을 주소창에다 입력하면, 해당 http request의 server_name 값에 123.123.123.123 값이 들어오게 된다. 이러한 요청을 처리하겠다는 것이다.

  • location / {...} 은 해당 80 port와 server_name, 그리고 / url로 요청을 어떻게 처리할 것인지에 대해서 적는다. proxy_pass http://127.0.0.1:8000는 해당 request 를 127.0.0.1:8000 으로 보내겠다는 말이다.

    이제123.123.123.123 으로 오는 request 는 123.123.123.123:8000 으로 보내지게 된다.

  • location /static/ {...} 도 역시 위와 마찬가지다. 123.123.123.12/static/ 으로 들어오는 request 는 /home/ubuntu/django-website/ypc/staticfiles 을 참고하게 된다. 참고로 저 폴더에는 django 앱에서 python3 manage.py collectstatic 을 통해 모아진 정적파일들이 들어있다.

이렇게 된 설정파일을 이제 저장하고, nginx 을 재 실행한 뒤, 정말 작동하는지 테스트 해보자.

$ sudo nginx -t

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

$ sudo sevice nginx restart

이제 브라우저에서 123.123.123.123을 입력하면, 우리가 원하던 웹페이자가 뜬다. 80 포트로 접속하면 nginx 에서 서버 내부의 8000포트 앱서버로 요청을 보내주는 것이다.


3. ec2 ip 를 내가 구매한 도메인에 연결하기

이제 http://123.123.123.123으로 접속하는게 아니라, http://www.abcdabcd.com 이런식으로 접속하고 싶다. 이를 위해서는 먼저 도메인 업체에서 http://www.abcdabcd.com 도메인을 산 뒤에, 이를 우리의 ip와 연결해야 한다.

도메인 구입은 whois 와 같은 업체에서 쉽게 구매할 수 있다. 나는 ~.org 로 구매했는데, 2년에 약 5만원 5천원 정도. 더 싼데 있으면 다른 곳에서 해도 된다.

도메인을 ip랑 연결하는 작업은 AWS route53을 이용하면 된다. 다른 방법이 있는지는 잘 모르겠는데, 아무래도 서버를 aws ec2를 쓰니, 도메인 연결 작업도 aws 를 쓰면 좋을거 같다는 생각에 사용하였다. 이 방법은 아래 링크에서 아주 자세히 설명해주니, 읽어보면 된다!

AWS route53 을 이용하여 도메인 연결 : http://wingsnote.com/57

이 설정 후, 실제로 내가 등록한 도메인에 연결되는대는 짧게는 몇시간, 길게는 몇 일 걸린다고 한다. 사실 나도 하라는대로 했는데, 아직까지 안된다 .... 정확하게 해결되면 이후에 또 글을 이어나가겠다.