Пример книги команд: непрерывная доставка и постепенные обновления
- Что такое непрерывная доставка?
- Развертывание сайта
- Многократно используемое содержимое: роли
- Конфигурация: переменные групп
- Постепенное обновление
- Управление другими балансировщиками нагрузки
- Непрерывная доставка по всей цепочке
Что такое непрерывная доставка?
Непрерывная доставка (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 core, для общения с ними вместо этого. Возможно, вам также захочется использовать другие модули мониторинга вместо 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'ы, или в оба.
В зависимости от вашей среды, вы можете непрерывно развертывать в тестовую среду, выполнять набор интеграционных тестов в этой среде и затем автоматически развертывать в производство. Или вы можете упростить процесс и использовать только постепенное обновление для развертывания по требованию в тестовую или производственную среду. Решать вам.
Для интеграции с системами непрерывной интеграции вы можете легко запускать выполнение playbooks с помощью инструмента 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: Непрерывная доставка
- Введение в непрерывную доставку с помощью Ansible
© 2012–2018 Michael DeHaan
© 2018–2019 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/2.9/user_guide/guide_rolling_upgrade.html