web-dev-qa-db-ja.com

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:
_

Fabric docs に示されているset_hosts()タスクを作成すると、env.hostsが適切に設定されます。ただし、これは実行可能なオプションではなく、デコレータでもありません。コマンドラインでホストを渡すと、最終的にfabfileを呼び出す何らかの種類のシェルスクリプトになります。1つのツールで適切に処理することをお勧めします。

Fabricのドキュメントでは、「env.hostsは単純にPythonリストオブジェクト」です。私の観察から、これは単に真実ではありません。

誰もがここで何が起こっているのか説明できますか?デプロイ先のホストを設定するにはどうすればよいですか?

107
ssc

これを行うには、各環境の実際の関数を宣言します。例えば:

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関数を展開するだけでなく、any fab関数の前に使用できることです。とても便利です。

128
Zac

roledefs を使用します

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を使用して役割を選択します。

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

serverhorrors answer の簡単なバージョンを次に示します。

from fabric.api import settings

def mystuff():
    with settings(Host_string='192.0.2.78'):
        run("hostname -f")
49
tobych

私はこれで立ち往生しましたが、最終的にそれを理解しました。単にできない env.hosts設定をwithinタスクから設定します。各タスクは、指定されたホストごとに1回ずつN回実行されるため、設定は基本的にタスクの範囲外です。

上記のコードを見ると、次のことが簡単にできます。

@hosts('dev')
def deploy_dev():
    deploy()

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

def deploy():
    # do stuff...

それはあなたが意図していることをするように思えます。

または、グローバルスコープでカスタムコードを記述して、引数を手動で解析し、タスク関数を定義する前にenv.hostsを設定することもできます。いくつかの理由で、それが実際に私の設定方法です。

21
GoldenBoy

Fab 1.5以降、これはホストを動的に設定する文書化された方法です。

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

以下のドキュメントから引用してください。

動的に設定されたホストリストでの実行の使用

Fabricの一般的な中級から上級のユースケースは、実行時に1つのターゲットホストリストのルックアップをパラメーター化することです(ロールの使用では不十分な場合)。 executeは、これを次のように非常に単純にすることができます。

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)
18
j-a

他のいくつかの答えとは異なり、タスク内でenv環境変数を変更することは可能ですis。ただし、この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から渡されたものが使用されます。

10
pztrick

なぜそれが問題なのかを説明するため。コマンドfabは、ホストリストでタスクを実行するためにライブラリのファブリックを活用しています。タスク内のホストリストを変更しようとすると、リストを繰り返し処理しながらリストを変更しようとすることになります。または、ホストが定義されていない場合は、リストをループオーバーに設定したコードが実行されない空のリストをループオーバーします。

Env.Host_stringの使用は、接続するホストを関数に直接指定するという点でのみ、この動作の回避策です。これにより、いくつかのホストを実行する場合に実行ループを作り直すという問題が発生します。

実行時にホストを設定できるようにする最も簡単な方法は、すべてのホスト文字列、ユーザーなどを設定する個別のタスクとしてenvをポピュレートし続けることです。その後、デプロイタスクを実行します。次のようになります。

fab production deploy

または

fab staging deploy

ステージングとプロダクションは、指定したタスクに似ていますが、次のタスク自体は呼び出しません。このように動作しなければならない理由は、タスクが終了し、(ホストの場合、envの場合はNoneですが、その時点では1つのループである)ループから抜け出し、ループを終了する必要があるためです。新たにホスト(前のタスクで定義されたもの)。

9
Morgan

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, ))
9
serverhorror

私はまったく新しいファブリックですが、ファブリックが複数のホストで同じコマンドを実行するようにする(たとえば、1つのコマンドで複数のサーバーにデプロイする)には、次を実行できます。

fab -H staging-server,production-server deploy 

staging-serverおよびproduction-serverは、デプロイアクションを実行する2台のサーバーです。 OS名を表示する単純なfabfile.pyを次に示します。 fabfile.pyは、fabコマンドを実行したディレクトリと同じディレクトリにある必要があることに注意してください。

from fabric.api import *

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

これは、少なくともファブリック1.8.1で機能します。

3
Brad Parks

Env.hostsは、タスク関数内ではなく、モジュールレベルで変更する必要があります。私も同じ間違いを犯しました。

from fabric.api import *

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

env.hosts = _get_hosts()

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

そのため、ホストを設定し、すべてのホストでコマンドを実行するには、次から開始する必要があります。

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

タスクを実行する前にenv.hostsを設定するため、PROD関数にリストされているすべてのサーバーでデプロイタスクを実行するもの。

3
athros

とても簡単です。 env.Host_string変数を初期化するだけで、次のすべてのコマンドがこのホストで実行されます。

from fabric.api import env, run

env.Host_string = '[email protected]'

def foo:
    run("hostname -f")
3

サブタスクを実行する前に、env.hoststringに割り当てることができます。複数のホストを反復処理する場合は、ループ内でこのグローバル変数に割り当てます。

あなたと私にとって残念なことに、ファブリックはこのユースケース向けに設計されていません。 http://github.com/bitprophet/fabric/blob/master/fabric/main.pymain関数をチェックして、その仕組みを確認してください。

2
Andrew B.

_fab my_env_1 my_command_の使用を可能にする別の「夏」パターンは次のとおりです。

このパターンを使用すると、辞書を使用して環境を一度定義するだけで済みます。 _env_factory_は、ENVSのキー名に基づいて関数を作成します。 ENVSを独自のディレクトリに配置し、_secrets.config.py_を設定して、ファブリックコードから構成を分離します。

欠点は、書かれているように、_@task_デコレーターを追加すると break it になることです。

注: late binding のため、工場ではdef func(k=k):の代わりにdef func():を使用します。 このソリューション で実行中のモジュールを取得し、パッチを適用して関数を定義します。

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
_
2
whp

ロールを使用することは現在、これを行うための「適切な」および「正しい」方法であると考えられており、あなたがそれを「すべき」です。

とはいえ、「希望」または「希望」の大部分に似ている場合、「ツイストシステム」を実行したり、ターゲットシステムをオンザフライで切り替えたりすることができます。

したがって、娯楽のみを目的として(!)、次の例は、多くの人が危険でありながら、どうにか完全に満足できる、このようなことを行う操作を検討していることを示しています。

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
0
user1180527