Как установить целевые хосты в файле Fabric - PullRequest
107 голосов
/ 24 февраля 2010

Я хочу использовать Fabric для развертывания кода моего веб-приложения на серверах разработки, подготовки и производства. Мой файл:

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 , env.hosts устанавливается правильно. Тем не менее, это не жизнеспособный вариант, а также не декоратор. Передача хостов в командной строке в конечном итоге приведет к некоторому сценарию оболочки, который вызывает fabfile, я бы предпочел, чтобы один инструмент выполнял эту работу правильно.

В документе Fabric говорится, что «env.hosts - это просто объект списка Python». По моим наблюдениям, это просто неправда.

Может кто-нибудь объяснить, что здесь происходит? Как настроить хост для развертывания?

Ответы [ 15 ]

128 голосов
/ 14 января 2011

Я делаю это, объявляя фактическую функцию для каждой среды. Например:

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, а не только для развертывания. Это невероятно полезно.

75 голосов
/ 11 июня 2011

Использовать 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'
...
49 голосов
/ 17 июня 2011

Вот более простая версия ответа serverhorrors :

from fabric.api import settings

def mystuff():
    with settings(host_string='12.34.56.78'):
        run("hostname -f")
21 голосов
/ 03 марта 2010

Застрял на этом сам, но наконец понял. Вы просто не можете установить конфигурацию env.hosts из в пределах задачи. Каждое задание выполняется N раз, по одному разу для каждого указанного хоста, поэтому этот параметр принципиально выходит за пределы области действия задания.

Глядя на ваш код выше, вы можете просто сделать это:

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

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

def deploy():
    # do stuff...

Кажется, что это будет делать то, что вы собираетесь.

Или вы можете написать некоторый пользовательский код в глобальной области видимости, который анализирует аргументы вручную и устанавливает env.hosts до того, как определена ваша задача. По нескольким причинам именно так я и настроил.

18 голосов
/ 22 августа 2013

Начиная с fab 1.5 это документированный способ динамической установки хостов.

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

Цитата из документа ниже.

Использование execute с динамически устанавливаемыми списками хостов

Распространенным вариантом использования промежуточного и продвинутого уровня для Fabric является параметризация поиска целевого списка хостов во время выполнения (при использовании Ролей не хватает). выполнить может сделать это чрезвычайно просто, как так: * * 1011

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)
10 голосов
/ 03 июня 2013

В отличие от некоторых других ответов, позволяет изменять переменные окружения 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.

9 голосов
/ 29 марта 2011

Чтобы объяснить, почему это даже проблема. Команда fab использует матричную библиотеку для запуска задач в списках хостов. Если вы пытаетесь изменить список хостов внутри задачи, вы по сути пытаетесь изменить список, перебирая его. Или в случае, когда у вас не определены хосты, переберите пустой список, где код, для которого вы задали зацикливание списка, никогда не выполняется.

Использование env.host_string является обходным решением для этого поведения только в том, что оно указывает непосредственно функциям, с которыми хосты соединяются. Это вызывает некоторые проблемы в том, что вы будете переделывать цикл выполнения, если вы хотите иметь несколько хостов для выполнения.

Самый простой способ, с помощью которого люди могут устанавливать хосты во время выполнения, состоит в том, чтобы заполнить env как отдельную задачу, которая настраивает все строки хоста, пользователей и т. Д. Затем они запускают задачу развертывания. Это выглядит так:

fab production deploy

или

fab staging deploy

Там, где постановка и производство подобны заданным вами задачам, но они сами не вызывают следующую задачу. Причина, по которой он должен работать следующим образом, состоит в том, что задача должна завершиться и выйти из цикла (хостов, в случае env Нет, но это цикл из одного на тот момент), а затем сделать цикл более хосты (теперь определяемые предыдущей задачей) заново.

9 голосов
/ 10 марта 2010

Вам необходимо установить 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, ))
3 голосов
/ 05 апреля 2014

Итак, чтобы установить хосты и запустить команды на всех хостах, вы должны начать с:

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 перед запуском задачи.

3 голосов
/ 30 января 2014

Я совершенно новичок в матрице, но чтобы заставить матрицу запускать одни и те же команды на нескольких хостах (например, развертывать на нескольких серверах одной командой), вы можете запустить:

fab -H staging-server,production-server deploy 

, где staging-server и production-server - это 2 сервера, с которыми вы хотите запустить действие развертывания. Вот простой файл fabfile.py, который будет отображать имя ОС. Обратите внимание, что файл fabfile.py должен находиться в том же каталоге, в котором вы запускаете команду fab.

from fabric.api import *

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

Это работает как минимум с тканью 1.8.1.

...