Заметки о контейнеризации
Не о всей конечно, о кусочке.
Забавно, но я за карьеру мало взаимодействовал с контейнерами. Максимум — писал код, который в них работал. Контейнеры либо особо не требовались, либо готовились другими людьми.
В конце прошлого года решил обновить инфраструктуру Сказки, заодно посмотреть чего да как с контейнерами. Взял 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 — сильным преимуществом на рынке труда.
Читать далее
- Модная типизация в Python
- О проектировании миров
- Автоматический генератор квестов
- Как я делал и делал бы поддержку GDPR
- Сходил в гости в подкаст «Две Столицы — Уютный подкаст IT панков»
- Концепт-документ игры Space Opera Engine
- Миграции backend на практике
- GraphQL & Python
- Python & OpenAPI
- Концепт-документ «Сказки»