Философия: почему 29, а не 290
Язык сценариев NKT — построчный декларативный язык развёртывания. Он не пытается быть полноценным языком программирования: в нём нет условий, циклов и функций. Это осознанный выбор.
Принцип: язык решает одну задачу — «Разверни сервис, проверь, что он работает, и перенеси, если нужно». Для этой задачи не нужны сотни абстракций. Нужны 29 глаголов.
Каждый глагол покрывает атомарное действие:
-
Установить пакет
-
Управлять службой
-
Развернуть стек
-
Создать ВМ
-
Перенести контейнер
-
Проверить доступность
Из 29 глаголов собираются сценарии любой сложности. Если нужна логика сложнее — есть описания сервисов и внешние плагины, которые расширяют язык без изменения его ядра.
Все 29 команд
Переменные и параметры
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
set создаёт переменную для подстановки как ${ИМЯ}. Вариант ask спрашивает значение при запуске и не записывает его в текст и журнал — для паролей и токенов.
param создаёт параметр, значение которого спрашивается при запуске с подсказкой. Так один сценарий разворачивает разные стенды. Незаполненные параметры сверяются при запуске (Script.Unresolved).
Пример:
set IMAGE ubuntu-24.04set DB_PASS askparam ADDR "адрес нового сервера"param NAME "имя хоста в хабе"host ${NAME} ${ADDR} user root password ask
Хосты и группы
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
group создаёт группу хостов на хабе. С профилем — машины, создаваемые в ней, делаются по нему.
host добавляет хост на хаб по SSH — то же, что «Добавить хост» в интерфейсе. Варианты аутентификации: password ask, password "…", key hub.
install ставит nkt на перечисленные хосты и ждёт, пока каждый ответит.
wait имеет пять вариантов:
wait ХОСТ online [ДЛИТЕЛЬНОСТЬ]wait ХОСТ port ПОРТ [ДЛИТЕЛЬНОСТЬ]wait ХОСТ http URL [КОД] [ДЛИТЕЛЬНОСТЬ]wait ХОСТ service ИМЯ active [ДЛИТЕЛЬНОСТЬ]wait url URL [КОД] [ДЛИТЕЛЬНОСТЬ]
Последний вариант — проверка адреса снаружи: запрос с хаба по настоящему имени (DNS, шлюз, сертификат проверяются), хост указывать не нужно.
Пример:
host web1 192.0.2.10 user root password ask group web-farmhost web2 192.0.2.11:2222 user deploy key hubinstall web1 web2wait web1 online 5mwait web1 port 443 30swait web1 service nginx activewait url https://git.example.com/api/healthz 200 10m
На хосте: пакеты, службы, файрвол
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Пакеты ставятся одной транзакцией. Файрвол работает через ufw или firewalld — в зависимости от того, что установлено на хосте.
Пример:
on web1 web2 web3 packages install nginx htopon web1 web2 web3 service nginx enableon web1 web2 web3 service nginx starton web1 web2 web3 firewall allow 80/tcpon web1 web2 web3 firewall allow 443/tcp
На хосте: Docker и ВМ
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docker stack имеет четыре действия:
-
up— поднять (скачав образы, если нужно) -
down— остановить и убрать -
start— запустить имеющееся без обращения к registry -
stop— остановить, оставив контейнеры
С блоком до end команда сначала записывает compose-файл по указанному пути:
on web1 docker stack /srv/app/docker-compose.yml upservices: web: image: nginx:alpine ports: - "8080:80"end
Стек, выложенный конвейером, запускают и останавливают командой deploy КОНВЕЙЕР start|stop — «Проверить» об этом напомнит.
vm create создаёт виртуальную машину из облачного образа. Опции: cpu, mem, disk, user, network, profile, install (поставить nkt внутрь).
На хосте: файлы, пользователи, система, сертификаты, git
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cert issue работает через certbot --standalone: службы на 80/443 останавливаются на время проверки и запускаются снова.
git clone для приватных репозиториев: token ask (спросят при запуске) или key hub (deploy-ключ хоста).
На хосте: применение профилей и Kubernetes
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
k8s create — самая «тяжёлая» команда языка. Создаёт кластер на виртуальных машинах: машины из облачного образа, control plane, присоединение узлов, ожидание Ready, проброс портов, скачивание kubeconfig на хаб.
Опции: flavor (k3s или kubeadm), nodes (размещение по хостам), image, cpu/mem/disk, network, cni, no-kube-proxy, version, expose, api/http/https порты.
Конвейеры, выкладки, переносы
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
pipeline сохраняет конвейер выкладки. Значения env: уходят в секреты конвейера. generate: создаёт значение (пароль, токен) на хабе.
deploy выкладывает конвейер и ждёт итога. С start или stop — запускает или останавливает уже выложенное на всех его хостах.
migrate переносит стек на другой хост: план с препятствиями до остановки стека, остановка, данные через хаб, запуск на новом. Упавший перенос откатывается сам. Опции: switch-fly, switch-pipeline, clean-leftovers.
Kubernetes: Ingress, манифесты, ожидание
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
k8s apply в сухом прогоне выполняет kubectl diff и показывает изменения без применения.
Traffic Fly
|
Команда |
Синтаксис |
Пример |
|---|---|---|
|
|
|
|
|
|
|
|
Виды маршрутов: https, http, tcp, udp, tcpudp. Цели: targets cluster КЛАСТЕР port N (все узлы кластера) или target ХОСТ container|vm|lxd ИМЯ ПОРТ (конкретная цель).
Новый маршрут записывается и применяется. У существующего цели меняются плавно: новые добавляются и проверяются, прежние выводятся. В сухом прогоне — проверка карты с маршрутом.
Реальный пример: полный цикл
Из репозитория, examples/scripts/forgejo-fly.nkt:
# Forgejo за Traffic Fly: выкладка и перенос на другой хостparam FROM "хост, где Forgejo будет выложен сначала"param TO "хост, куда его перенести"param DOMAIN "имя Forgejo — A-запись на адрес шлюза Traffic Fly"pipeline forgejo repo: https://github.com/piqab/nkt.git ref: main action: compose compose: file: examples/forgejo/deploy/docker-compose.yml project: forgejo hosts: [${FROM}] wait_timeout: 5m env: POSTGRES_PASSWORD: generate:password FORGEJO_DOMAIN: ${DOMAIN} FORGEJO_ROOT_URL: https://${DOMAIN}/ FORGEJO_ADMIN_USER: gitadmin FORGEJO_ADMIN_PASSWORD: generate:password fly: - route: forgejo-web kind: https names: [${DOMAIN}] tls: gateway service: forgejo target_port: 3000 check: http check_path: /api/healthz - route: forgejo-ssh kind: tcp port: 2222 service: forgejo target_port: 2222enddeploy forgejo 20mwait url https://${DOMAIN}/api/healthz 200 10mmigrate ${FROM} forgejo-forgejo-1 to ${TO} switch-fly switch-pipelinewait url https://${DOMAIN}/api/healthz 200 5m
Что делает язык в этом сценарии:
-
param— спрашивает хосты и домен при запуске -
pipeline— сохраняет конвейер с Docker Compose и генерацией паролей -
deploy— выкладывает и ждёт до 20 минут -
wait url— проверяет доступность через шлюз с проверкой сертификата -
migrate— переносит контейнер с переключением маршрутов и конвейера -
Финальная проверка доступности
47 строк. Вместо связки docker-compose.yml + deploy.sh + migrate.sh + healthcheck.sh.
Использование через веб-интерфейс
Где найти
Профили → вкладка «Сценарии» → «Новый сценарий»
Редактор
-
«Готовые сценарии ▾» — выпадающий список с примерами (Forgejo, Kubernetes). Пример вставляется целиком, с вопросом, если в тексте уже есть свои правки.
-
«?» — значок в заголовке окна редактора и на карточке сценария открывает страницу «Команды сценариев» на сайте документации.
-
Вкладка «Справка» — показывает ту же страницу с сайта. Переключатель «Сайт / Из nkt» — встроенная справка, если сайт недоступен. Выбор запоминается.
-
Проверка — кнопка анализирует синтаксис и показывает ошибки. Имена хостов из параметров (
on ${HOST}) не ругаются при проверке — они сверяются при запуске. Предупреждения не мешают сохранить и выполнить. -
История версий — таблица: №, время, автор, заметка, размер, метка «текущая». У каждой редакции: «Содержимое» (текст целиком), «Разница» с текущей или предыдущей, «Вернуть в редактор».
-
Кнопка «ИИ ▾» — три действия:
-
Объяснить — разбор написанного кода
-
Написать по
# ИИ:— генерация кода из комментариев -
Написать по описанию — создание сценария из текстового описания задачи
-
Результат вставляется через дифф — пользователь проверяет и подтверждает. Сценариям от ИИ предупреждения показываются как ошибки на исправление.
Запуск
При нажатии «Запустить»:
-
Диалог с полями для незаполненных
param -
Чекбокс Сухой прогон — проверка без применения
-
Журнал задания — пошаговый лог с таймстампами
Использование через API
Запуск сценария
# Список сценариевcurl -H "Authorization: Bearer $TOKEN" \ https://hub.example.com/api/hub/scripts# Запускcurl -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ https://hub.example.com/api/hub/scripts/42/run \ -d '{ "answers": { "param:FROM": "web1", "param:TO": "web2", "param:DOMAIN": "forgejo.example.com" }, "dry_run": true }'
Права токенов
|
Роль |
Может |
|---|---|
|
Чтение |
Просмотр сценариев, запуск сухого прогона |
|
Администратор |
Запуск, перенос, применение |
Пределы — хосты и группы, к которым токен имеет доступ. Сценарии проверяются по разбору до запуска: хосты/группы вне предела — отказ (scriptScopeError).
Ограничения для токенов: создавать и править сценарии по токену нельзя. Только запускать сохранённые.
Ошибки
|
Сообщение |
Причина |
|---|---|
|
|
Незаполненные параметры |
|
|
Разбор до запуска: хосты/группы вне предела |
|
«Хост вне пределов API-токена» |
Хост не в хостах или группах токена |
Интеграция с n8n
Готовые ноды для n8n:
|
Узел |
Действия |
|---|---|
|
Script → Run |
Запуск сохранённого сценария с ответами и сухим прогоном |
|
Script → Get Many |
Список сценариев |
|
Migration → Preview |
План переноса |
|
Migration → Move Stack |
Выполнение переноса |
|
Cluster → Apply |
Применение манифестов |
|
Cluster → Diff Manifest |
Diff манифестов |
Ответы на параметры передаются как JSON: {"param:FROM": "web1"}.
Расширяемость языка
Описания сервисов
Помимо 29 встроенных команд, язык расширяется через описания сервисов — YAML-файлы, которые добавляют поддержку новых сервисов без перекомпиляции.
name: myappdetect: any: - file: /etc/myapp/config.yml - unit: myapp.servicesources: - type: file path: /etc/myapp/config.yml format: yamlextract: endpoints: - each: $.listeners[*] address: $.address port: $.port
Описания живут в /etc/netknownsthat/services.d/, в профиле хаба или встроены в nkt. Старшее описание перекрывает младшее. disabled: true выключает описание.
Встроенные описания: Traefik, RabbitMQ, Redis, PostgreSQL, Mosquitto, Envoy — с правилами безопасности.
Встроенные разборщики (nginx, HAProxy, Caddy, Docker, Podman, LXD, libvirt, Kubernetes, firewall, fail2ban) — тоже описания с engine:. Их можно выключить или настроить через описание на хосте или в профиле.
Внешние плагины
Плагины добавляют новые команды в скриптовый язык:
# Команда от плагинаplugin cloudflare-dns create-record example.com A 192.0.2.1plugin mail create-domain example.complugin llm list-models
Плагины подключаются через конвейеры и предоставляют:
-
Команды сценариев (
plugin ИМЯ СЛОВО …) -
Инструменты MCP для ИИ-агентов
-
Разделы в веб-интерфейсе
ИИ-интеграция
Ассистент в редакторе
Кнопка «ИИ ▾» в редакторе сценариев:
-
Объяснить — разбор написанного кода
-
Написать по
# ИИ:— генерация кода из комментариев -
Написать по описанию — создание сценария из текстового описания задачи
Результат вставляется через дифф — пользователь проверяет и подтверждает.
Ассистент хаба
Переписка с ассистентом: окно запуска, имена ссылками с меню, избранное. Имена объектов — ссылки с меню по типу: хост, контейнер и служба, конвейер, сценарий, кластер, маршрут, группа.
MCP-сервер
Хаб предоставляет ~40 инструментов для ИИ-агентов (Claude Code, Claude Desktop, Cursor):
-
Хосты, находки, службы, контейнеры
-
Конвейеры (создание, правка, сухой прогон)
-
Задания, сценарии, переносы
-
Traffic Fly, кластеры
Каждый инструмент — вызов того же API под тем же токеном, права и пределы — токена.
Честные ограничения языка
Чего нет
❌ Условий (if/else) — язык линейный
❌ Циклов (for) — для нескольких хостов используется перечисление
❌ Функций — повторное использование через готовые сценарии
❌ Состояния между запусками — каждый запуск изолирован
❌ Языка выражений — нет арифметики, конкатенации строк
Почему это осознанный выбор
Язык решает одну задачу: линейное развёртывание с проверками. Для сложной логики есть описания сервисов и внешние плагины.
Принцип: если в языке нет условия — значит, вам оно не нужно для этой задачи.
Заключение
29 команд. Одна страница документации. Пример к каждой. Готовые сценарии в редакторе. Сухой прогон перед применением. Журнал с таймстампами. История версий. ИИ-ассистент. Описания сервисов. Плагины.
Это и есть «Краткость — сестра таланта» в инженерном исполнении: не добавлять абстракции, а убирать всё лишнее, пока не останутся только те 29 глаголов, которые реально нужны.
А когда нужна расширяемость — описания сервисов и плагины добавляют новые команды без изменения ядра языка.
Ссылки:
-
👉 GitHub: github.com/piqab/nkt
-
📚 Документация: github.io/nkt
-
📚 Справочник: github.io/nkt/guide/script-commands
ссылка на оригинал статьи https://habr.com/ru/articles/1099564/