서버 배포 방법

인스턴스에 배포하기 전에 서버를 구성하는 방법의 종류를 알아 보겠습니다

Django로만 배포

Django 프로젝트를 인스턴스에 그대로 옮겨 python manage.py runserver 를 켜서 서버를 돌리는 방법입니다.
하지만 Django의 runserver는 개발할 때 쓰는 작은 디버깅용 웹 서버이기 때문에 실제 프로젝트를 이 방법으로 배포하지는 않고, 아래의 두가지 방법을 이용합니다.

uWSGI + Django로 배포

Django 프로젝트를 생성하면 wsgi.py 파일이 생성되는 것을 많이 보셨을 겁니다. 우선 WSGI의 개념부터 살펴보면, Web Server Gateway Interface의 약자로 웹 서버(Apache, Nginx 등)와 웹 애플리케이션(Django, Flask 등) 사이를 이어주는 역할을 합니다. 즉 웹 서버로 들어온 요청을 Python 언어로 만들어진 웹 애플리케이션과 소통할 수 있도록 해주는 역할을 합니다.
uWSGI는 이 WSGI 규격으로 만들어진 서버 중에 하나이며 Django 프로젝트를 배포할 때 가장 일반적으로 사용됩니다.

Django는 수많은 request를 처리할 수 있도록 설계되어 있지 않기 때문에, Django로만 배포하지 않고 수많은 request를 처리하도록 설계된 uWSGI를 함께 사용해서 배포합니다. 그렇기 때문에 물론 uWSGI + Django 만으로도 충분히 배포가 가능하지만 추천하지는 않습니다

Nginx + uWSGI + Django로 배포


위의 그림에서처럼 Nginx는 클라이언트로부터 HTTP 요청을 받아 request에 따라 바뀌지 않는 즉 정적파일(HTML, CSS, JavaScript)을 돌려주고, 동적데이터가 필요한 요청은 웹 애플리케이션으로 보내줍니다. 이게 바로 웹 서버가 하는 일입니다. 위의 방법을 추천하지 않은 이유 역시 이 웹 서버가 정적파일을 미리 클라이언트에게 보여줌으로써 서버 성능을 높여주기 때문입니다.

Nginx에서 받은 요청은 unix socket을 거쳐 uWSGI로 보내지며, uWSGI가 받은 요청을 Python 개발 환경에서 이해할 수 있도록 중계해주면 최종적으로 Django에서 요청을 처리하게 됩니다.

그럼 Nginx + uWSGI + Django의 일련의 과정을 실제로 배포하는 방법을 아래에서 알아보도록 하겠습니다.

Django Setting

인스턴스에 배포하기 전에 Django를 세팅합니다.

구조

먼저 제가 사용 중인 프로젝트tree 구조는 아래와 같습니다.

nginx_test
└── app
    ├── .config
    │   ├── nginx-app.conf
    │   ├── uwsgi.ini
    │   └── uwsgi.service
    ├── config
    │   ├── __init__.py
    │   ├── __pycache__
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── manage.py
    └── post (app)
        ├── /__pycache__
        ├── /migrations
        │   └── __init__.py
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── models.py
        ├── serializers.py
        ├── urls.py
        ├── tests.py
        └── views.py
    

cf) 오류가 나는 경우

forigenKeyauthor_name field와 잡아놨기 때문에

1
$ python manage.py createsuperuser 

을 통해 계정을 만들고 로그인 후 올리면 가능할 것입니다.

Django static, media 파일 처리 설정

settings.py

1
2
3
4
5
6
7
8
9
10
11
12
ROOT_DIR = os.path.dirname(BASE_DIR)

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(ROOT_DIR, '.static')

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(ROOT_DIR, '.media')
STATIC_DIR = os.path.join(BASE_DIR, 'static')

STATICFILES_DIRS = [
STATIC_DIR,
]

.gitignore 추가

1
2
3
# Custom
/.media
/.static

cf) .gitignore는 링크된 사이트에서 만들 수 있습니다.

Django DEBUG 설정 및 ALLOWED_HOST 설정 변경

개발을 완료하고 배포하는 단계이므로 DEBUG 모드를 False로 바꾸고, ALLOWED_HOST에 aws 주소를 허용합니다.

settings.py

1
2
3
4
5
6
DEBUG = False

ALLOWED_HOSTS = [
'.amazonaws.com',
# '*'
]

uWSGI 사이트 파일 작성

app폴더와 같은 위치에 .config폴더를 만든후, uwsgi.ini 파일에 아래와 같이 작성합니다.

app/.config/uwsgi.ini

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[uwsgi]
chdir = /srv/<nginx_test/app>
module = config.wsgi
home = /home/ubuntu/.pyenv/versions/가상환경이름(ec2-deploy)

uid = ubuntu
gid = ubuntu
socket = /tmp/app.sock
chmod-socket = 666
chown-socket = ubuntu:ubuntu

master = true
vacuum = true
logto = /tmp/uwsgi.log
log-reopen = true

–http 포트 번호를 지정한다.
–home virtualenv 가상환경 디렉토리를 지정한다.
–chdir manage.py가 들어있는 Django 프로젝트 디렉토리를 지정한다.
–module WSGI 모듈을 지정한다.

uWSGI 서비스 설정 파일 작성

.config폴더에 uwsgi.service 파일에 아래와 같이 작성합니다.
app/.config/uwsgi.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=EC2 Deploy uWSGI service
After=syslog.target

[Service]
ExecStart=/home/ubuntu/.pyenv/versions/ipsw/bin/uwsgi -i </srv/nginx_test/app/.config/uwsgi.ini>

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

[Install]
WantedBy=multi-user.target

Nginx 설정 파일 작성

.config 폴더에 nginx-app.conf라는 Nginx 설정 파일을 만들고 아래 내용을 작성합니다.
nginx_test/.config/nigx-app.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80;
server_name *.amazonaws.com;
charset utf-8;
client_max_body_size 128M;

location / {
uwsgi_pass unix:///tmp/app.sock;
include uwsgi_params;
}

location /static/ {
alias /srv/app/.static/;
}

location /media/ {
alias /srv/app/.media/;
}
}

requirements.txt 파일 업데이트

uWSGI를 설치 후, Django 프로젝트에 사용된 패키지들을 서버에 올리기 전에 최종적으로 업데이트 해줍니다.

1
$  pip install uwsgi

“requirement” 파일에 기술된 모든 라이브러리를 설치하는 pip 명령은 다음과 같습니다.

1
pip install -r requirements.txt

Pillow 동작을 위한 Ubuntu python 라이브러리 설치

1
sudo apt-get install python-dev python-setuptools

인스턴스 세팅

인스턴스에 복사한 파일들로 인스턴스에도 세팅을 해줍니다.

pip list 설치

인스턴스 접속

1
2
3
$  ssh -i <pem경로> <user name>@<public dns name>

or `putty`

인스턴스에서 복사한 프로젝트 폴더로 들어가 로컬 프로젝트와 동일한 가상환경명으로 만들어줍니다.

  • 프로젝트폴더 cd

    1
    $ cd /srv/nginx_test
  • python 3.7.3 버전 설치

    1
    2
    3
    4
    5
    6
    7
    $ pyenv install 3.7.3

    # 사용할 Python 버전 정해주기
    $ pyenv global 3.7.3

    # + 현재 디렉토리에서 사용할 python 버전 설정
    $ pyenv local 3.7.3
  • ec2-deploy 가상환경을 생성

    1
    $ pyenv virtualenv 3.7.3 ec2-deploy
  • /home/ubuntu/.pyenv/versions/3.7.3/envs/ec2-deploy 위치로 저장

  • 중간 확인

    1
    2
    # app폴더 (manage.py가 있는 폴더)
    $ python manage.py runserver [aws Public주소:8000]
  • 가상환경 실행

    1
    pyenv activate ec2-deploy
  • requirements.txt로 pip list 설치

    1
    $ pip install -r requirements.txt

인스턴스에 Nginx 및 uWSGI 설정

아래 내용은 모두 로컬이 아니라 인스턴스에서 진행해야 합니다.

Nginx 설치

1
2
3
$  sudo add-apt-repository ppa:nginx/stable
$ sudo apt-get update
$ sudo apt-get install nginx

nginx-app.conf 파일 sites-available 폴더에 복사

1
$  sudo cp -f /srv/nginx_test/app/.config/nginx-app.conf /etc/nginx/sites-available/nginx-app.conf

site-enabled에 링크 걸어주기

1
$  sudo ln -sf /etc/nginx/sites-available/nginx-app.conf /etc/nginx/sites-enabled/nginx-app.conf

uwsgi.sevice 파일 system 폴더에 복사

1
$  sudo cp -f /srv/nginx_test/app/.config/uwsgi.service /etc/systemd/system/uwsgi.service

manage.py가 복사된 위치로 이동 후, manage.py collectstatic 실행

1
$  python manage.py collectstatic --noinput

uwsgi 사용

1
$  sudo systemctl enable uwsgi

daemon-reload

1
$  sudo systemctl daemon-reload

uwsgi 재부팅

1
$  sudo systemctl restart uwsgi nginx