首页 > 代码库 > python之fabric(二):执行模式(转)

python之fabric(二):执行模式(转)

执行模式

  • 执行模式可以让你在多个主机上执行多个任务。

执行策略:

  • 默认fabric是单个有序地执行方法,其行为如下:

      1. 创建一系列任务,通过fab任务执行要执行的任务;

      2. 根据主机列表定义,去执行每一个任务;

      3. 没有主机定义的任务,将在本地执行一次。

如:

from fabric.api import run, envenv.hosts = [host1, host2]def taskA():    run(ls)def taskB():    run(whoami)# 将在host1, host2都执行taskA, taskB$ fab taskA taskB # 执行顺序如下taskA executed on host1taskA executed on host2taskB executed on host1taskB executed on host2

定义任务:

  • 如何定义任务,详情可见这里。

定义主机列表:

  • 大多数时候你都需要根据目标,来定义多个远程主机,而不是仅在本地。

主机:

  • python定义的主机字符串为:
username@hostname:port #用户名和端口都可以省略
  • 主机定义也支持IPV6格式,如:
::1, [::1]:1222user@2001:db8::1 user@[2001:db8::1]:1222

角色:

  • 角色提供了可以定义一个字符串对应于一组主机列表,而不是每次都针对的是整个主机列表,如:
from fabric.api import env#假如www1, www2, www3是你的web服务器, 你可以指定任务由特定的角色来执行env.roledefs[webservers] = [www1, www2, www3]# 定义多个角色env.roledefs = {    web: [www1, www2, www3],    dns: [ns1, ns2]}

怎么构建hosts:

  • 通过全局的env:
from fabric.api import env, runenv.hosts = [host1, host2]def mytask():    run(ls /var/www)
  • env配置会被每个任务检查,因此你完全可以在方法中设置env,如:
from fabric.api import env, rundef set_hosts():    env.hosts = [host1, host2]def mytask():    run(ls /var/www)# 调用fab set_hosts mytask, set_hosts
  • 通过命令行参数指定:
$ fab -H host1,host2 mytask #会被env.hosts所重写,类似角色也可以通过-R role1, role2, ...来定义
  • 你也可以扩展命令行定义的主机,如:
from fabric.api import env, runenv.hosts.extend([host3, host4])def mytask():    run(ls /var/www)# 那么执行主机将merge为host1,host2,host3,host4fab -H host1, host2 mytask
NOTE: env.hosts是python的list对象,所以你可以用list.append()方法添加host,或者其他方法。
  • 针对每个任务的命令行参数:这是fabric提供一种更细粒度的定制:
from fabric.api import rundef mytask():    run(ls /var/www)# 该定义将重写其他任何hosts定义,mytask仅会在host1,host2上执行fab mytask:hosts="host1;host2"
  • 针对每个任务进行修饰:
from fabric.api import hosts, run@hosts(host1, host2)def mytask():    run(ls /var/www)# 或者my_hosts = (host1, host2)@hosts(my_hosts)def mytask():    # ...# 这种修饰将重写env.hosts设置, 但不会重写上面通过命令行定义的任务。
  • 总结下上面这些host与task绑定定义的优先级:
# 由高到低1. 命令行 fab mytask:host=host12. fabfile中修饰任务@hosts(host1)3. 在fabfile中设置env.hosts = [host1]4. 在命令行中-H host1,host2,...
  • 主机列表组合
from fabric.api import env, hosts, roles, runenv.roledefs = {role1: [b, c]}@hosts(a, b)@roles(role1)def mytask():    run(ls /var/www)# 若命令行不包含task定义,则上面的mytask家在a, b, c主机上执行
  • 排除特定的主机
# 若myrole被定义运行[host1, host2,...,host15], 则该命令将排除host2,host5$ fab -R myrole -x host2,host5 mytask #该-x并不会修改env.hosts

 

用Execute智能地执行任务:

  • 看这么一个例子:
from fabric.api import run, rolesenv.roledefs = {    db: [db1, db2],    web: [web1, web2, web3],}@roles(db)def migrate():    # Database stuff here.    pass@roles(web)def update():    # Code updates here.    pass# 在fabric1.2之前,我们需要fab migrate update来执行这两个任务,# 在fabric1.2之后,我们可以通过execute函数来执行这两个任务:from fabric.api import run, roles, executedef deploy():    execute(migrate)    execute(update)

 

通过Execute访问多主机的结果:

  • 一个范例
from fabric.api import env, roles, task, execute, run, runs_onceenv.roledefs = {    zoo: [zooserver],    mysql: [mysqlmaster],}@taskdef workhorse():    return run("uname -a")@roles(zoo)@task@runs_oncedef go():    results = execute(workhorse)    print results#执行fab go

 

用Execute动态设置host列表:

  • 我们可以通过传递参数,动态设置hosts,如:
from fabric.api import run, execute, task# For example, code talking to an HTTP API, or a database, or ...from mylib import external_datastore# This is the actual algorithm involved. It does not care about host# lists at all.def do_work():    run("something interesting on a host")# This is the user-facing task invoked on the command line.@taskdef deploy(lookup_param):    # This is the magic you don‘t get with @hosts or @roles.    # Even lazy-loading roles require you to declare available roles    # beforehand. Here, the sky is the limit.    host_list = external_datastore.query(lookup_param)    # Put this dynamically generated host list together with the work to be    # done.    execute(do_work, hosts=host_list)# 调用fab deploy:appfab deploy:other
  • 一个替代的方法:
from fabric.api import run, taskfrom mylib import external_datastore# Marked as a publicly visible task, but otherwise unchanged: still just# "do the work, let somebody else worry about what hosts to run on".@taskdef do_work():    run("something interesting on a host")@taskdef set_hosts(lookup_param):    # Update env.hosts instead of calling execute()    env.hosts = external_datastore.query(lookup_param)#调用fab set_hosts:app do_work#相比上一种方法,该方法可以轻易替换其他任务,如fab set_hosts:db snapshotfab set_hosts:cassandra,cluster2 repair_ringfab set_hosts:redis,environ=prod status

故障处理:

  • 故障处理也是一个比较实用的东西,fabric默认是"快速失败"的,一旦出现错误,就立即停止。
  • 我们可以通过settings上下文中的env.warn_only=True来临时指定,失败也继续执行。

连接:

  • fab本身是不对任何主机做远程连接的,她只会设置env.host_string的值(即当前的host)。
  • NOTE: 连接对象Connection会保存在fabric.state.connections中,并做缓存,以减少创建新连接的消耗。

懒连接:

  • fab在执行远程操作前,并不会事先去连接远程主机,如:
from fabric.api import *@hosts(host1)def clean_and_upload():    local(find assets/ -name "*.DS_Store" -exec rm {} \;)    local(tar czf /tmp/assets.tgz assets/)    put(/tmp/assets.tgz, /tmp/assets.tgz) //这里才会开始进行远程连接相关的操作:cache.get() == null?new : cache.get()    with cd(/var/www/myapp/):        run(tar xzf /tmp/assets.tgz)

关闭连接:

  • 连接缓存并不会关闭连接。fab工具会帮你管理连接,它会遍历所有连接,并在其退出前关闭连接。你也可以调用fabric.network.disconnect_all()来确保关闭所有连接。

多次连接尝试和跳过错误的主机:

  • fabric在连接远程主机出错之前,会尝试多次(env.connection_attempts,默认为1)连接,每次连接超时env.timeout 秒(默认为10)。

密码管理:

  • Fabric在内存中维护了两层的密码缓存管理,会记住你登录的密码和sudo时的密码,以免以后再次输入。
  • 第一层密码由 env.password保存,其值为ssh默认密码或sudo提示输入密码。第二层密码保存在env.passwords中,主要用于缓存user/host/port:password。
  • Fabric还是建议使用密钥进行ssh访问,如无密登录。

使用本地的SSH配置文件:

  • 你可以为Fabric指定特定的ssh配置文件(--ssh-config-path/env.ssh_config_path.),但默认Fabric并没有开启加载配置文件,需要设置env.use_ssh_config为True。
  • 若开启了Fabric加载配置文件的选项,则Fabric为使用以下一些配置项:

      User/Port ,HostName, IdentityFile, ForwardAgent, ProxyCommand。

具体参考可见:

http://docs.fabfile.org/en/1.9/usage/execution.html

python之fabric(二):执行模式(转)