Пример плейбука: непрерывная доставка и поэтапные обновления
- Что такое непрерывная доставка?
- Развертывание сайта
- Многократно используемый контент: роли
- Настройка: переменные групп
- Поэтапное обновление
- Управление другими балансировщиками нагрузки
- Непрерывная доставка от начала до конца
Что такое непрерывная доставка?
Непрерывная доставка (CD) означает частую поставку обновлений вашего программного приложения.
Идея заключается в том, что, обновляясь чаще, вам не придётся ждать определённого периода времени, и ваша организация станет лучше реагировать на изменения.
Некоторые пользователи Ansible развертывают обновления для конечных пользователей каждый час или даже чаще — иногда каждый раз, когда происходит одобренное изменение кода. Для достижения этой цели необходимы инструменты для быстрого применения этих обновлений без останова работы.
В данном документе подробно описано, как достичь этой цели, используя один из самых полных примеров плейбуков Ansible в качестве шаблона: lamp_haproxy. Этот пример использует множество функций Ansible: роли, шаблоны и переменные групп, а также содержит плейбук оркестрации, который может выполнять поэтапные обновления веб-приложения без останова работы.
Примечание
Плейбуки развертывают Apache, PHP, MySQL, Nagios и HAProxy на наборе серверов на основе CentOS.
Мы не будем рассматривать здесь, как запускать эти плейбуки. Прочитайте включенный README в проекте github вместе с примером для получения этой информации. Вместо этого мы подробно рассмотрим каждую часть плейбука и опишем, что она делает.
Развертывание сайта
Давайте начнём с site.yml. Это наш плейбук развертывания сайта. Он может использоваться для первоначального развертывания сайта, а также для внесения обновлений на все серверы:
--- # This playbook deploys the whole application stack in this site. # Apply common configuration to all hosts - hosts: all roles: - common # Configure and deploy database servers. - hosts: dbservers roles: - db # Configure and deploy the web servers. Note that we include two roles # here, the 'base-apache' role which simply sets up Apache, and 'web' # which includes our example web application. - hosts: webservers roles: - base-apache - web # Configure and deploy the load balancer(s). - hosts: lbservers roles: - haproxy # Configure and deploy the Nagios monitoring node(s). - hosts: monitoring roles: - base-apache - nagios
Примечание
Если вы не знакомы с терминами, такими как плейбуки и плейи, вам следует ознакомиться с Работой с плейбуками.
В этом плейбуке есть 5 плейев. Первый из них нацелен на all хосты и применяет роль common ко всем хостам. Это для общих задач, таких как конфигурация репозитория yum, конфигурация брандмауэра и любых других задач, которые должны применяться ко всем серверам.
Следующие четыре плейа выполняются на конкретных группах хостов и применяют к ним определённые роли. Вместе с ролями для мониторинга Nagios, базы данных и веб-приложения, мы реализовали роль base-apache , которая устанавливает и настраивает базовый Apache. Она используется как веб-приложением, так и хостами Nagios.
Многократно используемый контент: роли
К этому моменту вы должны немного понять роли и как они работают в Ansible. Роли — это способ организации контента: задач, обработчиков, шаблонов и файлов в многократно используемые компоненты.
В этом примере есть шесть ролей: common, base-apache, db, haproxy, nagios, и web. Как организовать свои роли, зависит от вас и вашего приложения, но большинство сайтов имеют одну или несколько общих ролей, которые применяются ко всем системам, а затем ряд ролей, специфичных для приложения, которые устанавливают и настраивают определённые части сайта.
Роли могут иметь переменные и зависимости, и вы можете передавать параметры ролям, чтобы изменить их поведение. Вы можете узнать больше о ролях в разделе Роли.
Настройка: переменные групп
Переменные групп — это переменные, которые применяются к группам серверов. Они могут использоваться в шаблонах и плейбуках для настройки поведения, предоставления легко изменяемых настроек и параметров. Они хранятся в каталоге group_vars в том же месте, что и ваш инвентарь. Вот файл group_vars/all lamp_haproxy. Как можно ожидать, эти переменные применяются ко всем машинам в вашем инвентаре:
--- httpd_port: 80 ntpserver: 192.0.2.23
Это файл YAML, и вы можете создавать списки и словари для более сложных структур переменных. В данном случае мы просто задаём две переменные: одну для порта веб-сервера и одну для NTP-сервера, который наши машины должны использовать для синхронизации времени.
Вот ещё один файл переменных групп. Это group_vars/dbservers , который применяется к хостам в группе dbservers:
--- mysqlservice: mysqld mysql_port: 3306 dbuser: root dbname: foodb upassword: usersecret
Если вы посмотрите на пример, есть переменные групп для группы webservers и группы lbservers, аналогично.
Эти переменные используются в различных местах. Вы можете использовать их в плейбуках, например, так, в roles/db/tasks/main.yml:
- name: Create Application Database
mysql_db:
name: "{{ dbname }}"
state: present
- name: Create Application DB User
mysql_user:
name: "{{ dbuser }}"
password: "{{ upassword }}"
priv: "*.*:ALL"
host: '%'
state: present
Вы также можете использовать эти переменные в шаблонах, например, в roles/common/templates/ntp.conf.j2:
driftfile /var/lib/ntp/drift
restrict 127.0.0.1
restrict -6 ::1
server {{ ntpserver }}
includefile /etc/ntp/crypto/pw
keys /etc/ntp/keys
Вы можете видеть, что синтаксис подстановки переменных {{ и }} одинаков для шаблонов и переменных. Синтаксис внутри фигурных скобок — это Jinja2, и вы можете выполнять все виды операций и применять различные фильтры к данным внутри. В шаблонах вы также можете использовать циклы for и операторы if для обработки более сложных ситуаций, как в этом примере в roles/common/templates/iptables.j2:
{% if inventory_hostname in groups['dbservers'] %}
-A INPUT -p tcp --dport 3306 -j ACCEPT
{% endif %}
Это проверка, существует ли имя инвентаря машины, на которой мы в данный момент работаем (inventory_hostname), в группе инвентаря dbservers. В случае положительного результата, эта машина получит строку iptables ACCEPT для порта 3306.
Вот ещё один пример из того же шаблона:
{% for host in groups['monitoring'] %}
-A INPUT -p tcp -s {{ hostvars[host].ansible_default_ipv4.address }} --dport 5666 -j ACCEPT
{% endfor %}
Это цикл по всем хостам в группе под названием monitoring, и добавляет строку ACCEPT для каждого мониторингового хоста по умолчанию IPv4 адрес к текущей конфигурации iptables, чтобы Nagios мог отслеживать эти хосты.
Вы можете узнать гораздо больше о Jinja2 и его возможностях здесь, а также можете прочитать больше о переменных Ansible в целом в разделе Использование переменных.
Поэтапное обновление
Теперь у вас есть полностью развернутый сайт с веб-серверами, балансировщиком нагрузки и мониторингом. Как его обновить? Именно здесь вступают в действие возможности оркестрации Ansible. Хотя некоторые приложения используют термин «оркестрация» для обозначения базового упорядочивания или отправки команд, Ansible использует «оркестрацию» как «ведение машин, как оркестра», и имеет довольно сложный движок для этого.
Ansible имеет возможность выполнять операции на многоуровневых приложениях координированным образом, что облегчает оркестрацию сложного поэтапного обновления нашего веб-приложения без останова работы. Это реализовано в отдельном плейбуке под названием rolling_update.yml.
Глядя на плейбук, вы можете увидеть, что он состоит из двух плейев. Первый плей очень прост и выглядит так:
- hosts: monitoring tasks: []
Что происходит здесь и почему нет задач? Вам, возможно, известно, что Ansible собирает «факты» с серверов перед выполнением операций над ними. Эти факты полезны для множества целей: информация о сети, версии ОС/дистрибутива и так далее. В нашем случае нам нужно знать что-то о всех серверах мониторинга в нашей среде перед выполнением обновления, поэтому этот простой плей принуждает к сбору фактов на наших серверах мониторинга. Вы увидите эту схему иногда, и это полезный трюк, чтобы знать.
Следующая часть — это плей обновления. Первая часть выглядит так:
- hosts: webservers user: root serial: 1
Это просто обычное определение плейа, работающего с группой webservers. Ключевое слово serial указывает Ansible, сколько серверов нужно обработать одновременно. Если это не указано, Ansible будет параллелизировать эти операции до предела «вилок», указанного в файле конфигурации. Но для поэтапного обновления без останова работы вам, возможно, не захочется обрабатывать так много хостов сразу. Если у вас всего несколько веб-серверов, вы можете установить serial в 1, для одного хоста за раз. Если у вас 100, может быть, вы могли бы установить serial в 10, для десяти за раз.
Вот следующая часть плей обновления:
pre_tasks:
- name: disable nagios alerts for this host webserver service
nagios:
action: disable_alerts
host: "{{ inventory_hostname }}"
services: webserver
delegate_to: "{{ item }}"
loop: "{{ groups.monitoring }}"
- name: disable the server in haproxy
shell: echo "disable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
delegate_to: "{{ item }}"
loop: "{{ groups.lbservers }}"
Примечание
- Ключевое слово
serialзаставляет плей выполняется в «пакетах». Каждый пакет считается полным плейем с подвыборкой хостов. Это имеет некоторые последствия для поведения плейа. Например, если все хосты в пакете завершаются неудачно, плей завершается неудачно, что, в свою очередь, завершает весь запуск неудачно. Вам следует учитывать это при объединении сmax_fail_percentage.
Ключевое слово pre_tasks просто позволяет перечислить задачи, которые нужно выполнить перед вызовом ролей. Это станет более понятным через минуту. Если вы посмотрите на имена этих задач, вы увидите, что мы отключаем оповещения Nagios и затем удаляем веб-сервер, который мы в данный момент обновляем, из пула балансировки нагрузки HAProxy.
Аргументы delegate_to и loop вместе заставляют Ansible пройтись по каждому серверу мониторинга и балансировщику и выполнить эту операцию (передать эту операцию) на сервере мониторинга или балансировки, «от имени» веб-сервера. В терминах программирования внешний цикл — это список веб-серверов, а внутренний цикл — список серверов мониторинга.
Обратите внимание, что шаг HAProxy выглядит немного сложно. В этом примере мы используем HAProxy, потому что он свободно доступен, но если у вас (например) F5 или Netscaler в вашей инфраструктуре (или, возможно, у вас настроен AWS Elastic IP?), вы можете использовать модули Ansible для связи с ними вместо этого. Вы также можете захотеть использовать другие модули мониторинга вместо nagios, но это просто демонстрирует основную цель раздела «предварительные задачи» — вывести сервер из мониторинга и вывести его из вращения.
Следующий шаг просто повторно применяет соответствующие роли к веб-серверам. Это приведет к применению всех деклараций управления конфигурацией в ролях web и base-apache, включая обновление самого кода веб-приложения. Мы не обязаны делать это так — мы могли бы просто обновить веб-приложение, но это хороший пример того, как роли могут использоваться для повторного использования задач:
roles: - common - base-apache - web
Наконец, в разделе post_tasks мы отменяем изменения в конфигурации Nagios и возвращаем веб-сервер в пул балансировки нагрузки:
post_tasks:
- name: Enable the server in haproxy
shell: echo "enable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
delegate_to: "{{ item }}"
loop: "{{ groups.lbservers }}"
- name: re-enable nagios alerts
nagios:
action: enable_alerts
host: "{{ inventory_hostname }}"
services: webserver
delegate_to: "{{ item }}"
loop: "{{ groups.monitoring }}"
Опять же, если вы использовали Netscaler, F5 или Elastic Load Balancer, вы просто подставите соответствующие модули вместо них.
Управление другими балансировщиками нагрузки
В этом примере мы используем простой балансировщик нагрузки HAProxy для веб-серверов. Он легко настраивается и управляется. Как мы уже упоминали, Ansible поддерживает множество других балансировщиков нагрузки, таких как Citrix NetScaler, F5 BigIP, Amazon Elastic Load Balancers и другие. Более подробную информацию см. в документации Работа с модулями.
Для других балансировщиков нагрузки вам может потребоваться отправлять им командные строки (как мы делаем для HAProxy выше) или вызывать API, если ваш балансировщик нагрузки его предоставляет. Для балансировщиков нагрузки, для которых у Ansible есть модули, вы можете запустить их как local_action при обращении к API. Вы можете узнать больше о локальных действиях в разделе Управление местом выполнения задач: делегирование и локальные действия. Если вы разработаете что-то интересное для какого-либо оборудования, где нет модуля, это может стать хорошим вкладом!
Поэтапная доставка
Теперь, когда у вас есть автоматизированный способ развертывания обновлений вашего приложения, как связать всё вместе? Многие организации используют инструмент непрерывной интеграции, такой как Jenkins или Atlassian Bamboo, чтобы связать шаги разработки, тестирования, выпуска и развертывания. Возможно, вы также захотите использовать инструмент, такой как Gerrit, чтобы добавить шаг проверки кода к коммитам как самого кода приложения, так и ваших Ansible playbook'ов, или обоих.
В зависимости от вашей среды вы можете осуществлять непрерывное развертывание в тестовую среду, проводить там полный набор интеграционных тестов, а затем автоматически развертывать в производство. Либо вы можете сохранить простоту и просто использовать пошаговое обновление для запрошенного развертывания в тестовую или производственную среду. Всё зависит от вас.
Для интеграции с системами непрерывной интеграции вы можете легко запускать выполнение playbook с помощью инструмента командной строки ansible-playbook или, если вы используете Red Hat Ansible Tower, tower-cli или встроенный REST API. (Команда tower-cli «joblaunch» запустит удаленную задачу через REST API и довольно удобна).
Это должно дать вам хорошее представление о том, как структурировать многоуровневое приложение с помощью Ansible и управлять операциями над ним с конечной целью непрерывной доставки вашим клиентам. Вы можете расширить идею пошагового обновления на множество разных частей приложения; например, добавить фронт-энд веб-серверы вместе с серверами приложений или заменить базу данных SQL чем-то вроде MongoDB или Riak. Ansible предоставляет вам возможность легко управлять сложными средами и автоматизировать общие операции.
См. также
- lamp_haproxy example
-
Обсуждаемый здесь пример lamp_haproxy.
- Работа с playbook'ами
-
Введение в playbook'и
- Роли
-
Введение в роли playbook
- Использование переменных
-
Введение в переменные Ansible
- Ansible.com: Continuous Delivery
-
Введение в непрерывную доставку с Ansible
© 2012–2018 Michael DeHaan
© 2018–2021 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.11/user_guide/guide_rolling_upgrade.html