NKT: Киллер фича №7, скрипты: краткость — сестра таланта

Язык сценариев NKT — построчный декларативный язык развёртывания. Он не пытается быть полноценным языком программирования: в нём нет условий, циклов и функций. Это осознанный выбор.
Принцип: язык решает одну задачу — «Разверни сервис, проверь, что он работает, и перенеси, если нужно». Для этой задачи не нужны сотни абстракций. Нужны 29 глаголов.
Каждый глагол покрывает атомарное действие:
Установить пакет
Управлять службой
Развернуть стек
Создать ВМ
Перенести контейнер
Проверить доступность
Из 29 глаголов собираются сценарии любой сложности. Если нужна логика сложнее — есть описания сервисов и внешние плагины, которые расширяют язык без изменения его ядра.
Все 29 команд
Переменные и параметры
Команда | Синтаксис | Пример |
|---|---|---|
|
|
|
|
|
|
set создаёт переменную для подстановки как ${ИМЯ}. Вариант ask спрашивает значение при запуске и не записывает его в текст и журнал — для паролей и токенов.
param создаёт параметр, значение которого спрашивается при запуске с подсказкой. Так один сценарий разворачивает разные стенды. Незаполненные параметры сверяются при запуске (Script.Unresolved).
Пример:
set IMAGE ubuntu-24.04
set DB_PASS ask
param 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-farm
host web2 192.0.2.11:2222 user deploy key hub
install web1 web2
wait web1 online 5m
wait web1 port 443 30s
wait web1 service nginx active
wait url https://git.example.com/api/healthz 200 10m
На хосте: пакеты, службы, файрвол
Команда | Синтаксис | Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
Пакеты ставятся одной транзакцией. Файрвол работает через ufw или firewalld — в зависимости от того, что установлено на хосте.
Пример:
on web1 web2 web3 packages install nginx htop
on web1 web2 web3 service nginx enable
on web1 web2 web3 service nginx start
on web1 web2 web3 firewall allow 80/tcp
on web1 web2 web3 firewall allow 443/tcp
На хосте: Docker и ВМ
Команда | Синтаксис | Пример |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
docker stack имеет четыре действия:
up— поднять (скачав образы, если нужно)down— остановить и убратьstart— запустить имеющееся без обращения к registrystop— остановить, оставив контейнеры
С блоком до end команда сначала записывает compose-файл по указанному пути:
on web1 docker stack /srv/app/docker-compose.yml up
services:
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: 2222
end
deploy forgejo 20m
wait url https://${DOMAIN}/api/healthz 200 10m
migrate ${FROM} forgejo-forgejo-1 to ${TO} switch-fly switch-pipeline
wait 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: myapp
detect:
any:
- file: /etc/myapp/config.yml
- unit: myapp.service
sources:
- type: file
path: /etc/myapp/config.yml
format: yaml
extract:
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.1
plugin mail create-domain example.com
plugin 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
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.