§ О контейнерах как о будущем инфраструктуры.

Я если честно давно вынашивал эту статью, но всё никак не доходили руки до её написания. Положа руку на сердце я считаю что эра виртуализации постепенно подходит к своему логическому завершению, и в ближайшем будущем на её долю останется только случаи в которых нельзя никак иначе, а именно виртуализация платформ с принципиально разными ядрами, как например Windows на Linux (или наоборот), а так же эмуляция других аппаратных архитектур типа ARM или MIPS на x86_64 и тому подобное.

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

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

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

Здесь, я думаю, важно сделать некоторое отступление и немного пояснить что же такое контейнер и с чем его едят. На самом деле контейнеров (грубо округлив) бывает два типа. Первый - это Контейнер Приложений (Application Container), а второй - это контейнер с операционной системой (OS Container). Основное отличие между ними - это то, что в первом случае в контейнере выполняется непосредственно одно приложение, а во втором случае запускается процесс init, который инициализирует сервисы. Если проводить сравнение, то окажется что Application Container (AC) на самом деле может спокойно обойтись без различных пакетов и библиотек, без который OS Container (OSC) обойтись не может. Например bash, apt, yum или pam. Если данные пакеты не нужны непосредственно приложению, то без них AC вполне будет работать, в то время как OSC будет иметь ряд серьёзных проблем.

Классическими примерами AC являются Docker, Rocket, LMCTFY и LXC, классическими же примерами OSC являются LXC, Systemd-nspawn, ну и с определёнными оговорками OpenVZ. Да я не оговорился, LXC предоставляет возможность создавать как AC так и OSC, чем сильно выделяется на фоне остальных решений.

Но давайте вернёмся к классической инфраструктуре. Её уже обязательными аттрибутами являются следующие компоненты: 

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

Система управления конфигурацией. Современная инфраструктура уже немыслема без системы управления конфигурацией типа Puppet или Chef. Сейчас ещё набирает популярность Ansible на волне покупки его компанией RedHat. Данный класс систем возник как следствие повсеместного внедрения виртуализации. Если раньше компания имевшая 10 серверов настраивала только 10 конфигураций, то с появлением виртуализации и под действием догмы "один сервис - одна система" количество систем расположенных на 10 серверах выросло на порядки. Естественно потребовалось управлять конфигурацией такого количества серверов. Puppet и Chef прошли много этапов взросления, включая разделение кода и конфигурации (первые версии и Chef и Puppet расчитывались с тем что конфигурация машины должна содржаться в самом рецепте\модуле, и только позже пришли к мысле о Hiera и Data Bags). Но когда количество виртуальных машин подходит к тысячам, такой подход приносит ещё больше проблем, так как теперь любым вашим изменением в коде модуля вы можете просто положить на обе лопатки всю инфраструктуру. А из-за зоопарка систем который является естественной реакцией инфраструктуры на эволюцию, управление конфигурациями становится огромным адом. Ansible немного упрощает данный подход, в первую очередь тем что не требует ни установки агентов, ни возьни с проверкой подлинности хостов, но при этом и существенно ничего не меняет, и не решает ключевых проблем. В зрелой инфраструктуре количетсво модулей или рецептов заходит за сотню и к каждому хосту применяется не меньше дюжины а то и две дюжины этих модулей. А теперь представьте что производитель используемого вами дистрибутива выпустил новую версию, и вам нужно переписать эти модули под новую систему, да так что бы не поломать совместимость со старыми системами и ничего не уронить в процессе... Нувыпонели...

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

Система управления доступом. Тут на самом деле всё достаточно очевидно. У вас много виртуалок, и сколько-то пользователей, и управлять доступом необходимо. При этом чем больше хостов и пользователей у вас в инфраструктуре, тем, как правило, сложнее настройка прав доступа всех этих самых пользователей и хостов. Есть неплохие системы типа FreeIPA, которые поддерживают даже Host Based Access Control, однако нужно признать что управление доступом - это всегда геморрой.

Система контроля обновлений. Обновления - это всегда больная тема для многих инфраструктур. Не умрёт ли чего после очередного обновления, останется ли система работоспособной? Насколько критична исправленная уязвимость, что бы обновлять сервер прямо сейчас? Конечно система резервного копирования позволит в случае чего откатиться назад, но никто не останется счастлив от того факта что во-первых был простой, а во-вторых по факту никаких изменений сделано не было.

Система хранения данных. Тут на самом деле огромная плеяда различных решений, но в основе любой виртуализации лежит идея что некоторый файл мы представляем гостевой машине как блочное устройство, со всеми вытекающими. Да, часть данных мы можем монтировать непосредственно внутри виртуальной машины через CIFS, NFS, iSCSI итд, но во-первых никакая СХД не будет счастлива от большого количества подключений к ней, а во-вторых без блочного устройства виртуальная машина все равно работать как правило не будет.

И вот мы переходим к интересной части моей статьи: контейнеры позволяют существенно, нет СУЩЕСТВЕННО, упростить данную систему.

Системой контейнеров так-же нужно управлять. Однако для того-же Docker созданно огромное количетсво инструментов позволяющих не только управлять контейнерами, но и создавать целые готовые инфраструктуры из контейнеров, описание которых хранится в yaml-файле. Фактически в одну команду вы можете установить не просто контейнер, а всю инфраструктуру, причём с нуля и очень быстро.

Для контейнеров не нужна сложная система управления конфигурацией. Для того-же докера вся конфигурация хранится в Dockerfile на основании которого и собирается собственно образ контейнера. Когда образ собран, никаких дополнительных конфигурирований ему не требуется. Если необходимо изменить конфигурацию - изменения вносятся в Dockerfile, после чего пересобирается образ и он снова готов к работе без дополнительного конфигурирования.

Примерно так же обстоят дела с обновлением. Для обновления контейнера нам нужно просто собрать его снова используя в качестве базы обновлённую версию выбранного дистрибутива. Если необходимо обновить версию ПО внутри контейнера то сделать это тоже обычно несложно. Заменить старый контейнер на новый - дело считанных минут.

Если мы используем AC то никакой системы "доступа" к ним быть не может. В таких контейнерах нет никаких SSH-серверов, к которому можно было бы обратиться и контролировать доступ там как правило не нужно. Тут правда может возникнуть другая проблема, в случае необходимости инженер будет иметь доступ ко всей корневой системе контейнеризации, но как правило таких прав не требуется, и всё тестирование вполне может производиться на домашней машине, при этом инженер будет чётко понимать что контейнер имеет абсолютно идентичную конфигурацию с боевым. Это же кстати помогает и в тестировании (QA). Вместо тестирования отдельного бинарника в отдельно взятой среде, используя контейнер мы можем тестировать всё в одинаковых условиях, и иметь достаточно детальное представление о работе не просто системы, а системы в конкретной среде.

Говоря о доступе не стоит забывать о OS Containers, в таком случае мы естественно не уменьшаем необходимость в контроле доступа, но как правило OSC используются для разработки и тестирования, в то время как для production использются Application Containers, что серьёзно сокращает количетсво узлов доступ к которым необходимо контролировать, и как правило особо тщательно контролировать доступ мы хотим именно к production, а не тестовым стендам.

Мы постепенно приходим к системе мониторинга. Контенеризация превращает тезис "один сервер - один сервис" в аттавизм. AС по сути является сервисом, и по сути работающем на одном хосте с другими сервисами. Однако в данном случае работа различных сервисов на одном хосте не может ни помешать их работе (по крайней мере не более чем при использовании виртуализации) ни привести к коллизиям и несовместимостям, так как каждый из контейнеров имеет при себе всё что ему нужно и ровно той версии которой нужно. Более того каждый контейнер может иметь свой собственный сетевой интерфейс и свой собственный IP адррес, что серьёзно упрощает разделение ресурсов между контейнерами в том числе и сетевых. Лично я не вижу веских причин для мониторинга каждого отдельного контейнера. По сути каждый контейнер - это не более чем приложение запущенное на хосте. Да нам необходимо мониторить параметры самого хоста (как это и происходило при виртуализации), но что конкретно мы хотим мониторить внутри самого контейнера? Да мы можем захотеть видеть определённую статистику запущенного процесса. Например количество запросов к БД, но мы можем напрочь игнорировать такие параметры как использование CPU, памяти, swap и свободного места на диске. Эти данные мы итак получим от самого хоста на котором запущен контейнер. Таким образом мы снижаем нагрузку на систему мониторинга в десятки раз.

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

Более того OSC может работать поверх OverlayFS или AUFS, что позволяет очень гибко настраивать, как сам контейнер, так и обновлять его, и даже дедуплицировать повторяющиеся данные. Да существуют системы дедупликации на некоторых файловых системах типа ZFS, но во-первых системы дедупликации на файловой системе требуют много памяти, во-вторых они есть на очень ограниченном количестве файловых систем, а в третьих работают значительно лучше если данные представленны не в виде файла блочного устройства. OverlayFS и AUFS из коробки поддерживаются практически на всех файловых системах как Docker'ом так и LXC.

Ещё одним интересным преимуществом контейнеров является скорость их старта. Если классическая виртуалка с системой её автоматического конфигурирования занимает до 20 минут, пока система загрузится по сети, пока на ней будет запущен kickstart-скрипт установки, пока на системе будет установленн puppet или chef, и пока этот puppet\chef произведёт конфигурирование может пройти до 20 минут. Контейнер же запускается за секунды, при условии что ничего не нужно качать из интернет и у нас есть готовый образ.

Подводя небольшой итог, хочу отметить что системы контейнеризации имеют большое количество приемуществ перед ставшими уже классическими виртуальными машинами. И я даже не касаюсь скорости работы контейнеров, так как это все ещё тема для холиваров. Кто говорит что виртуализация за счёт большего количества кешей достигает чуть больших показателей I\O, кто говорит что контейнеризация быстрее так как использует меньшее количество прослоек между железом и софтом. Основным преимуществом контейнеризации на мой взгляд является серьёзное упрощение инфраструктуры, с одновременным увеличением её гибкости. Создание контейнера занимает секунды, в отличии от десятков минут при использовании виртуальных машин. Если верить докладам Google, то они используют контейнеры чуть менее чем везде. Каждый раз когда вы открываете сессию с любым из серверов Google - специально для вас создаётся контейнер, который обеспечивает все ваши запросы. Этот же контейнер расшифровывает для вас данные хранящиеся в Google, и как только ваша сессия прекратилась - контейнер бесследно исчезает. Эфимерность Application Containers так же заставляет инженеров более тщательно заниматься разделением данных от процедур, что всегда хорошо сказывается на сохранности этих самых данных, и качестве логики процедур.


comments powered by Disqus