Использование универсальных Helm-чартов в проектах
Как nxs-universal-chart упрощает работу с Kubernetes
Если у вас 1–2 проекта, а в каждом до 5 приложений, и вы раз в полгода создаёте новый чарт в Helm, можно просто копировать старые чарты или создавать новые с нуля. Но что делать, если у вас 5 проектов, в каждом по 20 микросервисов, и каждые две недели разработчики выкатывают новые и выводят из эксплуатации старые? При этом постоянно приходится что-то обновлять. А если проектов 20 или 50?
В таком случае приходится снова и снова копировать одни и те же паттерны и шаблоны с общим костяком.
Мы столкнулись с такой ситуацией на одном из проектов, где нужно было запустить 15 микросервисов, и решили создать единый чарт, через который можно было бы запускать практически всё.
В этой статье расскажем о практическом применении универсального Helm-чарта НИКСИС. На теории останавливаться подробно не будем — сосредоточимся на практическом примере.
Универсальный Helm-чарт
Когда мы запустили проект с 15 микросервисами и решили использовать единый чарт, результат оказался удачным. Мы экономили время на подготовке к релизу, а также накапливали и переиспользовали в новых проектах решения, которые хорошо себя показали.
Это позволяло выполнять релизы практически любой сложности и конфигурации и быстро подготавливать микросервисы к развёртыванию в Kubernetes.
Основные преимущества подхода:
- экономия времени при подготовке к релизам;
- повторное использование готовых решений;
- возможность быстро подготовить микросервисы к развёртыванию в Kubernetes;
- хранение чарта отдельно от кода;
- возможность размещать чарт в registry Nixys, чтобы потребители сразу получали новые версии и возможности;
- возможность добавлять новые шаблоны и работать с их конструкциями непосредственно внутри values-файла.
В примере через опцию extraDeploy можно добавить шаблонный NetworkPolicy. При этом ограничений на тип добавляемых манифестов нет: таким способом можно подключать кастомные ресурсы, необходимые приложению, и практически любые другие Kubernetes-манифесты.
У такого подхода есть и особенности. Из-за гибкости шаблонов и values-файлов их структура достаточно сложная. Поэтому для работы с универсальным чартом требуется хорошее понимание Helm и навыки создания сложных Helm-чартов.
Кроме того, поскольку в values используются конструкции шаблонов, существуют ограничения при работе с Go templates. В частности, это ограничения на длину строки конструкции шаблона и использование символов, отличных от букв и цифр. Из-за этого иногда возникают ошибки, которые приходится находить и исправлять экспериментальным путём.
Несмотря на эти ограничения, преимуществ у подхода больше. Рассмотрим работу универсального чарта на практике.
Использование универсального чарта на практике
Откроем репозиторий с универсальным Helm-чартом. Там находятся сам проект и документация.
В качестве примера будем разворачивать проект на Python — связку Django и Celery. Django будет использоваться в качестве web-приложения, а Celery — в качестве асинхронного воркера для обработки задач.
Проект максимально простой, но при этом хорошо показывает, как работает асинхронное приложение.
В окружении уже развернуты Redis и PostgreSQL. Предполагаем, что базы данных уже доступны и с ними нет проблем.
Для полноты примера они также могут быть развернуты с помощью Helm. Соответствующие values-файлы находятся в репозитории проекта, в каталоге k8s.
Посмотрим, сколько времени займёт развёртывание такого проекта с использованием универсального чарта.
Для проверки работы приложения заранее подготовлены Kubernetes-манифесты и выполнено ручное развёртывание.
Создание values-файла
Начнём с создания пустого values-файла и его заполнения.
Если возникают вопросы по структуре, можно обращаться к документации и существующим манифестам.
Сначала укажем дефолтный image и дефолтный pull policy. Образ заранее собран и размещён в Docker Hub registry. Будем использовать лёгкий образ, поэтому defaultImagePullPolicy поменяем на Always.
Подготовка переменных окружения
У чарта есть две встроенные сущности для работы с переменными окружения:
- Из envs генерируется ConfigMap. В качестве префикса используется название релиза приложения, которое мы запускаем, а постфикс — envs.
- В secretEnvs создаётся Secret. В качестве префикса используется название релиза, а постфикс — secretEnvs.
Практически любой проект использует переменные окружения, поэтому такой подход позволяет значительно упростить их подключение.
Для CELERY_BROKER из уже проверенного манифеста забираем Redis. POSTGRES_USER у нас дефолтный, а пароль сгенерирован. База будет называться Demo, порт используем стандартный.
Одна из возможностей работы с секретами заключается в том, что в чарте секрет можно указать как в открытом виде, так и сразу в Base64-кодировке, добавив соответствующий префикс. Это удобно, особенно при подготовке конфигурации.
Поскольку приложение работает на Django, а Django умеет обрабатывать бизнес-логику, но не предназначен для отдачи статики, будем использовать nginx. Он будет отдавать статику и проксировать запросы к Django.
Для этого создадим ConfigMap, в который положим конфигурацию nginx. В ней будет указано, что сервер на 80-м порту слушает все запросы, отдаёт статику из нужного каталога и проксирует остальные запросы на Django-сервер. Этот конфиг будем монтировать внутрь контейнера с nginx.
Настройка приложения
Django использует миграции, которые будем запускать с помощью hook. Для этого потребуется само приложение и дефолтный контейнер. У него минимум настроек и параметров: название контейнера и команды, с помощью которых выполняется миграция. Добавим переменную окружения для подключения к базе данных.
Чтобы сразу подключить все переменные окружения к контейнеру, воспользуемся envConfigMap и envSecret. В этот параметр передаётся список сущностей, создаваемых чартом и содержащих переменные окружения.
По умолчанию всегда создаются ConfigMap-envs и секрет secret-envs, где префиксом является название релиза.
Если отдельным блоком добавить секреты или ConfigMap, в которых также содержатся переменные окружения, их тоже можно подключить с помощью этих опций.
На этом настройка hook для миграции закончена.
Настройка деплойментов
Деплойментов всего три:
- web-приложение;
- celery-воркер;
- небольшой дашборд для Celery Flower, который позволяет видеть работающие воркеры, количество выполненных ими задач и их текущий статус.
Настроим каждый из них:
1. Web-приложение
Так как мы работаем с несколькими деплойментами в рамках одного приложения, потребуется дополнительный селектор лейблов, чтобы корректно работала маршрутизация. Укажем для каждого приложения, к какому компоненту оно относится, и настроим контейнеры.
Запустим Django. В используемом образе Django стартует по умолчанию, поэтому дополнительно указывать команду не нужно. При этом потребуются переменные окружения, которые подключим с помощью опций envConfigMap и envSecret.
Вторым контейнером будет nginx, который будет проксировать запросы и отдавать статику.тДля запуска nginx укажем образ и тег. В качестве образа используем Alpine, поскольку он небольшой и лёгкий. Для nginx не потребуются переменные окружения, но нужны конфигурация и статика.
Подключим volumeMounts с конфигурацией. Конфиг будет подключаться в стандартный путь, при этом необходимо заменить один файл. Статику будем отдавать из каталога, указанного в конфигурации nginx.
Также нужно объявить volumes, которые будем использовать. В чарте есть стандартные volumes, но через блок volumes можно добавить специальную сущность. Это типизированный volume, который позволяет подключить конкретную сущность — ConfigMap, Secret или PersistentVolumeClaim — к контейнеру.
Если требуется подключить нативный Kubernetes volume, для этого есть опция extraVolumes.
В нашем случае создадим emptyDir. При запуске статика будет распаковываться в этот volume, а после завершения работы удаляться.
Volume с конфигурацией nginx подключим из ConfigMap.
Из-за использования subPath и отличия путей потребуется указать items. Внутри ConfigMap находится файл nginx.conf, но доступен он должен быть как default.conf.
Осталось добавить init-контейнер, в котором будет распаковываться статика. Здесь выполняется команда Python, которую также можно взять из подготовленного YAML-файла.
После этого всё необходимое для запуска основного web-приложения готово.
2. Celery-воркер
Укажем, что хотим получить на старте несколько реплик. Контейнер будет похож на тот, который использовали ранее, но команда будет отличаться. Назовём контейнер воркером, добавим команду и переменные окружения, которые используются и в основном приложении.
В зависимости от проекта может потребоваться работа с volumes. Например, статику можно разместить в PersistentVolume и добавлять туда пользовательские файлы. В нашем примере воркеру для работы больше ничего не требуется.
3. Dash
Dash также будет состоять из одного контейнера, в котором запустим Flower. Для работы ему нужна всего одна переменная окружения — broker. Подключим её с помощью envFrom и ConfigMap или Secret. Укажем имя envs и перечислим, какие переменные окружения должны быть доступны контейнеру.
В данном случае это broker, но в нашем ConfigMap она называется иначе. Поэтому укажем, по какому имени искать нужную переменную.
Деплоймент настроен. Теперь нужно добавить Service и Ingress, чтобы получить доступ к web-приложению извне.
Service и Ingress
Для настройки сервисов обратимся к дефолтному values-файлу и посмотрим, как добавить необходимые сущности.
Нам потребуются два сервиса:
- Web, который будет работать на 80-м порту и обращаться к tier: Web;
- Dash, который будет обращаться к дашборду Flower.
Для Dash укажем порт 5555, а extraSelectorLabels для этого workload — dash.
Осталось настроить Ingress. В качестве ключа в названии используем доменное имя. Понадобится одна аннотация. В кластере, с которым мы работаем, уже развёрнут cert-manager и есть cluster-issuer.
Настроим сервисы для Ingress. Дефолтный корневой путь будет указывать на сервис web, а servicePort — на http. Также добавим дашборд, доступный по пути /flower. Он будет обращаться к serviceName: dash, а servicePort у него будет 5555.
Важно учитывать версию Kubernetes и Ingress-контроллера. В используемом кластере Kubernetes и Ingress-контроллер поддерживают Ingress Class, поэтому необходимо указать IngressClassName. Это позволит избежать ошибки при доставке манифестов в Kubernetes.
Тестирование и отладка
Для проверки корректности работы укажем файл, из которого будем подключать values, и посмотрим, корректно ли универсальный чарт генерирует Kubernetes-манифесты.
При первой проверке получили ошибку и внесли необходимые правки.
Чтобы убедиться, что всё работает, заменим команду на install и запустим развёртывание.
Сначала не стартует миграция, потому что потерялись секреты. Причина оказалась в ручном вводе: вместо secret-envs было указано secrets-env. После исправления всё заработало, и начали создаваться контейнеры приложения.
Теперь перейдём в web-приложение, введём название домена и увидим, что не работает статика. Причина в том, что при запуске init-контейнера мы не подключили volume, в который должна распаковываться статика. Исправляем конфигурацию.
После этого проверим Flower. Дашборд доступен, мы видим воркеры, но UI не работает. Причиной снова стала ошибка ручного ввода. Мы не видели init-контейнер, потому что его не было в конфигурации. Блок называется initContainer, а чарт ожидает initContainers. После исправления проблема была устранена.
Теперь UI появился, а все основные компоненты приложения работают.
Что даёт использование универсального Helm-чарта
Универсальный Helm-чарт позволяет сократить время подготовки приложений к развёртыванию, переиспользовать уже проверенные решения и стандартизировать работу с Kubernetes.
При большом количестве проектов и микросервисов такой подход позволяет не создавать отдельный набор однотипных Helm-чартов для каждого приложения, а описывать необходимые параметры через values.
При этом гибкость чарта позволяет добавлять дополнительные Kubernetes-ресурсы, настраивать переменные окружения, секреты, volumes, init-контейнеры, сервисы, Ingress и другие компоненты.
Главное — внимательно работать с документацией и структурой values-файла. Это позволяет избежать значительной части ошибок при подготовке конфигурации и быстрее запускать новые приложения.
Оставьте заявку, чтобы обсудить проект
Спасибо за обращение
Мы свяжемся с вами в ближайшее время.