동기

최근 도메인을 산 김에 토이 프로젝트로 요즘 유행하는 익명 질답 서비스를 간단하게 만들어보려고 했다. Bitnami에서 클릭 몇 번으로 스택을 구성할 수 있는 것처럼 보여서 AWS 프리 티어랑 합쳐서 두 세 시간 정도 삽질을 했다. 무료 티어에서는 micro급 EC2 인스턴스를 무료로 제공해 주는데, Auto Scaling이 필요한 수준은 돼야 AWS의 장점을 느낄 수 있을 것 같고 이 정도로는 다른 VPS랑 비교해서 딱히 장점이 느껴지지 않았다. 그래서 차라리 오래 쓸 목적으로 싸고 적당한 VPS 하나를 맞추고자 가상서버 호스팅 비교글을 보고 가격 대비 트래픽과 하드 용량이 좋은 ConoHa에서 제일 싼 서버를 하나 맞췄다.

예전에 학교 서버에서 급식 평가 사이트 돌릴 때 서버 세팅에서 삽질을 많이 했었기 때문에 이번에는 세팅하면서 삽질 과정을 포스팅에 메모해 놓기로 했다. 서버 첫 설치부터 시작해서 삽질의 과정을 내가 나중에 다시 보면 기억날 수준으로만 간단하게 정리해 보았다. nginx + Django + uWSGI로 디플로이 할 일이 제일 많을 것 같아서 이 세팅으로 저장해 두었고, 사용하는 프레임워크 따라 적절히 변경해서 사용하면 된다. 작성하면서 이 글을 참고했다.

1. 설치

처음 설치하고 나서 root 계정으로 필요한 것들을 깔아준다. Ubuntu 14.04 기준이고 대부분의 작업에 python3를 사용한다.

$ apt-get update
$ apt-get upgrade
$ apt-get install build-essential zsh git python3-dev python3-pip nginx lrzsz
$ pip3 install virtualenv

2. SSH 키 생성하기

  1. useradd qwaz로 유저를 추가한다.
  2. groupadd admin으로 어드민 그룹 생성.
  3. usermod -a -G admin qwaz로 유저를 어드민 그룹에 추가.
  4. login qwaz로 로그인 후 sudo 되는지 테스트.
  5. XShell 등에서 사용자 키를 생성하고 공개키를 .ssh/authorized_keys 위치에 업로드한다. 디렉터리가 없는 경우 생성하고 권한은 700으로 준다.
  6. 로그아웃 후 Key 사용해서 로그인 되는지 테스트.

3. zsh 세팅하기

  1. curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh로 oh-my-zsh 설치
  2. chsh -s /bin/zsh로 기본 셸을 zsh로 변경 후 터미널 재접속.
  3. .zshrc를 수정해 테마나 플러그인을 취향대로 변경. bullet-train이 괜찮더라.

4. virtualenv 세팅

개발용 계정은 하나를 쓰고 projects 디렉터리 내부에 개발하는 프로젝트를 관리할 것이다. 프로젝트마다 계정을 생성해도 되는데 개인적으로는 이 방식이 편하게 느껴진다.

프로젝트 디렉터리가 없는 경우 mkdir projects로 생성하고 ~/projects 디렉터리로 이동한다.

toytest라는 프로젝트를 예시로 진행해보겠다.

$ virtualenv toytest
$ cd toytest
$ source bin/activate
$ pip install django
$ cd django

virtualenv toytest로 가상 환경을 만든다. 이렇게 만든 가상환경은 source toytest/bin/activate로 활성화하고 deactivate로 비활성화 할 수 있다.

virtualenv를 켜기 전에는 파이썬 버전에 따라 pip, pip3로 명령어가 다르지만 virtualenv를 python3로 설치했기 때문에 활성화 후에는 그냥 pip 명령어만 입력해도 python3 버전으로 작업할 수 있다. pip가 사용중인 파이썬 버전은 pip -V로 확인할 수 있다.

5. Django 프로젝트 생성

서버에서 바로 django-admin startproject toytest로 시작해도 되고, 로컬에서 생성한 뒤 git clone 등을 이용해 가져오게 설정해도 된다. 과정은 거의 동일하기 때문에 서버에서 바로 생성하는 방법으로 설명하겠다.

~/projects/toytest 디렉터리에서 django-admin startproject toytest 명령으로 toytest 프로젝트를 시작하자.

6. nginx 세팅

생성한 Django 프로젝트를 수정하기 전에, ~/projects/toytest에 로그나 소켓 등을 먼저 설정한다.

$ mkdir misc log
$ cd log
$ touch access.log error.log toytest.log
$ cd ..
$ cd misc

misc 디렉터리에 toytest_nginx.conf를 만들고 다음과 같은 내용을 추가하자.

server {
        # the port your site will be served on
        listen 80;

        # the domain name it will serve for
        server_name     toytest.qwaz.io;
        charset         utf-8;

        access_log      /home/qwaz/projects/toytest/log/access.log;
        error_log       /home/qwaz/projects/toytest/log/error.log;

        location / {
                include /etc/nginx/uwsgi_params;
                uwsgi_pass unix:///home/qwaz/projects/toytest/misc/toytest.sock;
        }
}

이 파일을 symlink로 nginx 디렉터리에 추가한다.
sudo ln -s ~/projects/toytest/misc/toytest_nginx.conf /etc/nginx/sites-enabled/

7. uWSGI 세팅

여기를 참고했다.

우선 virtualenv를 잠시 꺼두고, pip3로 uWSGI를 system-wide로 설치한다.

$ deactivate
$ sudo pip3 install uwsgi

virtualenv를 다시 켜고 python manage.py runserver 0.0.0.0:8000로 Django를 돌려보자. 웹브라우저에서 toytest.qwaz.io:8000로 접속해 접속이 되는지 테스트 해본다.

잘 동작했다면 이번에는 uwsgi --http :8000 --module toytest.wsgi -H ~/projects/toytest로 uWSGI를 사용해 다시 한 번 테스트 해본다.

여기까지 성공했다면 이제 아래 toytest_uwsgi.ini파일을 misc 폴더에 만들자. 설정 파일 만들 때는 여기를 참고했다.

[uwsgi]

# Django-related
chdir           = /home/qwaz/projects/toytest/toytest
module          = toytest.wsgi

# virtualenv
home            = /home/qwaz/projects/toytest

# process-related
master          = true
processes       = 8

# permission
uid             = qwaz
gid             = www-data

# misc files
socket          = /home/qwaz/projects/toytest/misc/toytest.sock
chmod-socket    = 664
pidfile         = /home/qwaz/projects/toytest/misc/toytest.pid
daemonize       = /home/qwaz/projects/toytest/log/toytest.log

harakiri        = 60
max-requests    = 4096
reload-on-as    = 512
reload-on-rss   = 192
limit-as        = 1024

no-orphans      = true
vacuum          = true

uwsgi --ini toytest_uwsgi.ini로 uwsgi를 실행시키고 sudo service nginx restart로 추가한 sites-enabled를 읽도록 nginx를 재시작하고 브라우저로 접속해보자. 접속에 성공했다면 sudo kill -9 $(cat /home/qwaz/projects/toytest/misc/toytest.pid)로 프로세스를 죽인다.

8. Emperor Mode

웹서버 관련 설정은 거의 다 끝났다. 이제 uWSGI를 Emperor Mode로 실행시키고 서버가 시작했을 때 자동으로 실행되도록 만들어주자. Emperor Mode에서는 ini 파일이 변경될 때마다 새로운 인스턴스를 띄워준다.

sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/vassals
sudo ln -s /home/qwaz/projects/toytest/misc/toytest_uwsgi.ini /etc/uwsgi/vassals/
sudo uwsgi --emperor /etc/uwsgi/vassals --uid qwaz --gid www-data

실행 되는 것을 확인했다면 프로세스를 종료하고, 마지막으로 서버가 켜질 때 uWSGI가 뜨도록 해 보자.

/etc/rc.local 파일을 열어 /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid qwaz --gid www-data를 추가하고 서버를 재부팅해서 사이트가 뜨는지 확인한다.

여기까지 마치면 디렉터리 구조는 다음과 같이 된다.

~/
└ projects/
  └ toytest/
    ├ (virtualenv 관련 파일들)
    ├ log/
    │├ access.log
    │├ error.log
    │└ toytest.log
    ├ misc/
    │├ toytest_nginx.conf
    │└ toytest_uwsgi.ini
    └ toytest/
      ├ manage.py
      └ toytest/
        ├ settings.py
        ├ urls.py
        └ wsgi.py

몇 부까지 있을지는 모르겠지만 다음에는 DB 세팅과 Django 세팅 파일 설정 등을 건드릴 예정이다.