Ansible — популярная система для управления конфигурацией серверов, но не только: с помощью ansible можно также выкатывать (deploy) приложения на тестовые и боевые площадки. Разберем как это делается на примере.
Задача
Пусть у нас есть веб-приложение, исходные коды которого хранятся в системе контроля версий, например git.
Мы хотим реализовать механизм выката новых версий приложения на площадки, который удовлетворял бы следуюшим требованиям:
- выкат должен происходить без перерывов в работе (без downtime);
- если в процессе выката возникнут ошибки, выкат должен быть прерван и старая версия приложения должна продолжить работу;
- должна быть возможность при необходимости откатиться к предыдущей версии веб-приложения;
- процедура выката должна хорошо масштабироваться с увеличением числа серверов на площадке.
Метод
Один из способов выката без downtime основан на использовании специальной структуры каталогов. Этот способ, в частности, используется в Capistrano. Я впервые познакомился с ним в замечательном автосборщике Haru.
Каждая новая версия приложения выкатывается в отдельный каталог. Например, если сейчас выкачена версия v1.0.0, структура каталогов выглядит следующим образом:
/vagrant
├── releases
│ ├── current -> /vagrant/releases/v1.0.0
│ └── v1.0.0
│ └── index.html
├── shared
└── Vagrantfile
Папка releases
содержит версии приложения. Каждая версия – копия приложения, соответствующая определенному тегу
из репозитория. Символическая ссылка current
указывает на текущую версию.
Файлы, которые должны быть доступны независимо от версии, например, загруженные пользователем картинки, сгенерированные
файлы и т.п., помещаются в папку shared
.
Процесс выката делится на три этапа:
- install: новая версия приложения извлекается из репозитория в отдельный каталог рядом с текущей версией;
- configure: на основе шаблонов генерируются необходимые файлы;
- link: переключение на новую версию путем изменения символической ссылки.
Первые два этапа подготавливают новую версию приложения к работе, пока работает старая версия приложения. Если установка и конфигурация выполнены успешно на всех серверах, запускается третий этап, линковка, – трафик переключается на новую версию.
После успешного выката версии v1.0.1 структура каталогов примет вид:
/vagrant
├── releases
│ ├── current -> /vagrant/releases/v1.0.1
│ ├── v1.0.0
│ │ └── index.html
│ └── v1.0.1
│ └── index.html
├── shared
└── Vagrantfile
В случае необходимости, легко можно вернуться к предыдущей версии: для этого достаточно изменить символическую ссылку.
Реализация
Исходники сценарие можно посмотреть здесь.
Для выката версии приложения, помеченной тегом v1.0.1, можно воспользоваться следующей командой:
ansible-playbook -i develop deploy/webapp.yml -e deploy_tag=v1.0.1
Сценарий /vagrant/deploy/webapp.yml
создает необходимые каталоги и запускает роль library
,
которая собственно и выкатывает приложение. В данном сценарии выкатывается только одна библиотека,
но роль можно использовать несколько раз в одном сценарии, передавая URL репозитария и нужный тег через параметры.
- hosts: webapp
any_errors_fatal: true
pre_tasks:
- name: make sure shared directories exist
file: dest={{ item }} state=directory
with_items:
- "{{ path_releases }}"
- "{{ path_shared }}"
roles:
- role: library
library_name: example
library_deploy_tag: "{{ deploy_tag }}"
library_deploy_src: https://github.com/sergeytsivin/example-lib.git
Скрипт выката описан в файле ansible/deploy/roles/library/tasks/main.yml
:
- name: checkout {{ library_name }} lib
git:
dest: "{{ library_deploy_dest }}"
repo: "{{ library_deploy_src }}"
version: "{{ library_deploy_tag }}"
accept_hostkey: true
tags:
- install
- name: schedule linking {{ library_name }} lib
command: /bin/true
notify:
- link {{ library_name }}
tags:
- link
Скрипт очень простой: извлекается версия из репозитория (этап install) и уведомляется обработчик, который должен произвести произвести переключение на новую версию (этап link). Использование обработчика гарантирует, что линковка всех библиотек (если их несколько) будет выполнена после того, как все библиотеки будут успешно установлены и ни один обработчик не будет вызван в случае возникновения ошибок.
Ansible последовательно выполняет команды сценария (playbook), но при этом каждая команда выполняется параллельно на нескольких машинах.
Обработчик ansible/deploy/roles/library/handlers/main.yml
переключает символическую ссылку current
:
- name: link {{ library_name }}
file: src={{ item.src }} dest={{ item.dest }} state=link
with_items:
- src: "{{ library_deploy_dest }}"
dest: "{{ library_deploy_current }}"
Благоларя тегам можно запускать этапы выката по отдельности, например для выполнения только этапа установки:
ansible-playbook -i develop deploy/webapp.yml -e deploy_tag=v1.0.1 --tags install
Вывод
Таким образом, с помощью ansible можно реализовать простой и удобный механизм выката, не прибегая к помощи пециализированных инструментов, таких как Capistrano. Дополительное преимущество Ansible – скорость выката на площадки, состоящие из многих серверов.