Автоматизация развертывания Docker-контейнеров на произвольной инфраструктуре

в 13:44, , рубрики: deploy, devops, docker, fabric, python, redmadrobot, Блог компании REDMADROBOT, ит-инфраструктура, Серверное администрирование

Автоматизация развертывания Docker-контейнеров на произвольной инфраструктуре - 1

Контейнеризация приложений сегодня является не просто модным трендом. Объективно такой подход позволяет во многом оптимизировать процесс серверной разработки путем унификации поддерживаемых инфраструктур (dev, test, staging, production). Что в итоге приводит к значительному сокращению издержек на протяжении всего цикла жизни серверного приложения.

Хотя большая часть из перечисляемых достоинств Docker является правдой, тех, кто на практике столкнется с контейнерами, может постигнуть легкое разочарование. И так как Docker не является панацеей, а всего лишь входит в список из «лекарственных средств» от рецепта автоматического деплоя, разработчикам приходится осваивать дополнительные технологии, писать дополнительный код и т.д.

Разработкой своего собственного рецепта автоматизации конфигурирования и разворачивания контейнеров на различные инфраструктуры мы занимались в последние несколько месяцев, параллельно с коммерческими проектами. А полученный результат практически полностью удовлетворил наши текущие потребности в автодеплое.

Выбор инструмента

Автоматизация развертывания Docker-контейнеров на произвольной инфраструктуре - 2

Когда впервые возникает потребность в автоматическом деплое Docker-приложений, то первое, что подсказывает опыт (либо поисковик) — это постараться приспособить для этой задачи Docker Compose. Изначально задумывавшийся как инструмент для быстрого запуска контейнеров на тестовой инфраструктуре, Docker Compose, однако, можно использовать и на бою. Еще один вариант, который рассматривался нами в качестве подходящего инструмента — Ansible, имеющий в своем составе модули для работы с контейнерами и образами Docker.

Но ни то, ни другое решение нас как разработчиков не устроило. И самая главная причина этого кроется в способе описания конфигураций — при помощи файлов YAML. Для того, чтобы понять эту причину, я задам простой вопрос: кто-нибудь из вас умеет программировать на YAML? Удивлюсь, если кто-то ответит утвердительно. Отсюда главный недостаток всех инструментов, использующих для конфигурирования всевозможные варианты разметок (от INI/XML/JSON/YAML до более экзотических, вроде HCL) — невозможность расширения логики стандартными способами. Среди недостатков можно также упомянуть отсутствие autocomplete и возможности прочитать исходный код используемой функции, отсутствие подсказок о типе и количестве аргументов и прочих радостей использования IDE.

Далее, мы посмотрели в сторону Fabric и Capistrano. Для конфигурирования они используют обычный язык программирования (Python и Ruby, соответственно), то есть позволяют писать кастомную логику внутри конфигурационых файлов с возможностью использования внешних модулей, чего мы, собственно, и добивались. Мы не стали долго выбирать между Fabric и Capistrano и почти сразу остановились на первом. Наш выбор, в первую очередь, был обусловлен наличием экспертизы в Python и почти полным ее отсутствием в Ruby. Плюс, смутила довольно сложная структура проекта Capistrano.

В общем, выбор пал на Fabric. Благодаря его простоте, удобству, компактности и модульности он поселился в каждом нашем проекте, позволяя хранить само приложение и логику его развертывания в одном репозитории.

Наш первый опыт написания конфига для автоматического деплоя при помощи Fabric дал возможность выполнять основные действия по обновлению приложения на боевой и тестовой инфраструктурах и позволил экономить значительный объем времени разработчика (отдельного релиз-менеджера у нас нет). Но при этом файл с настройками вышел довольно громоздким и трудным для переноса на другие проекты. Мы задумались над тем, как задачу адаптации конфигов под другой проект решать легче и быстрее. В идеале хотелось получить универсальный и компактный шаблон конфигурации развертывания на стандартный набор имеющихся инфраструктур (test, staging, production). К примеру, сейчас наши конфиги для автодеплоя выглядят примерно так:

fabfile.py

from fabric import colors, api as fab
from fabricio import tasks, docker

##############################################################################
# infrastructures
##############################################################################


@tasks.infrastructure
def STAGING():
    fab.env.update(
        roledefs={
            'nginx': ['devops@staging.example.com'],
        },
    )


@tasks.infrastructure(color=colors.red)
def PRODUCTION():
    fab.env.update(
        roledefs={
            'nginx': ['devops@example.com'],
        },
    )

##############################################################################
# containers
##############################################################################


class NginxContainer(docker.Container):

    image = docker.Image('nginx')

    ports = '80:80'

##############################################################################
# tasks
##############################################################################


nginx = tasks.DockerTasks(
    container=NginxContainer('nginx'),
    roles=['nginx'],
)

Приведенный пример кода содержит в себе описание нескольких стандартных действий для управления контейнером, в котором запускается небезызвестный веб сервер. Вот что мы увидим, попросив Fabric вывести список команд из директории с этим файлом:

fab --list

Available commands:

    PRODUCTION
    STAGING
    nginx           deploy[:force=no,tag=None,migrate=yes,backup=yes] - backup -> pull -> migrate -> update
    nginx.deploy    deploy[:force=no,tag=None,migrate=yes,backup=yes] - backup -> pull -> migrate -> update
    nginx.pull      pull[:tag=None] - pull Docker image from registry
    nginx.revert    revert - revert Docker container to previous version
    nginx.rollback  rollback[:migrate_back=yes] - migrate_back -> revert
    nginx.update    update[:force=no,tag=None] - recreate Docker container

Здесь стоит немного пояснить, что кроме типовых deploy, pull, update и пр. в списке присутствуют также таски PRODUCTION и STAGING, которые при запуске никаких действий не совершают, но подготавливают окружение для работы с выбранной инфраструктурой. Без них большинство других тасков выполниться не сможет. Это «стандартный» workaround (обход) того факта, что Fabric не поддерживает явного выбора инфраструктуры для работы. Следовательно, для того, чтобы запустить процесс развертывания/обновления контейнера с Nginx, к примеру на STAGING, нужно выполнить следующую команду:

fab STAGING nginx

Как нетрудно уже было догадаться, практически вся «магия» скрыта за этими строками:

nginx = tasks.DockerTasks(
    container=NginxContainer('nginx'),
    roles=['nginx'],
)

Ciao, Fabricio!

В общем, позвольте представить Fabricio — модуль, который расширяет стандартные возможности Fabric, добавляя в него функционал для работы с контейнерами Docker. Разработка Fabricio позволила нам перестать думать о сложности реализации автоматического деплоя и целиком сосредоточиться на решении поставленных бизнес-задач.

Очень часто мы сталкиваемся с ситуацией, когда на боевой инфраструктуре заказчика имеются ограничения на доступ в интернет. В этом случае мы решаем задачу деплоя при помощи приватного Docker Registry, запущенного в локальной сети администратора (либо просто на его рабочем компьютере). Для этого в примере выше нужно всего лишь заменить тип тасков — DockerTasks на PullDockerTasks. Список доступных команд в этом случае приобретет вид:

fab --list

Available commands:

    PRODUCTION
    STAGING
    nginx           deploy[:force=no,tag=None,migrate=yes,backup=yes] - prepare -> push -> backup -> pull -> migrate -> update
    nginx.deploy    deploy[:force=no,tag=None,migrate=yes,backup=yes] - prepare -> push -> backup -> pull -> migrate -> update
    nginx.prepare   prepare[:tag=None] - prepare Docker image
    nginx.pull      pull[:tag=None] - pull Docker image from registry
    nginx.push      push[:tag=None] - push Docker image to registry
    nginx.revert    revert - revert Docker container to previous version
    nginx.rollback  rollback[:migrate_back=yes] - migrate_back -> revert
    nginx.update    update[:force=no,tag=None] - recreate Docker container

Новые команды prepare и push готовят скачивают образ из основного Registry и закачивают его в локальный, откуда уже через туннель образ попадает на боевую инфраструктуру (команда pull). Запустить приватный Registry локально можно выполнив в терминале следующую строчку кода:

docker run --name registry --publish 5000:5000 --detach --restart always registry:2

Пример со сборкой образа отличается от первых двух, аналогично, только типом тасков — в данном случае это BuildDockerTasks. Список команд для задач со сборкой такой же, как и в предыдущем примере, за исключением того, что команда prepare вместо скачивания образа из основного Registry строит его из локальных исходников.

Для использования PullDockerTasks и BuildDockerTasks необходим установленный на компьютере администратора клиент Docker. После анонсирования публичных бета-версий Docker для платформ MacOS и Windows это уже не такая головная боль для пользователей.

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

  • сборка образов Docker
  • запуск контейнеров из образов с произвольными тегами
  • совместимость с режимом параллельного выполнения тасков на разных хостах
  • возможность отката к предыдущему состоянию (rollback)
  • работа с публичными и приватными Docker Registry
  • группировка типовых задач в отдельные классы
  • автоматическое применение и откат миграций Django-приложений

Установить и попробовать Fabricio можно через стандартный пакетный менеджер Python:

pip install --upgrade fabricio

Поддержка пока ограничена Python 2.5-2.7. Данное ограничение является прямым следствием поддержки соответствующих версий модулем Fabric. Надеемся, что в ближайшем будущем Fabric обзаведется возможностью запуска на Python 3. Хотя необходимости в этом особой нет — в большинстве дистрибутивов Linux, а также MacOS, версия 2 является дефолтной версией Python.

Буду рад ответить в комментариях на любые вопросы, а также выслушать конструктивную критику.

Автор: REDMADROBOT

Источник

Поделиться новостью

* - обязательные к заполнению поля