Helm
Helm был разработан для упрощения развертывания приложений Kubernetes, но со временем он стал еще одним уровнем абстракции, который вводит ненужную сложность. Helm-чарты часто скрывают процесс развертывания с помощью слоев Go-шаблонов и вложенных файлов values.yaml
, что затрудняет понимание того, что именно развертывается. Отладка часто требует навигации через эти файлы, что может скрывать реальную конфигурацию. Этот подход отходит от инфраструктуры как кода к чему-то менее прозрачному, что усложняет управление и устранение неполадок.
imagePullPolicy: {{ .Values.defaultBackend.image.pullPolicy }}
{{- if .Values.defaultBackend.extraArgs }}
args:
{{- range $key, $value := .Values.defaultBackend.extraArgs }}
{{- /* Accept keys without values or with false as value */}}
{{- if eq ($value | quote | len) 2 }}
- --{{ $key }}
{{- else }}
- --{{ $key }}={{ $value }}
{{- end }}
{{- end }}
{{- end }}
YAML сам по себе не является проблемой, и с современной поддержкой IDE, схемами проверки и инструментами линтинга он может быть ясным и эффективным форматом конфигурации. Проблемы возникают, когда YAML сочетается с Go-шаблонами, как это происходит в Helm. Хотя каждый компонент сам по себе является разумным, их сочетание создает сложность. Go-шаблоны в YAML вводят хрупкие конструкции, где чувствительность к пробелам и императивная логика делают конфигурации трудными для чтения, обслуживания и тестирования. Это смешение логики и данных подрывает прозрачность и предсказуемость, которые имеют решающее значение для управления инфраструктурой.
Управление зависимостями в Helm также добавляет ненужную сложность. Зависимости загружаются в директорию charts/
, но привязка версий и переопределения часто становятся хрупкими. Вместо чистого повторного использования компонентов, Helm поощряет вложенные чарты с собственными файлами values.yaml
, что усложняет настройку и требует понимания нескольких чартов для переопределения одного значения. На практике управление зависимостями в Helm может ощущаться как вложение shell-скриптов в другие shell-скрипты.
Kustomzie
Kustomize предлагает декларативный подход к управлению конфигурациями Kubernetes, но его структура часто размывает грань между декларативным и императивным. Kustomize применяет преобразования к базовому набору манифестов Kubernetes, где пользователи определяют оверлеи и патчи, которые кажутся декларативными, но на самом деле зависят от порядка и процедурны.
Он поддерживает различные механизмы патчинга, для которых требуется глубокое понимание объектов Kubernetes, что может привести к громоздким и трудным для обслуживания конфигурациям. Такие функции, как генераторы, вытягивающие значения из файлов или переменных окружения, вводят динамическое поведение, что еще больше усложняет систему. Когда встроенных функций недостаточно, пользователи могут использовать функции KRM (Kubernetes Resource Model) для преобразований, но они все равно определяются в структурированных данных, что приводит к сложному наложению данных как кода, которое лишено ясности.
Хотя Kustomize избегает явного шаблонирования, он вводит уровень оркестрации, который может быть не менее непрозрачным и требует обширных знаний, чтобы обеспечить предсказуемые результаты.
Во многих Kubernetes-средах конвейер конфигурации стал сложной цепочкой инструментов и абстракций. То, что API Kubernetes получает — это просто YAML или JSON, часто является результатом множества промежуточных этапов, таких как Helm-чарты, Helmsman или системы GitOps, такие как Flux или Argo CD. По мере того как эти слои накапливаются, они могут скрывать окончательный вывод, препятствуя инженерам легко получить доступ к полностью отрендеренным манифестам.
Отсутствие видимости делает трудным проверку того, что на самом деле будет развернуто, что ведет к операционным проблемам и снижению уверенности в системе. Когда команды не могут проверить или воспроизвести артефакты развертывания, становится трудно проверять изменения или устранять неполадки, в конечном итоге превращая когда-то прозрачный процесс в черный ящик, который усложняет отладку и подрывает надежность.
Другие подходы
Язык конфигурации Apple pkl (сокращение от "Pickle") был разработан для замены YAML, предлагая большую гибкость и динамичные возможности. Он включает в себя такие функции, как классы, встроенные пакеты, методы и привязки для нескольких языков, а также интеграцию с IDE, что делает его похожим на полноценный язык программирования, а не просто формат конфигурации.
Тем не менее, сложность pkl может быть излишней. Его обширная документация и широкий набор функций могут оказаться лишними для большинства случаев, особенно когда YAML сам по себе способен удовлетворить потребности в управлении конфигурацией. Если проблема заключается в повторяемости YAML, более простым подходом мог бы стать песочничный JavaScript, который генерировал бы чистый YAML без накладных расходов на новый язык.
KISS
Управление конфигурациями Kubernetes в конечном счете сводится к проблеме манипуляции строками. Makefiles в сочетании со стандартными Unix-инструментами идеально подходят для решения этой задачи. Make предоставляет декларативный способ определения шагов для генерации манифестов Kubernetes, при этом каждый шаг ясно изложен и выполняется только при необходимости. Инструменты такие как sed
, awk
, cat
и jq
отлично подходят для трансформации текста и дополняют простоту Make, позволяя быстро манипулировать YAML или JSON файлами.
Этот подход является прозрачным — вы можете видеть, что делает каждая команда, и легко отлаживать, если это необходимо. В отличие от более сложных инструментов, которые скрывают внутренние процессы, Makefiles и инструменты Unix предоставляют полный контроль, что делает процесс управления конфигурацией простым и поддерживаемым.
https://github.com/avkcode/vault
HashiCorp Vault — это инструмент для управления секретами и конфиденциальными данными, предлагающий такие функции, как шифрование, контроль доступа и безопасное хранилище. Он использовался как пример критической инфраструктуры, развернутой на Kubernetes без Helm, подчеркивая ручное, настраиваемое управление ресурсами.
Этот Makefile автоматизирует развертывание Kubernetes, сборку Docker-образов и операции с Git. Он обрабатывает конфигурации, специфичные для окружения, проверяет манифесты Kubernetes и управляет ресурсами Vault, такими как сборка Docker-образов, получение ключей unseal/root и взаимодействие с Vault-подами. Он также облегчает операции с Git, такие как создание тегов, отправка релизов и генерация архивов или пакетов. Файл включает задачи для управления ресурсами Kubernetes, такими как сервисы, statefulset и секреты, смена пространств имен и удаление сгенерированных файлов. Он также поддерживает интерактивные сессии развертывания, перечисление переменных и проверку манифестов как с клиентской, так и с серверной стороны.
Переменная rbac
в Makefile определяется с помощью ключевого слова define
для хранения многострочной YAML-конфигурации для RBAC Kubernetes, включая ServiceAccount
и ClusterRoleBinding
. Плейсхолдер ${VAULT_NAMESPACE}
используется для динамической подстановки. Переменная экспортируется с помощью export rbac
и затем включается в переменную manifests
. Это позволяет шаблонизировать YAML с переменными окружения и переиспользовать в целях таких как template
и apply
для развертывания в Kubernetes.
define rbac
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-service-account
namespace: ${VAULT_NAMESPACE}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: vault-server-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-service-account
namespace: ${VAULT_NAMESPACE}
endef
export rbac
manifests += $${rbac}
manifests += $${configmap}
manifests += $${services}
manifests += $${statefulset}
.PHONY: template apply delete
template:
@$(foreach manifest,$(manifests),echo "$(manifest)";)
apply: create-release
@$(foreach manifest,$(manifests),echo "$(manifest)" | kubectl apply -f - ;)
delete: remove-release
@$(foreach manifest,$(manifests),echo "$(manifest)" | kubectl delete -f - ;)
validate-%:
@echo "$$$*" | yq eval -P '.' -
print-%:
@echo "$$$*"
Массив manifests
содержит многострочные YAML-шаблоны для ресурсов Kubernetes, включая RBAC, ConfigMap, Services и StatefulSet. В целевой задаче apply
каждый манифест обрабатывается и передается в kubectl apply
для развертывания в кластер Kubernetes. Подобным образом, целевая задача delete
использует kubectl delete
для удаления ресурсов, определенных в манифестах.
Используя Make с инструментами, такими как curl
, можно получить более гибкий способ работы с развертыванием Kubernetes, и он может легко заменить некоторые функции Helm. Например, вместо использования Helm-чартов для управления релизами, мы просто используем kubectl
в Makefile для создания и удаления Kubernetes-секретов. Выполняя простые shell-команды и используя kubectl
, мы можем напрямую управлять версиями и конфигурациями в Kubernetes без всей сложности Helm. Этот подход дает больше контроля и является более легким, что идеально подходит для проектов, где требуется простота и гибкость без нагрузки на управление полноценными Helm-чартами.
.PHONY: create-release
create-release:
@echo "Создание Kubernetes-секрета с версией, установленной в Git commit SHA..."
@SECRET_NAME="app-version-secret"; JSON_DATA="{"VERSION":"$(GIT_COMMIT)"}"; kubectl create secret generic $$SECRET_NAME --from-literal=version.json="$$JSON_DATA" --dry-run=client -o yaml | kubectl apply -f -
@echo "Секрет успешно создан: app-version-secret"
.PHONY: remove-release
remove-release:
@echo "Удаление Kubernetes-секрета: app-version-secret..."
@SECRET_NAME="app-version-secret"; kubectl delete secret $$SECRET_NAME 2>/dev/null || true
@echo "Секрет успешно удален: app-version-secret"
Так как массив manifests
содержит все определения ресурсов Kubernetes, мы можем легко сбрасывать их в формате YAML и JSON. Целевая задача dump-manifests
выполняет make template
для генерации YAML-вывода и make convert-to-json
для преобразования того же вывода в JSON. Перенаправляя вывод в manifest.yaml
и manifest.json
, вы можете сохранить оба формата ресурсов для дальнейшего использования. Это простой и эффективный способ генерировать несколько форматов из одного набора манифестов.
.PHONY: dump-manifests
dump-manifests: template convert-to-json
@echo "Сброс манифестов в manifest.yaml и manifest.json..."
@make template > manifest.yaml
@make convert-to-json > manifest.json
@echo "Манифесты успешно сброшены в manifest.yaml и manifest.json."
Целевая задача validate-%
позволяет легко проверять любой конкретный манифест, пропуская его через yq
для проверки структуры или содержимого в читаемом формате. Это использует внешние инструменты, такие как yq
, для проверки и обработки YAML прямо в Makefile, без необходимости писать сложные скрипты. Подобным образом, целевая задача print-%
позволяет быстро вывести значение любой переменной Makefile, давая простой способ для инспекции переменных или выводов. Используя внешние инструменты, такие как yq
, можно повысить гибкость Makefile, делая его удобным для валидации, обработки и манипулирования манифестами прямо.
# Проверяет конкретный манифест с помощью `yq`.
validate-%:
@echo "$$$*" | yq eval -P '.' -
# Выводит значение конкретной переменной.
print-%:
@echo "$$$*"
С помощью Makefile и простого Bash-скриптинга можно легко реализовать вспомогательные функции, такие как получение ключей Vault. В этом случае цель get-vault-keys
выводит доступные Vault-поды, запрашивает имя пода и получает ключ Vault unseal и root token, выполняя команды на выбранном поде. Этот подход использует базовые инструменты, такие как kubectl
, jq
и Bash, что делает его гораздо более гибким по сравнению с синтаксисом Helm или другими сложными инструментами. Это упрощает процесс и дает полный контроль над логикой развертывания без необходимости полагаться на громоздкие инструменты или чарты.
.PHONY: get-vault-keys
get-vault-keys:
@echo "Доступные Vault-поды:"
@PODS=$$(kubectl get pods -l app.kubernetes.io/name=vault -o jsonpath='{.items[*].metadata.name}'); echo "$$PODS"; read -p "Введите имя Vault пода (например, vault-0): " POD_NAME; if echo "$$PODS" | grep -qw "$$POD_NAME"; then kubectl exec $$POD_NAME -- vault operator init -key-shares=1 -key-threshold=1 -format=json > keys.json; VAULT_UNSEAL_KEY=$$(cat keys_$$POD_NAME.json | jq -r ".unseal_keys_b64[]"); echo "Unseal Key: $$VAULT_UNSEAL_KEY"; VAULT_ROOT_KEY=$$(cat keys.json | jq -r ".root_token"); echo "Root Token: $$VAULT_ROOT_KEY"; else echo "Ошибка: Пода '$$POD_NAME' не найдена."; fi
Иногда самый простой способ использования только Unix-инструментов — это лучший способ. Полагаясь на основные утилиты, такие как kubectl
, jq
, yq
и Make, можно создавать мощные, настраиваемые рабочие процессы без необходимости в громоздких инструментах, таких как Helm. Эти простые, прямые скрипты предлагают больше контроля и гибкости. Плюс, с помощью LLM (крупных языковых моделей), таких как эта, создание и улучшение кода стало дешевым и легким, что делает автоматизацию доступной. Однако когда что-то идет не так, отладка сложных инструментов, таких как Helm, может стать экспоненциально более дорогой по времени и усилиям. Использование минимальных инструментов позволяет вам сохранить контроль, уменьшить сложность и упростить решение проблем, когда они возникают. Иногда меньше — это больше.