Эссе о разработке игр, мышлении и книгах

Заметки о контейнеризации

Не о всей конечно, о кусочке.

Забавно, но я за карьеру мало взаимодействовал с контейнерами. Максимум — писал код, который в них работал. Контейнеры либо особо не требовались, либо готовились другими людьми.

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

Что и зачем делал

До рефакторинга, сервисы Сказки крутились на голом железе. Включая базу, кэш, очереди, веб-сервер и прочее. Ставилось и конфигурировалось, само-собой, это автоматически — скриптами Ansible.

Решение неплохо работало, особенно по сравнению с ручной настройкой или чем-нибудь более древним, чем ansible.

Но неудобства при таком подходе к инфраструктуре всё равно остаются:

  • Обновление ОС может внезапно что-нибудь сломать. Особенно если это изменение мажорной версии.
  • Всегда есть риск несовместимости новых версий софта. Поэтому я их особо не менял — мало времени на тестирование.
  • Сторонние роли ansible, которые я использовал, не успевали за обновлением софта.
  • Со временем на сервере накапливается мусор, из-за постоянных обновлений и моих ошибок.  Хочется иметь простой и лёгкий способв сбросить всё и поднять чистую версию. Но поскольку зависимости непрозрачны, всегда остаётся риск что–нибудь сломать.

В итоге инфраструктура устаревала и требовала больше времени на поддержку.

Мне же хотелось больше стабильности и контроля.

Контейнеры обещали решать эти проблемы хотя бы частично.

Поэтому я решил оставить на Ansible только базовую конфигурацию ОС и установку Docker. Остальное вынес в контейнеры, управляемые через docker compose —  более сложной штуки не требовалось. По крайней мере я так думал.

По факту, инфраструктуру я переделал, сейчас она заканчивает испытания на тестовом сервере. До весны планирую применить изменения на проде.

Заметки

Самый главный вывод — контейнеры рулят, за ними будущее и всё такое.

Но есть нюансы :-)

Дисбаланс компетентности

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

Интернеты завалены детскими уроками и нубскими вопросами.

Вы с лёгкостью найдёте объяснения как правильно писать команды в Dockerfile, но потратите часы в попытке разобраться как идеалогически верно конфигурировать NGINX для доступа к сервисам по доменным именам вместо IP адресов.

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

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

Каждый третий считает, что Docker — это легко и просто.

Похоже большинство людей взамиодействуют с Docker на уровне «мне дали контейнер, я с ним работаю» или «я собрал из палок контейнер — я молодец». Мало кто понимает, что использование контейнеризации, даже без всяких Kubernetes, требует большой работы, изменения процессов  и так далее.

Сырость инфраструктуры

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

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

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

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

Я бы даже сказал, что многие новые и относительно новые утилиты/библиотеки создаются по инерции без учёта использования в контейнерах: мало у кого есть достаточно опыта, чтобы транслировать best practices.

В итоге за пару недель я подписался на большее количество issues, чем за последние год-два.

Мало инструментов для средних проектов

Как следствие предыдущего пункта.

Можно легко поднять простой бложик.

Можно собрать команду и/или деньги и относительно удобно деплоить большие проекты.

Но если вам нужно дёшево и комфортно делать средний проект, то вы будете страдать либо от слишком простых инструментов либо от слишком сложных.

В частности я попал в эту ловушку:

  • Подымать кластерные штуки для одного сервера — это ж явный перебор.
  • Но и docker compose уже начинает поскрипывать, когда настраиваешь в нём тридцарик сервисов.

В итоге Сказка управляется через docker compose, но для этого пришлось попотеть больше, чем я рассчитывал. Может быть потом расскажу об этом отдельно.

Всё стремится перетекать в контейнеры

Как только у вас появился первый контейнер, все остальные штуки будут пытаться перетечь в контейнеры.

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

Внешние штуки приходится как-то соединять с контейнерными и «проще всего» сделать это перенестя штуки в контейнеры.

Поэтому всякие PosrgreSQL, NGINX, Redis, RabbitMQ, сбор метрик и прочее начинает хотеться тащить в контейнеры, хотя не всегда это оправдано.

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

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

Контейнеры ориентированы на stateless логику

Тут я буду говорить про state-less/ful в широком смысле: не только как хранение данных сессии между обращениями, но и как просто хранение данных в рантайме с большой задержкой на синхронизацию с базой.

В большинстве проектов только stateless логика и есть: принял запрос, сделал что-то с базой, отдал данные, забыл обо всём — все счастливы. Видимо поэтому на этот аспект редко обращают внимание.

Для stateless логики контейнеры, конечно, особенно хороши — мы поднимаем и гасим их как нам удобно и когда удобно.

В случае stateful логики такие фокусы уже не проходят — если мы погасим контейнер не вовремя, можем сильно повредить данные, с которыми тот работал. Сервисы логики Сказки, например, останавливаются долго.

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

В целом, контейнеризация оказывает заметное эволюционное давление на архитектуру в сторону stateless логики.

Итого

Контейнеры — однозначно хорошая и полезная штука, но использовать их пока не очень просто.

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

Поэтому в ближайшем будущем контейнеры будут становится проще и полезнее.

Использование контейнеров будет оказывать эволюционное давление на вашу архитектуру в сторону большей контейнеризации и мгноменной заменимости сущностей в рантайме.

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