Хабрахабр

[Перевод] Пакетный менеджер для Kubernetes — Helm: прошлое, настоящее, будущее

Прим. перев.: Этой статьёй мы открываем цикл публикаций про пакетный менеджер для Kubernetes, который активно используем в повседневной работе, — Helm. Оригинальным автором материала является Matt Butcher — один из основателей проекта Helm, работающий над Open Source-проектами в Microsoft и написавший 8 технических книг (в частности, «Go in Practice»). Однако статья дополнена нашими (местами — обширными) комментариями, а в скором времени будет ещё больше расширена новыми заметками по Helm более практической направленности.

CNCF становится родительской организацией для лучших в своём роде cloud native-инструментов с открытым исходным кодом. В июне Helm перешёл из статуса ведущего проекта Kubernetes в фонд Cloud Native Computing Foundation (CNCF). И наш первый значимый проект под покровительством CNCF по-настоящему масштабный: мы создаём Helm 3. Поэтому большая честь для Helm стать частью такого фонда.

Краткая история Helm

Helm изначально появился как Open Source-проект компании Deis. Его моделировали по подобию Homebrew (менеджер пакетов для macOS — прим. перев.), а стоящей перед Helm 1 задачей была облегчённая возможность для пользователей быстро установить свои первые рабочие нагрузки на Kubernetes. Официальный анонс Helm состоялся на первой конференции KubeCon San Francisco в 2015 году.

перев.: С первой версии, которая называлась dm (Deployment Manager), для описания ресурсов Kubernetes был выбран синтаксис YAML, а при написании конфигураций поддерживались шаблоны Jinja и Python-скрипты. Прим.

Шаблон простого веб-приложения мог выглядеть следующим образом:

YAML

resources:
- name: frontend type: github.com/kubernetes/application-dm-templates/common/replicatedservice:v1 properties: service_port: 80 container_port: 80 external_service: true replicas: 3 image: gcr.io/google_containers/example-guestbook-php-redis:v3
- name: redis type: github.com/kubernetes/application-dm-templates/storage/redis:v1 properties: null

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

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

Следующая диаграмма иллюстрирует отношения между ними: Архитектура Helm 1 состоит из трёх компонентов.

  1. Manager выполняет функцию веб-сервера (общение с клиентами происходит по REST API), управляет развёртываниями в кластере Kubernetes и используется как хранилище данных.
  2. Компонент expandybird приводит пользовательские конфигурации к плоской форме, т.е. применяет Jinja-шаблоны и выполняет Python-скрипты.
  3. Получив плоскую конфигурацию, resourcifier выполняет необходимые вызовы kubectl и возвращает в manager статус и сообщения об ошибках, если таковые имеются.

Для понимания возможностей первой версии Helm приведу справку по команде dm:

Вывод help от dm

Usage: ./dm [<flags>] <command> [(<template-name> | <deployment-name> | (<configuration> [<import1>...<importN>]))]
Commands:
expand Expands the supplied configuration(s) deploy Deploys the named template or the supplied configuration(s)
list Lists the deployments in the cluster
get Retrieves the supplied deployment
manifest Lists manifests for deployment or retrieves the supplied manifest in the form (deployment[/manifest])
delete Deletes the supplied deployment
update Updates a deployment using the supplied configuration(s)
deployed-types Lists the types deployed in the cluster
deployed-instances Lists the instances of the named type deployed in the cluster
templates Lists the templates in a given template registry (specified with --registry)
registries Lists the registries available
describe Describes the named template in a given template registry
getcredential Gets the named credential used by a registry
setcredential Sets a credential used by a registry
createregistry Creates a registry that holds charts Flags: -apitoken string Github api token that overrides GITHUB_API_TOKEN environment variable -binary string Path to template expansion binary (default "../expandybird/expansion/expansion.py") -httptest.serve string if non-empty, httptest.NewServer serves on this address and blocks -name string Name of deployment, used for deploy and update commands (defaults to template name) -password string Github password that overrides GITHUB_PASSWORD environment variable -properties string Properties to use when deploying a template (e.g., --properties k1=v1,k2=v2) -regex string Regular expression to filter the templates listed in a template registry -registry string Registry name (default "application-dm-templates") -registryfile string File containing registry specification -service string URL for deployment manager (default "http://localhost:8001/api/v1/proxy/namespaces/dm/services/manager-service:manager") -serviceaccount string Service account file containing JWT token -stdin Reads a configuration from the standard input -timeout int Time in seconds to wait for response (default 20) -username string Github user name that overrides GITHUB_USERNAME environment variable --stdin requires a file name and either the file contents or a tar archive containing the named file. a tar archive may include any additional files referenced directly or indirectly by the named file.

А теперь вернёмся к оригинальному тексту об истории Helm…

Целью было сохранить простоту использования Helm, добавив в него следующее: Несколькими месяцами позже мы объединили усилия с командой Kubernetes Deployment Manager из Google и начали работу над Helm 2.

  1. шаблоны чартов («чарт» — аналог пакета в экосистеме Helm — прим. перев.) для кастомизации;
  2. управление внутри кластера для команд;
  3. полноценный репозиторий чартов;
  4. стабильный и подписываемый формат пакетов;
  5. твёрдая приверженность семантическому версионированию и сохранению обратной совместимости от версии к версии.

Чтобы достичь этих целей, в экосистему Helm был добавлен второй компонент. Им стал находящийся внутри кластера Tiller, который обеспечивал установку Helm-чартов и управление ими.

перев.: Таким образом, во второй версии Helm в кластере остаётся единственный компонент, который отвечает за жизненный цикл инсталяций (release), а подготовка конфигураций выносится в Helm-клиент. Прим.

они хранились в оперативной памяти), то в Helm 2 все данные хранятся в ConfigMaps, т.е. Если перезагрузка кластера при использовании первой версии Helm приводила к полной потере служебных данных (т.к. Ещё одним важным шагом стал переход от синхронного API (где каждый запрос был блокирующим) к использованию асинхронного gRPC. ресурсах внутри Kubernetes.

Было добавлено управление доступом на основе ролей (RBAC). Со времени выпуска Helm 2 в 2016 году проект Kubernetes пережил взрывной рост и появление новых значительных возможностей. Изобретены сторонние ресурсы (Custom Resource Definitions, CRD). Представлено множество новых типов ресурсов. Проходя через все эти изменения, Helm продолжал служить потребностям пользователей Kubernetes. А что самое важное — появились лучшие практики. Но нам стало очевидно, что настало время внести в него крупные изменения для того, чтобы потребности этой развивающейся экосистемы продолжали удовлетворяться.

Далее я расскажу о некоторых из новшеств, фигурирующих на дорожной карте проекта. Так мы пришли к Helm 3.

Поприветствуем Lua

В Helm 2 мы представили шаблоны. На раннем этапе разработки Helm 2 мы поддерживали шаблоны Go, Jinja, чистый код на Python и у нас даже был прототип поддержки ksonnet. Но наличие множества движков для шаблонов породило больше проблем, чем решило. Поэтому мы пришли к тому, чтобы выбрать один.

У шаблонов Go было четыре преимущества:

  1. библиотека встроена в Go;
  2. шаблоны исполняются в жёстко ограниченном песочницей окружении;
  3. мы могли вставлять в движок произвольные функции и объекты;
  4. они хорошо работали с YAML.

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

И мы узнали об их разочарованиях:

  1. Синтаксис сложен в чтении и плохо документирован.
  2. Проблемы языка, такие как неизменные переменные, запутанные типы данных и ограничительные правила в области видимости, превратили простые вещи в сложные.
  3. Отсутствие возможности определять функции внутри шаблонов ещё больше усложнили создание повторно используемых библиотек.

Самое важное — используя язык шаблонов, мы существенно «обрезали» объекты Kubernetes до их строчного представления. (Другими словами, разработчикам шаблонов приходилось управлять ресурсами Kubernetes как текстовыми документами в формате YAML.)

Работа над объектами, а не кусками YAML

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

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

У Lua простой синтаксис, он широко поддерживается и уже долгое время фигурирует в списке топ-20 языков. В 1993 году группа бразильских ИТ-инженеров создала легковесный скриптовый язык для встраивания в свои инструменты. Вот на такой уже существующей экосистеме мы бы и хотели развивать своё решение. Его поддерживают IDE и текстовые редакторы, есть множество руководств и обучающих книг.

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

Вот как выглядит пример шаблона пода с Alpine в Helm 2:

apiVersion: v1
kind: Pod
metadata: name: } labels: heritage: {{ .Release.Service }} release: {{ .Release.Name }} chart: {{ .Chart.Name }}-{{ .Chart.Version }} app: {{ template "alpine.name" . }}
spec: restartPolicy: {{ .Values.restartPolicy }} containers: - name: waiter image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} command: ["/bin/sleep", "9000"]

В этом незамысловатом шаблоне можно сразу увидеть все встроенные директивы шаблонов, такие как {{ .Chart.Name }}.

А вот как выглядит определение того же пода в предварительной версии кода на Lua:

unction create_alpine_pod(_) local pod = { apiVersion = "v1", kind = "Pod", metadata = { name = alpine_fullname(_), labels = { heritage = _.Release.Service or "helm", release = _.Release.Name, chart = _.Chart.Name .. "-" .. _.Chart.Version, app = alpine_name(_) } }, spec = { restartPolicy = _.Values.restartPolicy, containers = { { name = waiter, image = _.Values.image.repository .. ":" .. _.Values.image.tag, imagePullPolicy = _.Values.image.pullPolicy, command = { "/bin/sleep", "9000" } } } } } _.resources.add(pod)
end

Нет необходимости рассматривать каждую строчку этого примера, чтобы понять, что происходит. Сразу видно, что в коде определяется под. Но вместо использования YAML-строк со встроенными директивами шаблонов мы определяем под как объект в Lua.

Давайте сократим этот код

Поскольку мы напрямую работаем с объектами (вместо манипуляции с большим glob'ом текста), можем воспользоваться всеми преимуществами скриптования. Появляющиеся здесь возможности создания разделяемых библиотек выглядят по-настоящему привлекательно. И мы надеемся, что, представив специализированные библиотеки (или позволив сообществу их создать), сможем сократить приведённый выше код примерно до такого:

local pods = require("mylib.pods"); function create_alpine_pod(_) myPod = pods.new("alpine:3.7", _) myPod.spec.restartPolicy = "Always" -- set any other properties _.Manifests.add(myPod)
end

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

Шаблоны… Lua… Почему бы не всё вместе?

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

У скриптов Lua будет доступ к шаблонам Helm и до, и после того, как они отрендерены, что позволит разработчикам продвинутых чартов выполнять сложные преобразования для существующих чартов, сохраняя простую возможность создания Helm-чартов с шаблонами. Вместо этого мы хотим разрешить использовать одновременно и шаблоны, и Lua.

Мы очень воодушевлены поддержкой скриптов на Lua, но в то же время избавляемся от значимой части архитектуры Helm…

Прощаясь с Tiller

Во время разработки Helm 2 мы представили Tiller в качестве компонента интеграции с Deployment Manager. Tiller играл важную роль для команд, работающих на одном кластере: он делал возможным взаимодействие с одним и тем же набором релизов для множества различных администраторов.

И нашей схемой по умолчанию при инсталляции была разрешительная конфигурация. Однако Tiller работал как гигантский sudo-сервер, выдающий широкий диапазон прав каждому, у кого есть доступ к Tiller. Поэтому DevOps- и SRE-инженерам приходилось обучаться дополнительным шагам для установки Tiller в кластерах категории multi-tenant.

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

Поэтому одним из первых решений, принятых на этапе планирования Helm 3, был полный отказ от Tiller. Главная цель Tiller может быть достигнута и без самого Tiller.

Улучшение безопасности

Без Tiller модель безопасности Helm радикально упрощается. Пользовательская аутентификация делегируется Kubernetes. И авторизация тоже. Права Helm определяются как права Kubernetes (через систему RBAC), и администраторы кластера могут ограничить права Helm на любом необходимом уровне детализации.

Releases, ReleaseVersions и State Storage

В условиях отсутствия Tiller, для поддержания состояния различных релизов внутри кластера нам требуется новый способ взаимодействия всех клиентов (по управлению релизами).

Для этого мы представили две новые записи:

  1. Release — для конкретной инсталляции конкретного чарта. Если мы выполним helm install my-wordpress stable/wordpress, будет создан релиз с названием my-wordpress и поддерживаться на протяжении всей жизни этой инсталляции WordPress.
  2. ReleaseVersion — при каждом обновлении чарта Helm необходимо учитывать, что изменилось и было ли изменение успешным. ReleaseVersion привязан к релизу и хранит только записи с информацией об обновлении, откате и удалении. Когда мы выполним helm upgrade my-wordpress stable/wordpress, оригинальный объект Release останется прежним, но появится дочерний объект ReleaseVersion с информацией об операции обновления.

Releases и ReleaseVersions будут храниться в тех же пространствах имён, что и объекты чарта.

С этими возможностями команды пользователей Helm смогут отслеживать записи об инсталляциях Helm в кластере без потребности в Tiller.

Но подождите, это ещё не всё!

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

перев.: Парадокс, но пакетный менеджер Helm 2 при успешном выполнении install или upgrade, т.е. Прим. Возможно, новая событийная модель позволит добавлять дополнительные хуки для ресурсов и лучше контролировать процесс выката — скоро мы об этом узнаем. имея release в состоянии success, не гарантирует, что ресурсы приложения успешно выкатились (к примеру, нет ошибок типа ImagePullError).

Мы уверены, что хорошее управление пакетами для Kubernetes настолько же важно для облачной (cloud native) экосистемы, насколько важны хорошие пакетные менеджеры для Linux. С присоединением Helm к CNCF нас вдохновляет не только Helm 3, но и Chart Museum, замечательная утилита Chart Testing, официальный репозиторий чартов и другие проекты под эгидой Helm в CNCF.

P.S. от переводчика

Читайте также в нашем блоге:

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть