Поиск альтернатив Ansible в Python

Ссылка на оригинал - http://blog.rfox.eu/en/Explorations/Trying_Ansible_alternatives_in_python.html, автор публикации - Bystroushaak

Мой VPS (Virtual Private Server) стареет, и компания, которая его запускает, объявила, что в следующем месяце срок действия моей акции истечет, и теперь ее запуск обойдется более чем в три раза. Кроме того, я хотел бы обновить другие машины у меня дома, так как они используют старые Ubuntu.

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

Почему не Ansible

Мне не нравятся языки конфигурации на основе YAML, которые перерастают в языки сценариев. Вот классический пример десятого правила Гринспуна:

Любая достаточно сложная программа на C или Fortran содержит специальную, неформально определенную, медленную реализацию половины Common Lisp.

Это кошмар для отладки, нет поддержки в IDE и так далее.

⚠️ Было указано, что VS Code имеет надстройку специально для Ansible

Чувство хаоса возникало каждый раз, когда я пытался использовать Ansible. Это побудило меня исследовать альтернативы, предпочтительно на python, которые определяли бы «рецепты» или что-то вроде простого кода на python.

Как оказалось, их не так много.

Fabric

https://github.com/fabric/fabric

Практически в каждой статье на тему «альтернатива Python ANSI» упоминается Fabric. Выглядит отлично, но как-то немного по-другому. Это система для запуска команд на удаленных хостах. Что, безусловно, является частью того, что делает Ansible, но мне также хотелось бы иметь некоторый уровень абстракции.

>>> def disk_free(c):
...     uname = c.run('uname -s', hide=True)
...     if 'Linux' in uname.stdout:
...         command = "df -h / | tail -n1 | awk '{print $5}'"
...         return c.run(command, hide=True).stdout.strip()
...     err = "No idea how to get disk space on {}!".format(uname)
...     raise Exit(err)

Я имею в виду, я не хочу запускать вручную, apt install nginx, а затем анализировать вывод и пытаться решить, была ли команда выполнена успешно (я делал это раньше с paramiko, и это не весело, поверьте мне).

Я хочу что-то немного более продвинутое, которое выполняет для меня разбор стандартных утилит, и в идеале с поддержкой нескольких ОС, поэтому, когда я решу использовать CentOS вместо сервера Ubuntu, который я сейчас использую, он может справиться с различными утилитами для меня.

Fabtools

Fabtools выглядит почти так же, как я хочу:

from fabric.api import *
from fabtools import require
import fabtools

@task
def setup():
    # Require some Debian/Ubuntu packages
    require.deb.packages([
        'imagemagick',
        'libxml2-dev',
    ])

    # Require a Python package
    with fabtools.python.virtualenv('/home/myuser/env'):
        require.python.package('pyramid')

    # Require an email server
    require.postfix.server('example.com')

    # Require a PostgreSQL server
    require.postgres.server()
    require.postgres.user('myuser', 's3cr3tp4ssw0rd')
    require.postgres.database('myappsdb', 'myuser')

    # Require a supervisor process for our app
    require.supervisor.process('myapp',
        command='/home/myuser/env/bin/gunicorn_paster /home/myuser/env/myapp/production.ini',
        directory='/home/myuser/env/myapp',
        user='myuser'
        )

    # Require an nginx server proxying to our app
    require.nginx.proxied_site('example.com',
        docroot='/home/myuser/env/myapp/myapp/public',
        proxy_url='http://127.0.0.1:8888'
        )

    # Setup a daily cron task
    fabtools.cron.add_daily('maintenance', 'myuser', 'my_script.py')

У этого есть только один недостаток; это также выглядит мертвым.

Последняя фиксация произошла 9 месяцев назад, существует 78 нерешенных проблем, 28 ожидающих запросов на извлечение и список поддерживаемых операционных систем уже давно:

  • Семья Debian:
    • Debian 6 ( сжатие ), 7 ( хрипеть ), 8 ( Джесси )
    • Ubuntu 10.04 ( ясный ), 12.04 ( точный ), 14.04 ( верный )

Это 14 в Ubuntu 14.04 (верный) это год выпуска: 2014.

Документация также отстой, и есть странная путаница с форками.

Fabrix

Затем есть Fabrix , который выглядит как нечто среднее между Fabric и Fabtools:

from fabrix.api import is_file_not_exists, yum_install
from fabrix.api import edit_file, edit_ini_section, replace_line

def install_php():

    if is_file_not_exists("/etc/yum.repos.d/epel.repo"):
        yum_install("https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm")

    if is_file_not_exists("/etc/yum.repos.d/remi-php70.repo"):
        yum_install("https://rpms.remirepo.net/enterprise/remi-release-7.rpm")

    edit_file("/etc/yum.repos.d/remi-php70.repo",
        edit_ini_section("[remi-php70]",
            replace_line("enabled=0", "enabled=1")
        )
    )

    yum_install("""
            php-cli
            php-common
            php-fpm
            php-gd
            php-mbstring
            php-mysql
            php-pdo
            php-pear
            php-pecl-imagick
            php-process
            php-xml
            php-opcache
            php-mcrypt
            php-soap
    """)

Это довольно забавно для меня, потому что я создал нечто очень похожее с paramiko некоторое время назад.

Он также выглядит мертвым, только 204 коммитов и один участник, последний коммит 15 месяцев назад. Я не хочу строить свою систему на чем-то уже мертвом.

pyinfra

pyinfra выглядит многообещающе и совсем не мертво: 2233 коммитов, 14 участников, последний коммит вчера . Об этом я и говорю!

from pyinfra.operations import apt

apt.packages(
    {'Install iftop'},
    'iftop',
    sudo=True,
    update=True,
)

Пример работает именно так, как я хотел; декларативный язык, важные параметры, как вы знаете, параметры, а не строки. Единственное, что странно - указывать описание как set, но как бы там ни было, я вижу здесь причину рассуждений.

Документация также перспективна:

Пробуем pyinfra

Поскольку pyinfra - единственная вещь, которая выглядит так, как будто она не мертва, и она может делать то, что я хочу, решение не так уж сложно. Итак, давайте попробуем это:

$ pip install --user pyinfra

Теперь давайте попробуем привет мир. Я борюсь с портом на мгновение, потому что я использую нестандартный порт для туннелирования через Wi-Fi отеля и аэропорта, быстрый взгляд, чтобы помочь показать, что я должен использовать --port параметр:

$ pyinfra kitakitsune.org --port 443 exec -- echo "hello world"
--> Loading config...
--> Loading inventory...

--> Connecting to hosts...
    [kitakitsune.org] Connected

--> Proposed changes:
    Ungrouped:
    [kitakitsune.org]   Operations: 1   Commands: 1   

--> Beginning operation run...
--> Starting operation: Server/Shell (u'echo hello world',)
[kitakitsune.org] hello world
    [kitakitsune.org] Success

--> Results:
    Ungrouped:
    [kitakitsune.org]   Successful: 1   Errors: 0   Commands: 1/1

Выглядит хорошо. Давайте попробуем создать более сложное развертывание для виртуального сервера Ubuntu, которое я создал некоторое время назад в VirtualBox. Я немного борюсь с inventory.pyфайлом, но потом нахожу в документации правильные параметры:

my_hosts = [
    ('192.168.0.106', {"ssh_port": "4433", "ssh_user": "b"}),
]

Я быстро проверяю, работает ли он со следующим deployment.pyфайлом:

from pyinfra.modules import server

server.shell('echo "hello world"')

Который я запускаю с помощью следующей команды:

pyinfra -v inventory.py deployment.py
--> Loading config...
--> Loading inventory...

--> Connecting to hosts...
    [192.168.0.106] Connected

--> Preparing operations...
    Loading: deployment.py
    [192.168.0.106] Ready: deployment.py

--> Proposed changes:
    Groups: my_hosts / inventory
    [192.168.0.106]   Operations: 1   Commands: 1   

--> Beginning operation run...
--> Starting operation: Server/Shell ('echo "hello world"',)
[192.168.0.106] >>> sh -c 'echo "hello world"'
[192.168.0.106] hello world
    [192.168.0.106] Success

--> Results:
    Groups: my_hosts / inventory
    [192.168.0.106]   Successful: 1   Errors: 0   Commands: 1/1

sudo

Поддержка sudo пароля была добавлена ​​после публикации этой статьи через # 305 в 0.15 выпущенной версии,

Чтобы использовать sudo, установите глобальную переменную USE_SUDO_PASSWORD на True, чтобы pyinfra запрашивала его в интерактивном режиме, или вы можете установить его в качестве пароля, и он будет использоваться автоматически. Вы также можете использовать --use_sudo_passwordпараметр в командной строке или в inventory.py.

from pyinfra.modules import server

USE_SUDO_PASSWORD=True

server.shell('echo "hello world"', sudo=True)

sudo=TrueПараметр говорит, что эта команда должна выполняться с помощью sudo . USE_SUDO_PASSWORD говорит, что sudo использует пароль, так как вы можете также использовать sudo без пароля, установив %sudo ALL=(ALL:ALL) NOPASSWD:ALLв /etc/sudoers.

pyinfra -v inventory.py deployment.py
--> Loading config...
--> Loading inventory...

--> Connecting to hosts...
    [192.168.0.106] Connected

--> Preparing operations...
    Loading: deployment.py
    Use of `pyinfra.modules` is deprecated, please use `pyinfra.operations`.
    [192.168.0.106] Ready: deployment.py

--> Proposed changes:
    Groups: my_hosts / inventory
    [192.168.0.106]   Operations: 1   Commands: 1   

--> Beginning operation run...
--> Starting operation: Server/Shell ('echo "hello world"',)
[192.168.0.106] sudo password: 
    [192.168.0.106] Success

--> Results:
    Groups: my_hosts / inventory
    [192.168.0.106]   Successful: 1   Errors: 0   Commands: 1/1

Настройка nginx

Давайте попробуем что-то более сложное - настроить сервер nginx и загрузить для него правильный файл конфигурации:

from pyinfra.modules import apt

SUDO=True

apt.packages('nginx', update=True,present=True)

И это сработало, за одним исключением, который является разбором выходных данных (да, именно поэтому анализ отстой):

Traceback (most recent call last):
  File "src/gevent/greenlet.py", line 854, in gevent._gevent_cgreenlet.Greenlet.run
  File "/home/bystrousak/.local/lib/python2.7/site-packages/pyinfra/api/util.py", line 471, in read_buffer
    _print(line)
  File "/home/bystrousak/.local/lib/python2.7/site-packages/pyinfra/api/util.py", line 455, in _print
    line = print_func(line)
  File "/home/bystrousak/.local/lib/python2.7/site-packages/pyinfra/api/connectors/util.py", line 61, in <lambda>
    print_func=lambda line: '{0}{1}'.format(print_prefix, line),
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2192' in position 74: ordinal not in range(128)
2020-06-11T23:55:28Z <Greenlet at 0x7fde3f4dd6b0: read_buffer('stdout', <paramiko.ChannelFile from <paramiko.Channel 2 (op, <Queue at 0x7fde39f60f30 queue=deque([('stdout', u, print_func=<function <lambda> at 0x7fde39f5e950>, print_output=True)> failed with UnicodeEncodeError

    [192.168.0.106] Success

--> Results:
    Groups: my_hosts / inventory
    [192.168.0.106]   Successful: 1   Errors: 0   Commands: 1/1

Что опять-таки странно, но я, вероятно, использую чешскую локализацию, поэтому я не удивлен.

Повторное выполнение показывает, что операция была выполнена успешно:

--> Loading config...
--> Loading inventory...

--> Connecting to hosts...
    [192.168.0.106] Connected

--> Preparing operations...
    Loading: deployment.py
    Loaded fact deb_packages
    [192.168.0.106] Ready: deployment.py

--> Proposed changes:
    Groups: my_hosts / inventory
    [192.168.0.106]   Operations: 1   Commands: 1   

--> Beginning operation run...
--> Starting operation: Apt/Packages ('nginx', u'update=True', u'present=True')
[192.168.0.106] >>> sudo -S -H -n sh -c 'apt-get update'
[192.168.0.106] Hit:1 http://archive.ubuntu.com/ubuntu bionic InRelease
[192.168.0.106] Hit:2 http://archive.ubuntu.com/ubuntu bionic-updates InRelease
[192.168.0.106] Hit:3 http://archive.ubuntu.com/ubuntu bionic-backports InRelease
[192.168.0.106] Hit:4 http://archive.ubuntu.com/ubuntu bionic-security InRelease
[192.168.0.106] Reading package lists...
    [192.168.0.106] Success

--> Results:
    Groups: my_hosts / inventory
    [192.168.0.106]   Successful: 1   Errors: 0   Commands: 1/1

Конфиг

Итак, как мне загрузить файл конфигурации для nginx?

from pyinfra.modules import files

files.put(
    'configs/nginx.conf',
    '/etc/nginx/nginx.conf',
    user='root',
    group='root',
    mode='644',
)

Вы также можете загружать файлы, синхронизировать целые каталоги и так далее. Довольно мило.

Запуск nginx

from pyinfra.modules import server

init.systemd('nginx', running=True, restarted=True, enabled=True)

Прекрасно.

Вывод

Мне очень нравится pyinfra. У этого есть свои причуды, но они - только маленькие раздражения, в отличие от больших раздражений, которые я нахожу в других продуктах, таких как Ansible. Но в отличие от Ansible, он понятен, прост в использовании и использует Python, который я знаю и люблю. Моя IDE может дать мне автозаполнение и отладчик, в отличие от других DSL на основе YAML.

Вот целый конфиг для развертывания nginx:

from pyinfra.modules import apt
from pyinfra.modules import init
from pyinfra.modules import files


SUDO=True
USE_SUDO_PASSWORD=True


apt.packages(
    'nginx',
    update=True,
    present=True,
)

files.put(
    'configs/nginx.conf',
    '/etc/nginx/nginx.conf',
    user='root',
    group='root',
    mode='644',
)

init.systemd(
    'nginx',
    running=True,
    restarted=True,
    enabled=True,
)

Добавить комментарий

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Не нашли ответ на свой вопрос? Возможно, вы найдете решение проблемы на нашем канале в Youtube! Здесь мы собрали небольшие, но эффективные инструкции. Смотрите и подписывайтесь на наш youtube-канал!

Смотреть на Youtube

Руководства и обзоры

1 Что нужно восстановить?

Видео

MP4, AVI и HD видео хранятся на телефоне и / или по ошибке удаляются вместе с фотографиями и другими медиафайлами.

Контакты

Номера телефонов друзей и знакомых из приложения «Контакты Android», журналы вызовов; Восстановление SIM-карты.

Фото

Удалены файлы JPG / PNG из Галереи Android; фото, загруженные на мобильный, файлы повреждены после восстановления.

Смс и сообщения

Чаты WhatsApp и Facebook, текстовые сообщения в соцсетях, информация на сим-карте

2 Где пропали файлы?

На sd-карте

Фотографии и документы хранятся на SD-картах. Часто на них случайно удаляются файлы

На телефоне

Программы для восстановления не распознают внутреннее хранилище телефона как диск, но есть другие решения.

На USB флешке

Эти небольшие устройства хранения данных часто выходят из строя или на них появляются ошибки чтения.

На HDD или SSD

Несмотря на то, что настольные платформы становятся все менее популярными, проблема потери файлов всегда оставалась.