Fabric部署、维护

Fabric的应用范围是自动化运维,可以让以往需要ssh链接到远程服务器执行的部署、更新命令写入脚本中自动完成,大大降低项目部署、维护的成本, 官方文档:http://www.fabfile.org/。

指定hosts:

from fabric.api import *

env.hosts = ['dreamgo@192.168.1.102:22', ]

env.passwords = {
    'dreamgo@192.168.1.102:22': 'dreamgo',
}


# >fab -H dreamgo@192.168.1.10:22 host_type
@hosts('dreamgo@192.168.1.102:22')
def host_type():
    with cd('/django/SchoolMS_Server/SchoolMS'):
        run('uname -s')
        run('service apache2 reload')

指定roles:

from fabric.api import *

env.roledefs = {
    'test_server': ['dreamgo@192.168.1.102:22'],
}

env.passwords = {
    'dreamgo@192.168.1.102:22': 'dreamgo',
}


# >fab -R test_server host_type
@roles('test_server')
def host_type():
    with cd('/django/SchoolMS_Server/SchoolMS'):
        run('uname -s')
        run('service apache2 reload')

Django刷新程序命令:

from fabric.api import *
from contextlib import contextmanager as _contextmanager

env.roledefs = {
    'test_server': ['dreamgo@192.168.1.102:22'],
}

env.passwords = {
    'dreamgo@192.168.1.102:22': 'dreamgo',
}

env.directory = '/django/SchoolMS_Server/'
env.activate = 'source environment/bin/activate'


@_contextmanager
def virtualenv():
    with cd(env.directory):
        with prefix(env.activate):
            yield


@roles('test_server')
def update():
    with virtualenv():
        run('git pull origin master')
        run('python manage.py makemigrations')
        run('python manage.py migrate')
        run('deactivate')
    run('service apache2 reload')

执行结果:

(environment) C:\django\SchoolMS\SchoolMS>fab update
[dreamgo@192.168.1.102:22] Executing task 'update'
[dreamgo@192.168.1.102:22] run: git pull origin master
[dreamgo@192.168.1.102:22] out: 来自 https://git.coding.net/name/xxxx.git
[dreamgo@192.168.1.102:22] out:  * branch            master     -> FETCH_HEAD
[dreamgo@192.168.1.102:22] out: 更新 94bdffc..6e43223
[dreamgo@192.168.1.102:22] out: Fast-forward
[dreamgo@192.168.1.102:22] out:  SchoolMS/fabfile.py | 30 ?[32m++++++++++++++++++++++++++++++?[m
[dreamgo@192.168.1.102:22] out:  requirements.txt    |  3 ?[32m++?[m?[31m-?[m
[dreamgo@192.168.1.102:22] out:  2 files changed, 32 insertions(+), 1 deletion(-)
[dreamgo@192.168.1.102:22] out:  create mode 100644 SchoolMS/fabfile.py
[dreamgo@192.168.1.102:22] out:

[dreamgo@192.168.1.102:22] run: python manage.py makemigrations
[dreamgo@192.168.1.102:22] out: No changes detected
[dreamgo@192.168.1.102:22] out:

[dreamgo@192.168.1.102:22] run: python manage.py migrate
[dreamgo@192.168.1.102:22] out: ?[36;1mOperations to perform:?[0m
[dreamgo@192.168.1.102:22] out: ?[1m  Apply all migrations: ?[0madmin, auth....
[dreamgo@192.168.1.102:22] out: ?[36;1mRunning migrations:?[0m
[dreamgo@192.168.1.102:22] out:   No migrations to apply.
[dreamgo@192.168.1.102:22] out:

[dreamgo@192.168.1.102:22] run: deactivate
[dreamgo@192.168.1.102:22] run: service apache2 reload
[dreamgo@192.168.1.102:22] out:  * Reloading web server apache2
[dreamgo@192.168.1.102:22] out:  *
[dreamgo@192.168.1.102:22] out:


Done.
Disconnecting from dreamgo@192.168.1.102... done.

创建数据库的两种方法:

run('echo "drop database if exists {};"|mysql --batch --user=root --password'.format(env.db), pty=True)
run('echo "create database {} default character set utf8;"|mysql --batch --user=root --password'.format(env.db),
    pty=True)

run('mysql -uroot -p -e "drop database if exists {}"'.format(env.db))
run('mysql -uroot -proot -e "CREATE DATABASE {} DEFAULT CHARACTER set utf8"'.format(env.db))

常用命令:

fab -R [role_name] 函数名称
fab -H [host_name] 函数名称

小技巧:

  • passwords指定为''时运行命令时会提示输入密码。
  • 远程服务器为python2python3共存,以python3为默认python的服务器,使用命令python2 -m pip install Fabric来安装。本地服务器只有python3环境,使用命令pip install Fabric3来安装。
  • Fabric3requirements.txt应写为Fabric3==1.13.1.post1
  • 只需要本地安装Fabric即可,不要求远程也安装Fabric(所以无需把Fabric3==1.13.1.post1写入requirements.txt文件)。

Django常用的部署、更新脚本fabfile.py(推荐放置在manage.py`所在的根目录):

from fabric.api import *
from fabric.contrib import django, files
from contextlib import contextmanager as _contextmanager

django.settings_module('NingTe.settings')
from django.conf import settings

_ = settings.INSTALLED_APPS

env.roledefs = {
    'test_server': ['dreamgo@192.168.1.102:22'],
}

env.passwords = {
    'dreamgo@192.168.1.102:22': '',
}

# 项目存放目录
env.directory = '/django/portal/'
# 虚拟环境目录 从项目根目录算起 environment为默认的虚拟环境文件夹名称
env.activate = 'source environment/bin/activate'
# git仓库地址
env.repo = ''
env.db = settings.DATABASES['default']['NAME']


@_contextmanager
def virtualenv():
    with cd(env.directory):
        with prefix(env.activate):
            yield


# 数据库、目录、克隆代码、环境安装、插件安装、迁移数据库、创建超管
# >fab create 一键部署
@roles('test_server')
def create():
    run('mysql -uroot -p -e "drop database if exists {}"'.format(env.db))
    run('mysql -uroot -p -e "create database {} default character set utf8"'.format(env.db))
    run('rm -rf {}'.format(env.directory))
    run('mkdir {}'.format(env.directory))
    run('git clone {} {}'.format(env.repo, env.directory))
    with cd(env.directory):
        run('virtualenv environment')
        with virtualenv():
            run('pip install -r requirements.txt -i https://pypi.douban.com/simple')
            run('python manage.py makemigrations')
            run('python manage.py migrate')
            run('python manage.py createsuperuser --username=username --email=ykh@dreamgo.tech')
            run('deactivate')


# 拉取代码、迁移数据库、重载Apache
# >fab update 一键维护
@roles('remote_server')
def update():
    with virtualenv():
        run('git stash')
        run('git pull')
        run('git stash pop')
        run('pip install -r requirements.txt -i https://pypi.douban.com/simple')
        run('python manage.py makemigrations')
        run('python manage.py migrate')
        run('deactivate')
    run('service apache2 reload')


# 创建并配置Apache的conf文件 server_name-域名或IP environment为默认的虚拟环境文件夹名称
# >fab apache_conf 一键配置Apache设置
@roles('test_server')
def apache_conf():
    directory = env.directory.rstrip('/')
    run('chmod -R 777 {}'.format(env.directory))
    file_path = '/etc/apache2/sites-available/'
    filename = 'portal.conf'
    with cd(file_path):
        run('rm -f {}'.format(filename))
        run('touch {}'.format(filename))
        server_name = 'blog.dreamgotech.com'
        apache_template = \
            '<VirtualHost *:8001>\n' \
            '   ServerName {server_name}\n' \
            '   ServerAdmin ykh@dreamgo.tech\n' \
            '   DocumentRoot {document_root}\n' \
            '   \n' \
            '   Alias /static {document_root}/static \n' \
            '   <Directory {document_root}/static>\n' \
            '       Require all granted\n' \
            '   </Directory>\n' \
            '   \n' \
            '   Alias /media {document_root}/media\n' \
            '   <Directory {document_root}/media> \n' \
            '       Require all granted\n' \
            '   </Directory>\n' \
            '   \n' \
            '   <Directory {document_root}/Portal>\n' \
            '       <Files wsgi.py>\n' \
            '           Require all granted\n' \
            '       </Files>\n' \
            '   </Directory>\n' \
            '   \n' \
            '   WSGIPassAuthorization On\n' \
            '   WSGIDaemonProcess {server_name} python-path={document_root} python-home={document_root}/environment \n' \
            '   WSGIProcessGroup {server_name}\n' \
            '   WSGIScriptAlias / {document_root}/Portal/wsgi.py\n' \
            '   \n' \
            '   ErrorLog {document_root}/log/error.log\n' \
            '</VirtualHost>\n'.format(server_name=server_name, document_root=directory)
        files.append(filename, apache_template)
        run('sudo a2dissite {}'.format(filename))
        run('sudo a2ensite {}'.format(filename))
        run('service apache2 reload')


# usage:fab cmd:"service apache2 reload"
@roles('test_server')
def cmd(cmd_str=''):
    run(cmd_str)