<python模块>fabric

时间:March 27, 2018 分类:

目录:

什么是fabric

fabric封装了paramiko,可以执行进行本地执行和远程执行,我们用来和saltstack组成互备方案,这样确保有批量工具。

安装fabric

pip install fabric

简单的例子

需要创建一个fabfile.py文件,fabric默认会找这个文件

[root@why 18:25:15 ~]#vi fabfile.py
def hello():
    print "Hello Fabric!"

执行fab命令

[root@why 18:25:33 ~]#fab hello
Hello Fabric!

Done.

在fabfile.py文件中每一个方法都是一个任务,任务名就是函数名,可以通过-l来参数来列出所有任务。

[root@why 18:26:21 ~]#fab -l
Available commands:

    hello

包括对于输入错的命令也可以提示,具体原因未知。

[root@why 20:44:33 ~]#fab hoell

Warning: Command(s) not found:
    hoell

Available commands:

    hello

对于传递参数的函数可以直接在方法传入

[root@why 19:08:59 ~]#vi fabfile.py
def hello(name, value):
    print "Hello Fabric!%s. %s" % (name, value)
[root@why 19:46:57 ~]#fab hello:why,study
Hello Fabric!why. study

Done.

如果不使用fabfile.py文件,可以通过-f参数指定脚本文件

fab -f script.py hello

执行本地命令

本机的命令通过fabric.api中的local()方法提供,用来执行shell命令

[root@why 20:44:33 ~]#vi fabfile.py
from fabric.api import local
def hello():
    local('df -h')
[root@why 20:44:38 ~]#fab hello
[localhost] local: df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        40G   29G  9.0G  77% /
devtmpfs        3.9G     0  3.9G   0% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           3.9G  3.3M  3.9G   1% /run
tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup
tmpfs           783M     0  783M   0% /run/user/1001

Done.

默认输出会被打印到控制台,而local提供了参数来获取标准输出

[root@why 20:55:47 ~]#vi fabfile.py
from fabric.api import local
def hello():
    output = local('df -h', capture=True)
[root@why 20:56:01 ~]#fab hello
[localhost] local: df -h

Done.

可以看到仍然还是有信息输出的

远程执行

from fabric.api import run, env
env.hosts = ['whysdomain']
env.user = 'wanghongyu'
env.password = '123456'
env.port = '23'

def hello():
    run('whoami')

执行fab task

[root@why 18:56:22 ~]#fab hello
[whysdomain] Executing task 'hello'
[whysdomain] run: whoami
[whysdomain] out: wanghongyu
[whysdomain] out: 


Done.
Disconnecting from whysdomain:23... done.

host可以直接写入到fabfile中,也可以通过-H参数指定

[root@why 18:56:26 ~]#vi fabfile.py
[root@why 19:02:44 ~]#fab -H 47.52.207.229 hello
[whysdomain] Executing task 'hello'
[whysdomain] run: whoami
[whysdomain] out: mrwhy
[whysdomain] out: 


Done.
Disconnecting from whysdomain:23... done.

多台主机用','隔开即可,这些主机之间执行是串行执行的。

对于不同主机执行不同任务,可以对服务器角色进行定义

from fabric.api import env, roles, run, execute, cd

env.roledefs = {
    'staging': ['bjhee@example1.com','bjhee@example2.com'],
    'build': ['build@example3.com']
}

env.passwords = {
    'staging': '11111',
    'build': '123456'
}

@roles('build')
def build():
    with cd('/home/build/myapp/'):
        run('git pull')
        run('python setup.py')

@roles('staging')
def deploy():
    run('tar xfz /tmp/myapp.tar.gz')
    run('cp /tmp/myapp /home/bjhee/www/')

def task():
    execute(build)
    execute(deploy)

执行task

fab task

fab会先执行build,根据装饰器@roles('build')找到env.roledefs中记录的主机,执行build方法的操作,然后执行deploy,根据装饰器@roles('staging')找到env.roledefs中记录的主机['bjhee@example1.com','bjhee@example2.com'],执行deploy方法的操作。

如果任务没有指定角色,也可以通过-R参数来指定执行的主机列表,对于多个角色也要用','分割

fab -R build deploy

注意: 'staging'角色是装饰器默认的,不通过-R指定也执行。

对于上边的fabfile来说,fab会对于build角色和staging都执行了deploy操作。

SSH功能函数

Fabric还提供了其他丰富的功能函数来辅助执行命令,这里我们介绍几个常用的:

sudo: 以超级用户权限执行远程命令

功能类似于”run()”方法,区别是它相当于在Shell命令前加上了”sudo”,所以拥有超级用户的权限。使用此功能前,你需要将你的用户设为sudoer,而且无需输密码。

from fabric.api import env, sudo

env.hosts = ['bjhee@example1.com', 'bjhee@example2.com']
env.password = '111111'

def hello():
    sudo('mkdir /var/www/myapp')

get(remote, local): 从远程机器上下载文件到本地

它的工作原理是基于scp命令,使用的方法如下:

from fabric.api import env, get

env.hosts = ['bjhee@example.com',]
env.password = '111111'

def hello():
    get('/var/log/myapp.log', 'myapp-0301.log')

上述任务将远程机上”/var/log/myapp.log”文件下载到本地当前目录,并命名为”myapp-0301.log”。

put(local, remote): 从本地上传文件到远程机器上

同get一样,put方法也是基于scp命令,使用的方法如下:

from fabric.api import env, put

env.hosts = ['bjhee@example1.com', 'bjhee@example2.com']
env.password = '111111'

def hello():
    put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz')

上述任务将本地”/tmp/myapp-0301.tar.gz”文件分别上传到两台远程机的”/var/www/”目录下,并命名为”myapp.tar.gz”。如果远程机上的目录需要超级用户权限才能放文件,可以在”put()”方法里加上”use_sudo”参数:

put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz', use_sudo=True)

prompt: 提示输入

该方法类似于Shell中的”read”命令,它会在终端显示一段文字来提示用户输入,并将用户的输入保存在变量里:

from fabric.api import env, get, prompt

env.hosts = ['bjhee@example.com',]
env.password = '111111'

def hello():
    filename = prompt('Please input file name: ')
    get('/var/log/myapp.log', '%s.log' % filename)

现在下载后的文件名将由用户的输入来决定。我们还可以对用户输入给出默认值及类型检查:

port = prompt('Please input port number: ', default=8080, validate=int)

执行任务后,终端会显示:

Please input port number: [8080]

如果你直接按回车,则port变量即为默认值8080;如果你输入字符串,终端会提醒你类型验证失败,让你重新输入,直到正确为止。

reboot: 重启服务器

有时候安装好环境后,需要重启服务器,这时就要用到”reboot()”方法,你可以用”wait”参数来控制其等待多少秒后重启,没有此参数则代表立即重启:

from fabric.api import env, reboot

env.hosts = ['bjhee@example.com',]
env.password = '111111'

def restart():
    reboot(wait=60)

上面的restart任务将在一分钟后重启服务器。

上下文管理器

Fabric的上下文管理器是一系列与Python的”with”语句配合使用的方法,它可以在”with”语句块内设置当前工作环境的上下文。

cd: 设置远程机器的当前工作目录

cd()方法在之前的范例中出现过,”with cd()”语句块可以用来设置远程机的工作目录:

from fabric.api import env, cd, put

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with cd('/var/www/'):
        put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')

上例中的文件会上传到远程机的/var/www/目录下。出了”with cd()”语句块后,工作目录就回到初始的状态,也就是”bjhee”用户的根目录。

lcd: 设置本地工作目录

lcd()就是”local cd”的意思,用法同”cd()”一样,区别是它设置的是本地的工作目录:

from fabric.api import env, cd, lcd, put

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with cd('/var/www/'):
        with lcd('/tmp/'):
            put('myapp-0301.tar.gz', 'myapp.tar.gz')

这个例子的执行效果跟上个例子一样。

path: 添加远程机的PATH路径

from fabric.api import env, run, path

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with path('/home/bjhee/tmp'):
        run('echo $PATH')
    run('echo $PATH')

假设我们的PATH环境变量默认是/sbin:/bin,在上述with path()语句块内PATH变量将变为/sbin:/bin:/home/bjhee/tmp。出了with语句块后,PATH又回到原来的值。

settings: 设置Fabric环境变量参数

Fabric环境变量即是我们例子中一直出现的fabric.api.env,它支持的参数可以从官方文档中查到。

from fabric.api import env, run, settings

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with settings(warn_only=True):
        run('echo $USER')

环境参数”warn_only”暂时设为True,这样遇到错误时任务不会退出。

shell_env: 设置Shell环境变量

可以用来临时设置远程和本地机上Shell的环境变量。

from fabric.api import env, run, local, shell_env

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with shell_env(JAVA_HOME='/opt/java'):
        run('echo $JAVA_HOME')
        local('echo $JAVA_HOME')

prefix: 设置命令执行前缀

from fabric.api import env, run, local, prefix

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with prefix('echo Hi'):
        run('pwd')
        local('pwd')

在上述with prefix()语句块内,所有的run()local()方法的执行都会加上echo Hi &&前缀,也就是效果等同于:

run('echo Hi && pwd')
local('echo Hi && pwd')

配合后一节我们会讲到的错误处理,它可以确保在”prefix()”方法上的命令执行成功后才会执行语句块内的命令。

错误处理

默认情况下,Fabric在任务遇到错误时就会退出,如果我们希望捕获这个错误而不是退出任务的话,就要开启warn_only参数。在上面介绍settings()上下文管理器时,我们已经看到了临时开启warn_only的方法了,如果要全局开启,有两个办法:

  1. 在执行fab命令时加上-w参数
fab -w hello
  1. 设置env.warn_only环境参数为True
from fabric.api import env

env.warn_only = True

现在遇到错误时,控制台会打出一个警告信息,然后继续执行后续任务。那我们怎么捕获错误并处理呢?像run(),local(),sudo(),get(),put()等SSH功能函数都有返回值。当返回值的succeeded属性为True时,说明执行成功,反之就是失败。你也可以检查返回值的failed属性,为True时就表示执行失败,有错误发生。在开启warn_only后,你可以通过failed属性检查捕获错误,并执行相应的操作。

from fabric.api import env, cd, put

env.hosts = ['bjhee@example1.com', ]
env.password = '111111'

def hello():
    with cd('/var/www/'):
        upload = put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')
        if upload.failed:
            sudo('rm myapp.tar.gz')
            put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz', use_sudo=True)

并行执行

多台机器的任务默认情况下是串行执行的。Fabric支持并行任务,当服务器的任务之间没有依赖时,并行可以有效的加快执行速度。怎么开启并行执行呢?

办法也是两个:

  1. 在执行fab命令时加上-P参数
fab -P hello

貌似还有-z参数

  1. 设置env.parallel环境参数为True
from fabric.api import env

env.parallel = True

如果,我们只想对某一任务做并行的话,我们可以在任务函数上加上”@parallel”装饰器

from fabric.api import parallel

@parallel
def runs_in_parallel():
    pass

def runs_serially():
    pass

这样即便并行未开启,runs_in_parallel()任务也会并行执行。

反过来,我们可以在任务函数上加上@serial装饰器:

from fabric.api import serial

def runs_in_parallel():
    pass

@serial
def runs_serially():
    pass

这样即便并行已经开启,runs_serially()任务也会串行执行。

补充

终端输出带颜色

我们习惯上认为绿色表示成功,黄色表示警告,而红色表示错误,Fabric支持带这些颜色的输出来提示相应类型的信息

from fabric.colors import *

def hello():
    print green("Successful")
    print yellow("Warning")
    print red("Error")

限制任务只能被执行一次

通过execute()方法,可以在一个fab命令中多次调用同一任务,如果想避免这个发生,就要在任务函数上加上@runs_once装饰器。

from fabric.api import execute, runs_once

@runs_once
def hello():
    print "Hello Fabric!"

def test():
    execute(hello)
    execute(hello)

现在不管我们”execute”多少次hello任务,都只会输出一次”Hello Fabric!”字样