Количество серверов в нашей инфраструктуре уже перевалило за 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.

Установка


В данный момент поддерживается несколько вариантов установки:

  1. Kubernetes.
  2. Openshift.
  3. 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/
¦   L-- requirements.yml
L-- 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
¦   L-- main.yml
+-- files
+-- handlers
¦   L-- main.yml
+-- meta
¦   L-- main.yml
+-- tasks
¦   L-- main.yml
+-- templates
+-- tests
¦   +-- inventory
¦   L-- test.yml
L-- vars
    L-- 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а, который мы используем для части инфрастуктуры:

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


  1. manefesto
    28.03.2018 15:53

    Меня в принципе AWX всем устраивает, только не совсем понял как обновить до последней версии, всё время ломается структура БД


    1. aleshkashell
      28.03.2018 16:29
      +1

      Во избежание потерь, имеем резервную копию, помимо БД, в виде json файла со всей структурой и скрипт на питоне, который создает всю структуру через API из этого файла. Единственный недостаток, приходится держать этот файл в актуальном состоянии, но при желании можно сделать выгрузку текущей структуры в файл.


      1. manefesto
        29.03.2018 08:05

        не совсем понял как делать апгрейд кроме как

        docker pull

        Тоже самое касается запуска остановленных контейнеров, потому что нигде не могу найти docker-compose.yaml подготовленный для запуска


        1. de1m
          29.03.2018 15:12

          если делаете установку через ansible, то тогда он создаёт этот файл в "/var/lib/awx" (может я ошибаюсь, уже давно делал)


        1. Rverkh Автор
          29.03.2018 16:38

          Как уже написали, по умолчанию docker-compose.yml создаётся в /var/lib/awx
          Но, для того что бы использовался docker-compose в installer/inventory должны быть заданы переменные

          use_docker_compose=true
          docker_compose_dir=/var/lib/awx
          


          Посмотрите на всякий случай, может вы что-то не указали, и у вас всё установилось без использования docker-compose


    1. Rverkh Автор
      28.03.2018 16:41
      +1

      А какая у вас сейчас версия?
      Мы ловили проблему при обновлении на каком-то раннем релизе. Сейчас всё обновляется без проблем.
      Как вариант, если у вас сильно старая версия, можете в installer/inventory вместо dockerhub_version=latest указать версию явно. (Только сначала всё равно забэкапиться :) )
      Список версий можно вот тут посмотреть hub.docker.com/r/ansible/awx_task/tags


      1. manefesto
        30.03.2018 08:35

        У меня как раз возникла проблема с обновлением 1.0.1 -> 1.0.2
        Почитал, рекомендуют сделать чистую установку, мне эта идея не очень нравится, пока функционала хватает.
        Подскажите лучше как сделать дамп в виде json
        Я так понимаю через dumpdata?


  1. NikiN
    28.03.2018 16:02

    Жду продолжения!


  1. DimitryKarpov
    28.03.2018 16:35

    Есть одна неприятная особенность при установке awx «из коробки». postgres_data_dir по-умолчанию выставлена в /tmp/pgdocker и в какой-то момент можно словить «FATAL: could not open file „base/16384/2601“: No such file or directory», поэтому рекомендуется менять директорию. Issue на эту тему


    1. DimitryKarpov
      28.03.2018 16:55
      +1

      Извиняюсь, пропустил упоминание об этом в вашей статье.


  1. Ipeacocks
    28.03.2018 22:28

    Ну хоть что-то не о Докере и Кубернетисе.