RE:NODE

AI-агенты для кода в работе с серверами и DevOps: польза и вред

Автор Команда RE:NODE
16 мин чтения


AI-агенты для кода хорошо справляются с инфраструктурной работой, у которой давно известная форма: Dockerfile для приложения на Node или Python, unit systemd, серверный блок Nginx перед приложением, CI-пайплайн, который ставит зависимости, тестирует и собирает, миграция базы данных и тесты вокруг всего этого. Они выдают такое за секунды, и обычно результат близок к верному. Они плохи в деталях, меняющихся между версиями - флагах, ключах конфигурации, значениях по умолчанию - и неверное говорят с той же уверенностью, что и верное. А получив shell, они выполнят разрушительную команду так же спокойно, как ls. Правило, по которому мы работаем, короткое: агент пишет, инструмент проверяет, человек читает каждую команду, а агент никогда не держит продакшен-доступы. Всё ниже - о том, как это правило работает на практике.

AI-агенты для кода стали обычной частью разработки, а работа с серверами на хостинг-платформе вроде RE:NODE даёт нам мнение о том, что с сервером можно делать, а что нельзя. Этот текст - эти мнения. Историй из жизни в нём нет, только принципы, к которым мы пришли.

Где агенты быстры и правы#

Агенты хорошо делают то, что публично писалось десять тысяч раз с небольшими вариациями. Это удивительно большая часть DevOps.

  • Dockerfile для типовых стеков. Многоэтапная сборка приложения на Node, Go, Python или .NET с лёгким runtime-образом, не-root пользователем и установкой зависимостей, кэшируемой до копирования исходников. Агенты хорошо знают этот паттерн и расставляют слои правильно чаще, чем уставший человек.
  • Unit systemd. ExecStart, WorkingDirectory, User, Restart=on-failure, EnvironmentFile, секция [Install]. Если попросить усиление защиты, получится разумный набор из ProtectSystem=, NoNewPrivileges= и родственных параметров.
  • Блоки Nginx reverse proxy. proxy_pass, forwarded-заголовки, заголовки апгрейда WebSocket, редирект с HTTP на HTTPS. Типовой случай решается надёжно.
  • CI-пайплайны. Workflow GitHub Actions, который забирает код, настраивает runtime, кэширует зависимости, запускает lint и тесты и собирает артефакт. Агенты пишут его быстрее, чем получается найти последний репозиторий, где это уже делалось.
  • Миграции и тесты. По схеме и изменению они пишут миграцию и, что полезнее, тесты, фиксирующие поведение. От тестов больше всего пользы на минуту, потому что падающий сгенерированный тест - информация в любом случае.
  • Склеивающие скрипты. Помощники ротации логов, обёртки бэкапов, скрипт проверки срока сертификата. Короткий Bash и Python с понятными входами и выходами.

Общее у них: форма стабильна годами, ошибки видны сразу при запуске или проверке, а цена неверного первого черновика - минута просмотра. Это зона, где агент - однозначный выигрыш, и возвращаться к ручному написанию такого не хочется.

Есть и второй, менее заметный выигрыш: чтение. Агент, которому разрешено читать репозиторий, каталог конфигурации и несколько логов, а затем объяснять происходящее, часто быстрее, чем самостоятельный поиск по системе, которой касались месяцы назад. Чтение ничего не может сломать. Подробнее об этом ниже.

Где агенты уверенно ошибаются#

Сбой не в том, что агенты выдают бессмыслицу. Они выдают правдоподобную конфигурацию, которая была верной когда-то, для какой-то версии, на каком-то дистрибутиве, и ничто в выводе не говорит, какая часть устарела.

За какими шаблонами мы следим:

  • Выдуманные флаги. Флаг CLI, который звучит так, будто должен существовать, в стиле именования инструмента, но которого нет. Он выглядит точно так же, как настоящие флаги вокруг.
  • Устаревшие ключи. Конфигурация, которая была верной несколько мажорных версий назад. Модель видела гораздо больше старой документации, чем новой, поэтому старые формы представлены чаще.
  • Смешанные версии. Файл, где половина директив из одной мажорной версии, а половина из другой. Каждая строка выглядит нормально; такого сочетания никогда не существовало.
  • Размытие дистрибутивов. Пути и имена пакетов из Debian в файле для машины семейства RHEL или наоборот.
  • Молчаливые значения по умолчанию. Опция, явно выставленная в то, что агент считает значением по умолчанию, хотя оно изменилось, или пропущенная важная опция, потому что агент решил, что значение по умолчанию есть, а его нет.

Несколько настоящих примеров дрейфа, которые агенты воспроизводят и которые можно сверить с upstream-документацией:

Что пишет агентАктуальная формаПочему это важно
listen 443 ssl http2;listen 443 ssl; и http2 on;Nginx 1.25.1 объявил параметр устаревшим
ssl on;ssl в строке listenУстарело с Nginx 1.15.0, позже удалено
version: "3.8" в Composeне указыватьCompose игнорирует и предупреждает
MemoryLimit= в unitMemoryMax=Старое имя из эпохи cgroup v1
apt-key addkeyring в signed-by=apt-key объявлен устаревшим
echo "::set-output ..."запись в $GITHUB_OUTPUTGitHub объявил команду устаревшей

Ничто из этого не экзотика. В этом и суть. Неверная форма - та, которой годами пользовалась большая часть интернета, поэтому модель тянется к ней первой. Часть таких вариантов всё ещё работает с предупреждением, и это хуже отказа: файл попадает в репозиторий, предупреждение уходит в лог, который никто не читает, а следующее обновление удаляет старую форму.

Уверенность агента ничего не говорит о его правоте. Неверный ключ конфигурации и верный приходят с одинаково спокойной интонацией.

Неприятно и то, что агент будет защищать неверный ответ, если спросить "ты уверен?". Иногда он исправляется, иногда упорствует, а иногда извиняется и превращает верную строку в неверную. Вопрос к модели - не проверка. Проверка - запуск собственного валидатора инструмента.

Разрушительные команды и почему опаснее всего shell#

Запись файла легко отменить. Выполнение команды часто нет. Переход от "агент правит файлы в репозитории" к "агент выполняет команды на машине" - место настоящего риска, и люди делают этот шаг особенно небрежно.

Команды, которые мы считаем "никогда без человека", кто бы их ни предлагал:

  • rm -rf с переменной в пути. Если переменная пуста, rm -rf "$DIR/" превращается в rm -rf /. Агенты пишут такой шаблон, потому что его пишут люди.
  • docker system prune -a --volumes или docker volume rm. В томах живут базы данных. "Освободи место на диске" - вполне разумная просьба, которая заканчивается пустой базой.
  • docker compose down -v. Ключ -v удаляет именованные тома. Выглядит как безобидный флаг порядка.
  • git reset --hard, git clean -fdx, git push --force. Нормально на черновой ветке, губительно на общей или на сервере с незакоммиченной локальной конфигурацией.
  • DROP, TRUNCATE, DELETE без WHERE или миграция, которая удаляет столбец в том же релизе, где перестаёт в него писать.
  • chown -R и chmod -R не из того каталога.
  • Изменения firewall по SSH. ufw enable до ufw allow OpenSSH или iptables -F для цепочки с политикой по умолчанию DROP - и сессия, в которой вы печатаете, пропала.
  • terraform apply или ansible-playbook без предварительного просмотра плана или проверочного прогона.

Допустим, агента просят исправить "диск заполнен" на машине в три часа ночи. Быстрее всего освободить место, удалив самое большое, а самое большое на сервере очень часто - те данные, которые вы обязаны хранить. Человек бы тут засомневался. Агент, оптимизирующий "задача выполнена", не сомневается, если не устроить всё так, чтобы он не мог.

Случай с firewall заслуживает отдельной заметки, потому что он разрушает не данные, а доступ. Если агента вообще подпускают к firewall, безопасный порядок такой: сначала разрешить SSH и держать вторую сессию открытой на время включения - эта последовательность описана в руководстве по UFW.

Ограничения: сначала только чтение#

Самое полезное ограничение ничего не стоит: начинайте каждую инфраструктурную сессию с того, что агент читает, а не делает.

Чтение - это просмотр файлов конфигурации, список сервисов, чтение логов, запуск команд статуса. На сервере это такие вещи, как systemctl status, journalctl -u app --since "1 hour ago", ss -tlnp, df -h, docker ps, nginx -T. Ни одна из них не меняет состояние. Дайте агенту составить картину и предложить изменение в виде diff или списка команд, а решайте сами.

В Claude Code есть режим планирования, в котором он читает и предлагает без правок, а и Claude Code, и Cursor можно настроить так, чтобы они спрашивали перед запуском команд в терминале. Для всего, что касается инфраструктуры, оставляйте это включённым. В Claude Code можно пойти дальше с правилами разрешений в settings.json: разрешить команды только для чтения, чтобы они не спрашивали, и запретить те, что не должны выполняться без присмотра:

.claude/settings.json
{  "permissions": {    "allow": [      "Bash(systemctl status:*)",      "Bash(journalctl:*)",      "Bash(docker ps:*)",      "Bash(nginx -t)",      "Bash(git diff:*)"    ],    "deny": [      "Bash(rm -rf:*)",      "Bash(docker system prune:*)",      "Bash(git push --force:*)",      "Read(./.env)",      "Read(./secrets/**)"    ]  }}

Список запретов - ремень безопасности, а не стена. Команду можно сформулировать по-разному, а настойчивая цепочка shell обойдёт совпадение по префиксу. Относитесь к этим правилам как к способу остановить типичную случайность, а настоящую защиту держите там, где ей место: в том, до чего среда агента вообще может дотянуться.

Ограничения: пробные прогоны, просмотр и копии для staging#

После режима чтения следующий слой - чтобы любое изменение сначала показывало, что оно сделает. У большинства серьёзных инфраструктурных инструментов для этого есть режим, и агенту стоит каждый раз велеть им пользоваться.

bash
$ terraform plan -out=change.plan$ ansible-playbook site.yml --check --diff$ rsync -av --delete --dry-run ./site/ deploy@host:/var/www/site/$ kubectl apply -f deploy.yaml --dry-run=server$ kubectl diff -f deploy.yaml

Дальше читайте каждую команду перед запуском. Каждую. Это звучит медленно, но не медленно, потому что большинство команд в сессии - чтение, а прочитать однострочную команду - секунды. Те, что меняют состояние, стоят этих секунд. Как только заметите, что подтверждаете, не читая, переведите агента обратно в режим, где он просит план.

Просматривайте diff, а не резюме. Агенты хорошо пишут резюме своих изменений, и иногда резюме не совпадает с diff. "Обновлена конфигурация Nginx для добавления кэширования" может идти вместе с удалённым блоком location, о котором не сказано. Источник истины - git diff.

Последний слой - где работает агент. Агенты должны работать с локальной копией репозитория, контейнером или копией staging, но никогда не напрямую с продакшеном. Копия staging - вторая среда той же формы, что и продакшен, со своими доступами и своими данными, где неверная команда стоит пересборки, а не извинений. О практической стороне написано в статье staging и продакшен на одном аккаунте и, для игровых серверов, в тестовый сервер рядом с продакшеном. Там агент может работать так быстро, как хочет. Продвижение в продакшен - отдельный скучный шаг с проверкой человеком: merge и пайплайн деплоя, а не агент с SSH.

Никогда не давайте агенту продакшен-доступы#

Если оставить только одно правило, пусть будет это.

Сессия агента читает много текста, который вы не писали: исходники зависимостей, README, комментарии в issue, веб-страницы, строки логов, сообщения об ошибках сторонних сервисов. Любой из них может содержать инструкции, а современные модели можно направлять инструкциями, спрятанными в читаемом содержимом. Это prompt injection, и полного решения ни у кого нет. Если добавить к нему доступы и shell, вопрос не в том, добронамеренен ли агент, а в том, добронамерен ли каждый кусок текста, который он прочитает.

Поэтому практические правила:

  • Никаких URL продакшен-баз, облачных ключей, SSH-ключей к продакшен-хостам и токенов деплоя в окружении агента. Ни в .env, который он может прочитать, ни в истории shell, ни вставленными в чат "только в этот раз".
  • Агент работает с доступами staging, ограниченными staging, которые можно слить без большого вреда.
  • Деплой в продакшен идёт через CI со своими секретами, запускаемый merge, одобренным человеком. Агент может написать пайплайн. Запускать его он не должен.
  • Если агенту нужно посмотреть на продакшен, он читает выгруженные логи или метрики либо человек выполняет команду только для чтения и вставляет вывод.

Если серверы управляются через панель, а не по чистому SSH, минимальные привилегии работают так же: давайте каждому человеку и каждому инструменту только нужный доступ и только на нужное время. Идея изложена в субпользователи и минимальные привилегии, а работа с самими секретами - в переменные окружения и секреты.

Как проверять сгенерированную конфигурацию#

У каждого достойного формата конфигурации есть проверяющий инструмент, и главное - запускать его до того, как что-либо перезагрузится. Просите агента запускать валидатор как часть изменения, а его вывод читайте сами.

ФайлЧем проверятьЧто ловит
Конфигурация Nginxnginx -tСинтаксис, неизвестные директивы
Unit systemdsystemd-analyze verify app.serviceНеизвестные ключи, неверные пути
Dockerfilehadolint DockerfileПлохие практики, фиксация версий
Файл Composedocker compose configСхема, подстановки
GitHub ActionsactionlintВыражения, устаревший синтаксис
Shell-скриптshellcheck script.shКавычки, пустые переменные
Конфигурация sshdsshd -tСинтаксис до того, как вы заперли себя снаружи
sudoersvisudo -cСломанный файл, убивающий sudo

Валидация ловит синтаксис и неизвестные ключи. Она не ловит конфигурацию, которая валидна и неверна: proxy_pass не на тот порт, unit, который стартует от root, потому что пропущен User=, кэш, сохраняющий ответы с cookie. Для этого вручную проверяются три вещи:

  1. Каждый незнакомый флаг и ключ сверяется с upstream-документацией для реально установленной версии. Сначала nginx -v, docker --version, systemctl --version, затем документация этой версии. Здесь умирают выдуманные флаги.
  2. Что происходит при сбое. Что будет, если приложение упадёт, upstream недоступен, диск заполнится. Unit с Restart=always и без ограничений бесконечно перезапускает сломанное приложение; это может быть тем, что нужно, а может и нет.
  3. От кого запускается и до чего может дотянуться. Пользователь, права на файлы, открытые порты, смонтированные тома. Агенты по умолчанию стремятся к рабочему, а не к закрытому, так что настройка безопасности остаётся за человеком.

Затем изменение уходит в staging и там проверяется в деле: сервис чисто перезапускается, проверка работоспособности проходит, логи тихие. Только после этого тот же файл идёт в продакшен обычным путём. Для конкретных форматов сверять сгенерированное стоит с руководством по systemd, руководством по Nginx reverse proxy и Docker Compose для небольших стеков.

Миграциям, CI и тестам - больше подозрительности#

Эти три вещи выглядят как обычный код, поэтому проходят обычный review. Они заслуживают большего.

Миграции - это код, который выполняется один раз над единственной копией ваших данных. Агент охотно напишет миграцию, переименовывающую столбец, что в живой системе для работающего приложения равно удалению и добавлению. Он напишет backfill, блокирующий большую таблицу. Он напишет down-миграцию, которая не может восстановить то, что удалила up. Наше правило: каждая сгенерированная миграция выполняется на восстановленной копии данных реального размера, прежде чем приблизиться к продакшену, а разрушающие изменения разносятся по релизам - перестать использовать столбец, выкатить, затем удалить. Миграции без простоя описывают шаблон expand-and-contract, и агента стоит явно заставлять ему следовать, потому что по умолчанию он этого не делает. А восстановленная копия существует только если ваши бэкапы восстанавливаются, и это отдельная дисциплина: бэкапы, которые действительно восстанавливаются.

CI-пайплайны работают с секретами. Сгенерированный workflow, использующий pull_request_target, забирающий код pull request и запускающий его, отдал секреты репозитория всем, кто откроет pull request. Workflow, фиксирующий сторонние actions по подвижному тегу, а не по коммиту, доверяет тому, кто этим тегом управляет. Агенты выдают оба шаблона, потому что оба распространены в публичных репозиториях. Читайте триггеры и блок разрешений у каждого сгенерированного workflow и явно задавайте permissions: по минимуму.

Тесты - самое тонкое. Агенту, которого просят "сделай, чтобы тесты проходили", доступны два пути: исправить код или поменять тест. Иногда он молча выбирает второй - ослабляет проверку, добавляет пропуск, подменяет моком именно то, что тестируется. Зелёный набор тестов от агента не доказательство, пока вы не посмотрели, что изменилось в тестах. При просмотре diff агента изменения в тестах читаются первыми.

Как устроен цикл работы с AI#

Когда агенты - основной цикл сборки, а не помощник, работа человека меняется. Писать приходится меньше, просматривать больше, и просмотр и есть настоящая работа. Принципы, которым подчиняется цикл:

  • Небольшие изменения, которые легко проверить. Одна задача на изменение. Агент с удовольствием отрефакторит скрипт деплоя, Dockerfile и CI-пайплайн за один заход; лучше три diff, каждый из которых читается за несколько минут.
  • Утверждения агента - не доказательство. "Проверено" ничего не значит, пока не виден запуск команды и её вывод. "Этот флаг существует" ничего не значит, пока не видна документация.
  • Валидаторы и тесты внутри цикла, а не после него. Агент сам запускает nginx -t, shellcheck, набор тестов и читает ошибки. Это ловит большую часть дрейфа ещё до того, как человек увидит diff.
  • Инфраструктурные изменения идут тем же пайплайном, что и код. Никаких срочных правок на серверах руками агента. Если изменение стоит делать, оно стоит коммита.
  • За merge и продакшен отвечают люди. Агент может предложить что угодно. Выпустить он не может ничего.
  • Записывайте правила проекта для агента. Короткий файл инструкций в репозитории - какие команды безопасны, какие среды существуют, чего никогда не трогать - работает лучше, чем напоминания в каждой сессии.

Отдача реальна. Типовая инфраструктура, которая раньше съедала полдня, теперь - несколько минут просмотра, и это время уходит на то, что требует суждения: каковы режимы отказа, где живут данные, у кого есть доступ. Агент ускоряет простую часть. Сложная не исчезает, и делать вид, что исчезает, - способ превратить быстрый цикл в быстрый способ всё сломать.

Что не стоит отдавать агенту#

Кое-что остаётся за человеком не потому, что агент не способен выдать результат, а потому что цена правдоподобного неверного ответа слишком высока и его слишком трудно заметить:

  • Реагирование на инцидент в живой системе. Агент под давлением "почини немедленно" - ровно та обстановка, где возможен разрушительный сокращённый путь. Он может читать логи и предлагать причины; команды набирает человек.
  • Всё необратимое на реальных данных. Восстановление поверх живой базы, удаления, изменения политики хранения. Это достаточно редко, чтобы выигрыш в скорости был небольшим, а возможный ущерб неограниченным.
  • Проектирование контроля доступа. Кто до чего может дотянуться, политика firewall, роли IAM. Синтаксис агенты пишут хорошо, а политику плохо, обычно склоняясь к тому, что убирает ошибку.
  • Обновления версий базовой инфраструктуры. Мажорные версии баз данных, изменения ядра и init. Знания агента слабее всего там, где заметки о выпуске новее всего.

Всё остальное допустимо, если на месте описанные ограничения.

FAQ#

Что лучше для DevOps: Claude Code или Cursor?

Оба справляются с одними и теми же основными задачами, и модель за ними важнее редактора. Для инфраструктуры важен контроль над выполнением команд: подтверждение перед запуском, правила разрешений и запретов, режим планирования только для чтения. Выбирайте тот, чьи средства контроля вы действительно оставите включёнными.

Можно ли позволить агенту заходить на мой сервер по SSH?

На одноразовую машину staging - да, с подтверждением команд по одной. На продакшен - нет. Изменение в продакшене должно быть проверенным коммитом, который выкатывает ваш пайплайн, а если агенту нужна информация из продакшена, человек может выполнить команду только для чтения и передать вывод.

Как не дать агенту выдумывать параметры конфигурации?

Не дать нельзя, можно только поймать. Сообщите агенту точные установленные версии, пусть после каждого изменения запускает собственный валидатор инструмента, а любой незнакомый ключ или флаг сверяйте с документацией для этой версии. Сильно помогает и вставка нужного раздела актуальной документации в сессию.

Безопасны ли Dockerfile, сгенерированные AI?

Типовые шаблоны обычно да, после проверки. Убедитесь, что базовый образ зафиксирован на реальном актуальном теге, что финальный этап работает от не-root пользователя, что секреты не копируются внутрь и не передаются как build-аргументы, и прогоните hadolint. Затем соберите образ и запустите в staging как любой другой.

Пусть агент пишет и тесты, и код?

Да, это одно из самых полезных его занятий. Но в каждом diff читайте изменения в тестах раньше изменений в коде, потому что агент, стремящийся к зелёному результату, может ослабить проверку или пропустить тест, а не исправить ошибку.