Headlamp: Kubernetes UI без kubectl
Published: 2026-02-05
Headlamp — это open-source расширяемый дашборд для Kubernetes. Он stateless, запускается как Deployment и аутентифицирует пользователей через OIDC — каждый инженер видит свой кластер в рамках своих прав, без общего токена cluster-admin.
Этот пост — полная настройка для продакшена: конфигурация HelmRelease, интеграция OIDC с GitLab, RBAC, поддержка мультикластера и операционные паттерны, которые мы используем ежедневно.
Почему Headlamp, а не стандартный Dashboard
Официальный Kubernetes Dashboard требует либо токен-аутентификацию, либо proxy-тоннель. Ни то, ни другое не масштабируется: токен-аутентификация означает общие долгоживущие учётные данные, а proxy-тоннель требует, чтобы у каждого инженера были kubectl и доступ к кластеру — просто чтобы открыть вкладку в браузере.
Headlamp поддерживает OIDC из коробки: пользователь входит через GitLab, Headlamp передаёт токен API-серверу кластера и получает ровно те права, что разрешает RBAC. Никакого токена для ротации, никакого proxy для поддержки, никакого kubectl для read-доступа.
Плюс — система плагинов. Самый полезный встроенный: headlamp-plugin/prometheus — показывает графики ресурсов прямо на странице пода. Тренды CPU/памяти видны без открытия Grafana.
Другие встроенные возможности, заменяющие ежедневные kubectl-команды:
- Стриминг логов в реальном времени — аналог
kubectl logs -f, с подсветкой и поиском - Exec в контейнеры — аналог
kubectl exec -it, терминал в браузере - Редактирование ресурсов — YAML-редактор с валидацией схемы
- Лента событий — поток событий по namespace с фильтрацией
HelmRelease
yamlapiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: headlamp
namespace: observability
spec:
chart:
spec:
chart: headlamp
version: "0.25.*"
sourceRef:
kind: HelmRepository
name: headlamp
namespace: flux-system
values:
replicaCount: 1
config:
oidc:
clientID: "${HEADLAMP_OIDC_CLIENT_ID}"
clientSecret: "${HEADLAMP_OIDC_CLIENT_SECRET}"
issuerURL: "https://gitlab.example.com"
scopes: "openid profile email groups"
ingress:
enabled: false # обрабатывается через ApisixRoute
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
Client ID и Client Secret для OIDC берутся из ExternalSecret, который тянет данные из Vault. issuerURL — адрес вашего экземпляра GitLab: GitLab поддерживает OIDC нативно, дополнительная конфигурация не нужна.
Обратите внимание на scope groups: без него OIDC-токен не включает членство в GitLab-группах, и Kubernetes RBAC не сможет сопоставить группы с ролями.
Настройка OIDC-приложения в GitLab
В GitLab создайте приложение через Admin > Applications (для instance-level) или Group > Settings > Applications (для group-level):
- Redirect URI:
https://headlamp.dev.example.com/oidc-callback - Scopes:
openid,profile,email,groups - Сохраните Application ID и Secret в Vault по пути
secret/headlamp/oidc
ExternalSecret:
yamlapiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: headlamp-oidc
namespace: observability
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: headlamp-oidc-secret
creationPolicy: Owner
data:
- secretKey: clientID
remoteRef:
key: secret/headlamp/oidc
property: client_id
- secretKey: clientSecret
remoteRef:
key: secret/headlamp/oidc
property: client_secret
Затем в HelmRelease через valuesFrom:
yamlvaluesFrom:
- kind: Secret
name: headlamp-oidc-secret
valuesKey: clientID
targetPath: config.oidc.clientID
- kind: Secret
name: headlamp-oidc-secret
valuesKey: clientSecret
targetPath: config.oidc.clientSecret
ApisixRoute
Headlamp получает собственный роут в слое routes:
yamlapiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: headlamp
namespace: ingress-apisix
spec:
http:
- name: headlamp
match:
hosts:
- headlamp.dev.example.com
paths:
- "/*"
backends:
- serviceName: headlamp
serviceNamespace: observability
servicePort: 80
На уровне APISIX нет плагина аутентификации — её полностью обрабатывает OIDC-flow Headlamp. APISIX только проксирует запрос.
Если нужно ограничить доступ к URL Headlamp по IP до OIDC-редиректа (эшелонированная защита), добавьте плагин ip-restriction:
yamlplugins:
- name: ip-restriction
enable: true
config:
whitelist:
- "10.0.0.0/8"
- "192.168.0.0/16"
RBAC для OIDC-пользователей
Headlamp передаёт claim groups из GitLab в Kubernetes API. API-сервер использует этот claim для RBAC. ClusterRoleBinding связывает GitLab-группу с ClusterRole:
yamlapiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: headlamp-dev-readonly
subjects:
- kind: Group
name: "example/dev-team"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
Значение name в subjects должно точно совпадать с путём группы, который GitLab возвращает в OIDC claim groups. GitLab возвращает полный путь: родительская-группа/подгруппа. Если ваша группа acme-corp/platform/dev-team, используйте эту строку дословно.
Наша многоуровневая модель доступа:
| GitLab-группа | ClusterRole | Что может |
|---|---|---|
org/devops-team |
cluster-admin |
Всё |
org/platform-team |
edit |
Деплой, scale, рестарт подов |
org/dev-team |
view |
Только чтение, без секретов |
org/qa-team |
qa-viewer (custom) |
Чтение подов/логов, без конфигов |
Кастомная роль qa-viewer:
yamlapiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: qa-viewer
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "pods/exec", "services", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
QA-инженеры могут смотреть поды и стримить логи, но не могут читать Secrets или что-либо изменять. Все биндинги живут в spoke/rbac/ и применяются spoke-Kustomization — отдельно от HelmRelease Headlamp, чтобы изменения RBAC не требовали роллаута Headlamp.
Несколько кластеров в одном UI
Headlamp обнаруживает кластеры из kubeconfig, примонтированного в под. Для мультикластерного использования Secret с несколькими контекстами монтируется в /headlamp/kubeconfig:
yamlvolumes:
- name: kubeconfigs
secret:
secretName: headlamp-kubeconfigs
volumeMounts:
- name: kubeconfigs
mountPath: /headlamp/kubeconfig
readOnly: true
Секрет содержит объединённый kubeconfig с контекстами всех кластеров. Инженеры видят выпадающий список кластеров в левом верхнем углу и переключаются между dev, test, sre и другими без отдельной вкладки в браузере.
Генерация объединённого kubeconfig:
bashKUBECONFIG=kubeconfigs/dev.yaml:kubeconfigs/test.yaml:kubeconfigs/sre.yaml \
kubectl config view --flatten > merged-kubeconfig.yaml
kubectl create secret generic headlamp-kubeconfigs \
--from-file=config=merged-kubeconfig.yaml \
-n observability \
--dry-run=client -o yaml | \
kubeseal --cert flux-sealed-secrets.pem --format yaml > headlamp-kubeconfigs-sealedsecret.yaml
При добавлении кластера — перегенерируйте merged kubeconfig, перезапечатайте секрет, сделайте коммит. Headlamp подхватит изменения при следующем рестарте пода.
Управление плагинами
Плагины Headlamp загружаются из директории /headlamp/plugins внутри контейнера. Есть два способа их установки:
1. Init container (для кастомных и community-плагинов):
yamlinitContainers:
- name: install-plugins
image: alpine:3.19
command:
- sh
- -c
- |
wget -O /plugins/prometheus.tar.gz \
https://github.com/headlamp-k8s/plugins/releases/download/v0.8.0/prometheus-0.8.0.tar.gz
tar -xzf /plugins/prometheus.tar.gz -C /plugins/
volumeMounts:
- name: plugins
mountPath: /plugins
2. ConfigMap (для небольших плагинов, не рекомендуется для продакшена):
Монтируйте JS-файл плагина напрямую из ConfigMap. Подходит для тестирования, но обновление плагина превращается в обновление ConfigMap — неудобно.
Подход с init container чище: версия плагина зафиксирована, контрольная сумма неявно следует из истории git.
Полезные горячие клавиши и советы
Ctrl+Shift+K— открыть палитру команд (быстрый поиск любого ресурса)- Клик по имени пода → вкладка с живыми логами, без
kubectl logs - Вкладка «Exec» даёт терминал внутри контейнера — аналог
kubectl exec -it - Вкладка «Describe» показывает полный YAML ресурса с аннотациями — быстрее
kubectl describe
Фильтрация по лейблу
Список ресурсов Headlamp поддерживает фильтрацию по label selector в строке поиска. Введите app=redis — список отфильтруется по совпадающим ресурсам. Работает для всех типов ресурсов — удобно, когда 200 подов и нужно найти все поды конкретного сервиса.
Port forwarding
Headlamp поддерживает port-forward через UI: откройте под → вкладка «Port Forward» → укажите локальный и контейнерный порт. Работает так же, как kubectl port-forward, но без терминала.
Устранение неполадок
Вход через OIDC падает с ошибкой invalid client
Неверный Client ID или Secret, либо Redirect URI в GitLab не совпадает с URL Headlamp (включая суффикс /oidc-callback).
Пользователь входит, но везде видит forbidden
Claim groups не отправляется. Проверьте OIDC scopes — groups должен быть включён. Также проверьте groups claim в токене через jwt.io: декодируйте ID token из вкладки сети браузера и проверьте поле groups.
Мультикластерный kubeconfig показывает старые имена кластеров
Headlamp кэширует kubeconfig при запуске. Перезапустите под после обновления секрета с kubeconfig: kubectl rollout restart deployment/headlamp -n observability.