Как основной разработчик программного обеспечения в Cloudify.co, я часто работаю на удаленных хостах, развернутых в облачных сервисах, таких как AWS или Openstack. Этот тип работы вызывает у вас вопрос: как мне автоматически запускать одни и те же команды (скрипты) для всех из них? Ответ на этот вопрос обычно заключается в использовании Ansible или Fabric. В этой статье я решил сосредоточиться на решении Fabric.
Что такое ткань?
Fabric — это высокоуровневая библиотека Python (2.7, 3.4+), предназначенная для удаленного выполнения команд оболочки через SSH с получением взамен полезных объектов Python (веб-сайт Fabric).
Давайте сделаем с ним кодирование. (Я использую Python 3.6 и Fabric 2.5.0)
Начало работы — Создание подключения:
Поскольку мы используем Python, лучший способ показать возможности Fabric — это использовать объект, который мы назовем «Host».
class Host(object): def __init__(self, host_ip, username, key_file_path): self.host_ip = host_ip self.username = username self.key_file_path = key_file_path def _get_connection(self): connect_kwargs = {'key_filename': self.key_file_path} return Connection(host=self.host_ip, user=self.username, port=22, connect_kwargs=connect_kwargs)
В приведенном выше коде Host используется как удаленный хост, а функция _get_connection используется для создания к нему SSH-соединения. (Подробнее о `Соединение`). Мы будем использовать Connection в качестве диспетчера контекста, поскольку он имеет базовый жизненный цикл создать, подключить/открыть, выполнить работу, отключить/закрыть (из документации).
Запуск команд:
Теперь, когда у нас есть соединение, мы можем запускать команды на нашем удаленном хосте. К объекту `Host` добавляются следующие функции:
def run_command(self, command): try: with self._get_connection() as connection: print('Running `{0}` on {1}'.format(command, self.host_ip)) result = connection.run(command, warn=True, hide='stderr') except (socket_error, AuthenticationException) as exc: self._raise_authentication_err(exc) if result.failed: raise ExampleException( 'The command `{0}` on host {1} failed with the error: ' '{2}'.format(command, self.host_ip, str(result.stderr))) def put_file(self, local_path, remote_path): try: with self._get_connection() as connection: print('Copying {0} to {1} on host {2}'.format( local_path, remote_path, self.host_ip)) connection.put(local_path, remote_path) except (socket_error, AuthenticationException) as exc: self._raise_authentication_err(exc) def _raise_authentication_err(self, exc): raise ExampleException( "SSH: could not connect to {host} " "(username: {user}, key: {key}): {exc}".format( host=self.host_ip, user=self.username, key=self.key_file_path, exc=exc))
В приведенном выше коде функции:
`run_command` используется для запуска команды на удаленном хосте. Вы можете передать функции `Connect.run` множество различных параметров (как описано здесь), но все, что мне нужно для моего механизма отлова ошибок, это:
- `warn=True`: не создавать исключение, когда выполняемая команда завершается с ненулевым статусом (что означает сбой).
- `hide=’stderr’`: не копировать stderr подпроцесса на управляющий терминал.
Поведение Connection.run по умолчанию заключается в копировании stdout и stderr подпроцесса на управляющий терминал.
`put_file` используется для копирования файлов с локального хоста на удаленный хост. (подробнее о `Connection.put`)
Важно отметить, что класс Connection является «ленивым», что означает, что когда мы его инициируем, он не пытается подключиться к хосту. Поэтому мы помещаем наши команды в блок try-except, который отлавливает ошибки подключения.
Резюме:
Решение о выборе между Ansible и Fabric часто зависит от того, что вы хотите запускать на удаленных хостах? Если это простой скрипт, то Fabric должно хватить, в противном случае Ansible может быть лучшим выбором. В любом случае, Cloudify Manager позволяет вам выбрать решение, которое подходит именно вам, используя плагины Ansible и Fabric. Развлекайся! :)
Приложение:
Вот полный пример кода, который вы можете использовать (не забудьте заменить параметры `Host`, чтобы они соответствовали вашим собственным):