返回介绍

应用部署 Fabric

发布于 2025-04-20 18:52:16 字数 6671 浏览 0 评论 0 收藏

当服务器规模比较小时,部署应用通常需要使用 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')
)

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。