Глава 15. Заключительные мысли и последующие шаги

Поздравляем! Вы добрались до самого конца.

Давайте на минутку задумаемся о своём путешествии по данной книге. Мы начали с основ: что представляют из себя контейнеры и образы? Мы увидели как Docker обеспечивает упаковку (образы), доставку (автоматическое извлечение образов) и некое исполнение в реальном масштабе времени (контейнеры). Мы изучили как эти фундаментальные части предоставляют некий новый способ понимания относительно доставки программного обеспечения.

Остальная часть книги представляла расширенное руководство, проводящее вас через сам процесс создания, разработки и развёртывания полностью снабжённого приложения Rails в Docker. Мы выработали необходимое приложение Rails при помощи контейнера и мы создали некий персональный образ для исполнения своего приложения Rails, которое мы поступательно совершенствовали. Мы сделали введение в Docker Compose, воспользовались им для сборки приложения со множеством служб с применением Redis, базы данных и с отсоединённым томом для её данных.

Мы заставили свою настройку в Docker чудесно исполнять JavaScript, так как мы рассмотрели как работать и со стандартным ресурсом конвейера и с webpacker, новейшим подходом Rails для интеграции самых современных интерфейсов JavaScript. Мы также рассмотрели как проверять своё приложение, дойдя до того как выполнять всеобъемлющие проверки полагаясь на JavaScript в Chrome как в выхолощенном режиме, так и без него.

Однако, даже пребывая на землях Docker, ничто не бывает идеальным. Мы изучили пару раздражителей, которые могут встречаться вам при работе с Docker и мы смягчили их как только смогли.

Наконец, мы приступили к своему путешествию по промышленному применению. После быстрого тура по промышленному ландшафту для понимания основных требующихся нам возможностей и доступных инструментов, мы расширили своё приложение на его развёртывание в промышленной средею Мы изучили как доставлять свои образы Docker в некий Реестр, делая их доступными для доставки на прочие машины. Затме мы ввели Swarm и применили его для создания своего очень собственного подобного промышленному месту для представлений в своей локальной машине. Наконец, мы перевели его на следующий уровень и развернули наше приложение в Облаке, воспользовавшись кластером из трёх узлов и в DigitalOcean, и в AWS, а также мы применили это для демонстрации возможностей масштабирования Swarm.

Чертовски много удалось достичь (в особенности, принимая во внимание размер данной книги!) Я оставляю выбор поздравлений за это в ваших руках, хотя я и призываю вас подумать о чём- то большем чем простое похлопывание по спине.

Однако раз уж мы начали своё путешествие в промышленную среду, для получения полностью отказоустойчивой, защищённой и масштабируемой среды на основе конвейеров CI/ CD ещё предстоит много чего обсудить. Надеюсь, вы ощутили привкус той мощи, которую предлагает Docker и вы сможете разглядеть его потенциал и вдохновиться на дополнительное изучение.

Данная короткая глава представляет собой набор различных вещей, которые мы могли бы втиснуть куда- то ещё. Помимо того что вы выполните в своём разуме посев идей которые вы найдёте полезными, в этой главе вы также обнаружите некоторые указания на то как продолжать ваше путешествие с Docker.

Что мне следует изучать далее?

Отличный вопрос! Как несомненно вам известно, наше обучение никогда не завершается. Чем больше мы узнаём, тем больше мы осознаём как много нам ещё предстоит узнать. Такова жизнь - C’est la vie.

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

Наслаждайтесь!

Ограниченные ресурсы

По мере того как в своём кластере вы запускаете всё больше контейнеров, вы можете обнаружить потребность в принуждении определённых ресурсов ЦПУ и памяти к выделению определённым контейнерам. Как Swarm, так и Kubernetes предоставляют некий способ определения пределов дозволенных значений ресурсов для контейнеров.

Вот некий пример для Swarm, который означает для контейнера some-service возможность получать только 50 МБ памяти и одну десятую отдельного ядра ЦПУ.


​ 	services:
​ 	  some-service:
​ 	    deploy:
​ 	      resources:
​ 	        limits:
​ 	          cpus: ​"0.1"​
​ 	          memory: 50M
 	   

Автоматическое масштабирование

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

К сожалению, Swarm не предоставляет встроенного автоматического масштабирования, хотя и существуют возможности самостоятельного обустройства того, чтобы каждый узел в вашем кластере экспортировал измерения (при помощи чего- то подобного cadvisor в централизованную службу измерений, такую как Prometheus {Прим. пер.: подробнее в нашем переводе Prometheus: запуск и исполнение Брайана Брайзила}). Если это означает для вас слишком большую работу, вы можете рассмотреть один из вариантов решений с открытым исходным кодом, таким как Orbiter. Вашим окончательным вариантом является переключение на использование Kubernetes для оркестрации вашими контейнерами. Хотя он и более сложный, он и более упакован и имеет встроенное автоматическое масщтабирование. {Прим. пер.: подробнее в нашем переводе второго издания Полного руководства Kubernetes Джиджи Сэйфана.}

Нулевое время простоя, сине- зелёные развёртывания

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

Docker Swarm предоставляет некие возможности для осуществления такого типа накатывающих обновлений. Хотя это и было бы полезно, но Swarm в настоящее время не поддерживает родственность сеансов (session affinity), также именуемую клейкими сеансами (sticky sessions). Это означает, что после развёртывания обновлений версии вашего приложения все новые сеансы будут обрабатываться именно такой последней версией, но все имеющиеся сеансы пользователей будут по- прежнему обслуживаться имеющейся старой версией. Это важно по той причине, что старая версия может оказаться не совместимой с вашей обновлённой версией приложения, в частности, если изменились маршруты или схемы базы данных.

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

Безопасность

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

Хорошей отправной точкой является собственная документация Docker. Убедитесь что вы видели в своём меню всё разнообразие страниц в разделе "Security" - в нижней части этих страниц нет кнопки "Next". Ключевыми моментами являются: применение только доверенных образов, не запускать контейнеры от имени root, и блокировка сетевыми экранами до минимально необходимых вам портов, плюс более сложные способы блокирования вашей установки Docker.

Более современные архитектурные возможности

До сих пор мы применяли встроенные возможности балансировки нагрузки Swarm для распределения входящих запросов по различным контейнерам, лежащим в основе данной службы. Однако, по мере приобретения вами опыта вы можете пожелать воспользоваться более изощрёнными установками с вещами подобными HAProxy или NGINX для организации вами самостоятельного посредника и балансировки нагрузки.

Возможно не только это, но вы также можете запускать экземпляры HAProxy или NGINX в собственных контейнерах, собирая свои собственные образы из ваших файлов настроек. Вы можете также применять сетевые примитивы Docker для создания различных сетевых настроек. Это может позволить вам ограждать контейнеры друг от друга и контролировать какие именно контейнеры могут связываться с прочими контейнерами.

Управление ключами безопасности

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

Ключи безопасности Docker добавляются в Swarm с помощью команды docker secret create (имея первичной целью диспетчер Swarm). В качестве альтернативы вы можете определять ключи безопасности в своём файле развёртывания (в формате Compose).

Внутри хранящих их структур данных Swarm ключи безопасности шифруются (шифрование в покое), а также они требуют шифрования на всём пути по достижению своих контейнеров (шифрование при передаче). Они делаются доступными некому контейнеру через файловую систему в оперативной памяти, которая монтируется в /run/secrets/<secret_name>. Доступ к ключам безопасности контейнеры могут получать только в явном виде.

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

Перезапуск в случае отказа

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

Однако что относительно наших контейнеров приложения Rails в промышленной среде? Если вдруг что- то пошло не так, что вызвало крушение нашего приложения (к примеру, в случае утечки памяти), было бы чудесно если бы наши контейнеры сами были бы способны обрабатывать отказы более тщательным образом. Кто же захочет чтобы его будили посреди ночи чтобы исправить проблемы?

Docker делает возможным определять некую политику перезапуска, которая сообщает как следует себя вести в случае прекращения работы контейнера. Устанавливая её в on-failure:


​ 	deploy:
​ 	  restart_policy:
​ 	    condition: on-failure
 	   

Swarm теперь автоматически перезапустит наше приложение Rails в случае его падения. Вот хорошая статья с дополнительными подробностями.

Многоэтапные сборки

Большие образы Docker являются и более медленными для их вытаскивания и активной доставки. По мере того как вы приобретёте опыт работы с Docker, вы пожелаете находить пути создания своих образов. Начиная с версии 17.05 Docker обладает некой функциональностью с названием многоэтапной сборки - она позовляет вам применять множество операторов FROM в неком отдельном Dockerfile. Всякий новый FROM рассматривается как некий новый этап и начинается как некий новый образ заново. Тем не менее, имеющаяся инструкция COPY была расширена чтобы позволить вам копировать файлы с более ранних этапов.

Наиболее очевидный вариант применения - это когда вам требуется большое число инструментов разработки, которые создают окончательное произведение искусства. Задумайтесь о генераторе ститического сайта, такого как Jekyll или Middleman - вам требуются разнообразные инструменты для разработки и выработки сайта, но как только сами статические файлы сгенерированы, они являются единственным что требуется для исполнения самого сайта. Многоэтапные сборки позволяют вам создать некий начальный этап, который сгенерирует требуемый сайт и некий обособленный, окончательный этап, который скопирует эти файлы в чистый образ веб- сервера. То же самое относится и к языкам компиляции, таким как Go, для которых обычно единственное что вам требуется включить в свой окончательный образ, это сам скомпилированный двоичный файл.

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

Статистические данные Docker

Зачастую, в особенности при промышленном применении, полезно иметь некий быстрый вариант поиска измерений используемых ресурсов. Документация Docker предоставляет достаточно полезную информацию относительно различных измерений, которые вы можете выполнять.

Один из самых простых и наиболее полезных вариантов состоит в команде docker stats. Он предоставляет различные измерения, в том числе ЦПУ, использование памяти и сетевые ввод/ вывод, которые могут быть полезными при мониторинге или отладке контейнеров при промышленном применении.

Вот некий образец из документации Docker:


​ 	​$ ​​docker​​ ​​stats​​ ​​redis1​​ ​​redis2​
​ 	
​ 	CONTAINER  CPU %  MEM USAGE / LIMIT  MEM %  NET I/O           BLOCK I/O
​ 	redis1     0.07%  796 KB / 64 MB     1.21%  788 B / 648 B     ...
​ 	redis2     0.07%  2.746 MB / 64 MB   4.29%  1.266 KB / 648 B  ...
		

Совместное использование файлов настройки между файлами Compose

На данный момент мы сопровождаем два файла в формате Compose: docker-compose.yml и docker-compose.prod.yml. По мере разработки своего приложения вы можете обнаружить достаточно большое число дублирований между файлами Compose в различных средах.

Compose предоставляет некий механизм, который позволяет вам выделять имеющиеся общие места. Он делает это позволяя вам определять множество файлов Compose:


	docker-compose -f <file1> -f <file2> ... -f <fileN> up -d
 	   

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

Как и всегда, сохранение или устранение дублирования имеют собственные компромиссы. Положительным моментом является то, что извлечение дублирования в общий файл делает более ясными имеющиеся отличия в разных окружениях - именно эти части вам приходится указывать для той среды, которая выходит за рамки общей базы. Это также (потенциально) ускоряет (слегка) обновление конфигураций для обоих наборов служб. С другой стороны, вам требуется собрать воедино определения из нескольких файлов чтобы разобраться со своим приложением в целом. Как скорее всего вы можете сказать, это именно тот случай, когда я полагаю что преимущества дублирования перевешивают наши инстинкты программистов чтобы оставлять всё в ЗАСУШЕННОМ состоянии.

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

Устойчивость базы данных

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

Вот ряд различных способов осуществления этого:

  • Зависимое от платформы: Некоторые платформы контейнеров позволяют вам составлять расписание контейнеров (к примеру, выполняющиеся по расписанию задачи Amazon ECS). Использование таких планировщиков позволяет вам запускать контейнеры для резервного копирования имеющейся базы данных через регулярные промежутки времени. Кроме того, платформы могут предлагать возможности резервного копирования; например, тома Amazon EBS (Amazon Elastic Block Store) предоставляют автоматические возможности инкрементальных моментальных снимков. Это может быть подходом сопровождения резервных копий с более низкими порогами и при этом более надёжным.

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

  • Применение инструментов сторонних разработчиков: Например, Barman для Postgres.

Контейнеры в автопилоте

Существует более обширный подход, который начинает проявляться с названием автопилота. Он включает в себя выполнение ряда стандартных оперативных задач (таких как масштабирование и отказоустойчивость) непосредственно в самих ваших контейнерах.

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

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

Репликация базы данных и высокая доступность

Для репликации вашей базы данных обычно требуется полагаться на встроенные возможности вашей базы данных (вместо того чтобы применять слишком наивный подход использования разделяемой файловой системы).

Postgres предоставляет множество вариантов для кластеризации. Вместо того чтобы изобретать колесо, однако, вы можете воспользоваться той работой, которую сделали за вас прочие, например:

Вы также можете найти и для иных баз данных работы, которые были осуществлены для из кластеризации и репликации.