어쩌다보니 이제 내가 회사에서 fullstack 개발자 처럼 일하고 있다. 서버 세팅 같을 것을 자주하게 된다.
서버세팅할 때 AWS 를 사용하다보니 인스턴스 이미지 형태로 저장하였다가 다시 이용하는 형태를 많이 사용한다. 그러나 서버세팅을 처음 할 때나, 실서버에서 세팅하게 될 때는 매우 불편하다. 이를 쉽게 하기 위해 파이썬 fabric 라이브러리를 이용할 수 있다.
(fabric 이라는 프로젝트는 참 많다. 자바스크립트 Javascript Canvas Library fabric.js 라는 것이 있다. 그리고 모바일 앱 개발도구로 트위터에서 시작되고 지금은 google 이 운영하는 fabric 도 있다. 그래서 여기서는 파이썬 fabric 이라는 표현을 사용했다. )
이 fabric 대해 간단히 설명하면 SSH 에 대신 접속해서 명령어를 보내는 파이썬 라이브러리라고 생각하면 된다. AWS 에서는 SSH 접속시 pem 이라는 프라이빗 키를 이용하는데 이도 지원한다.
아래 샘플은 fabric 의 일반적인 형태가 아닌 파이썬 프로그램 처럼 run 하는 방식으로 짜여졌다. 내 경우 eclipse + pydev 나 vscode 에서 사용하는 편이라서 이런 식으로 만들어졌다. 아래 코드를 사용하기 위해서는 fabric 이 필수 이다. 아래 코드은 python3 기준이다.
========= fab.py =======
#-*- coding: utf-8 -*-
import os
from fabric.contrib.files import append, exists, sed, put, uncomment, upload_template, contains
from fabric.api import env, local, run, sudo, execute, task, settings
from fabric.context_managers import cd
#from fab.main import main
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(PROJECT_DIR)
RES_DIR = os.path.join(PROJECT_DIR, "res")
def server_virtualbox():
env.hosts = ['ssh_user@127.0.0.1:22']
env.passwords = {"ssh_user@127.0.0.1:22" : "password"}
env.URL_HOST = "localhost"
env.PROJECT_NAME = "re2view"
env.GIT_USER = "id_depoly"
env.GIT_USER_EMAIL = "depoly_email@gmail.com"
env.GIT_PASSWORD = "Depoly_pw1"
env.GIT_URL_INFO = "https://id_depoly:Depoly_pw1@github.com/yiunsr/re2view.git"
def server_aws():
env.hosts = ['ubuntu@11.123.13.15:22']
env.user = "ubuntu"
env.key_filename = "C:\work\key\pem.pem"
env.URL_HOST = "ec2-11-123-13-15.ap-northeast-2.compute.amazonaws.com"
env.PROJECT_NAME = "re2view"
env.GIT_USER = "id_depoly"
env.GIT_USER_EMAIL = "depoly_email@gmail.com"
env.GIT_PASSWORD = "Depoly_pw1"
env.GIT_URL_INFO = "https://id_depoly:Depoly_pw1@github.com/yiunsr/re2view.git"
def setting_virtualbox():
server_virtualbox()
common_setting_ubutu()
def setting_aws():
server_aws()
common_setting_ubutu()
def common_setting_ubutu():
execute(ubutu_update)
execute(common_install)
execute(ubuntu_nginx_install)
execute(ubuntu_nginx_conf)
execute(ubuntu_nginx_host_conf)
execute(git_clone)
def common_install():
sudo("apt-get install -y build-essential")
sudo("apt-get install -y python-dev")
# Python 내 이미지를 다룰 때, 필요한 라이브러리 인데, 미리 설치해두자.
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")
sudo("apt-get install -y libmysqlclient-dev")
sudo("apt-get install -y git")
def ubuntu_nginx_install():
## Nginx 설치
sudo("apt-get install -y nginx")
sudo("service nginx restart")
## Nginx 버전 확인
sudo("nginx -v")
def ubutu_update():
# 저장소 정보(/etc/apt/sources.list) 업데이트(-y 옵션은 Prompt 없이 가능하게 한다. )
sudo("apt-get -y update")
# 설치된 패키지 업그래이드, -y 옵션이 꼭 필요하다.
sudo("apt-get -y upgrade")
def ubuntu_nginx_conf():
# /etc/nginx/nginx.conf 파일 편집
# gzip 지원
uncomment("/etc/nginx/nginx.conf", "# gzip_vary on;", use_sudo = True )
uncomment("/etc/nginx/nginx.conf", "# gzip_proxied any;", use_sudo = True )
uncomment("/etc/nginx/nginx.conf", "# gzip_comp_level 6;", use_sudo = True )
uncomment("/etc/nginx/nginx.conf", "# gzip_buffers 16 8k;", use_sudo = True )
uncomment("/etc/nginx/nginx.conf", "# gzip_http_version 1.1;", use_sudo = True )
uncomment("/etc/nginx/nginx.conf", "# gzip_types", use_sudo = True )
# log format 추가
if contains("/etc/nginx/nginx.conf", "log_format post_log", use_sudo=True) == False:
old_string = r"""^http \{$"""
new_string = r"""http {\n"""
new_string += r"""\tlog_format post_log $remote_addr - $remote_user [$time_local] "$request" \n"""
new_string += r"""\t$status $body_bytes_sent "$http_referer" \n"""
new_string += r"""\t "$http_user_agent" $request_time "$request_body" \n"""
with settings(warn_only=True):
sed("/etc/nginx/nginx.conf", old_string, new_string , use_sudo = True, shell = True, flags="m")
def ubuntu_nginx_host_conf():
# /etc/nginx/sites-available/호스트명 으로 호스트 추가
vhost_context = {
'URL_HOST' : env.URL_HOST,
'PROJECT_NAME' : env.PROJECT_NAME
}
local_path = os.path.join(RES_DIR, "config.txt")
upload_template(local_path, '/etc/nginx/sites-available/%(URL_HOST)s' % vhost_context, context=vhost_context, use_sudo=True, backup=True)
## 파일이 있어 에러가 생긴 경우 그냥 넘어간다.
with settings(warn_only=True):
sudo("ln -s /etc/nginx/sites-available/%(URL_HOST)s /etc/nginx/sites-enabled/%(URL_HOST)s" % vhost_context)
## 이미 디렉토리가 있어 에러가 생긴 경우 그냥 넘어간다.
with settings(warn_only=True):
sudo("mkdir /usr/share/nginx/%(PROJECT_NAME)s/" % vhost_context)
sudo("chown -R www-data:www-data /usr/share/nginx/%(PROJECT_NAME)s/" % vhost_context)
sudo("service nginx restart")
def git_clone():
git_context = {
'GIT_URL' : env.URL_HOST,
'GIT_URL_INFO' : env.GIT_URL_INFO,
'PROJECT_NAME' : env.PROJECT_NAME,
'GIT_USER' : env.GIT_USER,
'GIT_USER_EMAIL' : env.GIT_USER_EMAIL,
}
with cd("/usr/share/nginx/%(PROJECT_NAME)s/" % git_context):
sudo("rm -rf ..?* .[!.]* *")
sudo("git clone %(GIT_URL_INFO)s ." % git_context)
sudo("""git config --global user.email "%(GIT_USER_EMAIL)s" """% git_context )
sudo("""git config --global user.name "%(GIT_USER)s" """% git_context )
sudo("chown -R www-data:www-data /usr/share/nginx/%(PROJECT_NAME)s/" % git_context)
sudo("service nginx restart")
def common_deploy_ubuntu():
execute(git_pull)
def git_pull():
git_context = {
'GIT_URL' : env.URL_HOST,
'GIT_URL_INFO' : env.GIT_URL_INFO,
'PROJECT_NAME' : env.PROJECT_NAME
}
with cd("/usr/share/nginx/%(PROJECT_NAME)s/" % git_context):
sudo("git pull")
sudo("chown -R www-data:www-data /usr/share/nginx/%(PROJECT_NAME)s/" % git_context)
sudo("service nginx restart")
SERVER_TYPE = "VX" ## "VX" 또는 "AWS
if __name__ == "__main__":
print("======== Start ========")
if SERVER_TYPE == "VX" :
#AWS 서버 세팅시
server_virtualbox()
common_setting_ubutu()
elif SERVER_TYPE == "AWS" :
#AWS 서버에 소스 배포시
server_aws()
common_setting_ubutu()
print("======== End ========")
==============================
server_aws 에서 보듯이 pem 파일을 사용 할 수 있다.
그리고 여기서는 서버 1대만 적용되었다.
그러나
env.hosts = ['ubuntu@11.123.13.15:22']
이 부분에 다른 서버를 추가 한다면 동일 작업을 그 서버에게도 적용할 수 있다.
uncomment 함수는 주석을 제거하는 함수이다. Nginx 를 설치하면 기본적으로 gzip 옵션이 주석처리되어 있기에
이 부분의 주석을 해제 시켰다.
contains 함수는 특정 파일에 특정 스트링이 있는지 검사하는 부분이다. 보통 서버세팅 로직을 한 서버에 한 번만 돌리지 않고
여러번 돌릴 수도 있다. (경우에 따라서는 실수로, 경우에 따라서는 예기치 않은 에러 때문에 다시 한 번 돌리기도 한다.)
따라서 이렇게 될 때, 아래 있는 코드 부분이 너무 여러번 동작하면 이상히기 때문에 검사하는 코드를 넣었다.
with settings(warn_only=True):
sed("/etc/nginx/nginx.conf", old_string, new_string , use_sudo = True, shell = True, flags="m")
sed 부분은 해당 파일에서 스트링을 replace 하는 함수이다. flags="m" 의 의미는 multi line 이라는 의미이다.
변경하려는 string 이 multi line 이기 때문에 넣었다.
실제 동작하는 것은 replace 가 아니라 스트링 추가이다. 위에 코드를 보다시피 new_string 이 old_string을 포함하고 시작하기 때문에
실제로는 스트링 추가이다.
http {
======>
http {
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 을 추가하는 코드이다.
upload_template 함수는 템플릿 파일을 업로드 하는 함수이다. 템플릿 파일은 아래 res\config.txt 에 있는 파일이다.
업로드 하는 파일은 아래와 같이 변수를 적용 할 수 있다.
======= res\config.txt ===============
server {
listen 80;
server_name localhost %(URL_HOST)s;
#server_name 133.130.101.36 %(URL_HOST)s;
client_max_body_size 100M;
root /usr/share/nginx/%(PROJECT_NAME)s/;
access_log /var/log/nginx/%(PROJECT_NAME)s_access.log;
access_log /var/log/nginx/%(PROJECT_NAME)s_request.log post_log;
error_log /var/log/nginx/%(PROJECT_NAME)s_error.log;
index index.html index.htm;
# Make site accessible from http://localhost/
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
=============================
메인 부분을
if __name__ == "__main__":
git_pull()
이라고 수정하면 서버 세팅이 아니라 git 를 통한 소스 업데이트를 편하게 적용할 수 있다.
여기에서는 간단하게 서버세팅과 git 를 통한 소스 업데이트를 보여줬지만 uwsgi 까지 완전히 세팅을 완료 할 수 있을 것이다.
이 부분은 나중에 구현 하면 여기에 추가 하도록 하겠다.