如何在Fabric文件中设置目标主机

我想使用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: 

当我创build一个set_hosts()任务,如Fabric文档中所示,env.hosts设置正确。 然而,这不是一个可行的select,也不是一个装饰者。 在命令行上传递主机最终会导致某种调用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(): ... 

使用上面的函数,我会input以下内容来部署到我的testing环境:

 fab test deploy 

…以及以下部署到生产:

 fab prod deploy 

这样做的prod在于, testprod函数可以在任何晶圆厂function之前使用,而不仅仅是部署。 这是非常有用的。

使用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') 

-Rselectangular色:

 $ fab -R test deploy [localhost] Executing task 'deploy' ... 

这是一个更简单的serverhorrors的答案 :

 from fabric.api import settings def mystuff(): with settings(host_string='12.34.56.78'): run("hostname -f") 

被困在这个自己,但终于搞清楚了。 你根本不能 一个任务中设置env.hostsconfiguration。 每个任务执行N次,每个主机指定一次,所以设置基本上在任务范围之外。

看看你的代码,你可以简单地这样做:

 @hosts('dev') def deploy_dev(): deploy() @hosts('staging') def deploy_staging(): deploy() def deploy(): # do stuff... 

这似乎会做你想要的。

或者,您可以在全局范围内编写一些自定义代码来手动分析参数,并在定义任务函数之前设置env.hosts。 出于几个原因,这实际上是我如何设置我的。

由于晶圆厂1.5这是一个文件化的方式来dynamic设置主机。

http://docs.fabfile.org/en/1.7/usage/execution.html#dynamic-hosts

从下面的文档引用。

使用dynamic设置的主机列表执行

Fabric的一个常见的中高级用例是在运行时(在使用angular色不足时)参数化一个目标主机列表的查找。 执行可以使这个非常简单,就像这样:

 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环境variables。 但是,这个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的普及作为一个独特的任务来build立,所有的主机string,用户等等。然后他们运行部署任务。 它看起来像这样:

 fab production deploy 

要么

 fab staging deploy 

在哪里分期和生产就像你所给的任务一样,但是他们自己并没有要求下一个任务。 它必须这样工作的原因是任务必须完成,并且打破主机的环路(在env情况下为None,但是在那一点上它是一个循环),然后循环主机(现在由前面的任务定义)重新。

您需要在模块级别修改env.hosts,而不是在任务函数内。 我犯了同样的错误。

 from fabric.api import * def _get_hosts(): hosts = [] ... populate 'hosts' list ... return hosts env.hosts = _get_hosts() def your_task(): ... your task ... 

这很简单。 只需初始化env.host_stringvariables,所有以下命令将在此主机上执行。

 from fabric.api import env, run env.host_string = 'user@exmaple.com' def foo: run("hostname -f") 

我完全不熟悉Fabric,但为了让Fabric能够在多个主机上运行相同的命令(例如,通过一个命令部署到多个服务器),您可以运行:

 fab -H staging-server,production-server deploy 

其中staging-serverproduction-server是要运行部署操作的2台服务器。 这是一个简单的fabfile.py,它将显示操作系统名称。 请注意,fabfile.py应该与您运行fab命令的目录位于相同的目录中。

 from fabric.api import * def deploy(): run('uname -s') 

这至less适用于面料1.8.1。

执行子任务之前,您可以分配给env.hoststring 。 如果要遍历多个主机,则在循环中分配给此全局variables。

不幸的是,你和我,面料不是为这个用例devise的。 看看http://github.com/bitprophet/fabric/blob/master/fabric/main.py的;mainfunction,看看它是如何工作的。

因此,为了设置主机,并让命令在所有主机上运行,​​您必须从以下开始:

 def PROD(): env.hosts = ['10.0.0.1', '10.0.0.2'] def deploy(version='0.0'): sudo('deploy %s' % version) 

一旦定义了这些,然后在命令行上运行该命令:

 fab PROD deploy:1.5 

什么将在PROD函数中列出的所有服务器上运行部署任务,因为它在运行任务之前设置了env.hosts。

下面是另一个“summersault”模式,它使fab my_env_1 my_command用法如下:

有了这个模式,我们只需要用字典来定义一次环境。 env_factory根据ENVS的键名创build函数。 我把ENVS放在自己的目录下,并把文件ENVS从结构代码中分离出来。

缺点是,正如所写的,添加@task装饰器将会破坏它 。

注意:我们使用def func(k=k):而不是def func():因为后期绑定在工厂。 我们得到这个解决scheme的运行模块,并修补它来定义这个function。

secrets.config.py

 ENVS = { 'my_env_1': { 'HOSTS': [ 'host_1', 'host_2', ], 'MY_OTHER_SETTING': 'value_1', }, 'my_env_2': { 'HOSTS': ['host_3'], 'MY_OTHER_SETTING': 'value_2' } } 

fabfile.py

 import sys from fabric.api import env from secrets import config def _set_env(env_name): # can easily customize for various use cases selected_config = config.ENVS[env_name] for k, v in selected_config.items(): setattr(env, k, v) def _env_factory(env_dict): for k in env_dict: def func(k=k): _set_env(k) setattr(sys.modules[__name__], k, func) _env_factory(config.ENVS) def my_command(): # do work 

目前使用angular色被认为是“正确”和“正确”的方式,这是你“应该”做的。

也就是说,如果你像大多数你所希望的那样,或者希望能够执行一个“扭曲的系统”,或者在运行中切换目标系统。

所以下面的例子仅用于娱乐目的(!),下面的例子说明了许多人可能会认为是有风险的,但是完全令人满意的,这样的机动:

 env.remote_hosts = env.hosts = ['10.0.1.6'] env.remote_user = env.user = 'bob' env.remote_password = env.password = 'password1' env.remote_host_string = env.host_string env.local_hosts = ['127.0.0.1'] env.local_user = 'mark' env.local_password = 'password2' def perform_sumersault(): env_local_host_string = env.host_string = env.local_user + '@' + env.local_hosts[0] env.password = env.local_password run("hostname -f") env.host_string = env.remote_host_string env.remote_password = env.password run("hostname -f") 

然后运行:

 fab perform_sumersault