Нейросеть выдаёт аккуратный Kubernetes-манифест за секунды. kubectl apply проходит без ошибок, Pod переходит в Running, сервис отвечает на запросы. Через неделю утечка памяти переводит контейнер в состояние «ложной смерти»: HTTP-порт он слушает, но запросы таймаутятся, а Liveness probe не срабатывает и не перезапускает Pod. Кластер при этом уверен, что ничего не сломалось, ведь API server принял ресурс в работу. В разборе этого случая отмечено: YAML проходил валидацию в Kubernetes 1.27, но семантика части полей изменилась, и они были молча проигнорированы.
Главное, что стоит принять до технических деталей: результат LLM - черновик, а не готовый артефакт для деплоя. Модель не спрашивает, какая версия кластера запущена, какие CRD установлены и что отклоняет admission controller. Она воспроизводит усреднённый паттерн из обучающих данных, и этот паттерн рассчитан на демонстрацию, а не на эксплуатацию. Разрыв между генерацией и развертыванием закрывает слой проверки: строгая проверка схемы через kubeconform, политики OPA/Rego через conftest и серверная проверка kubectl --dry-run=server.
Масштаб проблемы измерили. Исследование 2026 года дало цифру: средний уровень уязвимостей в сгенерированном ИИ инфраструктурном коде достигает 57,5%, и это заметно хуже результатов для обычного прикладного кода. Худший показатель среди типов артефактов у Dockerfile, близкий к повсеместному провалу. Полную методологию источник не раскрывает, поэтому 57,5% корректно читать как индикатор порядка величины, а не как точную метрику для любой команды.
Почему ИИ-манифесты опасны, даже когда проходят валидацию
Успешный kubectl apply и поднявшийся Pod не доказывают, что конфигурация безопасна. Валидация YAML проверяет структуру файла, а не то, как API server понял этот файл и что в итоге сохранил в etcd.
Как API server молча отбрасывает поля и почему это ломает пробники
API server принимает ресурс, если объект соответствует схеме его версии. Поля, которых в этой схеме нет или чья семантика поменялась между версиями, могут не попасть в сохранённый объект. Ошибки в логах не будет: с точки зрения кластера ресурс создан успешно. Пример с манифестом для Kubernetes 1.27 показывает, что произошло дальше: Liveness probe перестал выполнять свою функцию, а проблемы всплыли через неделю.
Практическое следствие простое. Пробники, политики и настройки безопасности, которые вы «задали» в YAML, могут не работать в кластере. Liveness probe отвечает за перезапуск зависшего контейнера, readiness probe - за то, чтобы трафик не шёл на неготовый Pod. Если probe отброшен или искажён, падение сервиса не вызовет реакции, и о проблеме вы узнаете от пользователей. Локальный парсинг YAML такую ситуацию не поймает: файл синтаксически валиден. Нужна проверка на стороне сервера, которая сверяет манифест со схемой конкретной версии кластера.
Смещение обучающих данных: почему LLM предлагают небезопасные значения по умолчанию
Основной корпус LLM составляют публичные Dockerfile, репозитории на GitHub и туториалы. Их объединяет общая черта: они запускаются на ноутбуке разработчика, а не безопасно работают в production-кластере. Модель учится на том, что встречается чаще, и чаще встречается самый простой вариант: контейнер от root, без лимитов ресурсов, с широким сетевым доступом. Для демонстрации удобно, для боевого кластера опасно.
Отсюда типовой набор дефектов в сгенерированном коде: старые apiVersion, отсутствие securityContext, небезопасные значения по умолчанию, упрощённые пробники из примеров приветственного приложения. Добавьте к этому результат исследования 2026 года: Dockerfile оказался худшим типом артефакта по уровню уязвимостей, а образ контейнера напрямую определяет, что попадёт в кластер.
Типичные ошибки LLM в Kubernetes-манифестах
Список ниже не исчерпывающий, но покрывает то, что встречается в сгенерированных манифестах чаще всего.
Устаревшие apiVersion и молчаливое игнорирование полей
apiVersion задаёт схему, по которой API server разбирает объект. Модель может подставить группу, которую видела в старом туториале, например extensions/v1beta1 для Deployment вместо apps/v1. Дальше два сценария. Если API-группы в кластере уже нет, kubectl apply вернёт ошибку, и вы её заметите. Если группа существует, но набор и смысл полей изменились, лишние или устаревшие поля отбрасываются тихо, и вы получите объект не с той конфигурацией, которую писали.
Проверять apiVersion нужно не «вообще», а под версию конкретного кластера: схемы 1.27 и 1.31 различаются. kubeconform с флагом -strict и явным указанием версии ловит и неизвестные поля, и несоответствие версии, о чём подробнее ниже.
Отсутствие securityContext и небезопасные значения по умолчанию
securityContext управляет правами процесса внутри контейнера. В сгенерированных манифестах его часто нет целиком. Значит, нет runAsNonRoot, нет readOnlyRootFilesystem, нет ограничения capabilities. По умолчанию контейнер получает права root внутри своего namespace, а это первое, что используют после компромисса приложения.
Вторая группа дефектов - небезопасные значения по умолчанию прямо в манифесте: privileged: true, hostNetwork: true, тома через hostPath, отсутствие resources.requests и limits. Последнее бьёт и по безопасности, и по стабильности: без лимитов один прожорливый Pod вытесняет соседей по ноде.
Третья группа - пробники. Модель часто копирует health-check из туториала: путь /healthz, порт 8080, таймауты по умолчанию. В вашем сервисе эндпоинт может называться иначе, а порт отличаться. Тогда probe либо всегда успешен, либо всегда неуспешен, и в обоих случаях бесполезен. Синтаксически такой манифест корректен, именно поэтому он и опасен.
Практический конвейер проверки сгенерированных манифестов
Три этапа, и каждый ловит свой класс ошибок. Порядок имеет значение: сначала схема, затем политики, затем сервер.
Шаг 1: kubeconform -strict под версию кластера
kubeconform сверяет манифест со схемами Kubernetes. Флаг -strict превращает неизвестные поля в ошибку, то есть ловит именно то, что API server может отбросить молча. Явное указание версии привязывает проверку к реальному кластеру, а не к абстрактной последней схеме.
kubeconform -strict -kubernetes-version 1.27.0 deployment.yaml
Что ловит: устаревшие apiVersion, опечатки в именах полей, поля, которых нет в этой версии. Чего не ловит: смысловые ошибки. Манифест с runAsNonRoot: false или с probe на несуществующий путь пройдёт проверку без замечаний.
Шаг 2: OPA/Rego-политики через conftest
OPA и язык Rego описывают правила, которым должен соответствовать манифест. conftest применяет эти правила к YAML и возвращает ненулевой код выхода при нарушении, что удобно для CI. Минимальный пример политики, требующей запуска не от root:
package main
deny[msg] {
input.kind == "Deployment"
not input.spec.template.spec.securityContext.runAsNonRoot
msg := "securityContext.runAsNonRoot обязателен"
}
Запуск политик по файлу манифеста:
conftest test deployment.yaml
Набор правил команда пишет сама: запрет privileged, обязательные resources.limits, белый список registry, запрет hostNetwork, требование тегов образов вместо latest. Универсального готового набора нет, и это главная сложность шага: политики нужно поддерживать вместе с инфраструктурой, иначе они устаревают и начинают пропускать то, что раньше ловили.
Шаг 3: kubectl --dry-run=server
Локальные инструменты работают с YAML, а API server работает с объектами. --dry-run=server отправляет манифест в кластер, прогоняет его через валидацию и admission controllers, но не сохраняет результат. Вы видите реакцию настоящего API server, включая подстановку значений по умолчанию и отклонения от webhook-политик.
kubectl apply --dry-run=server -f deployment.yaml
Что ловит: несоответствие версии кластера, конфликты с admission controller, отклонённые в webhook политики, ошибки в ссылках на ConfigMap и Secret. Нужен доступ к кластеру и права на соответствующие операции, поэтому шаг не всегда выполняется с ноутбука разработчика.
Как встроить проверку в CI/CD
Все три шага укладываются в один stage валидации, который идёт перед деплоем и останавливает сборку или merge при ошибке. Логичная последовательность: kubeconform по схеме целевого кластера, затем conftest с набором политик, затем kubectl --dry-run=server против тестового или dev-кластера.
Что стоит учесть. Для dry-run в CI нужен доступ к кластеру и отдельный service account с ограниченными правами, иначе учётные данные пайплайна сами становятся вектором атаки. Версия схемы в kubeconform должна совпадать с версией кластера, куда вы деплоите, иначе проверка теряет смысл. Политики conftest разумно держать в том же репозитории, что и манифесты, чтобы любое изменение правил проходило ревью.
Проверка манифестов - часть более широкой задачи: собрать разрозненные сканеры в единый контур с кросс-валидацией и убрать шум ложных срабатываний, иначе разработчики отключают проверки целиком, и вместе с шумом уходит сигнал.
Ограничения конвейера и что он не заменяет
kubeconform проверяет синтаксис и схему, но не смысл. conftest проверяет только те правила, которые вы написали, и ничего не знает о том, что в политике забыли. kubectl --dry-run=server зависит от версии кластера и настроек admission controllers: на другом кластере результат будет другим.
Самый неприятный класс ошибок остаётся за пределами автоматики: логически неверная, но синтаксически корректная конфигурация. Liveness probe с таймаутом в одну секунду в сервисе, который прогревается сорок секунд, пройдёт все три шага и будет перезапускать здоровый Pod по кругу. Probe на путь, который всегда возвращает 200, тоже пройдёт проверку и не заметит зависший процесс.
Поэтому конвейер дополняют, а не подменяют: ревью человеком, тестирование в staging, метрики и алерты после деплоя. Автоматика отсекает массовые и очевидные дефекты, человек отвечает за соответствие сервиса его реальному поведению под нагрузкой.
Вывод: ИИ - это черновик, а не готовый продукт
LLM ускоряют работу с Kubernetes заметно: каркас Deployment, Service и Ingress появляется за секунды, и возвращаться к ручному набору YAML уже не хочется. Проблема в том, что модель выдаёт усреднённый паттерн из туториалов, а средний уровень уязвимостей в сгенерированном инфраструктурном коде в исследовании 2026 года составил 57,5%.
Рабочая схема выглядит так: генерация ускоряет черновик, слой проверки отвечает за допуск в кластер. Связка из трёх шагов (kubeconform -strict под версию кластера, политики OPA/Rego через conftest, kubectl --dry-run=server) закрывает разрыв между генерацией и развертыванием и стоит дешевле, чем разбор инцидента в production.
Тот же принцип работает шире: ИИ предлагает, человек утверждает, детерминированный инструмент применяет только допущенное. Методология Agent-Ops 0.4.0 описывает это как формальный процесс с отдельной ролью Guardian и правилом «unknown не равно OK»: неизвестный результат проверки не считается успешным. Для Kubernetes-манифестов это ровно тот случай, когда непройденная или непонятная проверка означает запрет на деплой, а не повод разобраться потом.