Как установить целевые узлы в файле Fabric
Я хочу использовать 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 docs, env.hosts устанавливается правильно. Однако это не жизнеспособный вариант, и не декоратор. Передача хостов в командной строке в конечном итоге приведет к какой-то оболочке script, которая вызывает fabfile, я бы предпочел, чтобы один инструмент правильно выполнял работу.
В документах Fabric говорится, что "env.hosts - это просто объект списка Python". Из моих наблюдений это просто неверно.
Может ли кто-нибудь объяснить, что здесь происходит? Как установить хост для развертывания?
Ответы
Ответ 1
Я делаю это, объявляя фактическую функцию для каждой среды. Например:
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, а не только для развертывания. Это невероятно полезно.
Ответ 2
Используйте 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'
...
Ответ 3
Вот более простая версия ответа сервера:
from fabric.api import settings
def mystuff():
with settings(host_string='192.0.2.78'):
run("hostname -f")
Ответ 4
Застрял на этом сам, но, наконец, понял это. Вы просто не можете установить конфигурацию env.hosts из задачи. Каждая задача выполняется N раз, один раз для каждого указанного узла, поэтому настройка в основном за пределами области действия.
Посмотрев на свой код выше, вы можете просто сделать это:
@hosts('dev')
def deploy_dev():
deploy()
@hosts('staging')
def deploy_staging():
deploy()
def deploy():
# do stuff...
Кажется, что он будет делать то, что вы намереваетесь.
Или вы можете написать какой-то пользовательский код в глобальной области, который анализирует аргументы вручную и устанавливает env.hosts до того, как будет определена ваша функция задачи. По нескольким причинам, на самом деле, как я установил мой.
Ответ 5
Так как fab 1.5 это документированный способ динамически устанавливать хосты.
http://docs.fabfile.org/en/1.7/usage/execution.html#dynamic-hosts
Цитата из документа ниже.
Использование выполнения с динамически заданными списками хостов
Обычный промежуточный-расширенный вариант использования для 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)
Ответ 6
В отличие от некоторых других ответов, можно изменить переменные среды 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
на уровне модуля или все, что передается из CLI fab
.
Ответ 7
Вам нужно установить 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, ))
Ответ 8
Объяснить, почему это даже проблема. Команда fab использует структуру библиотеки для запуска задач в списках хостов. Если вы попытаетесь изменить список хостов внутри задачи, вы эссенциально пытаетесь изменить список во время итерации по нему. Или в случае, если у вас нет определенных хостов, выполните цикл над пустым списком, где код, в который вы устанавливаете цикл, не выполняется.
Использование env.host_string - это работа для этого поведения только в том случае, если он напрямую указывает на функции, с которыми соединяются хосты. Это вызывает некоторые проблемы в том, что вы будете перерабатывать цикл выполнения, если хотите, чтобы количество хостов выполнялось.
Самый простой способ заставить людей устанавливать хосты во время выполнения, заключается в том, чтобы сохранить env populatiing как отдельную задачу, которая устанавливает все строки хоста, пользователей и т.д. Затем они запускают задачу развертывания. Это выглядит так:
fab production deploy
или
fab staging deploy
В тех случаях, когда постановка и постановка подобны задачам, которые вы задали, они сами не называют следующую задачу. Причина, по которой он должен работать так, заключается в том, что задача должна заканчиваться и выходить из цикла (хостов, в случае env нет, но это цикл из одного в этой точке), а затем цикл хосты (теперь заданные предыдущей задачей) заново.
Ответ 9
Вам нужно изменить env.hosts на уровне модуля, а не в функции задачи. Я сделал ту же ошибку.
from fabric.api import *
def _get_hosts():
hosts = []
... populate 'hosts' list ...
return hosts
env.hosts = _get_hosts()
def your_task():
... your task ...
Ответ 10
Это очень просто. Просто инициализируйте переменную env.host_string, и на этом хосте будут выполняться все следующие команды.
from fabric.api import env, run
env.host_string = '[email protected]'
def foo:
run("hostname -f")
Ответ 11
Я абсолютно новичок в создании, но для того, чтобы заставить тетю запускать одни и те же команды на нескольких хостах (например, для развертывания на нескольких серверах в одной команде), вы можете запускать:
fab -H staging-server,production-server deploy
где сервер-сервер и производственный сервер - это 2 сервера, на которых вы хотите запустить действие развертывания. Вот простой файл fabfile.py, который отобразит имя ОС. Обратите внимание, что файл fabfile.py должен находиться в том же каталоге, где вы запускаете команду fab.
from fabric.api import *
def deploy():
run('uname -s')
Это работает с тканью 1.8.1, по крайней мере.
Ответ 12
Итак, чтобы установить хосты и выполнить команды на всех хостах, вы должны начать с:
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 перед запуском задачи.
Ответ 13
Вы можете назначить env.hoststring
перед выполнением подзадачи. Назначьте эту глобальную переменную в цикле, если вы хотите перебирать несколько хостов.
К сожалению, для вас и для меня ткань не предназначена для этого варианта использования. Проверьте функцию main
на http://github.com/bitprophet/fabric/blob/master/fabric/main.py, чтобы увидеть, как она работает.
Ответ 14
Вот еще один шаблон "summersault", который позволяет использовать fab my_env_1 my_command
:
С помощью этого шаблона нам нужно определять среды только один раз с помощью словаря. env_factory
создает функции на основе ключевых слов ENVS
. Я поместил ENVS
в свой собственный каталог и файл secrets.config.py
, чтобы отделить конфигурацию от кода ткани.
Недостатком является то, что, как написано, добавление декоратора @task
будет сломать его.
Примечания. Мы используем def func(k=k):
вместо def func():
в factory из-за позднего связывания. Мы получаем текущий модуль с этим решением и исправляем его для определения функции.
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
Ответ 15
Использование ролей в настоящее время считается "правильным" и "правильным" способом сделать это, и это то, что вы "должны" делать.
Тем не менее, если вы похожи на большинство того, что вам "хотелось бы" или "желание", это способность выполнять "скрученную систему" или переключение целевых систем "на лету".
Итак, только для развлекательных целей (!) следующий пример иллюстрирует то, что многие могут считать рискованным, но все же как-то полностью удовлетворительным, маневром, который выглядит примерно так:
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