Fabric使用自定义参数部署到多个主机

时间:2015-12-23 12:48:47

标签: python deployment fabric

我正在寻找在多个服务器上执行任务的方法(是的我知道roledefs -R和hosts -H选项)但是我需要一些额外的参数,例如每个主机等不同的用户,我想保持它干净并且整洁,因为我将我的主机作为函数定义(在某处的堆栈上建议),以便有可能在这样的一个上执行任务

def test():
    user='root' 
    env.host='myapp-test.com'

我开始修改env.tasks但事实证明它们是由生成器迭代的,并且通过context_manager访问它们仅用于视图(如fab文档中)。

我想保留我的“类似函数的主机”,所以我最终动态修改env.hosts并编写装饰器,根据当前的env.host更新服务器特定的数据(将来会覆盖默认的任务装饰器)工作代码下面(为了安全起见,我不得不在代码中更改名称希望没有破坏任何功能):

APP_SERVERS= {
    'test':{
        'envname':'test',
        'user':'deploy_user',
        'host':'myapp-test.com',
        'host_string':'myapp-test.com',
        'path':'/opt/myapp/test/',
        'www_root':'http://myapp-test.com/',
        'retries_before_killing':3,
        'retry_sleep':2
    },
    'staging':{
        'envname':'staging',
        'user':'deploy_user',
        'host':'myapp-staging.com',
        'host_string':'myapp-staging.com',
        'path':'/opt/myapp/staging/',
        'www_root':'http://myapp-staging.com/',
        'retries_before_killing':3,
        'retry_sleep':2
    },
    'uat':{
        'envname':'uat',
        'user':'deploy_user',
        'host':'myapp-uat.com',
        'host_string':'myapp-uat.com',
        'path':'/opt/myapp/uat/',
        'www_root':'http://myapp-uat.com/',
        'retries_before_killing':3,
        'retry_sleep':2
    },
    'live1':{
        'envname':'live1',
        'user':'deploy_user',
        'host':'myapp-live1.com',
        'host_string':'myapp-live1.com',
        'path':'/opt/myapp/live1/',
        'www_root':'http://myapp-live1.com/',
        'retries_before_killing':1,
        'retry_sleep':1
    },
    'live2':{
        'envname':'live2',
        'user':'deploy_user',
        'host':'myapp-live2.com',
        'host_string':'myapp-live2.com',
        'path':'/opt/myapp/live2/',
        'www_root':'http://myapp.com/',
        'retries_before_killing':1,
        'retry_sleep':1
    }
}

TEST_HOSTS = ['test','staging','uat']
LIVE_HOSTS = ['live1','live2']

def test():
    env.update(dict(APP_SERVERS['test']))

def staging():
    env.update(dict(APP_SERVERS['staging']))

def uat():
    env.update(dict(APP_SERVERS['uat']))

def live1():
    env.update(dict(APP_SERVERS['live1']))

def live2():
    env.update(dict(APP_SERVERS['live2']))


#  GROUPS OF SERVERS DEFINITION
def live_servers():
    env['hosts'] = [APP_SERVERS[a]['host'] for a in APP_HOSTS]

def test_servers():
    env['hosts'] = [APP_SERVERS[a]['host'] for a in APP_HOSTS]

def env_update(func):
    def func_wrapper(*args,**kwargs):
        if not len(env.hosts):
            return func(*args,**kwargs)
        else:
            env.update(dict(APP_SERVERS[filter(lambda x: APP_SERVERS[x]['host']==env.host,APP_SERVERS)[0]]))
            func(*args,**kwargs)
    return func_wrapper

@env_update
def pull_commits():
    #some_code
    run('uptime')

我有可能运行组执行任务fab live_servers pull_commits以及单个fab live1 pull_commits。

我知道可能还有像使用单独服务器的重复任务一样,live1 pull_commits live2 pull_commits但我相信这个结构是为分布式系统编写的,它有不同的应用程序和用户等路径。

所以我的问题是:有更简单的方法吗?喜欢构建到fabric中的东西(还有带额外dict键的roledef对我不起作用)?或者我没有看到一些面料功能? 我想保留这个简单的单/多主机部署命令,如:fab live_servers pull_commits,fab test pull_commits

1 个答案:

答案 0 :(得分:0)

不要使用当前的方法,而是考虑使用host_string作为ARG_SERVERS字典的关键字,然后根据需要在任务中加载信息(包括对结构环境的任何必要更新。进行此调整意味着你也应该能够让角色发挥作用。

例如:

@task
def pull_commits():
    hostinfo = ARG_SERVERS[env.host_string]
    # ... more code
    run(...)

另一个建议是考虑使用ssh配置文件(〜/ .ssh / config)为所有机器定义ssh别名。这会将您的所有host / host_string / username信息放在一个中心位置,并且具有以下优点:您可以引用具有单个有意义名称的主机并简化ARG_SERVERS。

# contents of $HOME/.ssh/config
Host test
    HostName myapp-test.com
    User deploy-user

Host staging
    HostName myapp-staging.com
    User deploy-user

Host uat
    HostName myapp-uat.com
    User deploy-user

Host live1
    HostName myapp-live1.com
    User deploy-user

Host live2
    HostName myapp-live2.com
    User deploy-user

根据更传统的结构任务模型,您现在应该可以执行以下操作:

# single host
$ fab -H live1 pull_commits

# multiple hosts
$ fab -H test,staging pull_commits

或角色(根据结构文档)。