Пример книги команд: Непрерывная доставка и поэтапные обновления
- Что такое непрерывная доставка?
- Развертывание сайта
- Многократно используемое содержимое: роли
- Конфигурация: переменные групп
- Поэтапное обновление
- Управление другими балансировщиками нагрузки
- Непрерывная доставка от начала до конца
Что такое непрерывная доставка?
Непрерывная доставка (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 в вашей инфраструктуре (или, возможно, у вас есть настроенный Elastic IP AWS?), вы можете использовать модули 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, или в обоих.
В зависимости от вашей среды, вы можете непрерывно развертывать приложение в тестовую среду, выполнять на ней пакет интеграционных тестов и затем автоматически развертывать в производство. Или вы можете сделать это проще и просто использовать плавное обновление для запрошенного развертывания в тестовую или производственную среду, конкретно. Это все зависит от вас.
Для интеграции с системами непрерывной интеграции вы можете легко запускать выполнение плейбука с помощью утилиты командной строки ansible-playbook, или, если вы используете AWX, команду tower-cli или встроенный API REST. (Команда tower-cli «joblaunch» запустит удаленную задачу через API REST и довольно умна).
Это должно дать вам хорошее представление о том, как структурировать многоуровневое приложение с помощью Ansible и оркестрировать операции над этим приложением с конечной целью непрерывной доставки вашему клиенту. Вы можете расширить идею плавного обновления на разные части приложения; возможно, добавить веб-серверы фронта и серверы приложений или заменить базу данных SQL базой данных NoSQL. Ansible предоставляет возможность легко управлять сложными средами и автоматизировать общие операции.
См. также
- Работа с плейбуками
-
Введение в плейбуки
- Роли
-
Введение в роли плейбука
- Использование переменных
-
Введение в переменные Ansible
- Ansible.com: Непрерывная доставка
-
Введение в непрерывную доставку с помощью Ansible
© 2012–2018 Michael DeHaan
© 2018–2024 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/latest/playbook_guide/guide_rolling_upgrade.html