Обновлена документация под новые аддоны (gitlab, redis, mongodb, kafka, kafka-ui, rabbitmq) и новую модель явного выбора зависимостей. Добавлены и унифицированы описания переключателей *_database_mode и *_redis_mode, обновлена таблица зависимостей аддонов, примеры конфигурации и список vault-секретов.
External Secrets Operator (ESO)
Синхронизирует секреты из внешних хранилищ (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager и др.) в нативные Kubernetes Secrets.
Когда использовать ESO вместо Vault Agent Injector:
- Нужны стандартные k8s Secrets (
secretRef,envFrom) — Helm чарты не менять - Секреты нужны до старта пода (init containers, admission webhooks)
- Нужна синхронизация между namespace
- Нет желания добавлять Vault annotations в каждый Deployment
Установка
# Базовая установка (настрой ClusterSecretStore вручную после)
make addon-external-secrets
# Сразу с настроенным ClusterSecretStore → Vault (если AppRole уже создан)
make addon-external-secrets ARGS="-e external_secrets_vault_role_id=<role-id>"
Настройка AppRole в Vault
ESO использует AppRole для аутентификации в Vault. Выполни один раз:
# 1. Port-forward к Vault
kubectl port-forward -n vault svc/vault 8200:8200 &
export VAULT_ADDR=http://localhost:8200
vault login <root_token>
# 2. Включить AppRole auth (если ещё не включён)
vault auth enable approle
# 3. Создать политику для ESO (read-only на все секреты)
vault policy write eso-policy - <<EOF
path "secret/data/*" {
capabilities = ["read"]
}
path "secret/metadata/*" {
capabilities = ["read", "list"]
}
EOF
# 4. Создать AppRole роль
vault write auth/approle/role/eso-role \
secret_id_ttl="0" \
token_ttl="1h" \
token_max_ttl="24h" \
token_policies="eso-policy"
# 5. Получить Role ID (публичный, вставить в addons.yml)
vault read -field=role_id auth/approle/role/eso-role/role-id
# → Скопируй в external_secrets_vault_role_id в group_vars/all/addons.yml
# 6. Получить Secret ID (секретный, вставить в vault.yml)
vault write -f -field=secret_id auth/approle/role/eso-role/secret-id
# → Скопируй в vault_eso_approle_secret_id в group_vars/all/vault.yml
# 7. Применить ClusterSecretStore
make addon-external-secrets ARGS="-e external_secrets_vault_role_id=<role-id>"
Проверка подключения
# Статус ClusterSecretStore (должен быть Valid)
kubectl get clustersecretstore vault-backend
# Подробности
kubectl describe clustersecretstore vault-backend
Использование в YAML манифестах
Простой ExternalSecret — синхронизация одного секрета
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-db-secret
namespace: myapp
spec:
refreshInterval: 1h # как часто синхронизировать
secretStoreRef:
name: vault-backend # ClusterSecretStore
kind: ClusterSecretStore
target:
name: myapp-db-credentials # имя k8s Secret который будет создан
creationPolicy: Owner # ESO управляет Secret (авто-удаление)
data:
- secretKey: DB_PASSWORD # ключ в k8s Secret
remoteRef:
key: myapp/database # путь в Vault: secret/data/myapp/database
property: password # поле внутри секрета
- secretKey: DB_USER
remoteRef:
key: myapp/database
property: username
После применения ESO создаст k8s Secret myapp-db-credentials.
Использование в Deployment (через envFrom)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- secretRef:
name: myapp-db-credentials # создан ExternalSecret выше
# ИЛИ выборочно:
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: myapp-db-credentials
key: DB_PASSWORD
Весь секрет целиком (все поля из Vault)
spec:
dataFrom:
- extract:
key: myapp/config # все поля из secret/data/myapp/config станут ключами в k8s Secret
ExternalSecret с template (форматирование)
spec:
target:
name: myapp-connection-string
template:
data:
DATABASE_URL: "postgresql://{{ .username }}:{{ .password }}@postgres:5432/mydb"
dataFrom:
- extract:
key: myapp/database
Использование в Helm чартах
Способ 1: ExternalSecret в том же Helm чарте
# templates/external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: {{ include "myapp.fullname" . }}-secrets
namespace: {{ .Release.Namespace }}
spec:
refreshInterval: {{ .Values.secrets.refreshInterval | default "1h" }}
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: {{ include "myapp.fullname" . }}-secrets
creationPolicy: Owner
dataFrom:
- extract:
key: {{ .Values.secrets.vaultPath }} # задаётся в values.yaml
# values.yaml
secrets:
vaultPath: "myapp/production/config"
refreshInterval: "30m"
# Deployment использует стандартный envFrom:
envFrom:
- secretRef:
name: "{{ include "myapp.fullname" . }}-secrets"
Способ 2: ESO как внешняя зависимость (рекомендуется)
Секреты создаются заранее через отдельный ExternalSecret, Helm чарт просто ссылается на k8s Secret:
# 1. Создать ExternalSecret (один раз, в GitOps репозитории)
kubectl apply -f external-secret.yaml
# 2. Установить Helm чарт — он найдёт уже существующий Secret
helm install myapp ./mychart --set existingSecret=myapp-db-credentials
# values.yaml чарта
existingSecret: "" # если задан — используется, иначе chart создаёт свой
# templates/deployment.yaml
{{- if .Values.existingSecret }}
envFrom:
- secretRef:
name: {{ .Values.existingSecret }}
{{- end }}
Способ 3: ArgoCD + ESO
# argocd Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
spec:
source:
helm:
values: |
# Секреты создаются ESO автоматически — чарт просто ссылается
envFrom:
- secretRef:
name: myapp-vault-secrets
PushSecret — запись секретов в Vault из k8s
ESO поддерживает обратную синхронизацию (k8s → Vault):
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-to-vault
namespace: myapp
spec:
refreshInterval: 10s
secretStoreRefs:
- name: vault-backend
kind: ClusterSecretStore
selector:
secret:
name: my-local-secret # существующий k8s Secret
data:
- match:
secretKey: password
remoteRef:
remoteKey: myapp/config
property: password
Управление несколькими SecretStore
Namespace-scoped SecretStore (для разных Vault paths/политик)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-myapp # только для namespace myapp
namespace: myapp
spec:
provider:
vault:
server: "http://vault.vault.svc.cluster.local:8200"
path: "myapp" # ограниченный путь
version: "v2"
auth:
appRole:
path: "approle"
roleId: "<myapp-role-id>"
secretRef:
name: vault-approle-myapp
key: secretId
Мониторинг
Если addon_prometheus_stack: true, метрики ESO доступны в Grafana:
external_secrets_sync_calls_total— количество синхронизацийexternal_secrets_sync_call_errors_total— ошибки- Дашборд: импортируй Grafana Dashboard ID 21045
# Статус всех ExternalSecrets в кластере
kubectl get externalsecrets --all-namespaces
# Подробности (условие Ready/SecretSynced)
kubectl describe externalsecret myapp-db-secret -n myapp
Официальные ресурсы
- Официальный сайт: https://external-secrets.io/
- Официальная документация: https://external-secrets.io/latest/
- Версии Helm chart / ПО: https://artifacthub.io/packages/helm/external-secrets-operator/external-secrets