转自:http://gibuloto.com/blog/deploy-django-with-fabric/
Deploy Django with Fabric
Install
1 2 | # 不要安裝在 virtualenv 裡面,不然每次 fab 都要進去 virtualenv 也是很麻煩 $ sudo pip install fabric
|
Operations
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 在 remote 執行指令 run()
# 在 remote 執行需要 sudo 的指令 sudo()
# 在 localhost 執行指令 local()
# 在 remote 執行 change directory,通常會搭配 with 使用 cd()
# 在 localhost 執行 change directory lcd()
|
cd()
、run()
、sudo()
只能用在 remote SSH 登入,本機的話要用 lcd()
、local()
、local('sudo some_command')
。
run()
默認的工作目錄就是 SSH 登入進去的家目錄,local()
, 則是 fabfile.py 所在的目錄。
References:
Example
我把 deploy 環境分成三種,production
、staging
、development
,分別對應到一個 fabfile。fabfile 實際上就是一個 .py 檔案,因為 Fabric 說穿了就是用 Python 來寫一串 bash 指令。
目錄結構如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | jojogo/ ├── app_product/ ├── fabfile.py ├── fabfiles/ │ ├── __init__.py │ ├── __init__.pyc │ ├── development.py │ ├── development.pyc │ ├── production.py │ ├── production.pyc │ ├── staging.py │ └── staging.pyc ├── jojogo/ ├── manage.py ├── requirements.txt ├── static/ └── templates/
|
in fabfile.py
1 2 3 4 5 | from fabric.api import *
from fabfiles import production as pro from fabfiles import staging as sta from fabfiles import development as dev
|
Usage:
1 2 3 4 | $ fab -l $ fab pro.uwsgi $ fab sta.uwsgi $ fab dev.django
|
in production.py (remote: Ubuntu)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | import os import sys from datetime import datetime
from fabric.api import * from fabric.colors import *
def set_env(): env.FABFILE_NAME = 'production' env.PROJECT_NAME = 'jojogo' env.PROJECT_PATH_REMOTE = '/src/jojogo' env.PROJECT_PATH_LOCAL = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) env.SOURCE_VIRTUALENVWRAPPER = 'source /usr/local/bin/virtualenvwrapper.sh' env.VIRTUALENV_NAME = env.PROJECT_NAME env.VIRTUALENV_WORKON = '%s && workon %s' % (env.SOURCE_VIRTUALENVWRAPPER, env.VIRTUALENV_NAME)
env.host_string = 'REMOTE SERVER IP' env.user = 'REMOTE SERVER SSH LOGIN USERNAME' env.key_filename = 'ABSOLUTE PATH OF KEY PAIR FILE'
@task def checkout(): ''' Checkout project from Subversion '''
set_env()
sudo('apt-get update') sudo('apt-get install git mercurial subversion') run('svn co --username USERNAME https://SVN_REPO_URL/')
@task def setup(): ''' Install all services & apps '''
def install_postgresql_and_postgis(): sudo('apt-get install binutils gdal-bin libproj-dev postgresql-9.1-postgis postgresql-server-dev-9.1 python-psycopg2')
def install_nginx(): run('wget http://nginx.org/keys/nginx_signing.key') sudo('apt-key add nginx_signing.key') put('config/etc/apt/sources.list.d/nginx.list', '/etc/apt/sources.list.d/nginx.list', use_sudo=True) sudo('apt-get update') sudo('apt-get install nginx')
# 上傳配置文件 put('config/nginx/nginx.conf', '/etc/nginx.conf', use_sudo=True) put('config/nginx/conf.d/guangdj.conf', '/etc/nginx/conf.d/guangdj.conf', use_sudo=True)
# 刪除範例的配置文件 sudo('rm /etc/nginx/conf.d/default.conf') sudo('rm /etc/nginx/conf.d/example_ssl.conf')
sudo('service nginx restart')
def install_pip(): sudo('curl http://python-distribute.org/distribute_setup.py | python') sudo('curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python')
def install_virtualenvwrapper(): sudo('pip install virtualenvwrapper')
def install_pil(): sudo('apt-get install apt-get build-dep python-imaging')
def install_uwsgi(): sudo('apt-get install build-essential python-dev libxml2-dev')
log_dir = '~/log/uwsgi'
run('mkdir -p %s' % log_dir) run('touch %s/guangdj.log' % log_dir)
set_env()
install_postgresql_and_postgis() install_nginx() install_pip() install_virtualenvwrapper() install_pil() install_uwsgi()
with prefix(env.SOURCE_VIRTUALENVWRAPPER): ''' 必須 source virtualenvwrapper.sh 否則會出現 /bin/sh: workon: command not found '''
run('mkvirtualenv --no-site-packages %s' % env.VIRTUALENV_NAME)
with prefix(env.VIRTUALENV_WORKON): with cd(env.PROJECT_PATH_REMOTE): run('pip install -r requirements.txt') run('yolk -l') run('mkdir -p static_root') run('python manage.py collectstatic --clear --noinput')
@task def syncdb(): ''' Update & migrate Django database '''
set_env()
with prefix(env.VIRTUALENV_WORKON): run('python manage.py syncdb')
@task def nginx(): ''' Reload nginx '''
set_env()
sudo('service nginx restart')
@task def celery(): ''' Reload Celery '''
set_env()
with prefix(env.VIRTUALENV_WORKON): try: sudo("ps auxww | grep 'celery' | awk '{print $2}' | xargs kill -9") except: print(green('雖然有錯誤訊息,但是 celeryd 還是有被 kill'))
sudo('python manage.py celeryd_detach')
@task def uwsgi(): ''' Reload uWSGI '''
set_env()
with prefix(env.VIRTUALENV_WORKON): run('svn up') run('python manage.py collectstatic --clear --noinput') run('killall -9 uwsgi') run('uwsgi --ini config/uwsgi_conf.ini')
|
in development.py (localhost: Mac)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | import os import socket import sys from datetime import datetime
from fabric.api import * from fabric.colors import *
def set_env(): env.FABFILE_NAME = 'development' env.PROJECT_NAME = 'jojogo' env.PROJECT_PATH_LOCAL = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) env.EXPORT_POSTGRESQL_BIN = 'export PATH=/usr/local/Cellar/postgresql/9.1.4/bin:$PATH' env.SOURCE_VIRTUALENVWRAPPER = 'source /usr/local/bin/virtualenvwrapper.sh' env.VIRTUALENV_WORKON = '%s && workon %s' % (env.SOURCE_VIRTUALENVWRAPPER, env.PROJECT_NAME) env.CURRENT_IP = socket.getaddrinfo(socket.gethostname(), None)[0][4][0]
@task def setup(): ''' Install all services & apps '''
def install_postgresql_and_postgis(): local('brew update') local('brew install postgresql') local('brew versions postgis')
def install_virtualenvwrapper(): local('sudo pip install --upgrade virtualenvwrapper')
def install_pil(): local('brew install jpeg lzlib')
set_env()
install_postgresql_and_postgis() install_virtualenvwrapper() install_pil()
with prefix(env.SOURCE_VIRTUALENVWRAPPER): ''' 必須 source virtualenvwrapper.sh 否則會出現 /bin/sh: workon: command not found '''
local('mkvirtualenv --no-site-packages %s' % env.PROJECT_NAME)
with prefix(env.VIRTUALENV_WORKON): local('pip install -r requirements.txt') local('yolk -l')
@task def postgresql(run_in='backgound'): ''' Run PostgreSQL '''
set_env()
with prefix(env.EXPORT_POSTGRESQL_BIN): if run_in == 'backgound': local('pg_ctl -D ~/Developer/postgresql/%s -l /tmp/postgresql.%s.log start' % (env.PROJECT_NAME, env.PROJECT_NAME)) else: local('pg_ctl -D ~/Developer/postgresql/%s start' % (env.PROJECT_NAME))
@task def celery(run_in='backgound'): ''' Run Celery '''
set_env()
with prefix(env.VIRTUALENV_WORKON): if run_in == 'backgound': try: local('python manage.py celeryd_detach') except: print(green('celery 已經在執行了')) else: local('python manage.py celery worker --loglevel=info')
@task def django(): ''' Run Django '''
set_env()
with prefix(env.VIRTUALENV_WORKON): local('python manage.py runserver %s:8000' % env.CURRENT_IP)
@task def run_all(): ''' Run all services in backgound '''
set_env()
with prefix(env.VIRTUALENV_WORKON): postgresql() celery() django()
|