아래 세팅 설정들은 AWS EC2, Virtualbox 내 우분투 설치, conoha(https://www.conoha.jp/ko/ ) 를 설정하면서 사용된 설정들이라, 어떤 경우, 맞지 않을 수 있다는 것을 주의하자. 그리고 내가 의식적으로 하는 작업이나 패키지 설치들이 있기 때문에 필수 적이지 않는 것도 꽤 많다.
(그런데 다 같은 Ubuntu 16.04 버전인데 조금씩 차이가 나는지 모르겠다. 아무래도 호스팅업체에서 제공하는 버전 자신들 서버에 맞게 조금씩 수정을 거친 것 같은 느낌이다. )
설정을 하는 경우 여러가지 설정파일을 생성하게 된다. 간혹 하나의 서버에 여러 프로젝트 서비스를 돌리는 경우가 있을 수 있기 때문에(나는 특히 이런 경우가 참 많았다. ) 가능한 한, 프로젝트 명에 맞추어 ini, pid , sock 파일 또는 리소스를 생성하는게 좋다. 내 경우에는 같은 프로젝트로 test 서버, 스테이징서버 같은 것을 한 서버에 운영하는 경우, 프로젝트명_test, 프로젝트명_staging 이런 식으로 구별해서 이름을 정하는 편이다. 그리고 내가 사용한 파일이름이나 리소스이름은 임의적인 것이므로 원하는 대로 변경할 수 있다.
1. 시간설정
1-1. timezone 설정
콘솔에 date 를 치면 시간 뒤에 붙는 세자리의 영문자를 통해서 timezone 을 알 수 있다.
timezone 설정은 서비스에 따라서 크게 뭘로 해도 문제가 안될 수도 있고 될 수도 있는 부분이라 본인의 서비스에 맞게 잘 설정해야 한다. 그리고 서비스와 관련이 없더라도 로그 볼 때의 기준이 되는지라 팀이나 관리를 하는 나라를 고려 할 필요가 있다.
내가 했던 서비스의 경우 django setting 의 timezone 에 의해 동작하는 경우가 많은 지라 이 서버 timezone이 크게 중요하지는 않았다. 그래서 그냥 Asia/Seoul 로 설정하는 편이다.
sudo timedatectl set-timezone Asia/Seoul |
단순히 timezone 리스트를 확인 하고 싶은 경우
timedatectl list-timezones
로 확인 하면 된다.
1-2. 주기적으로 시간 서버로 부터 시간을 맞추도록 설정
그리고 컴퓨터 시간일지라도 조금씩 오차가 발생하기 마련이다. 이 오차가 쌓이면 꽤 큰 차이의 시간 차가 생긴다. 어떤 서비스의 경우 시간차이 때문에 오류가 생길 수 있다. 그리고 관리하는 서버들 간의 시간이 차이가 발생하면 오류를 분석하기 어려워질 수도 있고, 오류도 발생할 수도 있다.
그래서 정확한 시간을 알고 있는 다른 서버로 부터 시간을 받아올 수 있도록 설정이 필요하다.
ntp(네트워크 타임 프로토콜) 라는 서비스가 돌아가는지 확인 해 보자.
ps -ef | grep ntpd
결과
ntp 9154 1 0 2017 ? 00:07:10 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 111:117 root 11308 11188 0 00:16 pts/0 00:00:00 grep --color=auto ntpd
|
위 명령어를 입력했을 때, 결과가 2줄이 나온다면 걱정하지 말고 다음 내용은 무시하자.
grep 이 부분의 결과만 나온다면 ntp 서비스가 나온느 것이 아니라서 주기적으로 시간을 맞출 수 있도록 설정이 필요하다.
를 이용해서 서비스를 돌린다.
이 방법이 사용되지 않는다면 단발성으로
sudo rdate -s time.bora.net
을 이용할 수 있다. (time.bora.net 은 많이 사용하는 서버이다.
이 방법을 crontab 을 이용해서 밤 12시에 하루에 1번 주기적으로 실행시키는 방법도 있다.
리눅스를 사용하다보면 컴파일해서 설치할 일이 많다. 내가 직접 컴파일(빌드)을 하지 않더라도 진행과정에 컴파일(빌드)하는 동작이 들어가는 경우가 많다. 그래서 기본적인 개발환경을 세팅할 필요가 있다. 이 때를 위해 build-essential 를 설치한다. pip install 하는 경우 꽤 많은 경우 자동으로 컴파일 해서 설치하는 경우가 많으니 필수이다.
# 기본적인 빌드 환경 제공 sudo apt-get -y install build-essential
# 파이썬 관련 패키지 설치 할 때 꼭 필요. sudo apt-get -y install python-dev
# 파이썬 mysql 관련 패키지 설치시 필요 sudo apt-get install -y libmysqlclient-dev
# 파이선 이미지 관련 라이브러리에서 필요, Pillow 같은 라이브러리 설치시 필요함 sudo apt-get install -y libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
# 소스 관리툴 git 를 위해 사용함
|
3. nginx 설치
nginx 를 사용하는 경우, 소스다운로드 받을 디렉토리가 /usr/share/nginx 안인 경우가 많다. nginx 를 설치하면 해당 경로를 자동으로 만들어주기 때문에
nginx 를 먼저 설치하는 편이다.
sudo apt-get -y install nginx |
4. 소스 다운로드
일반적으로 소스배포는 git를 이용하는 편이다. 그리고 git를 사용할 때는 배포용 전용 아이디를 만들어서 read 만 되게 관리한다.
소스 경로는 /usr/share/nginx/[프로젝트명] 식으로 하는 편이다.
cd /usr/share/nginx/[프로젝트명]
로 git로 소스를 다운받을 위치로 이동한다.
github 의 경우
sudo git clone https://아이디@github.com/github프로젝트경로 .
맨 끝의 .(점) 의 경우 현재 디렉토리에 넣겠다는 의미이다. 맨 끝의 점이 없는 경우 새롭게 디렉토리가 생성된다.
이를테면
sudo git clone htttps://yiunsr@github.com/yiunsr/re2view.git .
이런식으로 git의 경로는 https://github.com/yiunsr/re2view.git 이런식이다.
bitbucket 의 경우
sudo git clone https://아이디@bitbucket.org/bitbucket프로젝트경로 .
으로 github 와 유사하다. 역시 맨 끝에 현재 위치에 다운받으라는 의미로 .(점)이 들어 있다.
일반적으로 파이썬으로 만들어 프로그램 또는 서버는 virtualenv 로 가상환경을 설정해서 만들어진다. 나의 경우 실제 서버에서도 이런 방식으로 하는 편이다. 테스트 환경의 경우, 특히나 한 서버에 여러 서비스를 올리는 경우가 많았기 때문에 서로의 서비스가 영향을 받지 않도록 하려고 virtualenv 를 사용하는 편이다. 따라서 이 경우 pip freeze 한 결과파일 파일을 두고 다시 세팅하는 편이다. 이런 환경이 없다면 이번 기회에 이런 방식으로 사용하기를 추천한다.
5. python 라이브러리 및 virtualenv 설정
요즘에는 파이썬이 2버전과 3버전 모두 탑재되는 경우가 많다. 그래서 설치가 되어 있다고 가정하고 진행한다.
(아주 최신 버전을 고려할 경우 따로 컴파일 후 설치할 필요가 있다. )
아래 설명은 python2.7 기준이다. python3 버전은 차후에 여기 아래에 추가 하겠다.
python 패키지 관리자 pip 를 설치하고 pip 를 통해 virtualenv 를 설치한다.
apt-get install -y python-pip pip install virtualenv |
나 같은 경우 따로 설치한 프로그램은 /opt/ 에 넣는 편이다. 그래서 python virtual 환경 경로도 /opt/env/ 아래 두는 편이다.
우선 해당 디렉토리 까지 만든다. 그리고 가상환경을 만든다.
cd /opt/ mkdir py_envs cd py_envs virtualenv --no-site-packages --distribute vtest |
이 다음 중요한 것이 있다.꼭 가상환경을 설정하기 전에
sudo -s
를 해서 꼭 root 접속 필요하다.
sudo 를 이용해서 pip로 파이썬 패키지를 설치하면, path 가 맞지 않아서 뭔가 제대로 동작하지 않았다.
sudo -s ### 꼭 root 접속 필요(sudo 를 이용한 pip 를 화면 path 가 맞지 않는다. source /opt/py_envs/vtest/bin/activate 를 이용해서 해당 상태를 활성화 시킨다.
그리고 자신에게 필요한 패키지를 설치한다. 경우에 따라서는 아래 git 를 통해 소스를 다운받은 후에 pip install -r requirements.txt 식으로 pip freeze 를 한 결과를 저장해둔 파일을 이용해 한 방에 설치할 수도 있다. |
6. uwsgi 설치 및 설정
uwsgi 는 Djanog 와 wsgi 방식으로 연동시켜준다. Nginx 없이 uwsgi + Django 만으로도 실서비스가 가능하다. 그리고 uwsgi 도 어떠한 이유로 죽을 수 있고 경우에 따라서는 컴퓨터가 어떠한 이유로 죽어서 재부팅 할 수도 있다. 이를 위해 죽을 때 다시 살릴 수 있고 부팅할 때 자동으로 동작할 수 있도록 해야 하는데 이 때 사용하는게 요즘은 systemd 이다. 이 연동을 하기 위해 uwsgi를 Emperor Mode로 사용해야 한다.
우선 uwsgi 를 설치한다.
# (virtualenv 를 이용한 상태가 아니어야 한다. ) sudo pip install uwsgi
|
uwsgi 환경 설정을 할 수 있는 파일 생성한다. (차후 다른 프로젝트도 같이 돌아가는 경우, 이 경로에 다른 이름으로 파일을 생성한다. _
sudo mkdir -p /etc/uwsgi/sites cd /etc/uwsgi/sites/
## 환경설정 파일 생성 sudo vim [프로젝트명].ini
==== [프로젝트명].ini ==== [uwsgi]
#한 서버에 여러 프로젝트 파일을 올린 경우, 이 port 가 절대 겹치면 안된다. # 아래 있는 socket의 .sock 파일 대신 Nginx와 연결할 때 사용할 수도 있다. # 실제로 이 port 를 통해 외부에 접속할 수 있다. http-socket = :9001 master=True pidfile=/tmp/[프로젝트명].pid
chmod-socket=666 socket=/tmp/uwsgi_[프로젝트명].sock #Nginx 와 연동할 때 연결되는 부분이다. processes = 2 ## 생성된 process 개수, 일반적으로 process 개수와 맞게 생성한다. vacuum=True #max-requests=5000
### 해당 경로에 Django의 wsgi.py 경로가 있어야 한다. wsgi-file = /usr/share/nginx/[프로젝트명]/djanog내_경로/wsgi.py
## request 할 때 body를 제외한 사이즈, 기본사이즈는 4k 인데, 16k 로 늘렸다. ## datatable.js 를 매우 애용하는데, 이 때, 여려 옵션들을 이용하면 Get Method 인데도 엄청 많은 parameter 가 전달되어 ## 크게 늘렸다. buffer-size=65535
virtualenv = /opt/py_envs/virtual_env를생성한 경로 ## 여기서는 /opt/py_envs/vtest touch-reload = /usr/share/nginx/[프로젝트명]/djanog내_경로/wsgi.py logto = /var/log/uwsgi/[프로젝트명].log chdir=/usr/share/nginx/[프로젝트명] logfile-chown=www-data logfile-chmod = 644
## 실제 이 경로는 Django Project에서 어떤 곳에 static 이 위치에 있는지에 달려있다. static-map = /static=/usr/share/nginx/[프로젝트명]/static
========
|
위에서 tocuh-reload 는 서비스를 다시 시작하지 않아도 해당 파일의 수정일자에 변경이 있으면 자동으로 서비스가 동작하도록 하는 부분이다. 만일 다른 부분이 다 수정되어 있는데도, 해당 파일의 수정일자가 변경되지 않는다면 메모리에 올라간 시스템이 적용되지 않고 계속 적용된다.
그래서 내경우는 wsgi.py 파일의 주석을 변경하고 git 에 commit 해 두고 나중에 git pull 할 때 자동으로 수정하도록 한다. 경우에 따라서는 version.txt 파일을 만들어 운영하는 것도 좋은 방법이라고 생각한다.
딱히 해당 파일의 변경사항이 없을 때는
sudo touch /usr/share/nginx/[프로젝트명]/djanog내_경로/wsgi.py
touch 명령어도 최종수정일자를 갱실해 주면 된다.
다음은 위 ini 파일에서 로그 파일 까지의 디렉토리를 생성해 줘야 한다. 해당 파일이 없으면 에러가 발생하기 때문에 실행전에 꼭 log 디렉토리를 만들어 두어야한다.
uwsgi 전용 log 파일 경로 생성
sudo mkdir -p /var/log/uwsgi sudo touch /var/log/uwsgi/[프로젝트명].log #touch 는 파일이 없는 경우 빈 파일을 생성해 주기도 한다.
sudo chown -R www-data.www-data /var/log/uwsgi/ |
여기 까지 작업을 했다면 uwsgi 를 돌려볼 수 있다.
시작
uwsgi --uid www-data --gid www-data --ini /etc/uwsgi/sites/[프로젝트명].ini &
정지
uwsgi --stop /tmp/[프로젝트명].pid
으로 돌릴 수 있다. 에러가 발생하면 로그파일 생성한 곳( /var/log/uwsgi/[프로젝트명].log )으로 가서 로그 확인이 필요하다.
꼭 시작 명령어로 돌렸으면 정지 명령어로 종료해야 한다.
( kill 명령어로 죽였다면 다시 돌릴려면 /tmp/[프로젝트명].pid 파일을 삭제해야 돌아간다. 해당 pid 파일이 있는 상태로 다시 시작을 시키면 pid 파일이 있어서 이미 프로세스가 돌아가고 있다고 생각해서 시작하지 않는다. )
http-socket 를 설정하고 방화벽(리눅스내 ufw 방화벽, 그리고 서비스에서 제공하는 방화벽 모두 open 되어야 한다.) 이 open 되어 있다면 외부에서 접속할 수 있다. 이 접속까지 되어야 다음 설정을 진행할 수 있다.
7. uwsgi 의 systemd 설정
uwsgi 도 프로그램이기 때문에 죽을 수 있고 재부팅 마다 시작 설정(uwsgi --uid www-data --gid www-data --ini /etc/uwsgi/sites/[프로젝트명].ini &
) 을 하는 것을 정말 귀찮기 때문에 systemd 를 설정해 준다.
sudo vim /etc/systemd/system/uwsgi.service
==== uwsgi.service ==== [Unit] Description=uWSGI Emperor service After=syslog.target
[Service] ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always KillSignal=SIGQUIT Type=notify StandardError=syslog NotifyAccess=all
[Install]
WantedBy=multi-user.target ======== |
ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
에서 uwsgi 가 경로가 다르면 오류가 발생할 수 있다. 만일 그렇다면 which uwsgi 로 경로를 확인 할 수 있다.
설정 파일을 만들었으니 이제 systemd 에 부팅시에도 동작할 수 있도록 하자
sudo systemctl enable uwsgi # 부팅시 자동시작
(만일 부팅시 자동 시작을 막고 싶으면 sudo systemctl disable uwsgi
이제
sudo service uwsgi 형태로 서비스를 관리 할 수 있다.
서비스 시작
sudo service uwsgi start # 이 명령어가 안된다면 sudo systemctl start uwsgi 로 사용할 것
서비스 중지
sudo service uwsgi stop # 이 명령어가 안된다면 sudo systemctl stop uwsgi 로 사용할 것
서비스 재시작(stop + start)
sudo service uwsgi restart # 이 명령어가 안된다면 sudo systemctl restart uwsgi 로 사용할 것
서비스 재로드(환경설정 파일만 재로딩)
sudo service uwsgi reload # 이 명령어가 안된다면 sudo systemctl reload uwsgi 로 사용할 것
만일 uwsgi --uid www-data --gid www-data --ini /etc/uwsgi/sites/[프로젝트명].ini &
로 프로세스가 돌아가고 있다면 sudo service uwsgi start 가 안될 수도 있으니 꼭 종료하고 동작시키자.
8. nginx 설정
uwsgi 만으로도 서비스는 동작시킬 수 있지만, nginx 을 통해 보안기능을 좀더 확보하고, static 리소스(css, js, 이미지 파일들)를 접근하는데 더 안정적으로 동작하고, 여러 다양한 기능(gzip 기능 같은 것)을 이용할 수 있기 때문에 nginx 를 uwsgi 앞에 둔다.
이렇게 nginx 를 사용하는 경우, reverse proxy 를 사용한다고 합니다.
우선 로그 포멧 및 gzip 사용을 위해 아래 주소에 로그 포맷을 추가 한다.
sudo vim /etc/nginx/nginx.conf ==== nginx.conf ==== http{
## gzip 관련 주석 해제 gzip on; gzip_disable "msie6"; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
…….
log_format post_log '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $request_time "$request_body"'
…
}
======== |
log_format 의 경우 필요없는 경우 추가 할 필요 없다. 해당 코드는 웹에서 Post method 로 보낼 때에도 log 파일이 보일 수 있도록 하는 설정에 대한 포멧이다. 여기서 설정을 했다고 해서 post_log 가 보이는 것은 아니고 그냥 포멧을 지정하는 것이기 때문에 우선 추가한다.
만일 SSL 인증서를 적용하는 경우라면 해당 인증서에 나와있는 가이드와 아래 코드와 적절히 조화가 필요하다. 이 부분은 case 가 너무 많은지라 가이드 하기 어렵다....
이제 /etc/nginx/sites-available/
내에 실제 설정 파일 추가한다. 따로 설정파일이 없는 경우 /etc/nginx/sites-available/default 파일의 설정대로 동작한다.
설정파일이름은 프로젝트명으로 하기보다는 domain 으로 많이 이용하는 편이다. 그런데 거기에 따른 영향이 딱히 없기 때문에 그냥 구별할 수 있을 정도로 정하면 된다.
vim /etc/nginx/sites-available/[프로젝트명 또는 domain]
==== [프로젝트명 또는 domain ] server { listen 80; server_name [domain]; client_max_body_size 100M; ## 크기가 작을 경우 업로드 할 때 제한이 생길 수 있다.
root /usr/share/nginx/[프로젝트명]/; access_log /var/log/nginx/[프로젝트명]_access.log; access_log /var/log/nginx/[프로젝트명]_post.log post_log;
error_log /var/log/nginx/[프로젝트명]_error.log;
location /static/ { alias /usr/share/nginx/[프로젝트명]/static/; ## django 내 static 경로를 맞춰야 한다. }
location / { uwsgi_pass unix:/tmp/uwsgi_[프로젝트명].sock; include uwsgi_params; }
}
|
access_log /var/log/nginx/[프로젝트명]_post.log post_log;
이 부분이 들어갈 경우 Post Method 가 들어가 있는 부분에 로그가 출력되기 때문에 로그파일이 꽤 크게 생성된다.
내 경우 테스트 서버의 경우 디버깅을 위해 넣는다. (사내에서만 돌아가는 서비스 인 경우에도 넣는 편이다. )
실서버의 경우 굳이 해당 부분을 넣을 필요가 없을 것 같다.
이 부분은 열리는 포트이다. 일반적으로 80포트이나, SSL 이 이용되는 경우 443 포트를 이용할 수 있다.
server_name 은 실제 사용하는 domain, 없는 경우 public ip 주소를 사용할 수 있다. server_name 을 여러가 나열하거나 domain 과 ip 를 같이 사용할 수 도 있다. 그럴 경우
server_name www.labstoo.com imglst.labstoo.com m.labstoo.comm;
이런 식으로 옆으로 나열 하면 된다.
uwsgi_pass 부분은 uwsgi 에서 만든 sock 파일과 매칭되어야 정상적으로 동작한다.
이제 nginx 를 돌려보자. nginx 는 설치시 자동으로 service 로 등록되기 때문에 serivce 명령어를 사용할 수 있다.
시작
sudo service nginx start
종료
sudo service nginx stop
웹브라우저로 접속시 503 에러가 나면 nginx 에서 uwsgi 연결이 잘못된 것이다.
8. (옵션) 로그 관리 설정
서버를 운영하다보면 많은 로그가 생성된다. 이 경우 로그파일이 크면 서비스가 동작하지 않을 수도 있다. (로그파일을 만들지 못해 서비스가 동작하지 않는 경우가 있다. hard 가 작은 경우 이런 case 를 몇 번 겪어 봤다.)
그래서 로그를 압축하고 너무 오래된 로그는 지우도록 설정한다.
uwsgi 의 경우 이미 되어 있는 설정이 없기 때문에 설정이 필요하다.
로그 관리의 경우 /etc/logrotate.d 내 설정이 되어 있다.
nginx 의 경우 이미 로그 설정이 되어 있다. 그러나 기본적인 설정이기 때문에 설정변경이 필요하다.
sudo vim /etc/logrotate.d/nginx
==== nginx ==== /var/log/nginx/*.log { daily missingok rotate 30 compress delaycompress notifempty create 0640 www-data adm sharedscripts prerotate if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ run-parts /etc/logrotate.d/httpd-prerotate; \ fi \ endscript postrotate invoke-rc.d nginx rotate >/dev/null 2>&1 endscript dateext } ======== |
dialy 는 파일이 나눠어 지는 시간단위 이다. daily / weekly/ monthly 같이 사용할 수 있다.
rotate 의 경우 로그파일을 관리할 개수이다. dialy 로 관리되기 때문에 30일 까지 관리된다.
dateext 는 파일이 따로 분리 될 때 날짜를 확장자로 사용하겠다는 의미이다.
다음은 uwsgi 용 로그 설정이 필요하다. uwsgi 의 경우 생성되는 파일이 없어서 생성해야 한다.
sudo vim /etc/logrotate.d/uwsgi
==== uwsgi ==== "/var/log/uwsgi/*.log" "/var/log/uwsgi/*/*.log" { copytruncate daily rotate 30 compress delaycompress missingok notifempty dateext }
========
|
로 설정하면 된다.
logrotate 의 경우 해당 동작이 crontab 에 의해 동작한다.
(/etc/cron.daily/logrotate 파일이 존재한다. )
모든 설정이 끝났다. 혹시 추가 사항이 있는 경우 계속 최종수정일을 고쳐가면서 수정하도록 하겠다.
혹시 잘못된 설정이나 더 좋은 방법 있으면 댓글을 남겨두기 바란다.
최종수정일 2018년 03월 02일