Spec-Zone.ru › Ansible 2.7

Непрерывная доставка и поэтапные обновления

Введение

Непрерывная доставка — это концепция частого выпуска обновлений для вашего программного приложения.

Идея заключается в том, что, обновляя чаще, вам не придётся ждать определённого временного периода, и ваша организация лучше осваивает процесс реагирования на изменения.

Некоторые пользователи Ansible развертывают обновления для конечных пользователей каждый час или даже чаще — иногда каждый раз, когда вносится одобренное изменение кода. Для достижения этого вам нужны инструменты, позволяющие быстро применять эти обновления без простоев.

В этом документе подробно описано, как достичь этой цели, используя один из самых полных примеров playbooks Ansible как шаблон: lamp_haproxy. Этот пример использует множество функций Ansible: роли, шаблоны и переменные групп, а также содержит playbook для оркестрации поэтапных обновлений веб-приложения без простоев.

Примечание

Нажмите здесь для получения последних playbooks для этого примера.

Playbooks развертывают Apache, PHP, MySQL, Nagios и HAProxy на наборе серверов на базе CentOS.

Здесь мы не будем рассматривать, как запускать эти playbooks. Прочитайте включенный файл README в проекте github вместе с примером для получения этой информации. Вместо этого мы детально рассмотрим каждую часть playbook и опишем его действия.

Развертывание сайта

Начнём с site.yml. Это наш playbook для развертывания сайта. Его можно использовать для первоначального развертывания сайта, а также для внесения обновлений на все серверы:

---
# 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

Примечание

Если вы не знакомы с такими понятиями, как playbooks и plays, ознакомьтесь с Работа с playbooks.

В этом playbook у нас 5 задач. Первая задача направлена на all хосты и применяет роль common ко всем хостам. Это для общих настроек сайта, таких как конфигурация репозитория yum, конфигурация брандмауэра и всего остального, что необходимо для всех серверов.

Следующие четыре задачи выполняются для конкретных групп хостов и применяют определённые роли к этим серверам. Наряду с ролями для мониторинга Nagios, базы данных и веб-приложения, мы реализовали роль base-apache, которая устанавливает и настраивает базовый Apache. Она используется как для образцового веб-приложения, так и для хостов Nagios.

Повторно используемый контент: Роли

К этому моменту у вас должно быть некоторое понимание ролей и принципов их работы в Ansible. Роли — это способ организации контента: задач, обработчиков, шаблонов и файлов в повторно используемые компоненты.

В этом примере есть шесть ролей: common, base-apache, db, haproxy, nagios, и web. Как вы организуете свои роли — зависит от вас и вашего приложения, но большинство сайтов имеют одну или несколько общих ролей, применяемых ко всем системам, и ряд ролей, специфичных для приложения, которые устанавливают и настраивают конкретные части сайта.

Роли могут иметь переменные и зависимости, и вы можете передавать параметры в роли для изменения их поведения. Более подробную информацию о ролях вы можете найти в разделе Роли.

Конфигурация: Переменные групп

Переменные групп — это переменные, которые применяются к группам серверов. Они могут использоваться в шаблонах и playbooks для настройки поведения, предоставления легко изменяемых настроек и параметров. Они хранятся в каталоге 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, аналогичным образом.

Эти переменные используются в самых разных местах. Вы можете использовать их в playbooks, например, в 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 способен координировать операции с многоуровневыми приложениями, что облегчает оркестрацию сложных поэтапных обновлений веб-приложения без простоев. Это реализовано в отдельном playbook под названием rolling_update.yml.

Рассмотрим playbook, он состоит из двух задач. Первая задача очень проста и выглядит так:

- hosts: monitoring
  tasks: []

Что здесь происходит и почему нет задач? Вам, возможно, известно, что Ansible собирает «факты» с серверов перед их обработкой. Эти факты полезны для самых разных целей: информация о сети, версии ОС/дистрибутива и т. д. В нашем случае нам нужно узнать что-то о всех серверах мониторинга в нашей среде, прежде чем мы начнём обновление, поэтому эта простая задача заставляет сервера мониторинга собрать факты. Вы увидите эту модель иногда, и это полезный трюк.

Следующая часть — задача обновления. Первая часть выглядит так:

- hosts: webservers
  user: root
  serial: 1

Это просто обычная задача, выполняемая для группы webservers. Ключевое слово serial показывает Ansible, на скольких серверах одновременно проводить операцию. Если оно не указано, Ansible будет распараллеливать эти операции до предела «вилок», заданного по умолчанию в файле конфигурации. Но для поэтапного обновления без простоев вы, возможно, не захотите выполнять операции сразу на многих хостах. Если у вас всего несколько веб-серверов, вы можете установить serial в 1, для обновления одного хоста за раз. Если у вас 100, то, возможно, serial можно установить в 10, для обновления по 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 и организовать операции над этим приложением, с конечной целью — непрерывной доставки клиентам. Вы можете расширить идею поэтапного обновления на многие части приложения; например, добавить веб-серверы переднего плана вместе с серверами приложений или заменить базу данных SQL чем-то вроде MongoDB или Riak. Ansible предоставляет вам возможность легко управлять сложными средами и автоматизировать общие операции.

См. также

lamp_haproxy example
Приведённый здесь пример lamp_haproxy.
Работа с playbooks
Введение в 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.7/scenario_guides/guide_rolling_upgrade.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API