The Daily Newsstand · Free, Always
Monday, October 26, 2026

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

Translate

Язык сценариев NKT — построчный декларативный язык развёртывания. Он не пытается быть полноценным языком программирования: в нём нет условий, циклов и функций. Это осознанный выбор.

Принцип: язык решает одну задачу — «Разверни сервис, проверь, что он работает, и перенеси, если нужно». Для этой задачи не нужны сотни абстракций. Нужны 29 глаголов.

Каждый глагол покрывает атомарное действие:

  • Установить пакет

  • Управлять службой

  • Развернуть стек

  • Создать ВМ

  • Перенести контейнер

  • Проверить доступность

Из 29 глаголов собираются сценарии любой сложности. Если нужна логика сложнее — есть описания сервисов и внешние плагины, которые расширяют язык без изменения его ядра.

Все 29 команд

Переменные и параметры

Команда

Синтаксис

Пример

set

set ИМЯ значение или set ИМЯ ask

set IMAGE ubuntu-24.04 set DB_PASS ask

param

param ИМЯ "подсказка"

param HOST "целевой хост"

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

group ИМЯ [profile ПРОФИЛЬ]

group web-farm profile web-base

host

host ИМЯ АДРЕС[:ПОРТ] user ПОЛЬЗОВАТЕЛЬ …

host web1 192.0.2.10 user root password ask

install

install ХОСТ…

install web1 web2

wait

wait ХОСТ online [ДЛИТЕЛЬНОСТЬ]

wait web1 online 5m

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

На хосте: пакеты, службы, файрвол

Команда

Синтаксис

Пример

packages

on ХОСТ… packages install|remove ПАКЕТ…

on web1 packages install nginx htop

service

on ХОСТ service ИМЯ start|stop|restart|reload|enable|disable

on web1 service nginx restart

firewall

on ХОСТ firewall allow|deny ПОРТ[/tcp|udp] [from CIDR]

on web1 firewall allow 443/tcp on web1 firewall allow 5432 from 10.0.0.0/24

Пакеты ставятся одной транзакцией. Файрвол работает через 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 install

on ХОСТ docker install

on web1 docker install

docker stack

on ХОСТ docker stack ПУТЬ up|down|start|stop

on web1 docker stack /srv/app/docker-compose.yml up

vm create

on ХОСТ vm create ИМЯ image ОБРАЗ [опции]

on web1 vm create app1 image ubuntu-24.04 cpu 2 mem 2048 disk 20 install

vm start/shutdown/destroy

on ХОСТ vm start|shutdown|destroy ИМЯ

on web1 vm start app1

docker stack имеет четыре действия:

  • up — поднять (скачав образы, если нужно)

  • down — остановить и убрать

  • start — запустить имеющееся без обращения к registry

  • stop — остановить, оставив контейнеры

С блоком до 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

Команда

Синтаксис

Пример

file put

on ХОСТ file put ПУТЬ [mode 0644] + блок до end

on web1 file put /etc/motd Готово end

user add

on ХОСТ user add ИМЯ [sudo] [key "…"]

on web1 user add deploy sudo key "ssh-ed25519 …"

system

on ХОСТ system hostname|timezone|locale|ntp ЗНАЧЕНИЕ

on web1 system timezone Europe/Moscow

cert

on ХОСТ cert issue|renew ДОМЕН…

on web1 cert issue example.org

git clone

on ХОСТ git clone URL /КАТАЛОГ [опции]

on web1 git clone https://github.com/org/app.git /srv/app branch main token ask

cert issue работает через certbot --standalone: службы на 80/443 останавливаются на время проверки и запускаются снова.

git clone для приватных репозиториев: token ask (спросят при запуске) или key hub (deploy-ключ хоста).

На хосте: применение профилей и Kubernetes

Команда

Синтаксис

Пример

apply profile

on ХОСТ apply profile ПРОФИЛЬ

on web2 apply profile web-base

k8s create

on ХОСТ k8s create ИМЯ [опции]

on hv1 k8s create lab flavor kubeadm nodes "hv1: cp 1, w 2" cni cilium expose

k8s destroy

on ХОСТ k8s destroy ИМЯ

on hv1 k8s destroy lab

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

pipeline ИМЯ ... end

pipeline hello repo: https://github.com/org/app.git action: compose compose: file: docker-compose.yml hosts: [web1] env: DB_PASSWORD: generate:password end

deploy

deploy КОНВЕЙЕР [start|stop] [ДЛИТЕЛЬНОСТЬ]

deploy hello 20m deploy shop stop deploy shop start

migrate

migrate ХОСТ КОНТЕЙНЕР to ХОСТ [опции]

migrate web1 app-1 to web2 switch-fly switch-pipeline

pipeline сохраняет конвейер выкладки. Значения env: уходят в секреты конвейера. generate: создаёт значение (пароль, токен) на хабе.

deploy выкладывает конвейер и ждёт итога. С start или stop — запускает или останавливает уже выложенное на всех его хостах.

migrate переносит стек на другой хост: план с препятствиями до остановки стека, остановка, данные через хаб, запуск на новом. Упавший перенос откатывается сам. Опции: switch-fly, switch-pipeline, clean-leftovers.

Kubernetes: Ingress, манифесты, ожидание

Команда

Синтаксис

Пример

k8s ingress

k8s КЛАСТЕР ingress cilium [port ПОРТ]

k8s lab ingress cilium port 8080

k8s apply

k8s КЛАСТЕР apply + блок до end

k8s lab apply apiVersion: v1 kind: Namespace metadata: name: demo end

k8s wait

k8s КЛАСТЕР wait deployment ИМЯ [опции]

k8s lab wait deployment hello 5m

k8s apply в сухом прогоне выполняет kubectl diff и показывает изменения без применения.

Traffic Fly

Команда

Синтаксис

Пример

fly route

fly route ИД ВИД [опции]

fly route hello https names hello.example.com tls gateway targets cluster lab port 8080

fly apply

fly apply

fly apply

Виды маршрутов: 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

Что делает язык в этом сценарии:

  1. param — спрашивает хосты и домен при запуске

  2. pipeline — сохраняет конвейер с Docker Compose и генерацией паролей

  3. deploy — выкладывает и ждёт до 20 минут

  4. wait url — проверяет доступность через шлюз с проверкой сертификата

  5. migrate — переносит контейнер с переключением маршрутов и конвейера

  6. Финальная проверка доступности

47 строк. Вместо связки docker-compose.yml + deploy.sh + migrate.sh + healthcheck.sh.

Использование через веб-интерфейс

Где найти

Профили → вкладка «Сценарии» → «Новый сценарий»

Редактор

  1. «Готовые сценарии ▾» — выпадающий список с примерами (Forgejo, Kubernetes). Пример вставляется целиком, с вопросом, если в тексте уже есть свои правки.

  2. «?» — значок в заголовке окна редактора и на карточке сценария открывает страницу «Команды сценариев» на сайте документации.

  3. Вкладка «Справка» — показывает ту же страницу с сайта. Переключатель «Сайт / Из nkt» — встроенная справка, если сайт недоступен. Выбор запоминается.

  4. Проверка — кнопка анализирует синтаксис и показывает ошибки. Имена хостов из параметров (on ${HOST}) не ругаются при проверке — они сверяются при запуске. Предупреждения не мешают сохранить и выполнить.

  5. История версий — таблица: №, время, автор, заметка, размер, метка «текущая». У каждой редакции: «Содержимое» (текст целиком), «Разница» с текущей или предыдущей, «Вернуть в редактор».

  6. Кнопка «ИИ ▾» — три действия:

    • Объяснить — разбор написанного кода

    • Написать по # ИИ: — генерация кода из комментариев

    • Написать по описанию — создание сценария из текстового описания задачи

Результат вставляется через дифф — пользователь проверяет и подтверждает. Сценариям от ИИ предупреждения показываются как ошибки на исправление.

Запуск

При нажатии «Запустить»:

  1. Диалог с полями для незаполненных param

  2. Чекбокс Сухой прогон — проверка без применения

  3. Журнал задания — пошаговый лог с таймстампами

Использование через 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).

Ограничения для токенов: создавать и править сценарии по токену нельзя. Только запускать сохранённые.

Ошибки

Сообщение

Причина

Script.Unresolved

Незаполненные параметры

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 глаголов, которые реально нужны.

А когда нужна расширяемость — описания сервисов и плагины добавляют новые команды без изменения ядра языка.

Ссылки:

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.