Непрерывная доставка и поэтапные обновления
Введение
Непрерывная доставка — это концепция частой доставки обновлений для вашего программного приложения.
Идея заключается в том, что, обновляясь чаще, вам не нужно ждать определенного периода времени, и ваша организация лучше справляется с процессом реагирования на изменения.
Некоторые пользователи 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 командной строки или, если вы используете Ansible Tower, tower-cli или встроенный REST-API. (Команда tower-cli ‘joblaunch’ запустит удаленную задачу через REST-API, что довольно удобно).
Это должно дать вам хорошее представление о том, как структурировать многоуровневое приложение с Ansible и организовать операции над этим приложением с конечной целью непрерывной поставки клиентам. Вы можете расширить идею поэтапного обновления на многие части приложения; например, добавить веб-серверы front-end вместе с серверами приложения или заменить базу данных SQL чем-то вроде MongoDB или Riak. Ansible предоставляет возможность легко управлять сложными средами и автоматизировать общие операции.
См. также
- lamp_haproxy пример
- Здесь обсуждается пример lamp_haproxy.
- Работа с Playbook
- Введение в playbooks
- Роли
- Введение в роли 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.6/scenario_guides/guide_rolling_upgrade.html