Компания
153,19
рейтинг
8 октября 2013 в 12:59

Разное → Система управления Ansible tutorial



Представьте себе, что вам нужно управлять парком серверов, расположенных к тому же в разных географических точках. Каждый из этих серверов требует настройки, регулярного обновления и мониторинга. Конечно, для решения этих задач можно воспользоваться самым простым способом: подключиться к каждому серверу по ssh и внести необходимые изменения. При всей своей простоте этот способ сопряжен с некоторыми трудностями: он чрезвычайно трудоемок, а на выполнение однообразных операций уходит очень много времени.

Чтобы упростить процессы настройки и конфигурирования серверов, можно также писать shell-скрипты. Но и этот способ вряд ли можно назвать совершенным. Скрипты нужно постоянно изменять, подстраивая их под каждую новую задачу. При их написании необходимо учитывать различие операционных систем и версий. Не будем забывать и о том, что отладка скриптов отнимает много усилий и забирает немало времени.

Оптимальным вариантом решения описанных проблем является внедрение системы удаленного управления конфигурацией. В таких системах достаточно лишь описать нужное состояние управляемого узла. Система должна сама определить, что нужно сделать для достижения этого состояния, и осуществит все необходимые действия.

Со всеми сложностями, о которых идет речь выше, мы хорошо знакомы на собственном опыте: у нас имеется 10 точек присутствия с NS-серверами, расположенные в разных точках планеты. На них необходимо регулярно вносить различные изменения: обновлять операционную систему, устанавливать и обновлять различное ПО, изменять конфигурцию и т.п. Мы решили все эти операции автоматизировать и внедрить систему удаленного управления конфигурациями. Изучив имеющиеся решения, мы остановили свой выбор на Ansible.

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

Что такое Ansible?


Ansible — опенсорсное программное решение для удаленного управления конфигурациями, разработанное Майклом Де Хаанном в 2012 году. Название продукта взято из научно-фантастической литературы: в романах американской писательницы Урсулы Ле Гуин ансиблом называется устройство для оперативной космической связи.

Ansible берет на себя всю работу по приведению удаленных серверов в необходимое состояние. Администратору необходимо лишь описать, как достичь этого состояния с помощью так называемых сценариев (playbooks; это аналог рецептов в Chef). Такая технология позволяет очень быстро осуществлять переконфигурирование системы: достаточно всего лишь добавить несколько новых строк в сценарий.

Почему Ansible?


Преимущества Ansible по сравнению с другими аналогичными решениями (здесь в первую очередь следует назвать такие продукты, как Puppet, Chef и Salt) заключаются в следующем:

  • на управляемые узлы не нужно устанавливать никакого дополнительного ПО, всё работает через SSH (в случае необходимости дополнительные модули можно взять из официального репозитория);
  • код программы, написанный на Python, очень прост; при необходимости написание дополнительных модулей не составляет особого труда;
  • язык, на котором пишутся сценарии, также предельно прост;
  • низкий порог вхождения: обучиться работе с Ansible можно за очень короткое время;
  • документация к продукту написана очень подробно и вместе с тем — просто и понятно; она регулярно обновляется;
  • Ansible работает не только в режиме push, но и pull, как это делают большинство систем управления (Puppet, Chef);
  • имеется возможность последовательного обновления состояния узлов (rolling update).

Установка


Требования для установки Ansible минимальны. На машине с которой производится управление должен быть установлен Python 2.6 или выше. На управляемых узлах должен быть установлен только Python версии не ниже 2.4, но он, как правило, по умолчанию включен в состав большинства дистрибутивов linux-систем. MS Windows не поддерживается.

Вам также могут потребоваться следующие модули Python, устанавливаемые через pip или пакетный менеджер вашей операционной системы:

  • paramiko;
  • PyYAML;
  • jinja2.


В Ubuntu установка самого Ansible и зависимостей осуществляется добавлением репозитория и установкой пакета:

sudo add-apt-repository -y ppa:rquillo/ansible
sudo apt-get update
sudo apt-get install ansible -y

О процедуре установки в других ОС можно прочитать в официальной документации.

Группы серверов


Список групп серверов, которыми нужно управлять, Ansible может получать двумя основными способами:


Файл hosts


Дефолтное расположение файла — /etc/ansible/hosts, но оно может также быть задано параметром окружения $ANSIBLE_HOSTS или параметром -i при запуске ansible и ansible-playbook. Содержимое этого файла может выглядеть, например, так (в квадратных скобках указаны имена групп управляемых узлов, ниже перечисляются входящие в эти группы серверы):

[dbservers]
one.example.com
two.example.com
three.example.com

[dnsservers]
rs1.example.com ansible_ssh_port=5555 ansible_ssh_host=192.168.1.50
rs2.example.com
ns[01:50].example.com

Помимо списка управляемых узлов, в файле hosts могут быть указаны и другие сведения, необходимые для работы: номера портов для подключения по SSH, способ подключения, пароль для подключения по SSH, имя пользователя, объединения групп и т.п. В некоторых случаях — в частности, при работе с большими и сложными конфигурациями, — различные параметры можно выносить в отдельные файлы и каталоги (о структуре каталогов см. ниже).

Более подробно о файле hosts и правилах его написания можно почитать в официальной документации.

Информация об узлах (Facts)


Перед внесением изменений Ansible подключается к управляемым узлам и собирает информацию о них: о сетевых интерфейсах и их состоянии, об установленной операционной системе и т.п. Он может делать это как с помощью собственного модуля, так и с помощью инструментов ohai и facter, если они установлены (такая возможность специально предусмотрена для пользователей, уже имеющих опыт работы с системами удаленного управления конфигурациями: ohai и facter являются библиотеками фактов для Chef и Puppet).
Переменные

Во время деплоя, как правило, требуется не только установить какое-либо приложение, но и настроить его в соответствии с определенными параметрами на основании принадлежности к группе серверов или индивидуально (например, ip-адрес BGP-соседа и номер его AS или параметры для базы данных). Как уже было сказано, загромождать файл hosts будет не очень красиво, поэтому разработчики Ansible пошли следующим путём:

  • файлы с переменными групп хранятся в директории “group_vars/имя_группы”;
  • файлы с переменными хостов в директории “hosts_vars/имя_хоста”;
  • файлы с переменными роли (о них речь пойдет ниже) в директории “имя_роли/vars/имя_задачи.yml”;

Помимо пользовательских переменных можно (и даже нужно) использовать факты, собранные ansible перед выполнением сценариев и отдельных задач.

Модули Ansible


В состав Ansible входит огромное количество модулей для развёртывания, контроля и управления различными компонентами, которые можно условно разделить на следующие группы (в скобках приведены названия некоторых продуктов и сервисов):

  • облачные ресурсы и виртуализация (Openstack, libvirt);
  • базы данных (MySQL, Postgresql, Redis, Riak);
  • файлы (шаблонизация, регулярные выражения, права доступа);
  • мониторинг (Nagios, monit);
  • оповещения о ходе выполнения сценария (Jabber, Irc, почта, MQTT, Hipchat);
  • сеть и сетевая инфраструктура (Openstack, Arista);
  • управление пакетами (apt, yum, rhn-channel, npm, pacman, pip, gem);
  • система (LVM, Selinux, ZFS, cron, файловые системы, сервисы, модули ядра);
  • работа с различными утилитами (git, hg).

О том, с чем умеет работать Ansible “из коробки”, можно прочитать в официальной документации. Список действительно впечатляет.

Примеры простых задач


С помощью Ansible можно одновременно выполнить одну задачу на целой группе серверов. Попробуем, например, отправить запрос ping на серверы выбранной группы:

$ ansible dnsservers -m ping
dns1.example.com | success >> {
    "changed": false,
    "ping": "pong"
}

dns2.example.com | success >> {
    "changed": false,
    "ping": "pong"
}


Следующий пример соберёт информацию о хостах и выведёт её на консоль в формате JSON:
$ ansible dnsservers -m setup

Вывод
dns1.example.com | success >> {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.1.35"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::ac2a:eaff:fe96:ea53"
        ], 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "", 
        "ansible_bios_version": "", 
        "ansible_cmdline": {
            "barrier": "off", 
            "console": "ttyS0", 
            "panic": "15", 
            "ro": true, 
            "root": "UUID=c5412437-f80e-4db4-81bc-75f751a60792", 
            "xencons": "ttyS"
        }, 
        "ansible_date_time": {
            "date": "2013-10-04", 
            "day": "04", 
            "epoch": "1380891466", 
            "hour": "16", 
            "iso8601": "2013-10-04T12:57:46Z", 
            "iso8601_micro": "2013-10-04T12:57:46.130144Z", 
            "minute": "57", 
            "month": "10", 
            "second": "46", 
            "time": "16:57:46", 
            "tz": "MSK", 
            "year": "2013"
        }, 
        "ansible_default_ipv4": {
            "address": "192.168.1.35", 
            "alias": "eth0", 
            "gateway": "192.168.1.1", 
            "interface": "eth0", 
            "macaddress": "ae:aa:ea:96:ea:53", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.1.0", 
            "type": "ether"
        }, 
        "ansible_default_ipv6": {}, 
        "ansible_devices": {
            "xvda": {
                "holders": [], 
                "host": "", 
                "model": null, 
                "partitions": {
                    "xvda1": {
                        "sectors": "290816", 
                        "sectorsize": 512, 
                        "size": "142.00 MB", 
                        "start": "2048"
                    }, 
                    "xvda2": {
                        "sectors": "16482304", 
                        "sectorsize": 512, 
                        "size": "7.86 GB", 
                        "start": "292864"
                    }
                }, 
                "removable": "0", 
                "rotational": "0", 
                "scheduler_mode": "cfq", 
                "sectors": "16777216", 
                "sectorsize": "512", 
                "size": "8.00 GB", 
                "support_discard": "0", 
                "vendor": null
            }
        }, 
        "ansible_distribution": "Ubuntu", 
        "ansible_distribution_release": "precise", 
        "ansible_distribution_version": "12.04", 
        "ansible_domain": "", 
        "ansible_eth0": {
            "active": true, 
            "device": "eth0", 
            "ipv4": {
                "address": "192.168.1.35", 
                "netmask": "255.255.255.0", 
                "network": "192.168.1.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::ac2a:eaff:fe96:ea53", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "ae:aa:ea:96:ea:53", 
            "module": "xennet", 
            "mtu": 1500, 
            "type": "ether"
        }, 
        "ansible_form_factor": "", 
        "ansible_fqdn": "dns1.example.com", 
        "ansible_hostname": "dns1", 
        "ansible_interfaces": [
            "lo", 
            "eth0"
        ], 
        "ansible_kernel": "3.1.0-1.2-xen", 
        "ansible_lo": {
            "active": true, 
            "device": "lo", 
            "ipv4": {
                "address": "127.0.0.1", 
                "netmask": "255.0.0.0", 
                "network": "127.0.0.0"
            }, 
            "ipv6": [
                {
                    "address": "::1", 
                    "prefix": "128", 
                    "scope": "host"
                }
            ], 
            "mtu": 16436, 
            "type": "loopback"
        }, 
        "ansible_lsb": {
            "codename": "precise", 
            "description": "Ubuntu 12.04.3 LTS", 
            "id": "Ubuntu", 
            "major_release": "12", 
            "release": "12.04"
        }, 
        "ansible_machine": "x86_64", 
        "ansible_memfree_mb": 181, 
        "ansible_memtotal_mb": 1061, 
        "ansible_mounts": [
            {
                "device": "/dev/mapper/system-root", 
                "fstype": "ext4", 
                "mount": "/", 
                "options": "rw,errors=panic,barrier=0", 
                "size_available": 6332063744, 
                "size_total": 7798611968
            }, 
            {
                "device": "/dev/xvda1", 
                "fstype": "ext2", 
                "mount": "/boot", 
                "options": "rw", 
                "size_available": 110679040, 
                "size_total": 139539456
            }
        ], 
        "ansible_os_family": "Debian", 
        "ansible_pkg_mgr": "apt", 
        "ansible_processor": [
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz"
        ], 
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 8, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 8, 
        "ansible_product_name": "", 
        "ansible_product_serial": "", 
        "ansible_product_uuid": "", 
        "ansible_product_version": "", 
        "ansible_python_version": "2.7.3", 
        "ansible_selinux": false, 
        "ansible_ssh_host_key_dsa_public": "AAAAB3NzdC1kc3MAAACBAI09PTx0Jv2dAhmwGoPV45G6ZEiZ84TwjVm6HYbGOHUZe+CKnYwWThD8ZqXYzRyvVxCcVefiS6m0PKY6a5id2GySyQlTM952bDaifd09ot9pCWjwNp5q4/EQdIG3R9Kt96DfsraVrvmJWG1qQMaUlnsiZzxHWv4Fn+7BvP0Kn6AtAAAAFQDIeO7uTIVR/kzNTV9xHN/uW6KJ8wAAAIALATT5RMZUQhtwz42ek8254hrlEqSyMnWyq+vCDOp+2rE/dIkcBcd+xnfV2lTkeizAMTzYETOE8IES4rXWKFf2AlBTk9IQDnZI0ABlpUmXQVZvHxl8pKwLwzRPA7XeW4f4bXQXimUPHzCdnrwxLj7Qht4JaspL2znMCKOtpwWBrAAAAIB45bgP1JIlVpWaj1FJ/NKhDDv5D9yM7GXaljsUXL1T7KGtZ9yMA+sJa7Sw/HF88ag/gjxe6kUwmkrsvtrsza3WpfaMYupKFZtJwmQabxYPM1QWAtVONxeSo30IimFLQuaj6tgzfD1faJVyDdFydWNDUfZ3cn5iNsCz6khsc241zQ==", 
        "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHA43NTYAAABBBH3b5e6ZUbR+gMLMiOwcQzwuEPE+KIXHmzywNcOIltWY4ZiGRXlQZMyEFMENiOSivFHByMBV0wJj8VMxJocHd7s=", 
        "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDH5WKsJ0UJ8LDQMDBCcbkbdDVXcG2lhdBOmxCVm128ztp3PJrHQoNwy1njit/Sty34HYvwjVXvuaT8ksCSAGhi8VPvRo+oqGaSdt3T39Ew5DsKeJTOZDqL1Vz1jNbPvvVjsdB7v34zTgEdnjuTzlwPvtNtXyTJonXC0KDlLl5WAiYSb9XpLB0rjjKAGNautp0Mgx6olWadpMT/NWT0Ub5yHBJCWK+mYAwq0M2tK+QSrsukmG93flGLboVlWTfMIM+UUR2MH3OxI7ew6Oc5P2ligH3rcHhcAWwXLIAsMJ5vcmH0+pEvTGr9ucNMbXoZzAhX3hPN+KG8hbZ+AX3z0TXn", 
        "ansible_swapfree_mb": 482, 
        "ansible_swaptotal_mb": 487, 
        "ansible_system": "Linux", 
        "ansible_system_vendor": "", 
        "ansible_user_id": "root", 
        "ansible_userspace_architecture": "x86_64", 
        "ansible_userspace_bits": "64", 
        "ansible_virtualization_role": "guest", 
        "ansible_virtualization_type": "xen"
    }, 
    "changed": false
}

dns1.example.com | success >> {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.1.43"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::cc2b:97ff:fe7b:d221"
        ], 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "", 
        "ansible_bios_version": "", 
        "ansible_cmdline": {
            "autorun": "fsck", 
            "barrier": "off", 
            "console": "xvc0", 
            "ro": true, 
            "root": "/dev/mapper/system-root"
        }, 
        "ansible_date_time": {
            "date": "2013-10-04", 
            "day": "04", 
            "epoch": "1380891479", 
            "hour": "16", 
            "iso8601": "2013-10-04T12:57:59Z", 
            "iso8601_micro": "2013-10-04T12:57:59.276859Z", 
            "minute": "57", 
            "month": "10", 
            "second": "59", 
            "time": "16:57:59", 
            "tz": "MSK", 
            "year": "2013"
        }, 
        "ansible_default_ipv4": {
            "address": "192.168.1.43", 
            "alias": "eth0", 
            "gateway": "192.168.1.1", 
            "interface": "eth0", 
            "macaddress": "ce:cb:97:7b:d2:21", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.1.0", 
            "type": "ether"
        }, 
        "ansible_default_ipv6": {}, 
        "ansible_devices": {
            "xvda": {
                "holders": [], 
                "host": "", 
                "model": null, 
                "partitions": {
                    "xvda1": {
                        "sectors": "290816", 
                        "sectorsize": 512, 
                        "size": "142.00 MB", 
                        "start": "2048"
                    }, 
                    "xvda2": {
                        "sectors": "12288000", 
                        "sectorsize": 512, 
                        "size": "5.86 GB", 
                        "start": "292864"
                    }
                }, 
                "removable": "0", 
                "rotational": "0", 
                "scheduler_mode": "cfq", 
                "sectors": "12582912", 
                "sectorsize": "512", 
                "size": "6.00 GB", 
                "support_discard": "0", 
                "vendor": null
            }
        }, 
        "ansible_distribution": "Debian", 
        "ansible_distribution_release": "NA", 
        "ansible_distribution_version": "7.0", 
        "ansible_domain": "", 
        "ansible_eth0": {
            "active": true, 
            "device": "eth0", 
            "ipv4": {
                "address": "192.168.1.43", 
                "netmask": "255.255.255.0", 
                "network": "192.168.1.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::cc2b:97ff:fe7b:d221", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "ce:cb:97:7b:d2:21", 
            "module": "xennet", 
            "mtu": 1500, 
            "type": "ether"
        }, 
        "ansible_form_factor": "", 
        "ansible_fqdn": "dns2.example.com", 
        "ansible_hostname": "dns2", 
        "ansible_interfaces": [
            "lo", 
            "eth0"
        ], 
        "ansible_kernel": "3.1.0-1.2-xen", 
        "ansible_lo": {
            "active": true, 
            "device": "lo", 
            "ipv4": {
                "address": "127.0.0.1", 
                "netmask": "255.0.0.0", 
                "network": "127.0.0.0"
            }, 
            "ipv6": [
                {
                    "address": "::1", 
                    "prefix": "128", 
                    "scope": "host"
                }
            ], 
            "mtu": 16436, 
            "type": "loopback"
        }, 
        "ansible_lsb": {
            "codename": "wheezy", 
            "description": "Debian GNU/Linux 7.0 (wheezy)", 
            "id": "Debian", 
            "major_release": "7", 
            "release": "7.0"
        }, 
        "ansible_machine": "x86_64", 
        "ansible_memfree_mb": 9, 
        "ansible_memtotal_mb": 547, 
        "ansible_mounts": [
            {
                "device": "/dev/mapper/system-root", 
                "fstype": "ext3", 
                "mount": "/", 
                "options": "rw,relatime,errors=panic,barrier=0,data=ordered", 
                "size_available": 3733434368, 
                "size_total": 5684838400
            }, 
            {
                "device": "/dev/xvda1", 
                "fstype": "ext2", 
                "mount": "/boot", 
                "options": "rw,relatime,user_xattr,acl,barrier=1", 
                "size_available": 112991232, 
                "size_total": 139539456
            }
        ], 
        "ansible_os_family": "Debian", 
        "ansible_pkg_mgr": "apt", 
        "ansible_processor": [
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz"
        ], 
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 8, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 8, 
        "ansible_product_name": "", 
        "ansible_product_serial": "", 
        "ansible_product_uuid": "", 
        "ansible_product_version": "", 
        "ansible_python_version": "2.7.3", 
        "ansible_selinux": false, 
        "ansible_ssh_host_key_dsa_public": "AAAAB3szaC1kc3MAAACBAJFX2aR1G5QM57/3vLSlLmPR46nXNPAx0jtf6fPWkit/64W5FFBH7BW9YtPHGrucAagz1drKd9SiE+U5GlVqg/4xXOLMHmWUHitivVV9obtkyF2BM/+1OKTwxGIBP6Vu3YP/Wbpbv5TDCxjClWpZs3kCWrqRsScTdZTkk66YDTmbAAAAFQCEEjs6jtnyfF45scSgIxy60we9bQAAAIAzlb3pno+ljpE7yEjh6oBvl1RgUeYzwJZxHkBRMfOt30DyaCuXhNVhykhGYFqybv66BSu3C2br+Zk3peQRf6rie7QWV/lAXyDfInbGxgklFX6yAcd+JYj4u2vJ9j2k3GinnN9TLL3kafn0oqduy8sujozTCFZcG7dJx+4NZY29ZgAAAIBB94cFFAxC56HApvuRAcU/Wr+YeyKtJ3IHDz0hLRO+ziyuMgr2ajG80LNBGzG3rV2AEXSlH6egXaLfzcn9iPlB7VFpB/Fg/GZGOSpIUCFSSpEke6AoO8Z19Y5uR2EfcegyHhWVXGkIsaIon5KnH1bC//XAn9ir7AmANUCeXSz1Fg==", 
        "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM70PfnLbbXU+cJ27tWcKoom+P+TC08EncjB71bF4zp7Kw46YrWVjtPoFqAy3b1E2KkzUNcSrbJyEoCIgfzCC3Y=", 
        "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqltJwL3ThbfWbwBSuaZ2zZNRtcrjld0Z/ulAM6sygWTjHIeIuxT1lbJJFfKZneyo29nPho1q/HAlYGDRdcDZhKufNqDN/c9iFDbjnuPvCetUxxf+t9jKnUHnqDpO+fLYbosIEio9cmS/pOEwAU4+VBB8mdNAj9fjqrE08xcdEgt8QnAjIRlKDCdtTuYbisyt96GR10RrLPkr0epqGmHE6vzC1PyidqmQkGuCrcJHPJiS40J7S8QVP11TRS4Un+V6B7fTcfoZcPDrMsPj/NpOVh3egCJGg5VRJ2D3Fmoapg/R3ZPrMD/AW+PNQLa+1GSIVTc3cNu4ctXgnwQJwSjWL", 
        "ansible_swapfree_mb": 412, 
        "ansible_swaptotal_mb": 487, 
        "ansible_system": "Linux", 
        "ansible_system_vendor": "", 
        "ansible_user_id": "root", 
        "ansible_userspace_architecture": "x86_64", 
        "ansible_userspace_bits": "64", 
        "ansible_virtualization_role": "guest", 
        "ansible_virtualization_type": "xen"
    }, 
    "changed": false
}



А вот так можно создать логический том (или, в зависимости от текущего состояния, изменить его размер) с именем examplevolume в группе examplegroup:
$ ansible dnsservers -m lvol -a "vg=examplegroup lv=examplevolume size=1024 state=present"
dns1.example.com | success >> {
    "changed": true,
    "msg": ""
}

dns2.example.com | success >> {
    "changed": false,
    "msg": ""
}

Ansible позволяет не только выполнять единичные задачи, но и писать сценарии, которые необходимо выполнить на управляемых узлах. Рассмотрим структуру и правила написания таких сценариев более подробно.

Cценарии (playbooks)


Все сценарии в Ansible пишутся на YAML. Это — человекочитаемый формат сериализованных данных, гораздо более простой, чем XML или JSON.

Чтобы выполнить сценарий используется команда ansible-playbook со следующим сиснтаксисом:

ansible-playbook <имя_файла_сценария.yml> ... [другие параметры]

В начале сценария обязательно должна присутствовать последовательность символов «–––» (так в YAML обозначается начало документа). Перед каждым новым разделом списка ставится дефис ( — ):

---
- hosts: webservers

Основными параметрами/группами простого сценария являются:

  • hosts — в нем указываются управляемые узлы или группы узлов, к которым нужно применить изменения;
  • tasks — здесь описывается состояние, в которое необходимо привести управляемый узел, альтернативой этому могут служить роли;

Также в сценарии перед непосредственным описанием задач могут быть указаны следующие параметры или группы параметров:

  • gather_facts — собирать или нет информацию о хостах перед выполнением задач, по умолчанию — да;
  • vars — в нем указываются различные переменные, которые будут использованы при выполнении сценария;
  • connection — можно указать метод соединения с хостами: pure ssh, paramiko, fireball, chroot, jail, local, accelerate (применимо также для выполнения отдельного модуля);
  • sudo — после установления соединения выполнять задачу с привилегиями другого пользователя, по умолчанию другой пользователь — root;
  • sudo_user — в сочетании с предыдущим параметром можно указать с привилегиями какого именно пользователя будет выполнена задача;
  • vars_prompt — перед выполением плэйбука Ansible в интерактивном режиме может уточнить указанные в этом разделе параметры;
  • remote_user (в предыдущих версиях — просто user) — имя пользователя для авторизации на удалённом хосте.

Рассмотрим некоторые разделы более подробно.

В разделе hosts указывается группа управляемых узлов, к которой будут применены описываемые в сценарии изменения.

Так, строка формата:

hosts: webservers

означает, что изменения будут применены к узлам из группы webservers.

Сценарии могут выполняться не только от имени пользователя, под именем которого установлено соедиение, но и любого другого. В следующем примере авторизация на хосте будет произведена с именем yourname, но задачи будут выполняться от имени пользователя root (если, конечно, этому пользователю это разрешено использовать sudo):

---
- hosts: webservers
  user: yourname
  sudo: yes

Если добавить параметр “user: postgres”, то все действия будут выполняться с привилегиями пользователя postgres.

В разделе vars указываются переменные, которые будут использованы в сценарии, и их значения:

- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200

Список изменений/состояний, которые необходимо произвести на управляемом узле, приводится в разделе tasks. Каждой задаче (task) присваивается имя (name), его можно опустить. Далее указывается модуль Ansible, который будет задействован при её выполнении:

- hosts: webservers
  user: yourname
  tasks:
    - service: name=nginx state=started

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

---
- hosts: webservers
  user: yourname
  tasks:
    - service: name=nginx state=started
      sudo: yes


Шаблонизация


В Ansbile используется шаблонизатор Jinja2. Приведём пример простого шаблона (часть конфига powerdns):

# пароль для подключения к базе данных
gpgsql-password={{ lookup('password', 'credentials/' + inventory_hostname + '/postgresql/powerdns', length=15) }}

# IPv4-адрес, который будет “слушать” powerdns
local-address={{ ansible_default_ipv4.address }}

# IPv6-адрес, который будет “слушать” powerdns
local-ipv6={{ ansible_default_ipv6.address }}

# nsid dns-сервера (EDNS option 3, rfc5001)
server-id={{ ansible_hostname }}

В приведённом примере мы подставляем в шаблон следующие значения:
  • из заранее собранных фактов о хосте:
    • ansible_default_ipv4.address — основной IPv4-адрес хоста;
    • ansible_default_ipv6.address — основной IPv6-адрес хоста;
    • ansible_hostname — имя хоста (результат выполнения команды hostname).
  • inventory_hostname — имя хоста в инвентарном файле;
  • пароль пользователя powerdns из внешнего источника данных (в данном случае файл) для подключения к базе Postgresql, полученный с помощью lookup-плагина password из стандартной поставке. Особенность некоторых lookup-плагинов — если данных нет, то они могут их сгенерировать и сохранить для последующего использования.

Обработку шаблонов и, в данном случае, генерацию конфигурационного файла выполняет модуль template; он же может задать необходимые права доступа и изменить владельца/группу:

- name: generate powerdns config
  template: src=pdns.conf.j2 dest=/etc/powerdns/pdns.conf owner=powerdns group=powerdns mode=600

Обратим внимание на то, что файл шаблона и файл с паролем пользователя базы данных находятся на машине управления, а результатом будет файл на удалённом узле.

Обработчики событий (Handlers)


Ansible не просто выполняет задачи в указанном порядке, но и проверяет их состояние на наличие изменений. Если при выполнении сценария требовалось, например, добавить строку в конфигурационный файл, и в результате выполнения он изменился (необходимой строки действительно не было), то Ansible может выполнить специальную задачу, описанную как обработчик события (handler). Если при выполнении строка уже была в конфигурационном файле, то обработчик выполнен не будет. Обработчики событий описываются в конце сценария; в описании задачи они указываются через параметр notify. Приведём пример:

---
- hosts: webservers
  vars:
    max_clients: 200

  tasks:
    # сгенерируем файл конфигурации на основе шаблона
    # и укажем, что требуется выполнить задачу “restart apache”
    # если файл изменился
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache

  - name: ensure apache is running
    service: name=httpd state=started

  # раздел описания обработчиков
  handlers:
    - name: restart apache
      # используем модуль service для перезапуска веб-сервера
      service: name=httpd state=restarted


Контроль выполнения


Допустим, что при выполнении сценария нам нужно проверять определённые переменные или состояния и, в зависимости от них, выполнять или не выполнять какие-либо задачи. Для этого можно использовать оператор “when”:

tasks:
      # сохраняем файл шаблона и сохраняем результат задачи
      # в переменную last_result
    - template: src=/templates/foo.j2 dest=/etc/foo.conf
      register: last_result
      # проверяем переменную last_result.changed и если она имеет
      # значение true - задача будет выполнена, иначе - будет пропущена
    - command: echo 'the file has changed'
      when: last_result.changed


Делегирование задачи другому хосту


Иногда требуется выполнить задачу на определённом узле, но в контексте другого узла. Например, во время обновления узла может возникнуть необходимость отключить для него мониторинг, находящийся на отдельном сервере. Для этого используется управляющая директива delegate_to. Приведём пример:

- name: disable nagios alerts for this host webserver service
  nagios: action=disable_alerts host={{inventory_hostname}} services=dnsserver
  delegate_to: mon_host.example.com

Результатом выполнения этой задачи будет отключение сообщений для сервиса dnsserver в Nagios.

Роли


Ролью называется типовой набор переменных и задач, назначаемых для одного или нескольких серверов. Если вам нужно применить к серверу или группе серверов типовой набор операций, вам достаточно просто назначить ему роль. Предварительно в проекте каталоге проекта должна быть создана соответствующая структура. В сценариях роли назначаются следующим образом:

--- 
- name: check and apply basic configuration to all hosts
  hosts: all
  roles:
    - common

- name: check and apply configuration to group1
  hosts: group1
  roles:
    - pgsql

- name: check and apply configuration to group2
  hosts: group2
  roles:
    - fooapp


Структура проекта


├── production                # инвентарный файл для продакшн-серверов
├── stage                     # инвентарный файл для stage-окружения
│
├── group_vars/
│   ├── group1                # здесь назначаются переменные для
│   └── group2                # конкретных групп
├── host_vars/
│   ├── hostname1             # специфические переменные для хостов в
│   └── hostname2             # случае необходимости прописываются здесь
│
├── site.yml                  # основной сценарий
├── webservers.yml            # сценарий для веб-сервера
├── dbservers.yml             # сценарий для сервера базы данных
│
└── roles/
    ├── common/               # здесь описываются роли
    │   ├── tasks/            #
    │   │   └── main.yml      # - файл задач роли, может включать файлы
    │   │                     #   меньшего размера
    │   ├── handlers/         #
    │   │   └── main.yml      # - файл с обработчиками (handlers)
    │   ├── templates/        # - директория для шаблонов, в данном
    │   │   └── ntp.conf.j2   #   случае - для конфига ntp
    │   ├── files/            #
    │   │   ├── bar.txt       # - файл-ресурс для копирования на хост
    │   │   └── foo.sh        # - скрипт для выполнения на удалённом хосте
    │   └── vars/             #
    │       └── main.yml      # - ассоциированные с ролью переменные
    │
    ├── pgsql/                # такая же структура, как выше, для роли pgsql
    └── fooapp/               # такая же структура, как выше, для роли fooapp


Пример сценария


Чтобы понять, как это все работает, рассмотрим практический пример: простой сценарий развёртывания новой версии PostgreSQL 9.3 на debian-based ОС. Роли в этом примере не используются.

текст плэйбука
---
- name: install postgresql 9.3 # имя playbook'a
  # секция, описывающая параметры, которые нужно уточнить у пользователя в начале запуска
  vars_prompt:
    hosts: "Please enter hosts group name" # спрашиваем имя группы серверов в инвентаре (в нашем случае файл $ANSIBLE_HOSTS)
    username: "Please enter username for auth" # спрашиваем имя пользователя для подключения к серверам
  hosts: $hosts # 
  user: $username
  sudo: True
  accelerate: true
  vars:
    app_username: 'app_user'     # имя пользователя мифического приложения, которое работать с базой данных
    app_host_ip: '192.168.0.100' # ip-адрес хоста с запущенным приложением, с него будут поступать запросы в базу данных
    app_database_name: 'appdb'   # имя базы данных приложения

  tasks:
      # Проверяем установлен ли и устанавливаем пакет python-software-properties
      # для обеспечения работы модуля apt. Параметры модуля:
      # pkg - имя пакета для установки
      # state - устанавливаем последнюю версию пакета, 
      # update_cache - обновляем список доступных пакетов перед установкой
    - name: check add-apt-repository 
      apt: pkg=python-software-properties state=latest update_cache=yes

      # добавляем ключ официального apt-репозитория проекта postgresql
      # Параметры модуля:
      # url - URL до файла с ключём
      # state - добавить ключ
    - name: add apt key
      apt_key: url=http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc state=present

      # добавляем репозиторий, адрес формируется на основе имени релиза установленной ОС
    - name: add apt repo
      apt_repository: repo='deb http://apt.postgresql.org/pub/repos/apt/ ${ansible_lsb.codename}-pgdg main'

      # устанавливаем пакет с ключём для последующего возможного автоматического обновления
    - name: install pgdg-key
      apt: pkg=pgdg-keyring state=latest update_cache=yes

      # устанавливаем пакеты postgresql-9.3 (непосредственно сам сервер баз данных)
      # и python-psycopg2 - для работы модулей postgresql_user, postgresql_db, postgresql_privs
    - name: install packages
      apt: pkg=$item state=latest
      with_items:
        - postgresql-9.3
        - python-psycopg2
        - python-keyczar

      # создаём пользователя для работы нашего мифического приложения c атрибутом LOGIN
      # сгенерированный пароль будет сохранён в credentials/имя_хоста/postgres/имя_пользователя
    - name: create postresql user for some app
      # выполнение задачи будем производить с правами пользователя postgres (создаётся при установке postgresql)
      sudo: yes
      sudo_user: postgres 
      postgresql_user:
          user=${app_username}
          password="{{ lookup('password','example/credentials/' + inventory_hostname + '/postgres/' + app_username, length=15) }}"
          role_attr_flags=LOGIN

      # создаём базу данных для мифического приложения с говорящими за себя параметрами
    - name: create db for our app
      sudo: yes
      sudo_user: postgres
      action: postgresql_db name=${app_database_name} owner=${app_username} encoding='UTF8' lc_collate='en_US.UTF-8' lc_ctype='en_US.UTF-8' template='template0' state=present

      # Следующая задача будет выполнена хосте приложения, а не на текущем настраиваемом хосте
    - name: add app_user password to .pg_pass file on server with our app
      sudo: yes
      sudo_user: ${app_username}
      delegate_to: ${app_host_ip}
      lineinfile:
          dest=/home/${app_username}/.pgpass
          regexp='^{{ inventory_hostname }}\:\*\:${app_database_name}\:${app_username}'
          line='{{ inventory_hostname }}:*:${app_database_name}:${app_username}:{{ lookup('password','example/credentials/' + inventory_hostname + '/postgres/' + app_username, length=15) }}'
          create=yes
          state=present
          backup=yes

      # добавляем в pg_hba.conf строчку, описываюшую разрешение подключение с ip-адреса приложения для ранее созданной базы и пользователя
    - name: add entry to pg_hba.conf
      lineinfile:
          dest=/etc/postgresql/9.3/main/pg_hba.conf
          regexp='host ${app_database_name} ${app_username} ${app_host_ip}/32 md5'
          line='host ${app_database_name} ${app_username} ${app_host_ip}/32 md5' state=present
      # если файл изменился, то вызовем задачу по перечитыванию конфига postgresql
      # напоминаем что модули ansible возвращают состояние "изменилось/не изменилось" после выполнения,
      # хэндлеры описываются либо в конце playbook'a или в отдельном файле
      notify:
        - reload postgres

      # по умолчанию postgresql слушает только localhost
      # изменияем соответствующий параметр в postgresql.conf на ip-адрес сервера
    - name: add entry to postgresql
      lineinfile:
          dest=/etc/postgresql/9.3/main/postgresql.conf
          regexp='^listen_addresses'
          line="listen_addresses = '${ansible_default_ipv4.address}'"
          state=present
      # если файл изменился, то вызовем задачу по перезапуску postgresql, т.к.
      # параметр listen_addresses можно изменить только перезагрузкой сервера postgresql
      notify:
        - restart postgres

  # описание хэндлеров
  handlers:
      # перечитываем конфигурацию postgresql
    - name: reload postgres
      sudo: yes
      action: service name=postgresql state=reloaded

      # перезагружаем postgresql
    - name: restart postgres
      sudo: yes
      action: service name=postgresql state=restarted



Ansible AWX


Во всех приведенных выше примерах управление Ansible осуществляется с помощью интерфейса командной строки. Но с официального сайта можно загрузить графическую панель управления Ansibleworks AWX, очень симпатичную внешне и удобную в использовании. Собственно, за счет ее распространения и осуществляется монетизация продукта: управление небольшим (до 10) количеством серверов обходится бесплатно, если же серверов больше — нужно приобретать лицензию. Похожие варианты монетизации используются и разработчиками конкурирующих решений — Puppet и Chef.

Заключение


Ansible — достаточно простой, но при этом эффективный инструмент для удаленного управления конфигурациями. В рамках этой статьи мы сделали лишь беглый обзор его возможностей и рассказали, как пишутся сценарии для решения простых задач. Все возможности и варианты использования Ansible в рамках одной статьи охватить невозможно. О применении этого инструмента для решения более специфических задач мы расскажем в последующих публикациях.

Для желающих узнать больше — несколько ссылок:

https://github.com/ansible/ — официальный аккаунт на github c исходным кодом проекта и хорошим набором примеров проектов

http://www.ansibleworks.com/docs/ — официальная документация (активно пополняется);

http://jpmens.net/2012/06/06/configuration-management-with-ansible/ — статья Яна Пита Менса об управлении конфигурациями с помощью Ansible (в его блоге есть и много других материалов по теме).

https://gist.github.com/marktheunissen/2979474 — пример сценария с подробными комментариями, правда для старой версии.

www.ansibleworks.com/docs/contrib.html — ещё больше ссылок на примеры использования, включая в том числе и очень сложные конфигурации.

Для тех кто не может комментировать посты на Хабре, приглашаем к нам в блог.
Автор: @clickfreak
Селектел
рейтинг 153,19

Комментарии (49)

  • +4
    Отличная статья. Я как раз недавно тоже писал о ansible, но ваш пост мне понравился даже больше. Надеюсь от вас будет еще немало статей по этой теме.
    Кстати, хочу спросить, вы мигрировали с какой-то системы управления конфигурациями? Или сразу начинали на ansible? Если мигрировали, то какие были причины для миграции?
    • +3
      Однозначно будут ещё статьи, т.к. в одной статье раскрыть весь потенциал и возможности просто невозможно.

      Ansible был выбран изначально и про миграцию, слава богу, думать не пришлось.
      • 0
        Сколько процентов конфигурационных задач покрываете этой системой? Какой объем, возраст кодовой базы? С чем столкнулись при росте кодовой базы? Чокак рефакторинг? :)

        Спасибо за статью, пишите еще! :)
        • +2
          Ansible пока используется только в управлении инфраструктурой распределённых NS-серверов и покрывает все задачи конфигурирования (частичное использование было бы странно), кроме одной. Исключением является управление анонсированием префиксов (BGP), которое производится вручную для каждого узла, очень уж аккуратно нужно это делать (сами BGP-сессии поднимаются Ansible'ом). Объем кодовой базы не такой уж большой и разрастаться пока особо не собирается. Небольшой рефакторинг плэйбуков был при упрощении синтаксиса плэйбуков в одной из версий. А работать с Ansible начал примерно в апреле этого года.
      • 0
        А, ну и вопрос вечера (почему я собственно принялся гуглить «selectel chef» и нашел эту статью): есть ли плагин для провизионировния ваших виртуалок?

        Ради чего все ;)
        • +1
          Плагина пока нет. Но мы подумаем над этим :)
  • +1
    Ну спрошу и у вас — как с поддержкой FreeBSD?
    • 0
    • +1
      Если честно — не пробовали. Но, судя по исходному коду, как минимум начальная поддержка есть (определение оборудование, ZFS, pkgng, группы, пользователи, sysctl, сервисы). Кстати, там же обнаружил поддержку NetBSD, OpenBSD, HP-UX, AIX и горстку Solaris'ов (сам Solaris и производные от OpenSolaris'a), но без практического опыта утверждать что Ansible действительно хорошо работает на этих системах я не возьмусь.
  • 0
    Не нашел значительных преимуществ Ansible по сравнению с Puppet кроме как «на управляемые узлы не нужно устанавливать никакого дополнительного ПО, всё работает через SSH». Остальные преимущества имхо высосаны из пальца.
    • +4
      Эм. Вы с паппетом вообще работали? ОН АЦЦКИ СЛОЖНЫЙ. И сделать что-то на нем — мучение.
      • 0
        Да работал, он меня вполне устраивает. В сутки, полагаю, до 50 коммитов в свою конфигурацию провожу. К сожалению не использовал ни Chef, ни Ansible, поэтому мне сложно сравнивать.
      • 0
        Не сложный же он для простых вещей. Но очень хреново расширяемый.
    • +7
      Убедить Вас что Ansible лучше у меня вряд ли получиться, могу лишь добавить следующее:

      Puppet, за 8 лет существования оброс солидным количеством документации, howto, модулями и примерами конфигураций на все случаи жизни, но вот почему-то когда с ним работаешь складывается впечатление что тебе приходится общаться с монстром, наверное поэтому этих howto так много. И да, лишние зависимости, в числе которых Ruby…

      — за ~1.5 года существования Ansible (а стабильную версию зарелизили только около 8 месяцев назад) статей появилось не так много, но их достаточно чтобы системному администратору описать простым и лаконичным языком то что он делал до этого в консоли не задумываясь о программистских абстракциях.

      Немного о прошлом основного разработчика Ansible и по-совместительству CTO AnsibleWorks Michael DeHaan:
      — Автор и бывший лидер Cobbler (3 года)
      — бывший продакт менеджер Puppet…

      Видать что-то его не устроило в существующих инструментах и он решил сделать ещё один с учётом предыдущих ошибок. И Ansible у него пока получается просто конфектой.
      • 0
        С Ruby согласен на 100% =) Но к счастью не так часто приходится на Ruby темплейты писать.
      • 0
        Хорошо бы, чтобы из конфеКТы что-то приличное вышло ;)

        Если серьезно — думаю что не устроило его то, что не он владелец puppet и он решил придумать что-то свое с «карточными играми и неприличными девушками».

        Посмотрим, во что превратиться Ansible с течением времени.
    • 0
      * Он намного проще, чем Puppet (в т.ч. благодаря гибкости)
      * Модули на все или почти все случаи жизни из коробки (в паппете аналог этому — ресурсы)
      * Которых нет — очень просто дописываются (сравнимо с LWRP шефа, а может и проще) и на любом языке, хоть на awk.
      * Практически не вытягивает зависимостей в т.ч. для управляющей части (не помню как у puppet с этим)
      * Нет гемора с сертификатами
      * Нет гемора с hiera
      * Инвентарь проще (впрочем, из коробки в чем-то более ограничен, т.к. не хранит состояние клиентов), но тоже очень легко расширяется
  • 0
    >>в романах американской писательницы Урсулы Ле Гуин ансиблом называется устройство для оперативной космической связи.
    Еще анзибль как средство космосвязи упоминается у Орсона Карда в «Игре Эндера» (кстати, скоро фильм по этой книге выходит: )
  • +1
    Столкнулся с вроде как и простой (но на деле не очень) проблемой — допустим, я хочу ruby на сервере. Причём, не просто ruby, а ruby, установленный через chruby + ruby-install. Ок, тут всё легко, буквально в пару строк ставится chruby, ставится ruby-install, компилируется нужная версия ruby, в /etc/profile.d/ добавляется нужный файл для bash'а (загрузка chruby, установка актуальной версии ruby).

    А дальше начинается самое интересное — например, я хочу через модуль gem ansible установить хотя бы bundler — пишу

    gem: name=bundler state=latest

    и тут понимаю, что ansible выполняет все команды через sh и, соотвественно, мой файл в profile.d не читает.

    Искал долго — гугл не подсказал, как можно по умолчанию заставить ansible использовать bash в качестве оболочки.

    В общем, как-то так (получилось немного сумбурно, наверное).

    Знает ли кто-нибудь, как такое можно решить? Пока обошел проблему с помощью костыля через подключение chruby и установку требуемых гемов через модуль команд.
    • 0
      А выполнить удаленно bash -c «нужная команда»? ЕМНИП, баш можно подобным образом заставить стартануть с чтением нужных конфигов (правда, нужно проверить, которые из них для случая, когда баш — логиновый шелл, а какие нет) и выполнить потребную команду.

      Как-то так :)
      • 0
        Просто очень хочется использовать именно модуль gem в ansible для этого, так как это правильнее, на мой взгляд. Иначе в чём тогда его смысл? :)
        • 0
          Перечитав исходное, не совсем понял, откуда тут башевые конфиги вообще. Для подгрузки рубистического окружения для логинящихся в баш пользователей?.. Но это вроде не ваш случай, вы же на сервере.

          Может быть, нужно понять, как решить ту же задачу, что решается этими башевыми конфигами?

          Условно говоря, перед вызовом gem подгружать ему нужное ruby-окружение? Возможно, придать команде какой-то предхук, или отнаследоваться от команды gem ;)

          p.s. Я довольно абстрактно рассуждаю, не до конца понимая вашу задачу и инструменты )))
          • 0
            Ну, башевые конфиги не нужны. Просто я хочу вместо установки переменных окружения (PATH) и запуска команды gem, грубо говоря (что выльется в что-то вида:

            # YML task file
            — name: Install bundler
            script: install-bundler.sh


            # install-bundler.sh
            source /usr/local/share/chruby/chruby.sh
            chruby ruby-2.0
            gem install bundler


            ) использовать простой DSL и написать так:

            gem: name=bundler state=latest
          • 0
            Ну я с chrubi не работал. Но вот rvm подразумевает чтение bashrc при логине, так как Кое-что Делает. В таких случаях либо однострочник через модуль shell, либо патч для модуля. Собственно, как и в других системах, в которых это не предусмотрели.
        • 0
          Смысл модулядля gem или смысл ansible? В принципе любое отклонение от стандартного поведения черевато неожиданными последствиями :) ну и можно на основе модуля gem написать свой. Это не сложнее, а то и проще чем шефовские кукбуки.
          • 0
            Смысл ansible, если использовать его тупо как пускалку bash скриптов с немного продвинутой функциональностью :) Поэтому, я тут тоже вижу решение только в виде допиливания самому нужной функциональности ansible.
            • 0
              Ну тем не мение пускалка скриптов типа rundeck вполне себе популярна.
              • 0
                Спасибо, не видел такой штуковины, выглядит очень интересно! Правда, под задачи управления конфигурациями подходит слабовато, но это и не её ниша.
                • 0
                  Ну как сказать, с Chef на нее иногда переходят.
                  • +1
                    Там вроде не переходят на неё, а скорее, с помощью неё дополняют шефа.
                    • 0
                      Дополняют. Например для CD в CI — не всегда удобно использовать шеф. А в rundeck есть неплохая вебморда и вменяемое API.
    • +2
      Искал долго — гугл не подсказал, как можно по умолчанию заставить ansible использовать bash в качестве оболочки.
      /etc/ansible/ansible.cfg:

      # use this shell for commands executed under sudo
      # you may need to change this to bin/bash in rare instances
      # if sudo is constrained
      #executable = /bin/sh
      

      • 0
        Ни разу не использовал chruby, но если требуется установить переменные окружения, то возможно также подойдёт использование параметра «environment», пример:

        vars:
          chruby:
            rubypath: /usr/local/bin/ruby
            some_other_env_var: var_value
        
        tasks:
          - name: test env
            shell: echo $rubypath
            environment: chruby
        
    • +1
      Почитайте вот эту статью про различия profile и bashrc www.joshstaiger.org/archives/2005/07/bash_profile_vs.html. Должно помочь
  • +2
    О, и в vagrant'е появился провижнер ansible.

    Хороший знак.
    • +1
      Вот это очень интересно. Пропустил. Спасибо.
  • +1
    У ней консольный вывод цветной. Думаю, подружимся.
    • 0
      Apt-get install cowsay
      • 0
        *facepalm*
        У вас у первого *nix регистронезависимый что ли?!
        • 0
          Это у меня планшет регистрозависимый.
          • 0
            Вот оно! Машины уже считают себя умнее людей))
      • 0
        lol
  • 0
    Буквально на днях начал использовать Ansible, Всем нравится, но возникла проблема входа на сервер по ssh от имени другого юзера. Т.е. я залогинен под собой, а мне нужно подключиться под другим юзером:
    ansible-playbook test.yml -u test
    Выдает ошибку:
    Скрытый текст
    Authentication or permission failure. In some cases, you may have been able to authenticate and did not have permissions on the remote directory. Consider changing the remote temp path in ansible.cfg to a path rooted in "/tmp". Failed command was: mkdir -p $HOME/.ansible/tmp/ansible-1381305415.14-95794596656422 && chmod a+rx $HOME/.ansible/tmp/ansible-1381305415.14-95794596656422 && echo $HOME/.ansible/tmp/ansible-1381305415.14-95794596656422, exited with result 255

    Не понимаю почему. Сам юзер на сервере есть, ключ проброшен. Через консоль зайти могу, а через Ansible не получается. Работает почему-то только через sudo:
    sudo -u test ansible-playbook test.yml -u test
    • 0
      Так а ключ на этого юзера на сервере у вас чей лежит? Ваш? Или вашего локального юзера test?
      • 0
        На сервере лежат оба ключа: и мой, и test.
        • +1
          То есть вы можете подключится на сервер под юзером тест используя ваш ключ? Если да, то тогда я ничего не понимаю — у меня это отлично работает. Попробуйте еще использовать ключ -s. Или лучше всего сначала подключится через пароль — ключ -k и посмотреть что скажет сервер.
    • 0
      Выполните с параметром -vvvv, в процессе будет много отладочной информации и будет видно на каком этапе произошла ошибка.
      Судя по приведённому сообщению, авторизация прошла успешно, но не было прав для создания временных файлов. Проверьте может ли пользователь создать директорию $HOME/.ansible/tmp/. Если это по каким-то причинам запрещено и нельзя исправить — добавьте в начало плэйбука remote_tmp: /tmp, тогда ansible будет создавать временные файлы в соответствующей директории.
      • 0
        Добавил remote_tmp в плейбук, но сразу получил ошибку: «ERROR: remote_tmp is not a legal parameter in an Ansible Playbook».

        Пользователь имеет все необходимые права, я от его имени без проблем выполнил те команды, которые Ansible не смог.
        • 0
          Проверил, действительно не работает. Оказалось что этот параметр можно поменять только в ansible.cfg (о нём я в статье не упоминал, его местонахождение: /etc/ansible/ansible.cfg, ~/.ansible.cfg, `pwd`/ansible.cfg). Попробуйте изменить remote_tmp именно там. Хотя похоже проблема всё-таки не там.
          Выполните ansible <имя_группы> -vvvv -u <имя_проблемного пользователя> -m ping (до изменения ansible.cfg). Если выполнится без ошибок — проблема где-то в плейбуке, если с ошибкой — отладочной информации будет намного больше чем в случае с ansible-playbook.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разное