我想使用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:
当我创建一个set_hosts()
任务,如Fabric docs所示,env.hosts设置正确。但是,这不是一个可行的选择,也不是装饰者。在命令行上传递主机最终会导致调用fabfile的某种shell脚本,我宁愿让一个工具正常工作。
它在Fabric文档中说'env.hosts只是一个Python列表对象'。根据我的观察,这根本不是真的。
任何人都可以解释这里发生了什么吗?如何设置要部署的主机?
答案 0 :(得分:128)
我通过声明每个环境的实际功能来实现这一点。例如:
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
以这种方式执行此操作的好处是test
和prod
函数可以在任何 fab函数之前使用,而不仅仅是部署。它非常有用。
答案 1 :(得分:75)
使用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'
...
答案 2 :(得分:49)
以下是serverhorrors answer的简单版本:
from fabric.api import settings
def mystuff():
with settings(host_string='12.34.56.78'):
run("hostname -f")
答案 3 :(得分:21)
自己被困在这上面了,但终于明白了。您只需无法从 任务中设置env.hosts配置。每个任务执行N次,每个指定的主机执行一次,因此设置基本上超出了任务范围。
查看上面的代码,您可以这样做:
@hosts('dev')
def deploy_dev():
deploy()
@hosts('staging')
def deploy_staging():
deploy()
def deploy():
# do stuff...
看起来它会像你想要的那样。
或者您可以在全局范围内编写一些自定义代码来手动解析参数,并在定义任务函数之前设置env.hosts。出于几个原因,这实际上就是我如何设置我的。
答案 4 :(得分:18)
从fab 1.5开始,这是一种动态设置主机的文档化方法。
http://docs.fabfile.org/en/1.7/usage/execution.html#dynamic-hosts
从以下文档中引用。
将execute与动态设置主机列表一起使用
Fabric的常见中高级用例是 参数化在运行时查找一个目标主机列表(使用时) 角色不够)。执行可以使这非常简单,就像 这样:
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)
答案 5 :(得分:10)
与其他一些答案相反, 可以修改任务中的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传递的任何设置。
答案 6 :(得分:9)
您需要设置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, ))
答案 7 :(得分:9)
解释为什么它甚至是一个问题。命令 fab 正在利用结构库来运行主机列表上的任务。如果您尝试更改任务中的主机列表,那么在迭代它时,您实际上是在尝试更改列表。或者,在没有定义主机的情况下,循环遍历一个空列表,在该列表中,您设置要循环的列表的代码永远不会被执行。
使用env.host_string是解决此问题的唯一方法,因为它直接指定要连接的主机的功能。如果您想要执行多个主机,这会导致一些问题,即您将重新构建执行循环。
人们在运行时设置主机的最简单方法是将env作为一项独特的任务保存,设置所有主机字符串,用户等。然后他们运行部署任务。它看起来像这样:
fab production deploy
或
fab staging deploy
分段和制作就像你给出的任务一样,但他们不会自己调用下一个任务。它必须像这样工作的原因是,任务必须完成,并且突破循环(主机,在env情况下为None,但在那时它是一个循环),然后循环结束主机(现在由前面的任务定义)重新开始。
答案 8 :(得分:3)
您需要在模块级别修改env.hosts,而不是在任务函数中修改。我犯了同样的错误。
from fabric.api import *
def _get_hosts():
hosts = []
... populate 'hosts' list ...
return hosts
env.hosts = _get_hosts()
def your_task():
... your task ...
答案 9 :(得分:3)
这很简单。只需初始化env.host_string变量,所有以下命令都将在此主机上执行。
from fabric.api import env, run
env.host_string = 'user@exmaple.com'
def foo:
run("hostname -f")
答案 10 :(得分:3)
我对Fabric非常陌生,但是为了让Fabric能够在多个主机上运行相同的命令(例如,在一个命令中部署到多个服务器),您可以运行:
fab -H staging-server,production-server deploy
其中登台服务器和生产服务器是您要对其运行部署操作的2台服务器。这是一个简单的fabfile.py,它将显示操作系统名称。请注意,fabfile.py应该与运行fab命令的目录位于同一目录中。
from fabric.api import *
def deploy():
run('uname -s')
至少适用于Fabric 1.8.1。
答案 11 :(得分:3)
因此,为了设置主机并让命令在所有主机上运行,您必须从以下开始:
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。
答案 12 :(得分:2)
您可以在执行子任务之前分配给env.hoststring
。如果要迭代多个主机,请在循环中分配给此全局变量。
不幸的是,对于您和我来说,面料不是为这个用例而设计的。查看http://github.com/bitprophet/fabric/blob/master/fabric/main.py处的main
功能,了解其工作原理。
答案 13 :(得分:2)
这是另一个“summersault”模式,支持fab my_env_1 my_command
用法:
使用这种模式,我们只需要使用字典一次定义环境。 env_factory
根据ENVS
的键名创建函数。我将ENVS
放在自己的目录中,并将secrets.config.py
文件与结构代码分开配置。
缺点是,如上所述,添加@task
装饰器将break it。
注意:由于late binding,我们在工厂中使用def func(k=k):
代替def func():
。我们使用this solution获取运行模块并对其进行修补以定义函数。
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
答案 14 :(得分:0)
目前,使用角色被认为是“正确”和“正确”的方式,这是你“应该”做到的。
也就是说,如果你像你想要的那样,或者“欲望”的大部分就是能够在运行中执行“扭曲的系统”或切换目标系统。
因此,仅出于娱乐目的(!),以下示例说明了许多人可能会考虑的风险,但以某种方式完全令人满意的操作,如下所示:
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