
Привет! Я Даниил, DevOps-инженер в KTS.
Недавно я поработал со Stackland. Концепция облачноподобной платформы с кучей коробочных сервисов на своем железе в закрытом контуре давно была любопытна нам (и нашим заказчикам), так что пройти мимо готового решения от Яндекса было бы преступлением. По горячим следам я решил написать эту статью, чтобы поделиться опытом развертывания Stackland на голом железе. Так что ниже вас ждет скорее не обзор, а туториал.
Коротко о том, как это было: сначала я подготовил L2-сеть, бастион с NAT, DNS и NTP, три сервера, образ Stackland и YAML-конфиги. Затем запустил sladm install и через час получил консоль, готовый мониторинг с визуализацией в Grafana и возможность по кнопке в UI поднять managed PostgreSQL, Kafka, ClickHouse и другие сервисы.
Подробнее о том, как это было, рассказываю ниже.
Оглавление
Немного о том, кому и зачем нужен Stackland
Платформа решает узкую задачу: Kubernetes и managed-сервисы Яндекса внутри вашего периметра без сборки из Helm-чартов.
Смотреть на нее имеет смысл, если данные должны оставаться в своем ЦОДе или арендованном bare metal. Или если команда уже работает с PostgreSQL, Kafka и ClickHouse в экосистеме Яндекса, а легаси живет отдельно в on-prem.
При этом Stackland вряд ли уместен, если вам хватает одного маленького кластера без PaaS. Тогда проще обойтись k3s или managed K8s в облаке. Или если Kubernetes вообще не нужен.
Что нужно подготовить
Чтобы не плодить серверы, я решил не разделять control-plane и worker-ноды, сразу пошел через combined. Согласно доке, на каждую ноду с ролью combined нужно:
32 vCPU;
64 GB RAM;
два SSD от 100 GB.
Отдельно нужен бастион на Ubuntu 22.04+, для него хватит минимальной конфигурации. Между нодами нужна L2-связность в приватной /24, адреса можно раздать DHCP или прописать статикой. Еще нужны лицензия key.json от Яндекса, Docker на бастионе и sladm из дистрибутива Stackland. И baseDomain кластера, само собой. У нас это kts.internal, поэтому дальше по тексту я буду везде подставлять его. Если будете следовать моему мануалу, подставляйте свой.
Схема сети
Делим /24 на три зоны (рекомендация из доки):
10.0.0.0/24 ├── 10.0.0.0/25 — IP хостов (ноды, бастион) ├── 10.0.0.127 — VIP API Kubernetes (свободный IP) └── 10.0.0.128/25 — пул LoadBalancer (Cilium L2) Pod CIDR: 172.16.0.0/16 Service CIDR: 10.96.0.0/12 [ Internet ] | [ Бастион ] NAT + BIND + Chrony + VPN 10.0.0.3 | +----------------+----------------+ | | | node1 node2 node3 10.0.0.4 10.0.0.5 10.0.0.6 combined combined combined
Шаг 1. Подсеть и серверы в BareMetal
1.1. VRF (виртуальный сетевой сегмент)
Сначала создаем VRF. Это изолированный L2-домен для кластера.

В консоли Yandex BareMetal открываем раздел сетей и жмем Создать VRF. Мы назвали сегмент stackland, описание оставили пустым. Все серверы кластера потом вешаем только на этот VRF.
1.2. Приватная подсеть

BareMetal → Приватные подсети → Создать подсеть. На скриншоте уже есть 10.0.0.0/24 в пуле m4, привязанная к VRF stackland. При создании указываем CIDR, шлюз (первый IP подсети), включаем DHCP и задаем диапазон для нод.
У нас получилось так: пул ru-central1-m4, CIDR 10.0.0.0/24, шлюз 10.0.0.1, DHCP до .120, VRF с прошлого шага.
1.3. Бастион
Берем минимальную готовую конфигурацию. Нагрузка на бастионе небольшая.

BareMetal → Заказать сервер → выбираем пул и тариф из каталога. Для бастиона хватает минимального. Он проксирует трафик, отдает DNS/NTP и с него же запускаем sladm.

На шаге Доступ и сеть подключаем приватную подсеть из п. 1.2 и включаем эфемерный публичный IP. На шаге ОС ставим Ubuntu 24.04, добавляем SSH-ключ и пароль root. Жмем Заказать и ждем провижининг.
1.4. Три ноды кластера
Как я уже говорил, для прода нужны 32 vCPU, 64 GB RAM и 2 SSD. На тестовом стенде CPU можно урезать, но второй диск обязателен. Заказываем три сервера без ОС, в той же подсети, без публичного IP, все с ролью combined. Потом грузим их с ISO Stackland.
1.5. Загрузочный образ Stackland
Актуальные релизы лежат тут.
Мы ставили 26.1.5, чтобы потом сравнить с апгрейдом до 26.2.x. ISO лежит в публичном бакете:
https://storage.yandexcloud.net/stackland-public/stackland/26.1.5/images/stackland-amd64-26.1.5.iso
Загружаем образ в BareMetal → Пользовательские образы.
Шаг 2. Настройка бастиона
2.1. VPN и сетевые доступы
Ставим VPN-клиент (WireGuard или Firezone), чтобы ходить в приватную сеть и к DNS на бастионе. Про Firezone я, к слову, подробно рассказывал в этой статье. Многое там до сих пор актуально, так что можете свериться с ней при необходимости.
В security groups открываем подсеть BareMetal 10.0.0.0/24 и подсеть VPN-клиентов.
2.2. UFW и NAT
Без NAT ноды не скачают образы при install. В /etc/ufw/before.rules до блока *filter добавляем:
*nat :POSTROUTING ACCEPT [0:0] -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE -A POSTROUTING -s 100.64.0.0/10 -j MASQUERADE COMMIT

По SSH заходим на бастион, открываем /etc/ufw/before.rules и вставляем блок nat с MASQUERADE, как на скриншоте. Так ноды и VPN-клиенты выходят в интернет через бастион. После правки: ufw disable && ufw enable.
Правила UFW:
ufw disable ufw allow 22/tcp comment 'SSH' ufw allow from 10.0.0.0/24 to any port 53,123 proto udp ufw allow from 10.0.0.0/25 to any port 53 proto tcp ufw allow from 172.16.0.0/16 to any port 53 proto tcp ufw allow from 172.16.0.0/16 to any port 53 proto udp ufw enable
Последние две строки критичны. Они пускают pod-сеть 172.16.0.0/16 к DNS на бастионе. Без них CoreDNS не достучится до BIND, и IAM/YDB упадут на install.
2.3. Пакеты
sudo apt update sudo apt install -y bind9 bind9utils dnsutils chrony unzip docker.io
2.4. BIND
/etc/bind/named.conf.options:
options { directory "/var/cache/bind"; forwarders { 77.88.8.8; 77.88.8.1; }; listen-on { 10.0.0.3; }; dnssec-validation auto; listen-on-v6 { any; }; };

В /etc/bind/named.conf.options BIND слушает только IP бастиона (listen-on), внешние запросы уходят на DNS Яндекса (77.88.8.8). Дальше создаем зоны с именами нод и домена кластера.
Зона internal в /etc/bind/db.internal. NS на бастионе, делегирование домена кластера:
$TTL 1H @ IN SOA ns1.internal. admin.internal. ( 2025091801 3H 30M 1W 5M) @ IN NS ns1.internal. ns1.internal. IN A 10.0.0.3 kts.internal. IN NS node1.baremetal.internal. kts.internal. IN NS node2.baremetal.internal. kts.internal. IN NS node3.baremetal.internal.
В /etc/bind/db.baremetal.internal прописываем A-записи нод и сервисов YDB:
$TTL 15M @ IN SOA ns1.internal. admin.internal. ( 2025091801 3H 30M 1W 5M) @ IN NS ns1.internal. node1 IN A 10.0.0.4 node2 IN A 10.0.0.5 node3 IN A 10.0.0.6 ydb-storage-0 IN A 10.0.0.4 ydb-storage-1 IN A 10.0.0.5 ydb-storage-2 IN A 10.0.0.6

db.internal делегирует kts.internal на ноды. В db.baremetal.internal лежат node1..3 и обязательные ydb-storage-0..2. Подключаем зоны в named.conf.local, гоняем named-checkconf и перезапускаем bind9.
/etc/bind/named.conf.local:
zone "internal" { type master; file "/etc/bind/db.internal"; }; zone "baremetal.internal" { type master; file "/etc/bind/db.baremetal.internal"; };
sudo named-checkconf sudo systemctl restart bind9
2.5. Chrony
В /etc/chrony/chrony.conf:
allow 10.0.0.0/24 bindaddress 10.0.0.3

В chrony.conf разрешаем запросы времени с подсети кластера (allow 10.0.0.0/24) и привязываем сервис к IP бастиона (bindaddress). В cluster.yaml ноды Talos указывают этот адрес в timeservers.
sudo systemctl restart chrony
Шаг 3. ISO на каждую ноду
Через iKVM/IPMI монтируем ISO Stackland как виртуальный CD/DVD.

В консоли BareMetal открываем сервер → KVM / iKVM → Virtual Media → подключаем ISO → Reboot to cdrom. Так делаем на каждой из трех нод. В Talos жмем F3 → Network Configuration, задаем hostname, IP, gateway, interface и записываем MAC для hosts.yaml. MAC также виден в обзоре сервера в BMS. Ждем Talos Maintenance на всех трех нодах.
Шаг 4. Проверка DNS перед install
Перед sladm install проверяем, что BIND слушает 10.0.0.3:53, UFW пускает pod-сеть 172.16.0.0/16 на порт 53, а dig @10.0.0.3 node1.baremetal.internal и dig @10.0.0.3 ydb-storage-0.baremetal.internal отвечают. VPN-клиент должен ходить в DNS бастиона для *.kts.internal.
Шаг 5. Конфигурация sladm
На бастионе заводим каталог stackland/:
stackland/ ├── cluster.yaml # StacklandClusterConfig ├── hosts.yaml # StacklandHostsList └── secrets.yaml # секреты, хранить отдельно
cluster.yaml
cluster.yaml
apiVersion: stackland.yandex.cloud/v1alpha1 kind: StacklandClusterConfig metadata: name: main spec: platform: type: "baremetal" loadBalancer: type: "cilium-l2" ipPools: - cidrs: - 10.0.0.128/25 cluster: baseDomain: "kts.internal" networking: hostsNetwork: - cidr: 10.0.0.0/25 clusterNetwork: - cidr: 172.16.0.0/16 servicesNetwork: - cidr: 10.96.0.0/12 virtualIPs: api: 10.0.0.127 storage: defaultStorageClass: "stackland-ssd" genericHostConfig: disksConfig: - installDisk: name: "/dev/sda" # проверьте через lslbk на ноде - dataDisk: name: "/dev/sdb" # обязательно второй диск networkConfig: addresses: - interface: "eth0" dhcp: true routes: - to: "0.0.0.0/0" via: "10.0.0.3" iface: "eth0" resolvers: - "10.0.0.3" timeservers: - "10.0.0.3"
hosts.yaml
hosts.yaml
apiVersion: stackland.yandex.cloud/v1alpha1 kind: StacklandHostsList metadata: name: main spec: hosts: - hostname: "node1.baremetal.internal" role: "combined" networkConfig: interfaces: - macaddress: "d0:0d:2a:7b:a4:f0" # MAC из BMS / Talos F3 name: "eth0" addresses: - interface: eth0 ip: 10.0.0.4/24 - hostname: "node2.baremetal.internal" role: "combined" networkConfig: interfaces: - macaddress: "d0:0d:13:78:ed:9d" name: "eth0" addresses: - interface: eth0 ip: 10.0.0.5/24 - hostname: "node3.baremetal.internal" role: "combined" networkConfig: interfaces: - macaddress: "d0:0d:62:20:cf:db" name: "eth0" addresses: - interface: eth0 ip: 10.0.0.6/24
Шаблон в репозитории: stackland-configs/stackland-baremetal.yaml.example.
Перед install:
./sladm validate stackland/
Шаг 6. sladm install
./sladm install stackland/ --installation-timeout 4h0m0s
iKVM лучше держать открытым на всех нодах весь install. Он занимает около часа, в течение которого устанавливаются Talos, Cilium, LoadBalancer, IAM, консоль и готовый мониторинг. Talos иногда зависает на сети, и без консоли не видно, на какой ноде это случается. Если падает IAM, чаще всего CoreDNS не резолвит ydb-storage-*, и Job ydb-storage-pdisk-set-active уходит в timeout. Детальнее я разберу этот нюанс ниже, в разделе про ошибки.
Шаг 7. Проверка

С бастиона через VPN запускаем kubectl get nodes. Ждем три ноды в Ready. Если статус другой, смотрим iKVM и логи sladm install.
Дальше проверяем kubeconfig и DNS:
cat ~/.kube/config nslookup auth.sys.kts.internal

sladm install кладет kubeconfig в ~/.kube/config. nslookup auth.sys.kts.internal с бастиона должен отвечать. Если NXDOMAIN или timeout, возвращаемся к BIND и UFW.
Открываем https://grafana.sys.kts.internal. На дашбордах Node Exporter и etcd должны быть все три ноды.

В Grafana (логин из секретов кластера) смотрим Node Exporter: CPU, память, диски. Метрики должны идти со всех нод.

На дашборде etcd все три подняты. Если одна нода упадет, кластер продолжит жить, но установку и апгрейд провести не получится: они будут падать по таймауту, потому что ожидают все три ноды в строю.
Консоль Stackland: https://console.sys.kts.internal, логин admin@stackland.

В разделе Компоненты IAM, Ingress, Monitoring, Volumes и остальное в статусе Ready. Install на этом можно считать завершенным.

На главном экране 3 из 3 узлов работают, версия 26.2.0. Отсюда же заказываем managed PostgreSQL, Kafka и ClickHouse.
Самое сладкое: managed-сервисы из консоли
После install можно поднять PaaS из UI без отдельной установки операторов.
PostgreSQL 18

Консоль → Managed PostgreSQL → Создать кластер. Задаем имя, версию, количество хостов, storage class, лимиты CPU/RAM и размер диска. Оператор сам развернет StatefulSet.

Через несколько минут кластер в статусе running. У нас три инстанса, storage class stackland-other, лимиты от 250m/512Mi до 2 CPU/2Gi, диск 2Gi с авторасширением.
Apache Kafka 4.1

Консоль → Managed Kafka → Создать кластер. Задаем имя, количество брокеров, storage class и ресурсы на брокер и контроллер. Все так же, платформа поднимет оператор Kafka сама.

На вкладке События сначала может мелькнуть timeout на PVC, пока TopoLVM готовит том. Если он висит долго, проверьте свободное место на data-дисках.
ClickHouse 26.3

Консоль → Managed ClickHouse → Создать кластер. По уже сложившейся традиции задаем имя, версию, шарды, ресурсы и диск. Для демонстрации я взял минимальную конфигурацию без Keeper и бэкапов.
Что получилось не с первого раза
Небольшой раздел о граблях, на которые можно наступить в процессе.
CoreDNS направляет запросы на бастион. UFW должен пускать 172.16.0.0/16 на порт 53. Иначе в логах появится
dial tcp 10.0.0.3:53: i/o timeout, а IAM/YDB не поднимутся.В зоне
baremetal.internalмало прописать толькоnode1..3. Без A-записейydb-storage-0..2IAM не стартует.В
disksConfigнужны иinstallDisk, иdataDisk. С одним диском YDB не поднимет storage pool, в логах будетReasonBootBSError.На bare metal диски обычно
/dev/sdaи/dev/sdb, не/dev/vda, как на VM. Проверяемlsblkв Maintenance.При делении /24 не ставьте IP нод в пул LoadBalancer
128/25и зарезервируйте VIP API отдельно.iKVM лучше не закрывать на весь install. Talos может зависнуть на сети, и без консоли непонятно, где именно.
Подробнее про первый пункт. Install может сломаться на IAM из-за DNS или дисков. Job ydb-storage-pdisk-set-active уходит в timeout, если CoreDNS не достучится до BIND. В этом случае проверьте UFW для 172.16.0.0/16 и A-записи ydb-storage-*. Unresolved host ydb-storage-0.baremetal.internal может означать, что зона BIND дописана не до конца. BS_PROXY_DISCOVER и ReasonBootBSError почти всегда про отсутствие dataDisk в конфиге. Если через час видим Reconciliation failed, открываем логи CoreDNS и stackland-iam.
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50 kubectl logs -n stackland-iam -l app=ydb-storage-0 --tail=100 dig @10.0.0.3 ydb-storage-0.baremetal.internal
Первое приложение (Ingress)
На этом пункте не буду останавливаться подробно, благо документация полностью его покрывает. По туториалу expose-app-domain разворачиваем hello-world за Ingress. Когда у балансировщика появится внешний IP, добавляем DNS-запись *.sys.main.kts.internal.
Что еще есть в Stackland и не вошло в статью
Вместе с Talos и Cilium при sladm install поднимаются IAM, Object Storage, Ingress с cert-manager, Prometheus, Grafana, Loki, OpenBao, Kyverno, TopoLVM и Cilium L2 Load Balancer. Полный список в документации. Я в статье описал мониторинг и косвенно TopoLVM (когда Kafka ждала PVC).
Помимо PostgreSQL, Kafka и ClickHouse, в PaaS еще входят Trino, YTsaurus, Iceberg REST Catalog и векторные БД под RAG. DataLens и SpeechSense идут отдельными лицензиями. OpenSearch и расширение Trino Яндекс обещают подвезти в недалеком будущем.
Итог
Надеюсь, туториал получился не слишком сумбурным, и при необходимости вы сможете с его помощью развернуть Stackland на своем железе.
На BareMetal мы подняли VRF и подсеть, настроили бастион с NAT/BIND/Chrony, поставили три combined-ноды через ISO и sladm, разобрались с DNS и storage, проверили кластер и заказали PostgreSQL, Kafka и ClickHouse из консоли. Для старта работы этого более чем достаточно, а с остальным сложностей возникнуть не должно. Разве что харденинг под продакшен заслуживает отдельной статьи, но это уже выходит за рамки моего туториала.
Если столкнетесь с трудностями, пишите в комментарии, постараюсь подсказать. А если есть желание почитать о других проектах и экспериментах нашей команды, предлагаю вашему вниманию:
r_nastuha
Вау! Как интересно. Спасибо за статью!