Глава 2. Работа с контейнером Ansible

Как мы уже видели в Главе 1, Построение контейнеров с помощью Docker, контейнеризация изменила сам способ сопровождения и развёртывания критически важной инфраструктуры ИТ. По сере того, как методологии и склад ума DevOps овладевает организациями, черта между ролями разработки и выполнения становится размытой. В то время как такие инструменты как Docker растут и развиваются, для применения масштабирования и оснащения всё продолжающих рост контейнерных приложений необходима разработка инструсентария.

Ansible является уникальной инфраструктурой автоматизации, как мы это уже увидели в Главе 1, Построение контейнеров с помощью Docker, поскольку он полагается на некую архитектуру без агентов, переводя серверы и виртуальные приложения в нужное состояние из централизованного местоположения поверх протокола SSH. В сравнении с прочими обсуждавшимися основными инструментами автоматизации, Ansible приносит отличный от прочих инструментов управления настройкой подход, таких как Chef и Puppet, которые полагаются на агентов и централизованные серверы для хранения и сопровождения состояний настройки.

Данный проект контейнеров Ansible был запущен для решения потребностей привнесения техник управления критически важными настройками в являющийся ручным на текущий момент процесс построения и развёртывания образов контейнеров Docker с применением стандартной цепочки инструментов Docker. В настоящее время Docker и инструменты Docker строятся с упором на разработку контейнеров для естественных сред с применением Swarm и Docker Compose. Ansible Container (Контейнеры Ansible) являются обёрткой для многих имеющихся стандартных инструментов Docker и предоставляет полную функциональность в оснащении ваших проектов различных поставщиков облачных ресурсов, Kubernetes и OpenShift. На время написания книги прочие инструменты оркестрации контейнеров, такие как Docker Swarm и Apache Mesos не являлись поддерживаемыми. Если Dockerfile являлись родственными для сценариев оболочки на протяжении эры разработки монолитных приложений, Ansible Container являются решением привнесения автоматизации и надёжности в имеющуюся экосистему контейнеров. Так как ядро Ansible применяет для приведения в действие желаемых состояний в качестве интерфейса плейбуки и SSH, Ansible Container могут применять одни и те же ваши плейбуки и естественные API контейнеров для построения и развёртывания контейнеров.

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

В этой главе мы изучим:

  • Введение в архитектуру контейнеров Ansible и микрослужб

  • Быстрое введение в Docker Compose

  • Рабочий поток Ansible Container

  • <Быстрый запуск Ansible Container

Введение в контейнеры Ansible и архитектуру микрослужб

Хотя Ansible Container имеют громадное число преимуществ повторного применения имеющихся артефактов, модулей и плейбуков Ansible, стоит с аккуратностью относиться ко всем изменениям, необходимым при портировании ваших имеющихся служб. Ansible предоставляет вам большую свободу в спосбе написания плейбуков и ролей для соотвествия уникальным чертам архитектуры вашей организации и составляющим её ресурсам. Некое обычное веб приложение, к примеру, может иметь три различных суровня функционирования: какой- то веб сервер, который предоставляет вашим конечным пользователям вебсайт; базу данных для хранения данных; а также неий кэш, снабжающий имеющийся веб сервер обычно используемыми данными из его базы данных. В зависимости от предлагаемой архитектуры и всех составляющих ресурсов такие службы могут реализовываться большим числом вариантов. Вы можете иметь свои веб серверы, уровень кэширования и базу данных в трёх различных кластерах серверов. Вы можете выбрать вариант развёртывания своих веб сервера и уровня кэширования в одном и том же кластере, а их базу данных во втором кластере. Либо все три уровня могут быть развёрнуты в одном и том же голом железе или кластере серверов виртуализации, с некоторой балансировкой нагрузки, производимой при необходимости. Ваша инфраструктура является уникальной снежинкой, для которой Ansible предоставляет вам свободу написания и развёртывания плейбуков и ролей практически в любой конфигурации, которая соответствует вашим потребностям.

Архитектура микрослужб (Microservice architecture) является термином, применяемым для определения того независимого и модульного разбиения служб приложения в качестве его отличия и для элементов развёртывания. В мире контейнеров вы желаете чтобы каждый из ваших контейнеров сообразовывался с архитектурой микрослужб, создавая каждую службу в виде отдельного контейнера, который может оснащаться и масштабироваться независимо от прочих служб. Хотя и имеется возможность развёртывать множество служб в одном и том же контейнере, это в целом плохая мысль, поскольку всякая служба добавляет в ваши контейнеры уровни, создавая ненужную перегруженность при построении и развёртывании новых контейнеров.

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

Основным ключом к изъятиям при размышлении о портировании имеющихся ролей Ansible в проекты Ansible Container состоит в рассмотрении того, насколько тесно интегрированы ваши роли в настоящее время. В идеале роли Ansible должны иметь возможность быть отдельными с малой долей полагания на прочие характеристики среды. Не начинает ли вам казаться, во многом похожим на те упакованные в контейнеры микрослужбы, которые мы описывали ранее? Именно это делает Ansible Container уникальной платформой среди прочих инструментов управления настройкой. Примитивы Ansible уже разработаны для прекрасного соответствия экосистеме контейнеров. Даже если в в настоящее время и не используете Ansible в качестве своего инструмента управления конфигурацией, Ansible Container всё же являются фантастическим инструментом для построения, сопровождения и развёртывания контейнеров, причём начиная с их разработки на всём пути до промышленного применения.

Быстрое введение в Docker Compose

Docker Compose является одним из инструментов рабочего потока Docker, который позволяет вам легко строить и исполнять множество контейнеров за раз. Важно иметь основные понятия о том как работает Docker Compose, прежде чем приступить к работе с Ansible Container, так как большая часть основной функциональности ядра контейнеров Ansible является обёрткой Docker Compose.

В предыдущей главе проиллюстрировал некий пример, в котором были созданы три контейнера веб серверов Apache для демонстрации одновременного исполнения множества контейнеров с применением одного и того же базового образа контейнера. Воспользовавшись Docker Compose, вместо воспроизведения трёх различных команд docker run можно просто предоставить файл определения YAML, который описывает все те контейнеры, которые вы намереваетесь исполнять, любые параметры docker run, с которыми желаете создать для этих контейнеров, прежде чем вы исполните их. Когда выполнится Docker Compose, он автоматически попытается завести те контейнеры, которые описаны в файле данном YAML. Если эти образы ещё пока не кэшированы локально, он попытается выгрузить и через Интернет или построить эти образы контейнеров в случае, если предоставлены Dockerfiles. Давайте выполним короткое упражнение чтобы почуствовать как работает Docker Compose.

Если у вас пока нет развёрнутого оборудования лабораторной среды Vagrant, которое мы описали в Главе 1, Построение контейнеров с помощью Docker, вам вначале понадобится выгрузить Docker Compose, воспользовавшись приведённой ниже командой. Те шаги, которые мы собираемся выполнить, предполагают, что у вас уже имеется установленный и запущенный Docker Engine в какой- то машине Linux или macOS. Проверьте что вы установили Docker Compose с той же версией, что и уже работающий Docker Engine для обеспечения максимально совместимости. Для выгрузки исполняемого Docker Compose выполните следующие команды и скопируйте полученное в /usr/local/bin с привилегиями execute.


sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname
sudo chmod +x /usr/local/bin/docker-compose
		
[Совет]Совет

Самую современную документацию по установке можно найти на https://docs.docker.com/compose/install.

По умолчанию Docker Compose просматривает в вашем текущем рабочем каталоги наличие файла с названием docker-compose.yml. Я предоставил в качестве примера простой файл docker-compose.yml. В своей рабочей машине создайте каталог с названием docker-compose и в этом каталоге создайте пустой файл docker-compose.yml. Введите в него следующее содержимое:


version: '2'
services:для наших целей 
  Cache_Server:
    image: memcached:1.4.36
    ports:
      - 11211:11211
    volumes:
      - .:/var/lib/MyVolume
 	   

Давайте рассмотрим все строки в этом файле:

  • version: эта строка указывает какую версию API Docker Compose применяется. В данном случае мы пользуемся версией 2. На момент написания также имеется версия 3 для данного API, которая предоставляет некие новые функции. Однако, для наших целей мы удовольствуемся версией 2. Обычно данный параметр открывает файл Docker Compose и не имеет отступа.

  • services: Строка services начинает раздел вашего файла docker-compose.yml, который перечисляет все контейнерные службы, которые вы собираетесь создать. Конкретно в этом файле Docker Compose мы намереваемся создать службу с названием Cache_Server, которая раскрутит отдельный контейнер memcached. Все определяемы службы должны иметь отступ в два пробела под объявлением services. Следует также отметить, что эти названия служб определяются определяются пользователем и применяются для выработки имени соответствующего контейнера. При создании файлов Docker Compose со множеством контейнеров, Docker предоставляет разрешение имён DNS для контейнеров на основании данных названий служб. Дополнительно об этом излагается в Главе 8, Построение и развёртывание проекта со множеством контейнеров.

  • image: Этот параметр image применяется для определения того образа контейнера, на основе которого вы хотите получить свой контейнер. Для данного примера мы используем официальный образ memcached из Docker Hub, определяя версию 1.4.36. мы также можем применить ключевое слово latest вместо номера версии если мы желаем для данного образа всегда иметь самую последнюю версию.

  • ports: Данный параметр ports указывает какие порты вы желаете переправить в свой контейнер из данного хоста. В нашем случае мы для представления порта 11211 мы передаём порт 11211. Аналогично, для docker run, порты необходимо определять в формате host:container. Это перечень YAML, следовательно каждый порт должен предваряться отступом и префиксом дефиса -.

  • volumes: Этот параметр определяет все каталоги или тома хранения в данном хосте Docker, которые вы хотите сделать доступными для определяемого контейнера. Это полезно в тех случаях, когда в данном контейнере имеются данные, которые вы можете захотеть резервно копировать, экспортировать или иным образом совместно использовать с данным контейнером. Этот том монтируется главным образом в качестве примера синтаксиса. Аналогично предыдущему параметру ports, volumes получают перечень в виде hostDirectory:containerDirectory .

Для запуска нашего контейнера с применением Docker Compose вы просто исполняете соответствующую команду docker-compose up. Она по умолчанию запустит один за другим все те контейнеры, которые находятся в файле Docker Compose, если только не определены зависимости контейнера. Запускаемые с применением docker-compose контейнеры будут запущены в режиме attached, что означает, что этот процесс контейнера будет исполняться принимая тот Терминал, который вы используете. Аналогично docker run мы можем предоставить флаг -d для исполнения всех контейнеров в режиме detached. Поэтому мы можем выполнить проверку этого действия в том же самом Терминале:


docker-compose up -d
		

Вы можете видеть, что аналогично docker run Docker Compose автоматически определяет что требуемый образ данного контейнера присутствует в данном хосте Docker и успешно выгружает необходимый образ и соответствующие уровни из Интернета.


ubuntu@node01:/vagrant/Docker_Compose/test$ docker-compose up -d
Creating network "test_default" with the default driver
Pulling Cache_Server (memcached:1.4.36)...
1.4.36: Pulling from library/memcached
56c7afbcb0f1: Pull complete
49acdc7c75c9: Pull complete
152590a2a704: Pull complete
4dc7b8165378: Pull complete
4cb74c11bcdd: Pull complete
Digest: sha256:a2dfef5700944ec8bb2d2c0d6f5b2819324b1b91647dc09847ce81e7a91e3fe4n
Status: Downloaded newer image for memcached:1.4.36
Creating test_Cache_Server_1 ...
Creating test_Cache_Server_1 ... done
		

Исполнив docker ps -a мы обнаружим, что Docker Compose имел возможность успешно создать и исполнить контейнер с надлежащим образом выставленными портами и смонтированными томами, которые перечислены в нашем файле docker-compose.yml:


ubuntu@node01:/vagrant/Docker_Compose/test$ docker ps -a
CONTAINER ID        IMAGE COMMAND       CREATED                  STATUS
cacf58b455f3        memcached:1.4.36    "docker-entrypoint.sh"   7 minutes ago
		

Мы можем применить telnet чтобы убедиться что наше приложение memcached работает и переправляется в сетевой среде хоста. С помощью telnet мы можем непосредственно сохранять и извлекать данные из memcached:


ubuntu@node01:/vagrant/Docker_Compose/test$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.Escape character is '^]'.
STAT active_slabs 0
STAT total_malloced 0
END
		

Выполнение команды stats slabs позволяет нам узнать, что memcached был успешно развёрнут и работает как и ожидалось.

Теперь, когда мы сделали краткое введение в Docker и Docker Compose, у нас имеются базовые навыки необходимые для начала работы с Ansible Container.

Рабочий поток контейнеров Ansible

Аналогично прочим инструментам оркестрации и автоматизации, Ansible Container содержит набор средств, которые и составляют некий рабочий поток контейнеров. При помощи Ansible Container вы можете создавать, строить, исполнять и развёртывать контейнеры, причём начиная с их разработки и по всему пути до промышленного использования, применяя комплект инструментов, поставляемых совместно с Ansible Container. Методология батарейки в комплекте ядра Ansible заботится о том, чтобы Ansible Container предоставлял разработчикам и системным администраторам комплект решения рабочего потока контейнеров. Ниже приводится обзор первичных функций Ansible Container и то, как они соотносятся с типичным жизненным циклом приложения контейнеров.

  • ansible-container init: Применяется для начального запуска проекта Ansible Container. init строит и создаёт необходимую оснастку каталога и основных файлов, которые необходимы для запуска проекта Ansible.

  • ansible-container build: Аналогично тому, что предполагает его название, build осуществит синтаксический анализ файлов вашего проекта и попытается построить описанные в них контейнеры. Ansible Container имеет возможность осуществления этого первоначально создав то, что именуется контейнером conductor (проводник). Такой контейнер conductor является главным контейнером, который создаётся в процессе фазы построения вашего проекта и содержит исполняемую копию Ansible. После запуска прочих контейнеров этот контейнер conductor отвечает за выполнение ролей и плейбуков Ansible для них чтобы приводить эти контейнеры в желаемое состояние.

  • ansible-container run: run работает очень похоже на тот способ, который оносится к docker run при его исполнении, run принимает построенные контейнеры и пытается исполнить с помощью механизма контейнеров в данном хосте. По умолчанию команда run рассматривает все опции разработки, перечисляемые в имеющемся файле container.yml, если только в процессе исполнения не передаётся флаг -- production.

  • ansible-container destroy: Останавливает любые исполняемые контейнеры и удаляет все построенные образы. Данная команда полезна при повсеместном тестировании развёртывания из рабочей области.

  • ansible-container push: Эта команда помещает те образы контейнеров, которые вы построили с помощью Ansible Container в некий реестр контейнеров по вашему выбору, например, Docker Hub, Quay или GCR. Данная команда аналогична ansible-container push.

  • ansible-container deploy: deploy (первоначально ShipIt) принимает ваш первоначальный проект и вырабатывает индивидуальные плейбуки и роли Ansible для развёртывания вашего контейнера у поставщика облачной службы. На момент написания данной книги deploy порддерживал только OpenShift и Kubernetes. Исполнение данного плейбука с применением команды ansible-playbook развернёт ваши контейнеры у определённого провайдера.

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

[Совет]Совет

Все подкоманды Ansible Container можно отыскать выполнив ansible container --help.

Быстрый старт контейнера Ansible

Этот раздел данной главы предназначен для того, чтобы сосредоточиться на запуске Ansible Container, инициализации некоторого базового проекта и восстановления нашего примера memcached из созданного ранее. Если вы не следуете действиям лаборатории Vagrant, созданной из GitHub, вашим самым первым шагом будет установка Ansible Container с применением диспетчера пакетов python-pip. Приведённые ниже шаги установят Ansible Container с поддержкой Docker в дистрибутиве Linux на основе Debian:


sudo apt-get update
sudo apt-get install python-pip
sudo pip install ansible-container docker
		

init контейнер Ansible

Теперь у вас имеется установленный и готовый к исполнению в вашей среде Ansible Container. Самой первой командой, которая необходима для запуска нового проекта Ansible Container является команда ansible-container init. После регистрации в вашей ВМ Vagrant, создайте какой- нибудь пустой каталог в /vagrant и наберите:


ubuntu@node01:~$ mkdir /vagrant/demo
ubuntu@node01:~$ cd /vagrant/demo
ubuntu@node01:/vagrant/demo$ ansible-container init
Ansible Container initialized.
		
[Совет]Совет

Важно отметить, что это окончательное упражнение с лабораторией можно также найти в официальном репозитории нашей книги, а именно, в каталоге AnsibleContainer/demo.

Когда Ansible Container успешно создаст необходимый проект, он возвратит отклик Ansible Container initialized.

Как описывалось ранее, init создаёт всю основную структуру каталога и схему, требующуюся для запуска построения проектов Ansible Container. Переместившись в этот каталог и просмотрев перечень этого каталога мы представим вам идею того как выглядит проект Ansible Container:


demo
├── ansible.cfg
├── ansible-requirements.txt
├── container.yml
├── meta.yml
└── requirements.yml
 	   

Давайте рассмотрим каждый из этих файлов персонально чтобы понять их цели в проекте Ansible Container:

  • ansible.cfg: Это первичный файл настройки самого механизма Ansible. Все установки, которые вы хотите применять в своём контейнере Ansible conductor поступают в этот файл. Если вы знакомы с с применением Ansible для задач управления настройками, вы уже имеете основы преставления о файле ansible.cfg. В большинстве случаев вы можете безопасно оставить этот файл как есть, если только не имеются некие особые способы, которыми Ansible исполняется в процессе построения требуемых контейнеров. Дополнительную информацию о вариантах настройки Ansible можно найти в документации Ansible https://docs.ansible.com. {Прим. пер.: или в нашем переводе 2 издания Полного руководства Ansible Джесса Китинга.}

  • ansible-requirements.txt: Данный файл ansible-requirements.txt применяется для определения всех зависимостей Python pip, которые могут понадобиться для успешной работы ваших плейбуков. В этом файле перечисляются все дополнительные пакеты Python, которые требуются для исполнения имеющихся ролей Ansible.

  • container.yml: Описывает требуемое состояние ваших контейнеров, включая базовые образы, выставляемые порты, а также монтируемые тома. Сам синтаксис для container.yml аналогичен формату Docker Compose с небольшими отличиями, которые мы рассмотрим на протяжении этой книги.

  • meta.yml: Этот файл meta.yml содержит все метаданные о вашем проекте контейнеров, включая само конкретное его автора, информацию о версии, подробности лицензий программного обеспечения и теги. Эта информация сделает для прочих пользователей поиск вашего проекта, если вам следует выбрать его размещение для совместного применения в Ansible Galaxy.

  • requirements.yml: Определяет всю информацию о ролях и версии Ansible Galaxy, которые должен применять ваш проект контейнеров. В этом файле вы можете описать точные значения ролей и версий ролей, необходимых вашему проекту. Ansible Container выгрузит эти роли прежде чем построит ваш проект контейнеров. Определив свои роли в файле requirements.yml, вы можете быть уверенным, что ваши проекты согласованно применяют одни и те же роли для построения необходимых базовых образов контейнеров. Важно иметь в виду существенную разницу между ansible-requirements.yml и requirements.yml. requirements.yml применяется для управления теми ролями проекта Ansible,от которых зависит ваш проект, в то время как ansible-requirements.yml используется для управления теми пакетами pip Python, которые могут требоваться этим ролям.

Теперь, когда у нас имеется представление как выглядит некий проект Ansible Container, мы можем погрузиться глубже и начать эксперименты с созданием простого проекта Ansible Container. Помните наш созданный ранее проект Docker Compose? Давайте воспользуемся им как отправной точкой и портируем этот проект в Ansible Container изменив его файл container.yml. По умолчанию container.yml поставляется с некоторой заполненной структурой, которая во многом походит на какой- то файл Docker Compose. Ваш файл container.yml должен быть схожим с представленным ниже. Для сохранения места я удалил из него многочисленные комментарии и пример данных:


version: "2"
settings:
  conductor_base: centos:7

services: {}

registries: {}
 	   

Каждый из этих разделов имеет определённую цели в структурировании вашего проекта Ansible Container. Важно понимать для описания чего применяется каждое из этих определений YAML. Поставляемые с этим файлом комментарии по умолчанию отображают примеры различных установок, которые использует каждый из этих разделов. Ниже приводится перечень ключевых разделов данного файла container.yml и как применять эти разделы в вашем проекте Ansible Container:

  • version: Этот раздел version обозначает какую версию API Docker Compose применять. Как мы уже обсуждали ранее, Ansible Container является оболочкой вокруг многих имеющихся служб Docker Compose. Здесь мы можем определить какую версию API Docker Compose мы бы желали, чтобы наши контейнеры применяли.

  • settings: Данный раздел settings применяется для определения дополнительных интеграций или изменений любых определённых по умолчанию режимов работы нашего проекта Ansible Container. По умолчанию имеется одна включённая установка.

  • conductor_base: Здесь указывается какой базовый образ мы бы желали применять для своего проекта. Этот контейнер conductor ответственен за создание среды Python, используемой для исполнения плейбуков и ролей Ansible. Данный образ контейнера conductor будет соединяться с другими контейнерами, которые он создаёт, предоставляя доступ к его собственной среде в процессе построения. Поэтому очень важно использовать ту же самую операционную систему базового контейнера, что и для того образа контейнера, который вы планируете построить. Это обеспечит полную совместимость в смысле Python и Ansible. Представляйте себе образ conductor как контейнер, который работает аналогичным образом с узлом контроллера Ansible в стандартной реализации Ansible. Этот контейнер будет обращаться к другим узлам (контейнерам), применяя API Docker напрямую для приведения наших прочих контейнеров в требуемое состояние. После того как построим свои контейнеры, наш контейнер conductor удалит себя по умолчанию, если только вы не укажете Ansible Container оставить этот образ conductor для целей отладки. Помимо того что мы определяем в этом разделе свой образ conductor, мы можем определить здесь и другие интеграции, например, сертификаты Kubernetes или терминалы OpenShift. Мы более глубоко рассмотрим эти вопросы в последующих главах.

  • services: Данный раздел services почти идентичен аналогичному разделу services в нашем файле Docker Compose. В этом разделе мы предоставим свои определения YAML, которые описывают все исполняемые состояния наших контейнеров: какой базовый образ мы будем применять, само название контейнера, выставляемые порты, тома и прочее. Всякий описываемый в данном разделе служб контейнер является неким узлом (node), который будет настраиваться нашим образом conductor, исполняющим Ansible. По умолчанию раздел services запрещён двумя фигурными скобками, следующими сразу после определения YAML: []. Прежде чем добавлять определения в контейнер, удалите эти фигурные скобки, чтобы Ansible Container смог осуществлять доступ к своим дочерним данным.

  • registries: Завершающим разделом нашего файла container.yml является раздел registries. Именно здесь вы можете определять реестры контейнера, из которых контейнер Ansible будет вытаскивать образы. По умолчанию Ansible Container применяет Docker Hub, однако вы также можете определять и другие реестры, например, Quay, gcr.io или локально размещаемые реестры контейнеров. Этот раздел также совместно используется вместе с командой ansible-container push для активной доставки построенных вами контейнеров в соответствующую службу реестра по вашему выбору.

build контейнер Ansible

Второй частью рабочего потока Ansible Container является процесс построения. Теперь, когда у нас имеется инициализированным наш первый проект, мы можем изучить как работает функция ansible-container build, даже несмотря на то, что у нас нет никаких определений служб и ролей. В каталоге demo исполните команду ansible-container build. Вы должны увидеть вывод аналогичный следующему:


ubuntu@node01:/vagrant/AnsibleContainer/demo$ ansible-container build
Building Docker Engine context...
Starting Docker build of Ansible Container Conductor image (please be patient)...
Parsing conductor CLI args.
Docker™ daemon integration engine loaded. Build starting.       project=demo
All images successfully built.
Conductor terminated. Cleaning up.      command_rc=0 conductor_id=c4f7806f8afb0910e4f
		

Исполнение build Ansible Container в первый раз в вашей локальной рабочей станции может занять несколько минут для завершения, поскольку оно требует построения необходимого контейнера conductor прежде чем сможет стартовать. Имейте в виду, что контейнер проводника (conductor) отвечает за соединение с контейнерами служб с применением API Docker и исполнением в нём плейбуков и ролей Ansible. Так как это базовый пример команды ansible-container build, в нём нет никаких созданных нами плейбуков Ansible для исполнения в наших контейнерах. Позднее в этой книге мы напишем свои собственные роли для реального изучения того, как работает данный контейнер проводника. Ниже приводится иллюстрация того, как контейнер проводника соединяется с имеющимися контейнерами службы:

 

Рисунок 2-1


Контейнер проводника, переводящий контейнеры служб в требуемое состояние

Однако в этом контейнере Ansible Container вначале будет соединяться с API Docker в данном локальном хосте для определения выстраиваемого контекста, выгрузки требующихся зависимостей образов, а также исполнения соответствующего контейнера conductor. В предыдущем выводе вы можете увидеть, что наш контейнер conductor успешно потроил наш проект demo. Он также перечислил все коды возврата, которые подтверждают то, что наш образ был успешно создан, а также внутренний идентификатор проводника создаваемый Ansible Container.

Если мы исполним команду docker ps -a, мы обнаружим что на текущий момент никакой контейнер не исполняется и не существует. Это ожидаемо, поскольку мы ещё не определили никаких контейнеров в своём разделе services нашего файла container.yml. Вы также можете заметить, что поскольку мы не передаём никакие аргументы или настройки с целью уведомить Ansible Container о сохранении своего контейнера container, Ansible Container удаляет проводника после того,как он завершит исполнение.p>


ubuntu@node01:demo$ docker ps -a
CONTAINER ID  IMAGE  COMMAND  CREATED  STATUS  PORTS  NAMES
		

Однако, если мы взглянем на свой вывод docker images, мы обнаружим, что имеющийся построенный нами образ conductor кэширован., так же как и применяемый для его создания базовый образ. Отмектим, что этот образ проводника имеет префикс demo-*. Ansible Container автоматически присваивает имена образам контейнеров согласно номенклатуры project-service. Это гарантирует, что если вы выстраиваете и запускаете одновременно множество проектов контейнеров, будет легко сказать какие из контейнеров относятся к каким проектам.

В данном случае наш проект имеет название demo, а построенной нами службой является conductor.


ubuntu@node01:demo$ docker images
REPOSITORY  TAG  IMAGE ID  CREATED SIZE
demo-conductor  latest  a24fbeee16e2 38 seconds ago  574.5 MB
centos  7  3bee3060bfc8 3 weeks  ago 192.6 MB
		

Мы также можем построить свой проект передав через флаг --save-conductor-container необходимость оставить контейнер conductor по завершению всего процесса ansible-container build. Это полезно для целей отладки отказа в построении, поскольку имеется возможность рассмотреть наш контейнер на предмет того контекста, с которым исполнялся Ansible. Давайте попробуем повторно построить свой проект demo, причём в этот раз сохраним создаваемый контейнер conductor:


ubuntu@node01:demo$ ansible-container build --save-conductor-container
Building Docker Engine context...
Starting Docker build of Ansible Container Conductor image (please be patient)...
Parsing conductor CLI args.
Docker™ daemon integration engine loaded. Build starting.       project=demo
All images successfully built.
Conductor terminated. Preserving as requested.  command_rc=0 conductor_id=ff84fa95d590
		

На этот раз вы видите вывод, отражающий небольшое отличие: Conductor terminated. Preserving as requested дополнительно к рассмотренному ранее нами выводу. Это указывает на то, что хотя этот проводник и остановился по завершению своего задания, сам контейнер, demo_conductor, остаётся для нас видимым из docker ps -a:


ubuntu@node01:/vagrant/AnsibleContainer/demo$ docker ps -a
CONTAINER ID  IMAGE  COMMAND  CREATED  STATUS  PORTS  NAMES
c3c7dc04d251  a24fbeee16e2  "conductor build –pr"  3 minutes ago Exited (0) 3 minutes 
		

Вооружившись прочным пониманием того как работает сам процесс построения Ansible Container, а также того как Ansible Container строит необходимый образ проводника, мы можем применить это знание для перестроения того проекта Docker Compose, который мы ввели в самом начале этой главы. Мы можем воспользоваться Ansible Container для раскрутки своего контейнера сервера memcached, который мы создали ранее.

В своём текстовом редакторе откройте рассмотренный нами ранее документ conductor.yml. Удалите фигурные скобки после нашего объявления services: {} и ниже него добавьте приведённое ниже, согласно основному синтаксису YAML, предваряя отступами в два пробела:


services:
  AC_Cache_Server:
    from: memcached:1.4.36
    ports:
      - "11211:11211"
    volumes:
      - ".:/var/lib/MyVolume"
 	   

Вы можете видеть, что синтаксис, который мы используем для указания своего сервиса, очень похож на синтаксис Docker Compose, который мы создавали ранее. Для целей данной демонстрации мы собираемся применить те же самые параметры для ports и volume, которые мы использовали ранее с Docker Compose чтобы наш читатель легко мог обнаружить небольшие различия в синтаксисе. Вы заметите, что синтаксис нашего container.yml и синтаксис Docker Compose имеют множество сходств, однако основные отличия позволяют Ansible Containerбыть более гибким в отношении того как строятся и развёртываются службы контейнера.

Сохраните и закройте этот файл. Если вы исполните ту же команду ansible-container build вновь, вы должны увидеть следующий вывод:


ubuntu@node01:demo$ ansible-container build
Building Docker Engine context...
Starting Docker build of Ansible Container Conductor image (please be patient)...
Parsing conductor CLI args.
Docker™ daemon integration engine loaded. Build starting.       project=demo
Building service...     project=demo service=AC_Cache_Server
Service had no roles specified. Nothing to do.  service=AC_Cache_Server
All images successfully built.
Conductor terminated. Cleaning up.      command_rc=0 conductor_id=22126436967e7810aff
		

После того, как Ansible Container построил образ нашего проводника, мы можем увидеть из вывода, что Ansible Container теперь распознал что мы мы имеем включённой некую службу с названием AC_Cache_Server и она пытается строить его. Однако, у нас нет никаких ролей Ansible, ассоциируемых с данной службой, поэтому она возвращает сообщение Nothing to do. Обыкновенно это будет именно тем шагом, на котором будут исполняться наши плейбуки для выработки создаваемых нами служб. Ansible Container собирается пропустить этот шаг и завершить свой контейнер conductor обычным образом.

run контейнер Ansible

Теперь, когда у нас имеется определённой служба, мы можем воспользоваться командой ansible-container run для запуска своей службы. Эта команда исполнения быстро вырабатывает небольшое воспроизведение (плейбук) Ansible, которое отвечает за запуск тех контейнеров, которые содержатся в файле container.yml. Этот плейбук применяет имеющийся модуль Ansible docker_service для запуска, останова, перезапуска и удаления контейнеров. Данный модуль docker_service также полезен для взаимодействия с установленным в ОС хоста демоне Docker для активной доставки и удаления образов в (из) кэша образа самого Docker. Хотя это и не супер важно понимать все подробности реализации, находящиеся за данным модулем в этом месте, полезно понимать как Ansible Container работает за сценой для выполнения конейнеров. Исполнение команды ansible-container run отобразит все этапы данного исполняемого плейбука, а также play recap, аналогично следующему выводу:


ubuntu@node01:demo$ ansible-container run
Parsing conductor CLI args.
Engine integration loaded. Preparing run.       engine=Docker™ daemon
WARNING Image memcached:1.4.36 for service AC_Cache_Server not found. An attempt will be made

PLAY [localhost] ***************************************************************

TASK [docker_service] **********************************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                 : ok=1     changed=1    unreachable=0    failed=0

All services running.  playbook_rc=0
Conductor terminated. Cleaning up.      command_rc=0 conductor_id=17aaa7aac99ff12427a
		

Как вы можете увидеть прочитав данный вывод исполнения плейбука, вы легко можете следовать выделенным ключевым положениям нашего проекта по мере того, как мы приводим их в рабочее состояние:

  • Наш проект не может обнаружить тот образ memcached, который мы определили, поэтому Ansible Container выполнит его активную доставку из определённого по умолчанию репозитория (Docker Hub)

  • Для приведения нашего контейнера в рабочее состояние в нашем хосте было осуществлено одно изменение

  • Никакое из наших воспроизведений не завершилось неудачей; одна задача достигла цели (подняв наш контейнер) и эта успешная задача внесла изменения в наш хост с тем, чтобы поднять данный контейнер

  • Служба проводника была завершена

Понимание этих основных моментов из нашего плейбука Ansible Container критически важно для просмотра того, как оркестрация Ansible развёртывает и сопровождает наши приложения. Как мы уже обсуждали ранее, команда Ansible работает очень плотно над тем, чтобы гарантировать что исполнение плейбука Ansible очень простое для его понимания и лёгкое в отладке. Отобразив все те шаги, которые требуются для поднятия контейнерных проектов, очень просто отлаживать падения и видеть потенциальные области улучшения по мере нашего продвижения вперёд в развитии более сложных проектов. Тот плейбук, который был только что воспроизведён сгенерирован на-лету при выполнении ansible-container run и располагается в нашем каталоге ansible-deployment. Применение Ansible Container для исполнения проектов убирает большинство сложностей развёртывания и сопровождения проектов, так как мы абстрагируемся от всей имеющаяся сложности развёртывания. С точки зрения самого пользователя, вы заинтересованы в обеспечении надлежащего исполнения и построения необходимых контейнеров. Ansible Container становится всеобъемлющим инструментом жизненного цикла, который делает возможным всякий раз согласованное построение контейнеров и их исполнение в ожидаемом состоянии. Как мы увидим далее в этой книге, упрощение сложности развёртывания Ansible Container особенно важно в средах, применяющих Kubernetes или OpenShift.

Теперь, когда завершено исполнение нашего контейнера, давайте рассмотрим какие контейнеры выполняются в нашем хосте, воспользовавшись командой docker ps -a:


ubuntu@node01:/vagrant/AnsibleContainer/demo$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c4a7792fb1fb memcached:1.4.36 "docker-entrypoint.sh" 14 seconds ago Up 13 seconds 0.0.0.0:112
		

как мы и ожидали, легко онаружить что наш контейнер memcached (версия 1.4.36) находится в сосотоянии исполнения. Кроме того отметим, что наш контейнер conductor не исполняется и не отображён в нашем выводе docker ps. Ansible Container исполняет только те контейнеры, которые определены в имеющемся файле container.yml как desired state (желательное состояние), только если вы выбрали оставить свой контейнер conductor для целей отладки. Само название данного контейнера, который мы определили в нашем файле container.yml, установлено в значение demo_AC_Cache_Server_1. Вы можете спросить себя почему так получилось, так как когда мы просматривали файл container.yml при его создании, мы осознанно именовали свой контейнер AC_Cache_Server. Одним из величайших свойств Ansible Container является то, что он понимает, что будучи разработчиком, мы можем исполнять и проверять множество версий своих проектов одновременно в одном и том же хосте или группе хостов. По умолчанию, когда Ansible Container запускает контейнеры, он автоматически добавляет в конец самого названия нашего проекта (в нашем случае demo) название данного исполняемого контейнера и некое численное значение, указывающее идентификатор экземпляра этого исполняемого контейнера.

В данном соучае, так как у нас имеется исполняемым один экземпляр данного контейнера, Ansible Container автоматически добавляет demo_ и _1 в самое начало и в конец нашего названия контейнера с тем, чтобы не было конфликтов если бы мы проверяли множество версий данного контейнера в одном и том же хосте.

Поскольку мы улучшаем то упражнение, к которому мы приступили в начале данной главы на своём хосте, давайте выполним ту же самую проверку telnet, воспользовавшись соответствующей командой stats slabs, которую мы выполняли ранее, чтобы посмотреть исполняется ли наш контейнер memcached и отвечает ли он как ожидалось:


ubuntu@node01:/vagrant/AnsibleContainer/demo$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
STAT active_slabs 0
STAT total_malloced 0
END
		

Очевидно, что наши службы контейнеров надлежащим образом исполняются и принимают запросы, выполняя ожидание в своих сетевых интерфейсах нашего хоста Docker. Имейте в виду, что мы определили в нашем файле container.yml, что наш локальный порт (11211) должен передаваться в ожидание по порту (11211) в самом контейнере.

Давайте быстро заглянем в кэш образа в нашем хосте Docker. Мы можем выполнить это путём исполнения соответствующей команды docker images:


ubuntu@node01:/vagrant/AnsibleContainer/demo$ docker images
REPOSITORY  TAG  IMAGE ID  CREATED  SIZE
demo-conductor  latest  a24fbeee16e2  48 minutes ago 574.5 MB
centos 7   3bee3060bfc8  3 weeks ago  192.6 MB
memcached  1.4.36  6c32c12d9101  6 weeks ago  83.88 MB
		

Основываясь на данном выводе, мы можем более отчётливо понять как работает в своём сервере Ansible Container. Чтобы поднять наш проект demo, Ansible Container должен воспользоваться тремя образами: CentOS 7, memcached и demo-conductor. Соответствующий образ проводника с названием demo-conductor является образом того проводника, который был создан при процессе нашего построения. Чтобы построить этот образ проводника, Ansible Container пришлось выгрузить и кэшировать базовый образ CentOS 7, который также отображён в приводимом выводе. Наконец, memcached является тем контейнером, который Ansible должен активно доставить из имеющегося репозитория образов, как это было определено в соответствующем разделе services нашего файла container.yml. Внимательный читатель может также отметить, что созданный образ проводника предварён самим названием нашего проекта demo аналогично состоянию исполнения нашего контейнера службы в представленном выше выводе. Это вновь позволяет избежать конфликта имён и иметь необходимую гибкость при исполнении множества проектов контейнеров одновременно в одном и том же хосте.

destroy контейнер Ansible

Раз уж мы завершили экспериментировать со своим проектом demo, мы можем применить команду ansible-container destroy чтобы остановить все исполняемые экземпляры нашего контейнера и удалить все его следы из нашей системы. destroy полезен для очистки имеющихся оснащений и проверки наших контейнеров через его перепостроения в рабочей области. Для уничтожения контейнеров просто исполните ansible-container destroy в каталоге своего проекта.


ubuntu@node01:/vagrant/AnsibleContainer/demo$ ansible-container destroy
Parsing conductor CLI args.
Engine integration loaded. Preparing to stop+delete all containers and built images.

PLAY [localhost] ***************************************************************

TASK [docker_service] **********************************************************
changed: [localhost]

TASK [docker_image] ************************************************************
changed: [localhost]

TASK [docker_image] ************************************************************
changed: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=3    unreachable=0    failed=0
All services destroyed. playbook_rc=0
Conductor terminated. Cleaning up.      command_rc=0 conductor_id=1dc36baefde06235a8c7c187334
		

Аналогично выполненной нами ранее команде run, уничтожение исполняет тот же самый плейбук, который был автоматически выработан этим процессом run. Однако, в этот раз он останавливает и удаляет все контейнеры, которые определены в имеющемся файле container.yml. Вы можете обнаружить, что вывод docker ps -a теперь не отображает никаких контейнеров в нашем хосте.

Аналогично, эта функция destroy стирает сам образ контейнера conductor, а также все образы контейнеров служб в нашем хосте Docker. Мы можем убедиться в этом с помощью команды docker images:


ubuntu@node01:/vagrant/AnsibleContainer/demo$ docker images
REPOSITORY  TAG  IMAGE ID  CREATED  SIZE
<none> <none>  e23c420b896a  43 minutes ago  576.3 MB
centos  3bee3060bfc8  4 weeks ago  192.6 MB
		

Отметим, что единственным остающимся в системе контейнером является базовый контейнер CentOS. Лн может быть удалён вручную, однако по умолчанию Ansible Container оставляет его в этой системе для ускорения процесса удаления и повторного построения проектов.

Выводы

На протяжении данной главы мы изучили некоторые фундаментальные понятия о том, как работает Ansible Container, как он применяет имеющиеся API Docker Compose, а также основные инструменты управления жизненным циклом, мвстроенные в Ansible Container, включая init, build, run и destroy. Получив твёрдые навыки и понимание того, что выполняют эти функции и как они работают, является основой тому, когда наступит время пойти далее и погрузиться глубже в более сложные проекты, которые мы создадим в Ansible Container. Хотя данный пример включён в наш официальный репозиторий Git данной книги, не чувствуйте себя связанным им и подвигайте приведённые примеры чтобы ещё поэкспериментировать с тем, как работает Ansible Container. В нашей следующей главе мы изучим как применять Ansible Container для существующих ролей, используя эти роли для создания повторно применяемых артифактов контейнеров.