应用部署 Fabric
当服务器规模比较小时,部署应用通常需要使用 SSH 登录服务器,备份,执行部署命令,退出,再登录另外一台服务器执行上述操作。这个过程会花费很多时间做一些重复性的工作,还需要确保执行正确。Fabric 正是为了提高这些基于 SSH 的应用部署和系统管理效率而诞生的。它通过原生的 SSH 库 Paramiko(库的作者就是 Fabric 的作者)实现与远程服务器的自动化交互,直接用命令行就可以在远程服务器上执行任务。
我们先安装它:
> pip install fabric
看一个非常简单的例子(fabfile.py):
from fabric.api import run
def hostname():
run('hostname')
def ls(path='.'):
local('ls{}'.format(path))
执行一下:
> fab-H localhost,192.168.0.130-f chapter7/section2/fabfile.py hostname [localhost] Executing task 'hostname' [localhost] run:hostname [localhost] Login password for 'vagrant': [localhost] out:WEB [localhost] out: [192.168.0.130] Executing task 'hostname' [192.168.0.130] run:hostname [192.168.0.130] out:CODE [192.168.0.130] out: Done. Disconnecting from 192.168.0.130... done. Disconnecting from localhost... done.
上面的 hostname 函数没有参数,但是函数 ls 可以接受参数。带参数的函数通常使用默认参数:
> fab-H 192.168.0.132-f chapter7/section2/fabfile.py ls #通过默认值列出当前目录下的内容 > fab-H 192.168.0.132-f chapter7/section2/fabfile.py ls:/home #列出/home 目录下的内容 > fab-H 192.168.0.132-f chapter7/section2/fabfile.py ls:path=/home #和上面的等价,使用更明确地语法 > cd chapter7/section2 > fab-H 192.168.0.132 ls #如果切换到 fabfile.py 的父级目录,就不需要使用-f 参数了
上述任务在执行时需要输入密码,通常需要添加 SSH 信任,添加之后不需要密码就能登录了:
> ssh-keygen > ssh-copy-id-i~/.ssh/id_rsa.pub localhost > ssh-copy-id-i~/.ssh/id_rsa.pub 192.168.0.130
SSH 信任是把双刃剑,一定要注意服务器的安全,尤其是双向的 SSH 信任。
Fabric 应用接口
Fabric 提供非常多的接口。基于封装、便捷性等考虑,这些接口都放在 fabric.api 中来维护。主要分为如下 6 种接口。
操作
1.run:用来在远程服务器上执行 shell 命令。
run('uptime')
result=run('ls-l/var/www')
print result.failed #可以把执行的结果保存下来
2.sudo:使用超级用户(或者其他用户)权限在远程服务器上执行 shell 命令。
sudo('ls/tmp/')
sudo('mkdir/data/nginx/tmp', user='nginx')
sudo('ls/data', user=1001)
result=sudo('ls/tmp/')
3.require:检查环境变量中是否有 require 中定义的变量,没有的话会直接终止执行。
4.reboot:重新启动远程服务器。
5.prompt:交互提示,作用类似 raw_input。
username=prompt('Please specify username:')
prompt('Specify favorite choice:', 'choice', default=1, validate=int) #输入
的结果存在 env.choice 里面,默认值为 1,会验证这个值是否是 int
6.get:从远程服务器下载文件。
7.put:向远程服务器上传文件。
8.open_shell:在远程服务器行启动一个完备的 shell。
9.local:执行本地命令。
上下文管理
它们是一系列需使用 with 语法的函数。
1.cd:切换远程目录。
with cd('/var/www'):
run('ls') #相当于 cd/var/www&&ls
2.lcd:作用同 cd,但是切换的是本地目录。
3.char_buffered:强制本地终端的管道类型是 character 而不是 line 和 buffered。
4.hide:隐藏指定类型的输出。
with hide('running', 'stdout', 'stderr'):
run('ls/var/www')
hide 的可选类型有 7 种,如表 7.1 所示。
表 7.1 hide 的可选输出类型及其含义
| 类型 | 含义 |
| status | 状态信息,比如服务器断开了连接,用户使用了 Ctrl-C 等 |
| aborts | 中止信息,一般在把 Fabric 当作库的时候需要关闭这样的状态信息 |
| warnings | 警告信息,如果错误是预期的,警告信息就会关闭 |
| running | 运行过程中的一些输出 |
| stdout | 本地或者远程与命令相关的非错误输出 |
| stderr | 本地或者远程与命令相关的错误输出 |
| user | 用户创建的输出 |
5.path:增加环境变量 PATH 的路径。
6.prefix:对于 prefix 下的每个命令都整体执行一遍。
with prefix('echo 1'):
run('echo 2')
run('echo 3')
相当于:
echo 1 && echo 2 echo 1 && echo 3
7.quiet:相当于隐藏了全部信息,在执行错误的时候仅仅发出警告信息。
8.remote_tunnel:通过 SSH 的端口转发建立转发通道。
with remote_tunnel(3306):
run('mysql-u root-p password')
这样就在本地连接了远程的 MySQL 实例。
9.settings:临时覆盖 env 变量。
10.shell_env:设置 shell 环境变量。
11. show:和 hide 相反,显示指定类型的输出。
12. warn_only:settings(warn_only=True) 的别名。
装饰器
通过装饰器可以更方便地操作 Fabric。
1.hosts:定义会执行此函数的远程服务器列表。
@hosts('user1@host1', 'host2', 'user2@host3')
def my_func():
pass
2.parallel:使用并行执行。
@parallel
def runs_in_parallel():
pass
3.roles:对主机按角色分组,定义执行函数的角色。
env.roledefs.update({
'webserver':['www1', 'www2'],
'dbserver':['db1']
})
@roles('webserver', 'dbserver')
def my_func():
pass
4.runs_once:只执行一次,防止函数被多次运行。
5.serial:强制远程服务器使用串行执行。
6.task:定义任务的函数。
7.with_settings:是 fabric.api.settings 的装饰器版本。
功能函数
1.abort:终止函数执行,打印错误信息到 stderr,使用 1 作为退出码。
2.warn:输出警告信息,但是不会终止函数的执行。
3.puts:打印输出。
状态
1.env:当前环境的字典。我们可以在部署程序中直接指定环境的值。
2.output:当前输出类型的字典。
任务执行。
可以通过 execute 函数在运行期执行任务函数:
from fabric.api import task, execute, run
@task
def info():
return run("print info")
@task
def go():
results=execute(info)
print results
使用 Fabric 管理 Flask 应用
本节我们来实现一个部署 Flask 应用的例子,它实现如下两个功能:
- 同时查看全部服务器上的根据内存占用排序的进程。
- 通过拷贝文件,杀掉原来的应用进程,最后启动新应用来实现部署。同时检查远程服务器上是否安装了 Gunicorn,如果没有安装的话,就自动安装(fabfile_app.py)。
from fabric.api import (
cd, run, task, env, roles, put, execute, parallel, hide, settings, sudo)
from fabric.colors import red, green
from fabric.contrib.files import exists
env.roledefs.update({
'webserver':['192.168.0.130', '192.168.0.132'],
'dbserver':['192.168.0.175']
})
@task
@parallel(pool_size=5)
@roles('webserver', 'dbserver')
def top_mem_proc():
run('ps-ef|sort-rk4|head')
@task
@roles('webserver')
def upload():
put('chapter6/section1/run.py', '/tmp/run.py')
def check_command(cmd):
rs=run('command-v{}>/dev/null 2>&1'.format(cmd))
return rs.return_code==0
@task
def install_it(package):
sudo('pip install{}'.format(package))
print green('{}installed'.format(package)) #输出颜色为绿色
@task
@roles('webserver')
def restart_app():
with cd('/tmp'):
if exists('/tmp/app.pid'):
pid=run('cat/tmp/app.pid')
run('kill-9{};rm/tmp/app.pid'.format(pid))
else:
print red('pid file not exists!') #错误提示为红色
with settings(hide('everything'), warn_only=True):
if not check_command('gunicorn'):
install_it('gunicorn')
with hide('running', 'stdout'):
run('gunicorn-w 3 run:app-b 0.0.0.0:8000-D-p/tmp/app.pid--log-file/
tmp/app.log') # noqa
@task
def deploy():
execute(upload)
execute(restart_app)
现在就可以部署应用了:
> fab-f chapter7/section2/fabfile_app.py deploy
使用 put 在处理多文件时很低效,可以使用 rsync_project 或者 upload_project 的方式对整个项目进行部署。
from fabric.contrib.project import rsync_project
rsync_project(
remote_dir='/opt/app',
local_dir='.',
exclude=('*_local.py', '*.pyc')
)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论