关于python:如何在结构文件中设置目标主机

How to set target hosts in Fabric file

我想使用Fabric将我的Web应用程序代码部署到开发、分段和生产服务器上。我的FabFix:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

样品输出:

1
2
3
4
host:folder user$ fab deploy_2_dev
env.hosts: []
env.hosts: ['dev']
No hosts found. Please specify (single) host string for connection:

当我创建一个set_hosts()任务(如结构文档中所示)时,env.hosts设置正确。然而,这不是一个可行的选择,装饰师也不是。在命令行上传递主机最终会导致某种类型的shell脚本调用fabfile,我希望有一个单独的工具能够正确地完成这项工作。

它在结构文档中说"env.hosts只是一个python列表对象"。从我的观察来看,这根本不是真的。

有人能解释一下这是怎么回事吗?如何设置要部署的主机?


我做这个用declaring安实际函数为每个环境。例如: </P >

1
2
3
4
5
6
7
8
9
10
def test():
    env.user = 'testuser'
    env.hosts = ['test.server.com']

def prod():
    env.user = 'produser'
    env.hosts = ['prod.server.com']

def deploy():
    ...

使用以上的I型函数,将下面的deploy到我的测试环境: </P >

1
fab test deploy

...and下面的deploy的生产: </P >

1
fab prod deploy

在好的事关于做IT这通这是《testprod函数可以用在任何函数的Fab片段,不是任何deploy。。。。。。。这是incredibly有用。 </P >


使用roledefs </P >

1
2
3
4
5
6
7
8
9
10
11
from fabric.api import env, run

env.roledefs = {
    'test': ['localhost'],
    'dev': ['[email protected]'],
    'staging': ['[email protected]'],
    'production': ['[email protected]']
}

def deploy():
    run('echo test')

选择角色,与R: </P >

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


这是一个simpler版serverhorrors回答: </P >

1
2
3
4
5
from fabric.api import settings

def mystuff():
    with settings(host_string='12.34.56.78'):
        run("hostname -f")


这是卡是自己,但finally压延出来。你不能简单的配置集的env.hosts从A内的任务。每个任务是executed的时代,一次为每个主机specified,SO的设置是fundamentally外出任务范围。 </P >

寻找在你的上面的代码,你可以简单的做这个: </P >

1
2
3
4
5
6
7
8
9
10
@hosts('dev')
def deploy_dev():
    deploy()

@hosts('staging')
def deploy_staging():
    deploy()

def deploy():
    # do stuff...

这样看来,它会做什么,你是intending。。。。。。。 </P >

或你可以写一些自定义代码在全球范围,这将对arguments人力,和集env.hosts之前,你的任务是定义的函数。一个确实的原因,认为"S知道如何我已经集矿上。 </P >


由于fab 1.5,这是一种记录在案的动态设置主机的方法。

http://docs.fabfile.org/en/1.7/usage/execution.html动态主机

引用以下文档。

Using execute with dynamically-set host lists

A common intermediate-to-advanced use case for Fabric is to
parameterize lookup of one’s target host list at runtime (when use of
Roles does not suffice). execute can make this extremely simple, like
so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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将只被用于subsequent任务executed使用fabric.tasks.execute函数。 </P >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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或任何的设置是通过从fabCLI将可以使用。 </P >


到explain为什么它的平安夜的事件。在命令行的Fab片段是崩溃的织物的图书馆运行的任务的主机列表。如果你尝试和改变的主机列表内的任务,你是esentially attempting变革的一个列表,而迭代过它。或在案例在你已经定义好的主持人,过环空的列表在你的代码在集的列表来回路过executed是永远的。 </P >

使用env.host _字符串是一个全面的工作方法的这一行为,只有在它的中的直接的函数是什么主机的连接物。这个原因的一些问题,你会被执行的remaking环,如果你想有一个数的主机的执行。 </P >

在扩通的人"麦克的能力集的主机在运行时,是保持对环境populatiing作为一种distinct任务,是集上的所有主机的字符串,用户等。然后,他们deploy运行的任务。它看起来像这样: </P >

1
fab production deploy

或 </P >

1
fab staging deploy

在分期和生产是一类的任务你已经给定的,但他们做不呼叫下一任务本身。的原则,它有什么样的工作,这是你的任务来整理,和打破的回路(的主持人,在《环境案例NONE,但它是一个循环.这一点),然后有环过的主机(现在的定义由preceding任务anew)。 </P >


你需要host_string集将是一个实例: </P >

1
2
3
4
5
6
7
8
9
10
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, ))


我对Fabric完全陌生,但是要让Fabric在多台主机上运行相同的命令(例如,在一个命令中部署到多个服务器),您可以运行:

1
fab -H staging-server,production-server deploy

其中,临时服务器和生产服务器是要对其运行部署操作的两个服务器。下面是一个简单的fabfile.py,它将显示操作系统名称。注意fabfile.py应该与运行fab命令的目录相同。

1
2
3
4
from fabric.api import *

def deploy():
    run('uname -s')

这至少适用于织物1.8.1。


这是非常简单的。是的env.host _初始化的字符串变量和所有的下面将executed commands是这个主机。 </P >

1
2
3
4
5
6
from fabric.api import env, run

env.host_string = '[email protected]'

def foo:
    run("hostname -f")

你需要修改env.hosts在模块级的,而不是在一个函数的任务。我制造了同样的错误。 </P >

1
2
3
4
5
6
7
8
9
10
11
from fabric.api import *

def _get_hosts():
    hosts = []
    ... populate 'hosts' list ...
    return hosts

env.hosts = _get_hosts()

def your_task():
    ... your task ...


这是另一个"SummerSault"模式,它支持fab my_env_1 my_command的使用:

使用这种模式,我们只需要使用字典定义一次环境。env_factory根据ENVS的keynames创建函数。我将ENVS放在自己的目录中,并将secrets.config.py文件归档,以将config与结构代码分开。

缺点是,如文中所述,添加@task装饰器将破坏它。

注:由于后期绑定,我们在工厂使用def func(k=k):代替def func():。我们用这个解决方案得到了正在运行的模块,并对其进行了修补以定义函数。

机密.config.py

1
2
3
4
5
6
7
8
9
10
11
12
13
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'
    }
}

FabFiel.Py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

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

1
2
3
4
5
def PROD():
    env.hosts = ['10.0.0.1', '10.0.0.2']

def deploy(version='0.0'):
    sudo('deploy %s' % version)

一旦定义了这些内容,就在命令行上运行命令:

1
fab PROD deploy:1.5

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


你可以assign到env.hoststring之前执行的子任务。assign到这个全局变量中的一环,如果你想重复过多主机。 </P >

不幸的是为你和我,织物设计的方法,这是不使用的情况。结帐的main函数在http:/ / / / github.com bitprophet织物/片/主/纤维/ main.py看到它如何工作。 </P >


使用的角色是目前被视是"适当的"和"正确"的方式做了研究,这是什么,你的"应该"做的。 </P >

这是说,如果你知道你是什么,你最喜欢的"很喜欢"或"欲望"是to perform a"能力的"系统"或交换系统的目标是在飞。 </P >

用于娱乐用途的SO(只读)。下面的实例illustrates什么多考虑可能的危险,但不知什么原因,thoroughly satisfying manoeuvre,那就是这样的: </P >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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")

然后运行: </P >

1
fab perform_sumersault