Представим себе ситуацию, когда при тестировании или администрировании нужно выполнить однотипные действия на нескольких удаленных серверах. Можно использовать для этого shell команду вида:
for hostDN in host1 host2 host3; do ssh "root@$hostDN" 'ls -l /home'; done |
Но это не самое удачное решение хотя бы потому, что выполнение команды для разных серверов происходит последовательно. Для асинхронного выполнения можно использовать команду pee, но это тоже не совсем красиво. Я решил попробовать для этих целей Fabric (fabfile.org).
В этой статье я расскажу про возможности Fabric и свой опыт использования этого инструмента.
Основные преимущества:
- Простая классификация хостов и управление хостами в заданиях
- Большинство заданий можно вызвать изменив переменную в командной строке
- Ускорение выполнения заданий и небольшой отчет после выполнения
- Масштабируемость и простота поддержки
Установка
Для работы Fabric не требует какого-либо установленного приложения на подконтрольных серверах, нужен только запущенный SSH. На системе, которая будет управляющей, рекомендуется установить Fabric с помощью pip (Cross-platform Python package management system) или easy_install. Вы также можете использовать ActivePython’s package manager PyPM под Windows. Информация о зависимостях и шагах установки тут: official page
При установке на RedHat 6.4 сначала нужно удалить старую версию библиотеки PyCrypto:
$ yum remove pycrypto |
Только после этого получится установить Fabric с помощью pip, который ставит свежие версии python-crypto и python-paramiko. Со старой версией библиотеки установочный файл fab валится с ошибкой.
Первое знакомство
После установки не нужно дополнительное конфигурирование и сразу можно отсылать задания подконтрольным серверам.
Сервера указываются с помощью аргумента -H/—host, если логин или порт не дефолтные, то можно указать их явно:
$ fab -H host1,root@host2:555,host3 -- ls -l /home |
Пароль можно указать с помощью параметра -p/—password , но будьте готовы к тому, что он попадет в историю shell.
По умолчанию задания выполняются последовательно, если вы хотите изменить это поведение, то воспользуйтесь ключом -P/—parallel. Однако все задания выполнять параллельно не получится, для этого нужна библиотека для многопоточности. К счастью, она включена в стандартную установку Python версии 2.6 и старше.
Помощь по программе можно получить с помощью ключа -h.
Далее мы рассмотрим возможности использования Fabric в скриптах.
Модульный файл (module file) для Fabric
При запуске можно указать модуль с помощью ключа -f, к примеру -f my.py. Но это пригодится только в случае совсем простых заданий, а для более сложных есть другая возможность. По умолчанию Fabric ищет в текущей директории или уровнем выше файл с названием fabfile.py и именно его надо использовать для навороченных задач. В этих терминах задача — это функция Python, которую можно вызвать по имени. К примеру, если есть 2 функции ы файле по умолчанию: host_core and host_secondary. Вызвать их можно так:
$ fab host_core host_secondary |
Запоминать все функции не требуется, есть ключ —list:
$ fab --list |
У каждой папки (придумайте для нее говорящее имя) свой fabfile, это можно использовать для структурированного хранения скриптов.
Для специфических заданий в файле модуля понядобятся некоторые предопределенные функции (файл fabric.api). Импортировать их надо так:
from fabric.api import settings, run, env
или все скопом:
from fabric.api import *
Полный список API вызовов /usr/lib/python2.6/site-packages/fabric/api.py
Работа с переменными окружения
Fabric поддерживает обработку более 50 переменных окружения, полный список тут http://docs.fabfile.org/en/1.9/usage/env.html. Некоторые из них заданы, остальные пользователь может определить сам, если это потребуется. Переменная окружения может быть определена или переопределена в командной строке, в fabfile или файле .fabricrc.
У разных способов определения разный приоритет и может зависеть от самой переменной. Например, пароль, задаваемый в командной строке, имеет приоритет выше, чем пароль в fabricrc (который считывается первым) и в fabfile (его перекрывает fabricrc). Это обычное поведение. Но список подконтрольных хостов обрабатывается наоборот. В окружении скрипта переменные могут быть глобальными и локальными в каком-то блоке. К примеру, переменные user / password позволяют сформировать учетную запись для доступа на сервер:
$ vim ~/.fabricrc user = 'root' password = 'P@ssw0rd' |
В fabfile можно определить переменную окружения двумя способами. Если нужна глобальная переменная, то используется env.[parameter]:
$ vim fabfile env.user = 'root' env.password = 'P@ssw0rd)' env.parallel = 'True' env.cwd = '/spare/user' env.warn_only = 'True' |
Если переменная окружения нужна временно (для одиночного задания), то пишем так:
def task(…): with settings(warn_only=True): some code
или используем декоратор:
@with_settings(warn_only=True)
Пользовательские параметры
Пользователь может задать свои собственные параметры командной строки.
def hello(name=world): print(«Hello %s!» % name)
$ fab hello
Hello world! |
Можно переопределять значения:
$ fab hello:name=me Hello me! |
Если есть несколько аргументов, то они перечисляются через запятую. В этом случае задаем так: name=me. Если нужно задать значение для конкретной функции: hello:name=me_again. Можно комбинировать:
$ fab hello hello:me Hello world! Hello me! |
Подконтрольные сервера
Если не задан хост для задания, то оно будет выполнено один раз для текущего хоста. Основная задача программы — распараллеливание задач на хостах, поэтому задать эти самые хосты можно несколькими способами. Один из них — глобальное определение, с помощью ключа -H для команды fab.
Список всех хостов можно задать в переменной окружения env.hosts:
$ vim fabfile.py env.hosts = ['host1', 'root@host2:555', 'host3'] def task_hostname(): run('hostname -f') def status(): run('uptime') run('cat /proc/loadavg') run('free -m') run('df -h') |
После запуска «fab task_hostname status» все команды будут вызваны в указанном порядке на трех хостах.
Из-за того, что параметр командной строки, задающий хосты, читается раньше fabfile глобальные переменные окружения в fabfile перегружают (override) их. Если нужно добавить хосты из командной строки, нужно использовать env.hosts.extend вместо env.hosts. В этом случае задания будут выполнены и на хостах, указанных в командной строке.
Исключить хосты можно с помощью ключа —exclude-hosts/-x
$ fab task_hostname status -x host1 |
Если вам нужно определить список хостов для конкретного задания, то нужно использовать декоратор Python-а:
@hosts(‘host1’, ‘root@host2:555’)
или:
test_hosts=(‘host1’, ‘root@host2:555’)
@hosts(test_hosts)
или
def test() env.hosts = [‘host1’, ‘root@host2:555’]
В этом случае хосты из списка будут видны только в текущем задании.
Fabric позволяет сгруппировать хосты по ролям:
env.roledefs = {
‘be’ : [‘host1’],
‘fe’ : [‘host2’, ‘host3’],
‘db’ : [‘host4’, ‘host5’]
}
После задания ролей мы можем использовать их в декораторах:
@roles(‘be’)
def be():
…
@roles(‘fe’)
def check_status():
…
Декоратор @parallel позволяет выполнять задания на нескольких хостах одновременно.
@parallel
@roles(‘fe’)
def clear_cache():
…
Теперь после выполнения команды:
$ fab clear_cache |
Кеш будет очищен на всех фронт-эндах. Если количество хостов больше, то могут быть тормоза в работе. Для таких случаев пригодится ключ pool_size
@parallel(pool_size=5)
или -z в командной строке.
Декоратор @serial мы используем в случае, когда нужно выполнить задания последовательно. Он игнорирует параметры командной строки и значение env.parallel.
Часто используемые команды
- get — скачивание файла с удаленного хоста
- put — закачивание файла на хост
- run — выполнение команды от лица текущего пользователя
- sudo — выполнение команды от лица суперпользователя
- local — выполнение команды на локальном хосте
- open_shell — запуск полностью интерактивной оболоски shell на удаленном хосте
- warn and puts — вывод сообщений
- prompt – выводит текст и запрашивает ввод значения. Вводимое значение можно хранить в локальной или глобальной переменной. Есть возможность проверки по регулярному выражению.
Альтернативы