Автоматизация управления с помощью Ansible
В предыдущей статье мы достаточно подробно рассмотрели вопросы связанные с автоматизацией управлением и настройкой ПО в средних и крупных сетях. Рассмотрели Vagrant и основные методы работы с виртуальной инфраструктурой. В этой статье мы подробно поговорим об использовании такого интересного инструмента, как Ansible.
Данное решение позволяет автоматизировать развертывание и настройку ресурсов в сети, подготовку контейнеров и виртуальных машин, и многое другое. Само приложение Ansible работает в так называемом проталкивающем режиме. Вся работа с инфраструктурой осуществляется с сервера управления. И с этой машины ведется применение настроек к управляемым узлам.
В этой статье не будет длинных вступлений, рассказывающих о том, зачем вообще нужен Ansible, чем он отличается от других подобных решений и так далее. Вместо этого я предлагаю сразу перейти к практике и развернуть необходимую тестовую среду.
Сначала нам необходимо будет развернуть сервер управления Ansible. В качестве ОС я буду использовать Ubuntu 22.04. Предварительно сделав апгрейд и апдейт, выполним следующую команду:
sudo apt install ansible
Установка не должна вызвать каких-либо сложностей. После ее завершения мы получим узел управления со всем необходимым ПО для администрирования целевых узлов. Как настоящие DevOps мы будем работать через консоль и для начала нам необходимо внести правки в файл инвентаризации /etc/ansible/hosts. Это файл содержит информацию о тех хостах, которыми будем управлять, параметры конфигурации и переменные.
В качестве примера пропишем в этот файл два сервера .150 и .137. Также пропишем путь к интерпретатору Python.
[servers]
server1 ansible_host=192.168.222.150
server2 ansible_host=192.168.222.137
[all:vars]
ansible_python_interpreter=/usr/bin/python3
С этим значением параметра удаленный сервер использует исполняемый файл Python 3 /usr/bin/python3, который может отсутствовать в некоторых версиях Ubuntu.
Об аутентификации
С настройками по умолчанию Ansible пытается подключиться ко всем нодам по протоколу SSH и с помощью аутентификации по ключам. Однако, не всегда есть возможность аутентифицироваться по ключам, и в качестве альтернативы вы можете указывать пароль при подключении. Но для использовании парольной аутентификации необходимо предварительно установить на сервере управления Ansible утилиту sshpass:
sudo apt install sshpass
Однако, пароли - это не лучший механизм защиты и SSH-ключи надежнее. Для того, чтобы настроить аутентификацию по ключам. Для этого выполним следующие действия:
На сервере управления Ansible отобразим открытый ключ в терминале:
Если такого файла нет, его можно создать:
ssh-keygen -t rsa
Далее необходимо зайти на клиентскую машину, перейти в сессию root и открыть файл nano ~/.ssh/authorized_keys и поместить в него открытый ключ сервера управления:
В качестве проверки результатов настройки узнаем, какие версии ядра на управляемых машинах.
ansible all -a "uname -a"
Используем плейбуки
Мы научились выполнять команды на управляемых серверах и получать ответы от их операционных систем. Однако для настоящей автоматизации этого явно недостаточно. И здесь на помощь приходят плейбуки – базовые компоненты, которые записывают и исполняют конфигурацию Ansible, что позволяет по-настоящему автоматизировать выполнение задач на управляемых машинах.
Немного о терминологии. В последнее время есть тенденция к отказу от англоязычных терминов, поэтому в некоторых источниках, особенно связанных с ИБ можно встретить замену иностранного слова “плейбук” российским аналогом – карта реагирования. Однако в данной статье мы будем использовать термин плейбук.
Итак, что из себя представляет плейбук: по сути это набор сценариев, которые выполняются в заданном порядке. Сценарий – это список задач для определенной группы хостов.
Инструментом для создания плейбуков является язык YAML. При написании сценариев большое значение имеет отступы, которые необходимо делать только с помощью пробелов, TAB использовать нельзя. У одинаковых элементов отступы тоже должны быть одинаковые. Вот пример Hello world в Ansible:
---
- hosts: all
tasks:
- name: Print message
debug:
msg: Hello Ansible World
В этом сценарии мы указываем в качестве целей все узлы из нашего hosts файла. Наименование задачи Print message, и вывод сообщения Hello Ansible World.
Сохраним этот плейбук в файл и запустим на выполнение.
ansible-playbook pl_1.yml
Задача успешно выполнена. В случае, если нам необходимо выполнить плейбук только на одном управляемом узле, необходимо указать параметр –limit и имя данного узла, указанное в файле hosts.
ansible-playbook pl_1.yml --limit server1
Далее рассмотрим работу с переменными в Ansible. В следующем примере мы будем работать сразу с двумя видами переменных: встроенной переменной Ansible и определенной в нашем плейбуке.
Перед началом блока определяемых в плейбуке переменных мы указываем vars:, далее указываем сами переменные и их значения. В нашем примере мы определяем переменную txt со значением IP_address. В блоке tasks, в разделе msg переменные указываются в двойных фигурных скобках с отступом с каждой стороны. Сначала мы указываем нашу переменную txt, а затем встроенную переменную ansible_default_ipv4.address. Определять данную переменную не надо, так как она уже определена в самом Ansible.
- hosts: all
vars:
txt: IP_address
tasks:
- name: print facts
debug:
msg: "{{ txt }}: {{ ansible_default_ipv4.address }}"
Сохраним созданный файл и выполним плейбук для всех узлов.
ansible-playbook pl_2.yml
Плейбуки позволяют использовать условия, то есть мы можем наделить наши сценарии определенной логикой выполнения. Например, пусть нам необходимо проверить наличие на узлах определенного файла и в случае его отсутствия создать данный файл.
Для этого мы сначала определим переменные user и file_name
vars:
- user: otus
- file_name: test1
Затем выполним команду ls
command: ls /home/{{ user }}/{{ file_name }}
Далее мы используем ключевое слово register, которое создает новую переменную file_exists и присваивает ей выходные данные, полученные из команды.
Одна важная вещь, на которую следует обратить внимание, заключается в том, что по умолчанию Ansible прерывает работу плейбука, если команда, которую вы используете для оценки условия, завершается неудачей. По этой причине мы используем директиву ignore_errors, имеющую значение yes, и это заставит Ansible перейти к следующей задаче и продолжить работу.
register: file_exists
ignore_errors: yes
В Ansible вы можете определить условия, которые будут оцениваться перед выполнением задачи. Когда условие не выполняется, задача пропускается. Это делается с помощью ключевого слова when, которое принимает выражения, которые обычно основаны на переменной или факте.
Далее в случае, если fail_exists имеет значение failed, мы создаем новый файл.
- name: create file for user
file:
path: /home/{{ user }}/{{ file_name }}
state: touch
when: file_exists is failed
Если файл уже существует, мы просто выводим соответствующее сообщение:
- name: show message if file exists
debug:
msg: The user file already exists.
when: file_exists is succeeded
Далее приведен полный текст всего плейбука:
---
- hosts: all
vars:
- user: otus
- file_name: test1
tasks:
- name: Check if file already exists
command: ls /home/{{ user }}/{{ file_name }}
register: file_exists
ignore_errors: yes
- name: create file for user
file:
path: /home/{{ user }}/{{ file_name }}
state: touch
when: file_exists is failed
- name: show message if file exists
debug:
msg: The user file already exists.
when: file_exists is succeeded
Запустим наш плейбук на выполнение:
ansible-playbook pl_3.yml
Как видно, при первом запуске мы получили ругательства на тему отсутствия файла с таким именем. Однако, все эти сообщения были успешно проигнорированы и выполнение сценария было продолжено.
При повторном запуске плейбука:
Получаем сообщения, что файлы уже существуют.
Циклы в Ansible
Еще одной важной задачей автоматизации является необходимость повторения выполнения одной и той же задачи, с использованием разных значений. Например, вам может потребоваться изменить разрешения для нескольких файлов или создать нескольких пользователей. Чтобы избежать многократного повторения задачи в вашем файле playbook, для этого лучше всего использовать циклы.
В следующем примере мы объявляем в переменной file_name значение ansible, затем создаем файлы с именами, содержащими значение file_name и через дефис значение одной из переменных внутри loop.
---
- hosts: all
vars:
- user: otus
- file_name: ansible
tasks:
- name: creates users files
file:
path: /home/{{ user }}/{{ file_name }}-{{ item }}
state: touch
loop:
- test10
- test20
- test30
Запустим получившийся плейбук:
ansible-playbook pl_4.yml
В результате работы сценария получаем три файла с именами, соответствующими заданному шаблону:
ls
Заключение
В этой статье мы рассмотрели основные моменты связанные с установкой Ansible и выполнением базовых задач. Но это только малая часть того, что умеет данная система, например за кадром осталось управление узлами под Windows, поднятие привилегий, установка обновлений и многое другое. Так что мы еще обязательно вернемся к рассмотрению вопросов использования Ansible.
Также хочу порекомендовать всем бесплатный урок курса DevOps практики и инструменты от OTUS, где мои коллеги расскажут про 3 основных принципа безопасности инфраструктуры. В уроке будет демо, где эксперты OTUS разберут одну проблему на инфраструктуре - очень интересный протокол Диффи-Хеллмана на примере больших чисел с отсылкой на эллиптические кривые и криптографию.Преподаватель на пальцах покажет как работает алгоритм и как исправить проблему связанную с ним в nginx.