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

https://sweetcode.io/a-first-look-at-the-helm-3-plan/
  • Перевод
Прим. перев.: Этой статьёй мы открываем цикл публикаций про пакетный менеджер для Kubernetes, который активно используем в повседневной работе, — Helm. Оригинальным автором материала является Matt Butcher — один из основателей проекта Helm, работающий над Open Source-проектами в Microsoft и написавший 8 технических книг (в частности, «Go in Practice»). Однако статья дополнена нашими (местами — обширными) комментариями, а в скором времени будет ещё больше расширена новыми заметками по Helm более практической направленности. ОБНОВЛЕНИЕ (03.09.2018): вышло продолжение — «Практическое знакомство с пакетным менеджером для Kubernetes — Helm».



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

Краткая история 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…

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

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

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

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

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

Со времени выпуска Helm 2 в 2016 году проект Kubernetes пережил взрывной рост и появление новых значительных возможностей. Было добавлено управление доступом на основе ролей (RBAC). Представлено множество новых типов ресурсов. Изобретены сторонние ресурсы (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.

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

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

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

apiVersion: v1
kind: Pod
metadata:
  name: {{ template "alpine.fullname" . }}
  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. У скриптов Lua будет доступ к шаблонам Helm и до, и после того, как они отрендерены, что позволит разработчикам продвинутых чартов выполнять сложные преобразования для существующих чартов, сохраняя простую возможность создания Helm-чартов с шаблонами.

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

Прощаясь с Tiller


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

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

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

Главная цель Tiller может быть достигнута и без самого Tiller. Поэтому одним из первых решений, принятых на этапе планирования Helm 3, был полный отказ от 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). Возможно, новая событийная модель позволит добавлять дополнительные хуки для ресурсов и лучше контролировать процесс выката — скоро мы об этом узнаем.

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

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


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

Флант

272,00

Специалисты по DevOps и высоким нагрузкам в вебе

Поделиться публикацией
Комментарии 11
    +1
    Было бы классно если бы в статье про Helm было описано зачем он нужен.
    С обывательской позиции разработчика который не работает с бэкендом, есть kubernetes, в нем есть деплойменты, там можно указать образ. Образы льются куда нибудь вроде докерхаб или гугловый репозиторий. Какую именно задачу решает хелм?

    Может быть кто нибудь из коментаторов сможет подсказать.
      +1

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


      В rhel/centos:


      yum install unzip # можно ведь и самому собрать из исходников?

      В helm:


      helm install prometheus-operator # попробуйте сами создать такого монстрика из 46 yml-артефактов для кубера

      Понятное дело, что функционал можно не только удобно получить, но и удалить, и обновить, и откатить к предыдущей версии.

        +1
        Образы льются куда нибудь вроде докерхаб или гугловый репозиторий

        На пути от образа до деплоймента стоит пропасть из конфигов Кубернетеса.

        Представьте, что у вас есть бинарник. Например, скомпилированная библиотека .so.
        Ну да, есть. Но ведь ее надо положить в систему. Надо сделать ldconfig. Надо создать линки типа libabc.so.2 -->libabc.so.2.0.6. И тут вы используете какие-то скрипты или профильные утилиты для аккуратной упаковки со всякими post/pre-install и post/pre-delete скриптами. В результате вы получаете в место файла .so, например, какой-нибудь .deb пакет.
        А когда вам захочется обновить библиотеку, вы просто дадите короткую команду.

        Вот образ докера в этой аллегории — библиотека .so. А ворох скриптов по его установке, апргрейду и даунгрейду — пакет Helm.
          –1
          Коротко: это package manager для Kubernetes.

          Helm оперирует такими вещами как charts.
          Внутри одно чарта у тебя:
          — Шаблоны для самого kubernetes
          — Всякие переменные
          — Название, версия и все такое

          Ну и все прелести типичного package manager: можно указывать зависимости на другие чарты.

          В итоге это приводит к тому что можно писать вот так:
          helm install my-project

          Где my-project зависит от frontend, backend, database, message-queue.
          И helm это раскатает в kubernetes.

          Потом можно делать вот такие вещи:
          helm history my-project # История версий
          helm rollback my-project 128 # Сделать роллбек на вот такую версию
            0
            Один из юз-кейсов хелма: у меня есть микросервис (на самом деле не один), который я запускаю независимо для нескольких стран. У них типовая конфигурация, но для каждой страны значение отдельных параметров среды разное. Helm позволяет иметь шаблон конфигурации, на основе которого я могу развернуть все имеющиеся страны, не занимаясь копипастингом.
              0

              Автор этого материала ниже развернуто ответил, а ещё подробнее обещаем раскрыть в следующих статьях по теме.

              +2
              Q: Какую именно задачу решает helm?

              Helm может использоваться для выката приложений в кластер kubernetes. Helm не обрабатывает исходный код приложения, а работает с образами, для создания которых потребуется инструмент CI/CD.

              Q: Почему просто не использовать `kubectl apply -f`?

              Обычно проблемы начинаются когда идёт автоматизация процесса выката, настройка процессов CI/CD. К примеру, рассмотрим задачу с выкатом в различные окружения.

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

              Появляется потребность в шаблонизации и работе с секретами, и это только вершина айсберга.

              Таким образов, требуется комплексный подход к выкату, так почему бы не использовать helm?

              Helm содержит инструменты для создания, отладки, компоновки и сопровождения конфигураций kubernetes-ресурсов, а также упрощает управление выкатом приложений, за счёт хуков, версионирования, возможности многократного использования Chart-ов и многого другого.

              Kubernetes-ресурсы:
              1. Кастомизация и переиспользование за счёт шаблонизации.
              2. Официальный репозиторий с готовыми решениями, из которых можно компоновать собственные приложения.
              3. Инструменты отладки (lint).
              4. Использование секретов, рендер и использование других helm-плагинов.

              Выкат:
              1. Установка, обновление, удаление инсталяций.
              2. Версионирование, откат до определённой версии. К примеру, откат при неудавшемся выкате.
              3. Хуки, позволяющие вмешиваться в жизненный цикл релиза. К примеру, делать дамп базы перед удалением инсталяции или выкатывать фикстурные данные после установки, оповещать по API сопутствующие сервисы об установке, обновлении, удалении и т.д.

              Об этом и многом другом мы постараемся рассказать в этом цикле статей про helm.
                0

                Я верно понимаю, что за три года Helm брал переписан трижды и тем, кто его использует, придется опять переучиваться?

                  +1
                  Началом существования helm принято считать презентацию на KubeCon в San Francisco в ноябре 2015-го года. Менее чем через год вышла вторая версия и в настоящий момент она является актуальной.

                  Данная статья является анонсом следующей версии и нет никакой информации по дате выхода.

                  Основные принципы и задачи инструмента остаются прежними, поэтому переживать за пользователей не стоит.
                  0
                  А не в курсе, планируется ли возможность иметь два релиза с одинаковым именем в разных неймспейсах?
                    +1
                    Да, это есть в планах.

                    The release object contains information about a release, where a release is a particular installation of a named chart and values. This object describes the top-level metadata about a release.

                    At minimum, there are two necessary pieces of data a Release must track:

                    The name of the release
                    The curretly deployed version (release version Secret) of this release
                    The release object persists for the duration of an application lifecycle, and is the owner of all release version Secrets, as well as of all objects that are directly created by the Helm chart. (These relationships may be represented by owner references.)

                    With this change, release names can now be scoped to namespace, instead of globally scoped as they were with Helm 2.

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

                  Самое читаемое