我想使用Fabric将我的Web应用程序代码部署到开发,登台和生产服务器.我的fabfile:
def deploy_2_dev(): deploy('dev') def deploy_2_staging(): deploy('staging') def deploy_2_prod(): deploy('prod') def deploy(server): print 'env.hosts:', env.hosts env.hosts = [server] print 'env.hosts:', env.hosts
样本输出:
host:folder user$ fab deploy_2_dev env.hosts: [] env.hosts: ['dev'] No hosts found. Please specify (single) host string for connection:
当我创建Fabric文档中set_hosts()
显示的任务时,env.hosts设置正确.但是,这不是一个可行的选择,也不是装饰者.在命令行上传递主机最终会导致调用fabfile的某种shell脚本,我宁愿让一个工具正常工作.
它在Fabric文档中说'env.hosts只是一个Python列表对象'.根据我的观察,这根本不是真的.
谁能解释一下这里发生了什么?如何设置要部署的主机?
我这样做是通过声明每个环境的实际功能.例如:
def test(): env.user = 'testuser' env.hosts = ['test.server.com'] def prod(): env.user = 'produser' env.hosts = ['prod.server.com'] def deploy(): ...
使用上面的函数,我将键入以下内容以部署到我的测试环境:
fab test deploy
...以及以下部署到生产:
fab prod deploy
这样做的好处是可以在任何 fab函数之前使用test
和函数,而不仅仅是部署.它非常有用.prod
使用roledefs
from fabric.api import env, run env.roledefs = { 'test': ['localhost'], 'dev': ['user@dev.example.com'], 'staging': ['user@staging.example.com'], 'production': ['user@production.example.com'] } def deploy(): run('echo test')
用-R选择角色:
$ fab -R test deploy [localhost] Executing task 'deploy' ...
这是一个更简单的serverhorrors版本答案:
from fabric.api import settings def mystuff(): with settings(host_string='192.0.2.78'): run("hostname -f")
我自己也坚持了下来,但终于弄明白了.您无法在任务中设置env.hosts配置.每个任务执行N次,每个主机指定一次,因此该设置基本上在任务范围之外.
查看上面的代码,您可以简单地执行此操作:
@hosts('dev') def deploy_dev(): deploy() @hosts('staging') def deploy_staging(): deploy() def deploy(): # do stuff...
这似乎会做你想要的.
或者,您可以在全局范围内编写一些自定义代码,手动解析参数,并在定义任务函数之前设置env.hosts.出于几个原因,这实际上就是我如何设置我的.
从fab 1.5开始,这是一种动态设置主机的文档化方法.
http://docs.fabfile.org/en/1.7/usage/execution.html#dynamic-hosts
引用来自下面的文档.
将execute与动态设置的主机列表一起使用
Fabric的常见中高级用例是在运行时参数化一个目标主机列表的查找(当使用Roles时不够).执行可以使这非常简单,如下所示:
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. @task def 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)
相反,一些其他的答案,它是可以修改env
任务中的环境变量.但是,这env
仅用于使用该fabric.tasks.execute
功能执行的后续任务.
from fabric.api import task, roles, run, env from fabric.tasks import execute # Not a task, plain old Python to dynamically retrieve list of hosts def get_stressors(): hosts = [] # logic ... return hosts @task def stress_test(): # 1) Dynamically generate hosts/roles stressors = get_stressors() env.roledefs['stressors'] = map(lambda x: x.public_ip, stressors) # 2) Wrap sub-tasks you want to execute on new env in execute(...) execute(stress) # 3) Note that sub-tasks not nested in execute(...) will use original env clean_up() @roles('stressors') def stress(): # this function will see any changes to env, as it was wrapped in execute(..) run('echo "Running stress test..."') # ... @task def clean_up(): # this task will NOT see any dynamic changes to env
如果不包含子任务execute(...)
,将使用模块级env
设置或从fab
CLI 传递的任何内容.
你需要设置host_string
一个例子:
from fabric.context_managers import settings as _settings def _get_hardware_node(virtualized): return "localhost" def mystuff(virtualized): real_host = _get_hardware_node(virtualized) with _settings( host_string=real_host): run("echo I run on the host %s :: `hostname -f`" % (real_host, ))
解释为什么它甚至是一个问题.命令fab正在利用结构库来运行主机列表上的任务.如果您尝试更改任务中的主机列表,那么在迭代它时,您实际上是在尝试更改列表.或者在没有定义主机的情况下,循环遍历一个空列表,其中从未执行将列表设置为循环的代码.
env.host_string的使用是解决此问题的唯一方法,因为它直接指定要连接的主机的功能.如果您想要执行多个主机,这会导致一些问题,即您将重新构建执行循环.
人们在运行时设置主机的最简单方法是将env作为一个独特的任务保持,设置所有主机字符串,用户等.然后他们运行部署任务.它看起来像这样:
fab production deploy
要么
fab staging deploy
升级和生产就像你给出的任务一样,但他们不会自己调用下一个任务.它有这样的工作的原因,是该任务已经完成,并打破循环(主机,在ENV的情况下无,但在这一点上是一个的循环),然后有遍历主机(现在由前面的任务定义)重新开始.