VibeOps: безопасная конфигурация только для чтения при отладке Kubernetes с помощью ИИ
Сейчас много шума вокруг идеи, что ИИ должен «чинить» вашу инфраструктуру — будь то через команды AWS CLI или, как в этой статье, через Kubernetes. Вы вставляете сообщение об ошибке, ИИ предлагает kubectl apply, и вы надеетесь, что он понимает, что делает.
Так работать нельзя.
Когда прод ведёт себя странно, нужно сохранять полную ментальную модель системы. Как только вы передаёте ИИ управление, вы теряете эту картину.
Вместо этого используйте Claude для того, что я (и другие) называем «VibeDebugging» — получить второе мнение о состоянии кластера, пока вы сами проводите хирургическое вмешательство.
Когда возникает проблема или срабатывает алерт, я запускаю два параллельных потока:
Vibe Check (ИИ): я запускаю Claude с доступом по MCP к кластеру Kubernetes с широкой задачей — например: «Проанализируй логи и события для payment-service в неймспейсе prod. Найди корреляцию с базой данных.»
Глубокое погружение (я): пока Claude обрабатывает запрос, я начинаю собственное расследование — проверяю поды, читаю логи в реальном времени, смотрю Grafana… всё то же самое, что я делал до появления ИИ.
Это вынуждает меня оставаться в процессе. Я не слепо следую рекомендациям ИИ — я проверяю его выводы по тому, что вижу сам. Это не автопилот; это усилитель, который замечает странные строки в логах, мимо которых я мог проскроллить.
Работать в таком режиме было бы некомфортно, если бы у ИИ был доступ на запись или возможность читать секреты. Моё правило для ИИ в Ops: только чтение (read-only), никаких секретов.
Мне не нужно, чтобы LLM галлюцинировал команду, которая выведет мои пароли от БД в историю чата, или начал вносить изменения в кластер (и ещё больше усугубил инцидент, добавив в него этого недетерминированного агента).
Настройка MCP-сервера Kubernetes в режиме только для чтения
Шаг 1: Выделенный ServiceAccount только для чтения
Нам нужен ServiceAccount, у которого есть права видеть всё необходимое (Deployments, Pods, Logs), но который полностью слеп к чувствительным данным и не может ничего изменять.
Роли Kubernetes работают по стратегии явного разрешения (explicit-allow), поэтому доступ к ресурсам нужно явно предоставлять. Есть символ подстановки *, но, особенно для основной группы (core group), я рекомендую его избегать — мы не хотим, чтобы агент мог читать секреты.
Какие именно ресурсы открывать агенту/MCP-серверу — ваше решение. Ниже базовая конфигурация, которая даёт доступ к большинству «стандартных» ресурсов. (Если в кластере есть операторы и CRD, возможно, стоит разрешить доступ и к ним.)
Примечание: мы размещаем всё это в новом неймспейсе debug-access-ns, чтобы полностью удалить все ресурсы было так же просто, как удалить этот неймспейс — никаких следов не останется.
apiVersion: v1
kind: Namespace
metadata:
name: debug-access-ns
Подсказка: чтобы узнать все ресурсы в вашем кластере, используйте команду kubectl api-resources. Поле NAME — это «resource», а часть строки до / в APIVERSION — это «apiGroup».
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ai-read-everything-except-secrets
rules:
# 1. Основная группа API (Core API Group) — самая важная.
# Ресурсы перечислены явно, чтобы исключить 'secrets'.
- apiGroups: [""]
resources:
- bindings
- componentstatuses
- configmaps
- endpoints
- events
- limitranges
- namespaces
- nodes
- persistentvolumeclaims
- persistentvolumes
- pods
- pods/log
- pods/status
- podtemplates
- replicationcontrollers
- resourcequotas
- serviceaccounts
- services
verbs: ["get", "list", "watch"]
# 2. Все остальные общие группы API.
# Здесь безопасно использовать подстановку '*', потому что Secrets
# находятся в основной группе, описанной выше.
- apiGroups:
- "apps"
- "autoscaling"
- "batch"
- "cronjob"
- "extensions"
- "policy"
- "networking.k8s.io"
- "rbac.authorization.k8s.io"
- "storage.k8s.io"
- "apiextensions.k8s.io"
- "admissionregistration.k8s.io"
- "metrics.k8s.io"
- "discovery.k8s.io"
resources: ["*"]
verbs: ["get", "list", "watch"]
# 3. Не-ресурсные URL (необязательно, но рекомендуется для полной видимости).
# Позволяет проверять эндпоинты healthz, version и metrics.
- nonResourceURLs: ["*"]
verbs: ["get"]
Применяем ServiceAccount:
apiVersion: v1
kind: ServiceAccount
metadata:
name: ai-debugger
namespace: debug-access-ns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ai-debugger-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ai-read-everything-except-secrets
subjects:
- kind: ServiceAccount
name: ai-debugger
namespace: debug-access-ns
В современных версиях Kubernetes (1.24+) ServiceAccount’ы по умолчанию не получают долгоживущих токенов. Чтобы его сгенерировать, нужно вручную создать Secret.
apiVersion: v1
kind: Secret
metadata:
name: ai-debugger-token
namespace: debug-access-ns
annotations:
kubernetes.io/service-account.name: "ai-debugger"
type: kubernetes.io/service-account-token
Применяем все эти ресурсы, затем генерируем для них отдельный kubeconfig.
Шаг 2: Создание Kubeconfig
Теперь нужно извлечь данные (токен, CA-сертификат и URL сервера) и собрать из них корректный файл kubeconfig. Можно воспользоваться этим bash-скриптом для автоматической генерации readonly-config.yaml:
# 1. Получаем токен
TOKEN=$(kubectl get secret ai-debugger-token -n debug-access-ns -o jsonpath='{.data.token}' | base64 --decode)
# 2. Получаем CA-сертификат кластера
CA=$(kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')
# 3. Получаем URL API-сервера
SERVER=$(kubectl config view -o jsonpath='{.clusters[0].cluster.server}')
# 4. Записываем файл kubeconfig
cat <<EOF > ~/.kube/readonly-config.yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: $CA
server: $SERVER
name: secure-cluster
contexts:
- context:
cluster: secure-cluster
user: ai-debugger
name: secure-context
current-context: secure-context
users:
- name: ai-debugger
user:
token: $TOKEN
EOF
echo "Файл '~/.kube/readonly-config.yaml' успешно создан."
Шаг 3: Жёстко ограниченный MCP-сервер
Теперь, когда у нас есть удостоверение, нужно его подключить. Я использую Kubernetes MCP Server, чтобы Claude мог общаться с кластером.
Но я не просто запускаю его. Я ограничиваю его флагами, чтобы он не мог переключать контексты или пытаться записывать данные.
Вот команда, которой я добавляю его в конфигурацию Claude:
claude mcp add kubernetes --scope user -- npx -y kubernetes-mcp-server@v0.0.57 \
--read-only \
--kubeconfig ~/.kube/readonly-config.yaml \
--cluster-provider kubeconfig \
--disable-multi-cluster
Здесь происходит несколько важных вещей:
Фиксируем версию (@v0.0.57): никогда, никогда не используйте @latest для инфраструктурных инструментов. Я не хочу, чтобы автообновление изменило поведение или внесло баг прямо во время отладки. Проверяйте страницу релизов и фиксируйте версию.
--kubeconfig: я явно указываю на ограниченный конфиг, созданный выше. Даже если в коде MCP-сервера есть баг, Kubernetes API отклонит любые попытки записи.
--read-only: второй уровень защиты. Это говорит прикладному слою отключить вызов инструментов для создания или обновления ресурсов.
--disable-multi-cluster: держит ИИ сосредоточенным. Гарантирует, что Claude работает только с тем конкретным кластером, на который я его направил, и не может уйти в другие контексты, прописанные в моём стандартном kubeconfig.
Эта конфигурация даёт мне лучшее из обоих миров. Я получаю скорость и способность ИИ находить корреляции, но сохраняю ситуационную осведомлённость инженера. Я разбираюсь в инфраструктуре; Claude проверяет «вайбы». И благодаря этой настройке я знаю наверняка: он не может удалить мою базу данных.