Как основной разработчик программного обеспечения в 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`, чтобы они соответствовали вашим собственным):