Количество серверов в нашей инфраструктуре уже перевалило за 800, хотя еще год назад их было около 500. Для работы с этим всем активно используются решения от Red Hat. Про FreeIPA — для организации и управления доступами для Linux-серверов — мы уже писали, сейчас же я хочу затронуть тему управления конфигурациями. Для этих целей у нас юзается Ansible, а с недавних пор к нему добавился AWX — представленное полгода назад решение для централизованного управления плейбуками, расписанием их запусков, управления инвентори, учетными данными для доступа к серверам, а также механизм callback'ов для запроса конфигураций со стороны сервера.
Из-за ряда вещей мы не сразу смогли интегрировать его для работы с нашим основным проектом War Robots, но полей для проверки AWX нашлось предостаточно. Во-первых, в компании ведутся разработки новых проектов, которым нужны dev/stage-окружения и, само собой, production-окружения в перспективе. А недавно к этому добавился еще и проект для внутренней аналитики, которому потребовался полностью новый кластер.
Итак, начнём!
AWX был представлен в сентябре 2017 года — это бесплатный open source проект, распространяющийся под лицензией Apache-2.0 и являющийся апстримом для коммерческого проекта Ansible Tower. В целом, тут тот же принцип, что и у других проектов Red Hat: Red Hat Cloud Forms — ManageIQ; RHEV — Ovirt; Red Hat Identify Managment — FreeIPA и так далее.
Данная статья представляет из себя руководство — от установки AWX до запуска первого плейбука. Но сначала перечислим основные возможности AWX:
- Интеграция с системами контроля версий (git/mercurial/subversion).
- Отслеживание статуса выполнения плейбуков в реальном времени.
- Настройка расписания для автоматического запуска плейбуков.
- Выполнение нескольких плейбуков в рамках одного workflow.
- Удаленное выполнение команд без плейбуков (Ansible ad hoc).
- Поддержка механизма callbackов, позволяющих новым серверам запрашивать конфигурации со своей стороны.
- Управление Inventory для ансибла, в том числе с возможностью интеграции с платформами AWS/Azure/OpenStack и т.д., а также поддержка собственных скриптов для генерации Dynamic Inventory.
- Гибкая система разграничения прав доступа. Интеграция с LDAP/SAML/Active Directory и т.д.
- Встроенная поддержка уведомлений для Email/Slack/PagerDuty/HipChat/MatterMost/IRC.
- Интеграция с внешними системами агрегации логов: Logstash/Splunk/Loggly/Sumologic.
Установка
В данный момент поддерживается несколько вариантов установки:
- Kubernetes.
- Openshift.
- Docker/Docker Compose.
Все они описаны в документации на GitHub, а для примера рассмотрим вариант с чистым Docker, так как это самый быстрый вариант, не требующий дополнительной настройки OpenShift/Kubernetes.
Устанавливать всё будем на Centos 7. Установка Докера также описана в официальной документации, поэтому в статье ее трогать не будем.
Далее нам нужно установить ansible и модуль docker-py. Это можно сделать из pip:
pip install ansible
pip install docker-py
Клонируем репозиторий AWX:
git clone https://github.com/ansible/awx.git
cd awx/installer
Правим файл inventory. В первую очередь, рекомендую поправить переменную postgres_data_dir. По умолчанию она равна /tmp/pgdocker, но если оставить в таком виде — через некоторое время можно потерять postgresql базу, которую использует AWX.
Если вы хотите использовать внешнюю базу, укажите переменные:
pg_hostname
pg_username
pg_password
pg_database
pg_port
После этого запускаем:
ansible-playbook -i inventory install.yml
Эта команда запустит выполнение плейбука, который загрузит нужные docker-образы и запустит контейнеры непосредственно с AWX и дополнительными компонентами: Postgres, MemCached, RabbitMQ.
После завершения выполнения плейбука мы должны увидеть следующие контейнеры:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b1bd94f83420 ansible/awx_task:latest "/tini -- /bin/sh ..." 6 days ago Up 6 days 8052/tcp awx_task
abf30fd81335 ansible/awx_web:latest "/tini -- /bin/sh ..." 6 days ago Up 6 days 0.0.0.0:80->8052/tcp awx_web
b13ee3c7cbb7 memcached:alpine "docker-entrypoint..." 2 months ago Up 6 days 11211/tcp memcached
3c4cac5a4ce5 rabbitmq:3 "docker-entrypoint..." 2 months ago Up 6 days 4369/tcp, 5671-5672/tcp, 25672/tcp rabbitmq
b717c6019e02 postgres:9.6 "docker-entrypoint..." 2 months ago Up 6 days 5432/tcp postgres
Следить за процессом установки AWX можно следующим образом:
docker logs -f awx_task
AWX будет доступен по адресу сервера на 80 порту (если вы не поменяли порт в файле inventory).
Логин и пароль по умолчанию:
Admin:password
Использование и примеры
Теперь перейдем непосредственно к AWX, но сначала немного разберемся с тем, как всё организовано.
В первую очередь нас интересует раздел Projects. Projects в терминологии AWX означает набор плейбуков, которые расположены локально на сервере с AWX или в репозитории.
Создадим наш первый Project:
— Name: имя проекта, пускай это будет firstproject.
— Organization: в AWX это сущность, которая используется для разграничения доступа. К Organization могут относиться инвентори, пользователи, группы пользователей, плейбуки. Выберем Default.
— SCM TYPE: тип репозитория, может быть либо локальная директория, либо одна из систем контроля версий на выбор: git, mercurial, subversion.
— SCM Url: урл для репозитория.
— SCM branch/tag/commit: опционально можно указать необходимый branch/tag/commit.
— В поле SCM credentials можно выбрать реквизиты, которые будут использоваться для доступа к репозиторию.
Также можно отметить галочками чекбоксы:
— Clean: удалить все локальные изменения перед обновлением.
— Delete on Update: при обновлении Project'а удалять локальную копию и заново загружать новую копию репозитория.
— Update on Launch: при каждом запуске плейбука из этого Project'а обновлять локальную копию репозитория до актуальной версии. Можно использовать в связке с Delete on Update, но это может привести к более долгому выполнению плейбука, так как каждый раз потребуется ждать окончания синхронизации с репозиторием.
Выберем Clean и Update on Launch. Нажимаем Save и после этого создание нашего первого Project'а завершено.
Перейдем в раздел Projects и для нашего Project'а нажмем на кнопку Start an scm update. Дождемся завершения первой загрузки нашего репозитория (следить за статусом обновления можно в разделе Jobs).
Посмотрим на плейбук, который находится в нашем репозитории. На Хабре есть множество статей, в которых рассказывается об Ансибле и структуре его плейбуков/ролей поэтому подробно расписывать этот момент не станем (вот несколько статей для примера: 1, 2, 3).
firstproject/
├── ansible.cfg
├── group_vars/
├── host_vars/
├── roles/
│ └── requirements.yml
└── run.yml
Файлы .yml, расположенные в корне директории firstproject, AWX воспримет как плейбуки.
Содержимое файла run.yml:
---
- name: Test Role
hosts:
- all
gather_facts: true
roles:
- roleawx
То есть просто запускаем roleawx, предварительно собрав факты. Hosts указываем all, так как указать хосты и хост группы можно в самом AWX.
Дальше интересный момент — содержимое файла roles/requirements.yml:
- src: git@gitlab.example.com:ansible-roles/roleawx.git
scm: git
При синхронизации Project'а AWX сам установит все роли в директорию roles в соответствии с файлом requirements.yml. В данном случае он установит роль roleawx из git репозитория.
Создадим роль с помощью команды:
ansible-galaxy init roleawx
Структура роли будет такой:
roleawx/
├── README.md
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
Содержимое файла tasks/main.yml:
---
# tasks file for roleawx
- name: Test Message
debug:
msg: "AWX and Ansible"
Для начала просто выведем сообщение «AWX and Ansible».
Обратите внимание, для того чтобы AWX автоматически установил роли, указанные в файле requirements.yml, в самой роли должен быть корректно сформирован файл meta/main.yml (если вы создали шаблон для роли с помощью ansible-galaxy init, то с meta/main.yml всё хорошо и AWX сможет загрузить эту роль).
Итак, у нас есть репозиторий с плейбуком, есть репозиторий с ролью. А так же у нас есть несколько виртуальных машин с адресами 192.168.10.233, 192.168.10.234 и 192.168.10.239.
Перед тем, как запустить наш плейбук, нам надо сделать так, чтобы Ansible смог попасть с сервера AWX на наши хосты (в нашем случае по SSH). Мы можем указать пароли прямо в переменных внутри плейбука, но это неинтересно. AWX умеет управлять реквизитами для доступа к серверам и мы этим воспользуемся.
Перейдем во вкладку Credentials и нажмем кнопку Add:
— Name: укажем имя для наших реквизитов.
— Credential Type: здесь можно выбрать тип реквизитов. Нас интересует Machine, чтобы использовать их для доступа к нашим серверам. Помимо этого AWX предлагает множество разных типов реквизитов доступа, которые можно использовать для интеграции с различными сервисами, такими как scm (git, svn, mercurial), Red Hat Insight, AWS, Azure, Red Hat Satellite, RHEV, Vmware, OpenStack. А ещё можно создавать свои собственные типы credentials.
— Имя пользователя: выберем root.
— Пароль: оставляем пустым, так как мы будем использовать доступ по ключу.
— PRIVATE KEY PASSPHRASE: пароль для ключа, если ключ создан с паролем. В нашем случае ключ без пароля, поэтому поле оставляем пустым.
— PRIVILEGE ESCALATION METHOD: метод повышения привилегий. Например: sudo, su, pfexec и и т.д.
— PRIVILEGE ESCALATION USERNAME: пользователь, под которым Ansible должен получить привилегии.
— PRIVILEGE ESCALATION PASSWORD: пароль для повышения привилегий.
Поля, связанные с эскалацией привилегий, оставим пустыми, так как мы будем логиниться под root.
Обратите внимание: у ряда полей есть чекбокс Prompt on launch. При активации этого чекбокса соответствующее ему поле будет предложено заполнить вручную при каждом запуске плейбука, с которым ассоциированы данные реквизиты. Таким образом можно не сохранять пароль внутри AWX, а запрашивать его при каждом запуске плейбука.
Итак, мы создали Project и реквизиты для доступа к серверам. Теперь нам необходим Inventory.
Перейдем в раздел Inventory, нажимаем кнопку Add. На выбор будет предложено создать:
— Inventory: обычный Inventory.
— Smart Inventory: позволяет генерировать inventory на основе уже существующих хостов, основываясь на каких-либо параметрах, например, на типе операционной системы.
Создадим обычный Inventory:
— В поле Name указываем имя.
— Выбираем Default Organization.
— В поле Variables можно указать переменные, которые в дальнейшем будут доступны из плейбука.
Пробуем указать:
---
awxinvvar: "Test"
Остальные вкладки неактивны, пока мы не сохраним наш Inventory.
Сохраняем и возвращаемся в раздел Inventory. Наш Inventory появился в списке. Перейдем в него: после создания верхние вкладки стали активны.
— Permissions: здесь контролируется, кто из пользователей или групп пользователей имеет доступ к Inventory.
— Groups: список хост-групп.
— Hosts: список хостов.
— Sources: список источников для Inventory. Можно выгружать инвентори из AWS/Azure/OpenStack/RHEV и ещё ряда сервисов, можно указать инвентори файл, который находится внутри нашего Project'а или же указать в качестве источника свой собственный скрипт, который генерирует Dynamic Inventory.
— Completed Jobs: список запущенных плейбуков, ассоциированных с хостами из текущего Inventory.
Наш Inventory заполняем руками, так как у нас мало хостов. Перейдем во вкладку Hosts и создадим несколько хостов:
— В поле Host Name можно указать IP-адрес сервера или его DNS-имя.
— В поле Variables попробуем переопределить переменную awxinvvar для одного из наших хостов:
---
awxinvvar: "Host1"
После создания хостов перейдем во вкладку Groups и создадим группы для наших хостов:
Точно так же укажем имя группы и переопределим переменную awxinvvar для одной из групп.
Возвращаемся во вкладку Hosts, выбираем один из хостов из списка, переходим на вкладку Groups для этого хоста и нажмем Associate Group:
На кладке Hosts теперь можно увидеть, в каких группах состоит каждый из хостов:
Кстати, на вкладках Hosts и Groups также доступна кнопка Run command. Это аналог ad-hoc команд в Ansible, которые позволяют выполнить действия на удаленных хостах, без плейбука.
Давайте попробуем. Выберем наши хосты из списка, выберем ключ для доступа по SSH, который создали ранее и попробуем выполнить команду date c помощью модуля shell:
Нажимаем Launch. Нас должно перекинуть на страницу, на которой мы сможем в реальном времени следить за выполнением. Также все запуски плейбуков/ad-hoc команд видны в разделе Jobs.
Наша ad-hoc команда успешно выполнилась.
Давайте теперь попробуем запустить наш плейбук. Перейдем в раздел Templates, нажмем кнопку Add, выберем Job Template.
В терминологии AWX Template — это набор параметров, которые используются для запуска плейбука. В минимальном виде необходимо указать имя шаблона, выбрать project, выбрать playbook, выбрать inventory.
Выглядит создание шаблона так:
— Name и Description: имя и описание шаблона.
— Job Type: Run или Check. То есть запуск плейбука или только проверка плейбука (dry-run) без внесения изменений.
— Inventory: Inventory, с которым будет запускаться плейбук.
— Project и Playbook: предназначены для выбора Project'а с плейбуками и конкретного плейбука из Project'а.
— Limit: список хостов и групп, на которых будет запущен плейбук.
— Verbosity: насколько подробно Ansible будет выводить результат выполнения (аналог ключей -v -vv -vvv).
— Job tags: список тегов — задачи, с которыми должны быть запущены. Если не указывать, будет выполнены все задачи описанные в ролях.
— Skip tags: список тегов — задачи, с которыми не будут выполняться.
— Labels: метки, которые будут ассоциироваться с данным шаблоном. Можно использовать для фильтрации в самом AWX.
— Show diff: показывать изменения, которые делает Ансибл (аналог ключа --diff).
Сохраняем проект, возвращаемся в раздел Templates, и запускаем наш плейбук (кнопка в виде ракеты):
Ура, наш плейбук запустился, загрузил нужные роли и успешно отработал. Кстати, на этой же странице вы можете загрузить результат выполнения плейбука. Также имеется возможность поиска по выводу плейбука.
Помните, мы добавили переменную awxinvvar в нескольких местах? Давайте попробуем вывести её в и посмотреть, что получится.
Добавляем в нашу роль в файл tasks/main.yml следующее:
- name: Print var
debug:
msg: "{{ awxinvvar }}"
И снова запускаем наш template.
Плейбук видит нашу переменную, которую мы определили в Inventory и корректно её переопределяет в соответствии с приоритетом применения переменных.
Переменные, кстати, можно хранить и в репозитории в директориях host_vars group_vars, если вам так удобнее. Правда, в таком случае они не будут отображаться в Inventory в самом AWX.
Вместо заключения
AWX мы используем уже несколько месяцев и пока что нас всё устраивает. Конечно, так как это новый проект, мы периодически встречаемся с разного рода проблемами (в основном, мелкими багами в интерфейсе), но критичными они не являются и работе не мешают.
Примечание: новые релизы сейчас выходят часто и могут возникнуть проблемы при обновлении. Делайте бэкапы!
Данное руководство написано для ознакомления с базовыми функциями AWX, а если будет интересно — напишем про дополнительный полезный функционал, вроде интеграции с источниками для Dynamic Inventory, механизм Callback'ов и т.д.
P.S. Вот так, кстати, выглядит наш дашборд AWXа, который мы используем для части инфрастуктуры: