惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fox-IT International blog
Recent Announcements
Recent Announcements
D
Docker
IT之家
IT之家
B
Blog
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
量子位
C
Check Point Blog
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
博客园 - 司徒正美
李成银的技术随笔
美团技术团队
Blog — PlanetScale
Blog — PlanetScale
雷峰网
雷峰网
The GitHub Blog
The GitHub Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
T
The Blog of Author Tim Ferriss
酷 壳 – CoolShell
酷 壳 – CoolShell
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
L
LangChain Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
T
Tailwind CSS Blog
H
Help Net Security
Engineering at Meta
Engineering at Meta
小众软件
小众软件
B
Blog RSS Feed
Stack Overflow Blog
Stack Overflow Blog
月光博客
月光博客
M
Microsoft Research Blog - Microsoft Research
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Last Week in AI
Last Week in AI
Martin Fowler
Martin Fowler
Stack Overflow Blog
Stack Overflow Blog

Все публикации подряд на Хабре

iPad как инструмент разработчика в эпоху агентного программирования Как мы осваивали производство гибко-жёстких печатных плат: от проб и ошибок к рабочей технологии 30 лет мы внедряли в России Ansys. А потом он ушёл — и пришлось садиться писать собственный CAE для аддитивной печати Цифровой рубль и цифровой чек Облако под защитой от DDoS: чем On-Demand отличается от Always-On Распродажа в издательстве «Питер» Почему современный стадион больше похож на ЦОД, чем на арену Машина, которая учится думать Запихнули игровую приставку в короб и в первый же месяц продали на 3 млн Игровой ноутбук vs игровой ПК за те же деньги: что изменилось в 2026 году ГИС для Minecraft. Часть 1 Смена старого оборудования на новое убирает огромные затраты на его эксплуатацию — но куда девать всё это старое? Project Manager 2026: как AI-инструменты меняют профессию SLA как инструмент, а не отчёт. Часть 1. Как подружить бизнес и инженеров через общие цифры Послания от ангелов и первый шаг к компьютерам: стеганография Средневековья и Ренессанса Что новенького есть в CSS в 2026 году? Хватит мучить ChatGPT. Почему ваш промпт не сработает Как и зачем мы писали семантический слой для ИИ аналитики – SLayer Маленькая EVPN/VXLAN-фабрика без тупика: как мы запускали площадку в Амстердаме Репликация по DDIA: что я понял, только когда сам сломал прод RAG без downtime: настраиваем инкрементальное обновление документов на Qdrant и LangChain Тени истории: война машин. Как «Энигма» и «Фиалка» определили исход Второй мировой войны Как ускорить распознавание объектов нейросетями среди множества классов, не жертвуя памятью и точностью Как я хотел две странички для SAMBA и NFS, а сделал полноценную панель управления NAS на 20+ страницах Kubernetes для баз данных? CloudNativePG делает PostgreSQL по-настоящему Cloud-Native Как мы анализировали поведение пользователей Яндекс Музыки на 50 млн событий Как я разработал PoC-конструктор для приложений Android Стек российского сисадмина в 2026 Как я сделал обычную посудомоечную машину умной, с Home Assistant/ESPHome Контент-завод в 2026 году: разбор автономных систем, который отговорит вас это покупать I just want an agent. Часть 2. Как мы построили виртуальную команду для разработки ИИ-агентов Прототип игры с помощью Flowith: личный опыт и ограничения AI-инструмента Что вы не знаете об альтернативных документах, удостоверяющих личность Программные модули в DATAREON Platform: выносим повторяющуюся логику в C# функции Не прячьте интерфейс в код: защищаем внешний вид как промобразец, изобретение и товарный знак Могут ли взрослые учить язык как дети? Технология многовидового представления в nanoCAD BIM Строительство DRAйверы для GPU: как Kubernetes научился выделять устройства через стандартный API Рубрика эксперименты (в дизайне): наш опыт генерации и проверки гипотез Как повысить KPI сотрудников с помощью ИИ-агентов Визуализация данных как язык XXI века: от аналитики к сторителлингу Gradle под капотом: как перестать страдать и заставить сборку летать На что предприниматели делают ставку для роста в кризис? Результаты исследования Go-to-Market Academy Улучшить качество фото — задача на 1 минуту в SpeShu.AI Анти-кейс: внедрили Битрикс24, через полгода клиент вернулся на самописную CRM. Разбор ошибок Браузеры подстраиваются под большие сайты Почему ломается ваш AI-агент — и почему смена модели обычно его не чинит Распределённая система мониторинга и аналитики присутствия людей в инфраструктурных объектах без использования камер Почему российские компании остаются на серой Jira [Перевод] Тонус в реактивных системах Факап инженера АСУ ТП, как мы перепутали физические COM-порты на подстанции Как уместить DOOM в QR-код Cache is hard — почему инвалидация кэша — это проблема согласованности, а не производительности Щелевая коррозия: порча нержавейки и «ржавые» имплантаты — почему это происходит? Строим первую линию техподдержки на n8n за 250$ в месяц. Часть 2 Покопались в .cursorrules на GitHub и нашли там волка-фурри, Star Trek и 28.7% копипасты Не наступайте на наши грабли, если собираетесь использовать Temporal Как создать дебат-клуб в компании: пошаговое руководство от бизнес-тренера Как экономят на метановых автозаправках Самодельный elgato-like макропад. Часть 1, железная Всё есть код, или зачем внедрять GitOps в разработку Как получить root на Urovo DT40 Pro (CT48): Android 12 (Проверено на практике) C# мне нравится больше Java. Но в банковском enterprise мне всё равно понадобилась Java Биткоин на Московской бирже — что это? Как мы переводим миллионы iOS-пользователей на новое приложение каждые несколько месяцев Кейс. Zero Bug Policy: как мы снизили бэклог багов в 4 раза за месяц Shadow AI: 80% сотрудников уже пишут в ChatGPT. Почему мы делим задачи на красные, зелёные и серые Попытка пересмотреть ограничения рынка тяжелых БАС: нужен ли вообще кому-то легкий и дешевый электромотор Менеджер, который хакнул систему. И что AI на самом деле умножает Spec-driven development в микросервисах, часть 2: как archspec делает контекст сервисов явным Запись в Kubernetes: как контроллеры учились не перезаписывать друг друга Игровой движок 2.5D, короткие тренировки для ПК-пользователей –и еще 8 российских стартапов MCP в системе управления проектами: как поручить ИИ работу с корпоративными данными Бэклог болей: как hh работает с тем, что не нравится пользователям brec: контролируемая обратная совместимость протокола AI обнулил benchmark и пытался шантажировать инженера. И почему это решаемо Почему пластиковый корпус оказался в 3 раза дороже металлического Как спроектировать API, которое не придется переписывать через полгода Трекинг посетителей на fisheye-камерах: задача “со звездочкой” Красивый скриншот вашего кода. Большое обновление Я создаю проекты без единого созвона с командой Content Pipeline в MonoGame: почему я его не использую Гемблинг партнерки: Как выбрать, ТОП 5 в 2026 За пределами LLM, часть 2: якорная таблица Кэли, которая не является ни полем, ни моноидом Pixverse купить подписку: для чего нужна Пиксверс подписка, как выбрать тариф и оплатить в рублях Meshy AI нейросеть: как создавать 3D-модели из текста и изображений в Меши АИ на русском бесплатно Skywork AI: как использовать Скайворк АИ нейросеть на русском бесплатно, работать с промтами и создавать видео Технотекст 8: победа естественного интеллекта Capacitor: от веба к мобильным приложениям. Часть 4. Интегрируем локальный LLM в проект 20 лет видеокарт в цифрах: как росли FLOPS и TDP и кто вёл в дуэли NVIDIA vs AMD (+ открытый датасет на 13 500 GPU) Архитектура крипто-сканера для биржи: Open Interest, Funding Rate, EMA и MACD в реальном времени @tanstack/vue-table: почему я почти отказался от этого… WHERE превращает ваш LEFT JOIN в INNER JOIN. И никто вам об этом не скажет Гравитация не существует. Вы задали 454 вопроса о времени. Вот ответы с уравнениями Эйнштейна Конец бесплатного кремния: как Google AI Studio превратилась из рая для инженеров в симулятор смены аккаунтов Свой AI-агент из почты, systemd и LLM MemForge2: загрузочная флешка, которая за минуту говорит — какую планку памяти менять Лицензии важны. Разбор ошибок авторов и пользователей программ От RAG-прототипа к агенту в продакшн: путь по метрикам, а не по моде Serial Terminal: кастомный веб-терминал для последовательного порта на Web Serial API
Inspector v3: как я сделал свой центр управления Kubernetes на старом ноутбуке
artegaspb (Y · 2026-05-26 · via Все публикации подряд на Хабре

Привет, Хабр! Меня зовут Артём, в YADRO я работаю инженером инфраструктуры: виртуализация, мониторинг, контейнеризация — это мое ежедневное. Также занимаюсь инфраструктурной автоматизацией. В декабре ко мне пришли коллеги с запросом инфраструктуры под инференс. Но Kubernetes на старте этой истории был для меня темным лесом: уровень «развернуть и передать», не более. Чтобы закрыть пробел в квалификации, решил взяться за проект. Вся история и итоги — далее в посте.

Железо и инфраструктура

Работа с инфраструктурой предполагает, что нам нужен удобный и функциональный инструмент — центр управления кластером, а не просто дашборд с графиками. Чтобы в вебе, чтобы глаза не вытекали и чтобы всё в одном окне: ноды, поды, GPU, терминал. Открыл — и видишь, что происходит.

Стартовый дашборд — верхушка айсбер

Стартовый дашборд — верхушка айсбер

Я начал с того, что перепробовал стандартные готовые решения: Dashboard, Lens, k9s, Grafana. Где-то красиво, но нет GPU. Где-то есть метрики, но дизайн как из 2005-го. Где-то все хорошо, но нельзя открыть терминал пода. В итоге везде компромиссы.

После очередного такого компромисса понял: либо учусь уживаться с ними, либо пишу свое. Выбрал второе. Хотелось панель в стиле Proxmox: минимализм, темная тема, все по делу. И без тонны зависимостей, которые надо скачивать через VPN.

Так родился Inspector.

Стенд на старом ноутбуке

Полная формулировка задачи от коллег несильно отличалась от краткой: «Нужно поднять кластер, настроить координатора, наладить балансировку. Там все просто, почитаешь документацию — разберешься».

Свободного сервера с GPU, как и другого мощного «железа», сначала под рукой не оказалось. Но это не повод откладывать изучение Kubernetes. На полке в офисе пылился старый ноутбук Lenovo P15: i7, 32 ГБ RAM, 1 ТБ SSD, и главное — NVIDIA T1200 с 4 ГБ видеопамяти. «Можно я на нем поэкспериментирую?» — «Забирай, только не сломай».

Так что всё началось с однородного Kubernetes на списанном ноутбуке. Одна нода и управляет, и исполняет. 12 ядер, 24 ГБ RAM внутри виртуалки в Proxmox. Ноутбук гудел в стеллаже, иногда перегревался на бенчмарках, но работал.

Мне сказали: «Зачем мучаешься с 4 ГБ? Давай сразу на серьезное железо». Я отвечал: «Нет, сначала разберусь здесь. Когда перестану бояться — перейду дальше». Почему это было правильным решением?

Цена ошибки — ноль. Я ронял API-сервер в три часа ночи, переустанавливал драйверы NVIDIA по пять раз за вечер, удалял не тот namespace. Худшее, что грозило, — потерять пару часов.

4 ГБ видеопамяти — хардкор-режим. Когда у тебя всего 4 ГБ, ты думаешь об оптимизации с первой минуты. Если что-то работает на 4 ГБ, то на 80 ГБ оно будет летать. Если падает — ты нашел узкое место, которое на дорогом железе замаскировалось бы гигабайтами памяти.

Я учился на реальных проблемах. Не на best practices из документации, а на том, что драйверы NVIDIA отваливаются после обновления ядра. Что containerd не хочет логиниться в Harbor. Что Device Plugin падает с ошибками про стратегии. Все это я прошёл на ноутбуке, который не жалко уронить.

Итоговый конфиг: три стенда

Сразу расскажу, как выглядит финальная инфраструктура. Получилось три не связанных друг с другом кластера, и у каждого своя роль.

Домашний кластер: Proxmox из пяти нод на X99 с Xeon 2340v2 и одна нода с RTX 5060 Ti на 16 ГБ. Это мой полигон: здесь я обкатываю идеи, экспериментирую, запускаю тяжелые AI-модели. В сетапе — Qwen 2.5 14B, Open WebUI, SearXNG, Redis, Inspector v2 и v3, Jenkins, Prometheus + Grafana + DCGM, Ingress NGINX, MetalLB, NFS Provisioner. Inspector все это знает и показывает: 34 из 35 подов видны в Running. С рабочим кластером домашний не связан.

Lenovo P15 — тот самый списанный ноутбук с T1200. Одна нода, 4 ГБ видеопамяти. Крутит легкие инференсы на vLLM 0.5B.

Рабочий кластер: пять нод K8s на Huananzhi X79, которую разрешили принести в офис, одна нода с RTX A2000 на 6 ГБ. Здесь готовится инфраструктура для ревью кода с ИИ. Ревью у нас по большей части ручное, и мы решили подключить сюда ИИ при модерации человека. Через месяц, кстати, от разработчиков пришел такой же запрос. Сам сервис пока в разработке, но кластер для него уже живет.

K8s-кластер крутится на Proxmox-кластере из 8 нод со своей СХД. Там же находится вся доменная инфраструктура: Active Directory, RADIUS, центр сертификации, Keycloak для SSO. Всё по-взрослому.

Теперь — о конкретных частях проекта, которые стоят отдельного рассказа.

Почему Proxmox, а не голое железо

Я точно знал, что буду всё переустанавливать. Не «если что-то пойдет не так», а гарантированно. Когда осваиваешь технологию с нуля, ты убиваешь стенд снова и снова. Голое железо в такой ситуации — боль. Ошибка приводит к переустановке ОС и дополнительному дню на проект.

Proxmox решил это элегантно: с ним я поднял кластер внутри виртуалки. Накосячил — откатился к снапшоту за 30 секунд. Убил ВМ — поднял новую из шаблона за пять минут. Хост может спокойно жить своей жизнью.

Битва с Huananzhi X79: три часа на проброс GPU

На X99 все прошло гладко: включил IOMMU, пробросил карту, ВМ увидела 5060 Ti. На X79 начался замес на три часа. Я просто поменял сетевую карту и RTX A2000 местами в слотах. Система переименовала интерфейсы, сервер потерял сеть. Пришлось лезть в /etc/network/interfaces через консоль Proxmox. Это был первый звоночек.

VT-d на Huananzhi X79 запрятан в раздел Chipset. По умолчанию VT-d выключен. Включили — IOMMU не заработал. Начали перебирать параметры ядра.

Параметр

Результат

Комментарий

intel_iommu=on

Не сработало

Включает IOMMU, но на X79 этого недостаточно

iommu=pt

Не сработало

Pass-through режим, не помог

pci=realloc

Частично сработало

Чинит кривые ACPI-таблицы на китайских платах

pcie_acs_override

Убрал параметр

На X79 он ломает определение IOMMU для Proxmox

По пути перебрал три версии ядра, остановился на 6.8.12-13-pve — самой стабильной для этой платформы.

Актуальный сейчас драйвер NVIDIA v.580 встал в Device Plugin с первой попытки. Но nvidia-device-plugin падал с ошибками стратегии. Если вкратце, то стратегия здесь — это правило, по которому плагин решает, какие GPU показывать поду. Я перебрал несколько вариантов и в итоге выбрал envvar.

Далее я переписывал манифест раз десять. Монтировал /usr/lib/x86_64-linux-gnu, давал privileged: true, дергал DEVICE_LIST_STRATEGY=envvar. Наконец. kubectl describe node показал nvidia.com/gpu: 1. Китайское железо сдалось.

Скрипты: я сразу знал, что уроню десятки раз

Сначала у меня был один bash-скрипт на 800 строк. Он делал всё: ВМ, kubeadm, сеть, Jenkins-агенты, Prometheus, DCGM. Работало. Но ошибка в Device Plugin валила весь скрипт, логи перемешивались, и отладка превращалась в ад.

Распилил скрипт по отдельным компонентам. Обкатывал до состояния «запустился, отработал, почистил систему».

deploy/
├── prepare/          # Проверки перед развертыванием
│   ├── 01-check-harbor.sh
│   ├── 02-check-dependencies.sh
│   ├── 03-install-nvidia-driver.sh
│   ├── 04-mount-nfs.sh
│   └── 05-readiness-report.sh
├── base-k8s-deploy/  # Базовые компоненты K8s
│   ├── 01-containerd.sh
│   ├── 02-kubernetes.sh
│   └── ...
├── addons/           # Аддоны и сервисы
│   ├── 01-metallb.sh
│   ├── 02-ingress-nginx.sh
│   ├── 03-harbor.sh
│   └── ...
├── binaries/         # Все бинарники локально
│   ├── kubectl
│   ├── kubeadm
│   ├── helm
│   └── ...
└── deploy-k8s-all.sh # Оркестратор

Да, это не Ansible, а bash. Но работает — и это главное. Оркестратор просто вызывает всё по порядку:

./prepare/01-check-harbor.sh
./prepare/02-check-dependencies.sh
./base-k8s-deploy/01-containerd.sh
./base-k8s-deploy/02-kubernetes.sh
# ... и так далее

Один коллега увидел это и сказал: «Это уже не скрипты, это дистрибутив». Лучший комплимент за проект.

Драйверы NVIDIA — единственное, с чем приходится работать вручную. В Secure Boot нужно подписать модуль ключом, зарегистрировать в MOK, перезагрузиться. Пока не придумал, как автоматизировать этот танец. Но хотя бы один раз сделал — и дальше уже оркестратор.

Своя инфраструктура: полная автономия

С самого начала я хотел одного: чтобы голова от проекта не болела. Разворачиваю ли кластер скриптами, применяю ли манифесты — все должно быть доступно. Никаких «Docker Hub лег», «nvcr.io отвалился». Поэтому построил такую инфраструктуру:

  • Harbor — Docker-образы: Prometheus, Grafana, DCGM, образы Inspector. Всё здесь.

  • Nexus — бинарники. Kubectl, kubeadm, helm, deb-пакеты. Версии зафиксированы.

  • Bitbucket — код и манифесты. Почему не GitLab? Исторически сложилось, начинал с Bitbucket дома.

DCGM-экспортер, CUDA-образы, Device Plugin и бинарники один раз выкачиваются на личный сервер, оттуда в Harbor и Nexus. Скрипт автообновления сам подливает свежие версии. Настроил и забыл.

Развернуть кластер можно без интернета. Все образы в Harbor, все бинарники в Nexus. Никаких imagePullPolicy: Always. Так что голова в самом деле не болит.

Jenkins в K8s и автоматизация сборки

Когда инфраструктура была готова, встал вопрос: как собирать и деплоить?

Поначалу использовал WSL. Писал код, собирал образ, тегировал, пушил в Harbor руками, делал kubectl apply. Сделал десять правок за вечер — десять раз повтори все действия. Надоело.

Поднял Jenkins в домашнем K8s. Отдельный namespace, Ingress наружу. Билд-нода — ВМ в Proxmox, и Jenkins-агент как systemd-сервис. Код лежит на билд-ноде, оттуда идет пуш в Bitbucket, Jenkins собирает. Затем пуш в Harbor и kubectl apply. Написал, запушил, собрал, задеплоил — цепочка работает без ручных шагов.

Безопасность и разделение контуров

Домашний кластер — это песочница: могу уронить, никто не пострадает. От рабочего он изолирован.

В рабочем контуре уже всё по-взрослому: Active Directory, RADIUS, центр сертификации, Keycloak с SSO. Вся доменная инфраструктура на том же Proxmox из восьми нод. На боевом кластере с A2000 готовится AI Code Review — тестируется производительность, подключается квантования, распараллеливаются потоки.

В итоге инфраструктура состоит из трех кластеров с разными ролями: домашний для экспериментов, ноутбук Lenovo для легкой нагрузки, боевой для подготовки к продакшену.

Мониторинг: переломный момент с Grafana

После развертывания кластеров я занялся мониторингом. В основе — Prometheus, DCGM-экспортер и Grafana. Кроме того, предусмотрены ServiceMonitor, готовые дашборды, алерты в Telegram.

Первые дни шло как по маслу, а потом я уперся в нечто странное. GPU-метрики в Prometheus есть: температура, загрузка, память. А в Grafana они живут отдельно: здесь есть дашборд с подами и дашборд с GPU. Связи между этими частями нет.

Пытался слепить единый дашборд. Но Grafana заточена под графики времени — показывает, что было в течение часа, а не то, что есть сейчас. А мне нужна была панель как в Proxmox: открыл и видишь живой срез. Нода, CPU, память, GPU с температурой — всё в одном экране.

Я понял: Grafana отлична для алертов и ретроспективы. Но для того что я хочу — не подходит. Нужна своя панель. Свой Inspector.

Эволюция Inspector: от bash-скрипта до системы управления

Inspector существует в трех версиях. Скажу честно: это не эволюция, а три разных продукта. Каждый раз, когда архитектура упиралась в потолок, я переписывал с нуля. Путь от идеи до production-ready версии, Inspector v3, занял 46 дней: один человек, вечера и выходные.

Inspector v1: bash и статика

Первая версия Inspector — bash-скрипт, который дергал kubectl, собирал вывод и генерил HTML. Запускал его вручную, сохранял все в /root/cluster-reports/. Никакого UI, просто таблички.

Сделал за день. Выглядело убого, но работало. Когда у тебя однородный кластер на T1200 и под висит в Pending, красивые графики не нужны. Достаточно увидеть: вот нода, вот поды, вот события. Табличка с kubectl решала это лучше Grafana.

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

Inspector v2: Flask, Material Design и 11 модулей

Статический HTML мне надоел. Каждый чих требует ручной перегенерации страницы. Захотелось так: кликнул по поду — увидел детали, нажал Shell — попал в консоль. Почему выбрал Flask? Не потому, что он модный, а потому что это был единственный фреймворк, который я хоть как-то знал на тот момент.

За четыре дня поднял пять модулей — Nodes, Pods, Services, GPU, дашборд, — а также темную/светлую тему. Уже прилично.

Раздел GPU в Inspector v2

Раздел GPU в Inspector v2

Дальше понеслось: Ingress, Events, Storage, Network, Queues. GPU-графики на Chart.js, экспорт HTML/PDF, Prometheus-эндпойнт. Система плагинов с админкой позволила загружать модули без пересборки образа. Сделал также таймлайн с цветовой индикацией, фильтры, логи.

Три дня потратил на Shell: xterm.js + Flask-SocketIO. Потребовалось более 30 итераций: преодолел проблемы с source в dash, права RBAC, WebSocket-прокси. В итоге Shell все-таки завелся.

Итоговый Inspector v2 представлял собой полноценную observability-платформу:

  • 11 модулей,

  • терминал,

  • экспорт,

  • Prometheus,

  • плагины.

Но архитектура в v2 хромала. Flask сам по себе синхронный, и использование eventlet для WebSocket — это костыли. Код расползался, а я при этом смотрел еще и в сторону управления, а не просто мониторинга.

Секция Timeline в Inspector v2

Секция Timeline в Inspector v2

Параллельно я запустил Inspector на рабочем кластере — пять нод с A2000 — из отдельного образа, с GPU fallback, ExternalName Service и MetalLB-маршрутами. К этому моменту Inspector заработал на трех кластерах: домашний, минималистичный (ноутбук Lenovo) и рабочий.

Inspector v3: FastAPI, SPA и Proxmox-стиль

Flask синхронный, а 12 эндпойнтов ходили в разные места. Часть — в Kubernetes API, часть тянула Prometheus, часть — WebSocket. Он рвался, код расползался по файлам. Также, как я уже сказал, я начал смотреть в сторону управления, а не только мониторинга. Eventlet уже ломал асинхронные вызовы, его связка с Flask превратилась в черный ящик, который было невозможно дебажить.

FastAPI сразу дал нужную мне асинхронность на ASGI, а также нормальную документацию — Swagger UI из коробки. Для валидации ответов использую Pydantic-модели. Описал модель — FastAPI проверил и сгенерировал документацию.

Вторую версию на третью я переписал за пять дней — не копипастой, а с переосмыслением — портировав 63% функционала. Что получилось в итоге?

Inspector v3 разворачивается через компактный образ python:3.12-slim. Внутри — FastAPI с uvicorn, ttyd, kubectl и фронтенд. А вот общий список важных компонентов и фичей:

  • асинхронный FastAPI на ASGI из коробки: 12 API-эндпойнтов + Pydantic;

  • 9 JS-модулей (использую Vanilla JS SPA);

  • нативный WebSocket;

  • автодокументация через OpenAPI (Swagger);

  • ttyd вместо xterm.js;

  • автогенерация Changelog;

  • build badge;

  • провайдеры, абстракции, разделение на бэкенд и фронтенд.

Список эндпойнтов:

  • /api/dashboard — сводка: ноды, поды, сервисы, события. Один запрос.

  • /api/nodes — ноды с ресурсами и версиями kubelet.

  • /api/pods — все поды.

  • /api/pods/{namespace} — поды в namespace.

  • /api/services, /api/deployments, /api/ingresses, /api/events.

  • /api/namespaces — список namespace.

  • /api/info — версия, билд Inspector.

  • /health — пробы для K8s.

Теперь — подробнее об отдельных частях финального проекта.

Раздел Events

Раздел Events

CI/CD через Jenkins

CI/CD я настроил через Jenkins в домашнем K8s. Почему Jenkins? Давно использую его на основной работе и знаю, как настраивать. GitLab CI пришлось бы изучать с нуля.

Пайплайн состоит из семи стадий: checkout, тесты (пока пропускаются), сборка и пуш в Harbor, ручное подтверждение для RC, тегирование, деплой, очистка. Также я ввел версионирование: 3.1, 3.2, ..., 3.57. Номер — это количество удачных деплоев, неудачные не считаются.

Провайдеры: архитектура, которая готова к росту

Одна из главных проблем v2: всё было завязано на Kubernetes. Никаких абстракций: бэкенд напрямую дергал kubectl и парсил вывод. Когда я начал смотреть в сторону Proxmox и Jenkins, стало понятно, что так дальше жить нельзя.

В v3 я заложил слой провайдеров. Это классический паттерн «стратегия»: есть абстрактный базовый класс BaseProvider, от которого наследуются конкретные реализации. Сейчас работает только K8sProvider, но архитектура позволяет добавлять новые без переписывания ядра. Как это устроено:

class BaseProvider(ABC):
    @abstractmethod
    def get_nodes(self) -> List[NodeInfo]: ...

    @abstractmethod
    def get_pods(self, namespace: str = None) -> List[PodInfo]: ...

    @abstractmethod
    def get_services(self) -> List[ServiceInfo]: ...

Каждый провайдер реализует эти методы под свой источник данных. K8sProvider ходит в Kubernetes API через официальный клиент. ProxmoxProvider будет ходить в Proxmox API через Proxmoxer. JenkinsProvider — в Jenkins API.

K8sProvider умеет работать в двух режимах:

  • In-cluster config — когда под крутится внутри кластера, использует service account. Не требует никаких настроек, просто работает.

  • Kubeconfig fallback — для локального запуска и отладки. Загружает ~/.kube/config и подключается к внешнему кластеру.

Переключение автоматическое: если есть переменная KUBERNETES_SERVICE_HOST, значит, мы в кластере, используем in-cluster config. Если нет — загружаем kubeconfig. Удобно для отладки: можно запустить Inspector на билд-ноде и подключиться к любому из трех кластеров, не пересобирая образ.

Что дает слой провайдеров:

  • Единый интерфейс для всех источников данных. Фронтенд не знает, откуда пришли данные — из K8s, Proxmox или Jenkins. Он просто получает JSON.

  • Легкое добавление новых провайдеров. Хочу добавить Proxmox — пишу класс на 200 строк, реализую абстрактные методы, и фронтенд получает данные о виртуалках через те же эндпойнты.

  • Тестирование. Можно написать MockProvider который отдает заранее заготовленные данные, и гонять на нем фронтенд, не подключаясь к реальному кластеру.

Какие провайдеры планирую:

  • ProxmoxProvider — управление ВМ и контейнерами: список, статус, ресурсы хоста, кнопки выключения и перезагрузки, снапшоты.

  • JenkinsProvider — запуск джоб, просмотр билдов, статус пайплайнов, чтобы не ходить в Jenkins UI отдельно.

  • SNMPProvider — мониторинг сетевого оборудования: температура, загрузка портов, аптайм.

  • VMwareProvider — если доберусь до рабочего vSphere.

Почему это важно? Inspector из интерактивного дашборда развился в  центр управления инфраструктурой. Слой провайдеров — это фундамент будущего центра. Хотя пока готов только K8sProvider, архитектура не ограничивает дальнейшее развитие.

Ttyd для терминала

В Inspector v2 работала связка xterm.js + Flask-SocketIO. Работала нестабильно: WebSocket рвался. Firefox работал, Chrome нет. Переподключение глючило, eventlet ломал асинхронные вызовы.

В Inspector v3 я перешел на ttyd — это готовый инструмент на C. Терминал по-прежнему работает через WebSocket как отдельный процесс в поде.

Vanilla JS SPA на фронте

Я сразу отказался от React/Vue, так как не хотел тащить npm-зависимости, webpack, сотни пакетов. Для девяти модулей фреймворк не нужен. Вот текущая структура фронтенда:

  • App.js — роутинг, темы (dark/light из v2), build badge.

  • Dashboard.js — сводка.

  • Nodes.js — ноды.

  • Pods.js — proxmox-стиль: список + детали + Shell.

  • Services.js, deployments.js, ingresses.js, events.js, namespaces.js.

Каждый модуль — в отдельном файле, друг о друге модули не знают. App.js — ядро, вызывает нужный модуль, тот рендерит свой кусок. Vanilla JS — чистый JavaScript, без сборки, и этого мне достаточно. Правишь файл — обновляешь страницу.

Раздел PODS с терминалом

Раздел PODS с терминалом

Proxmox-стиль для подов

Inspector v2 работал на Material Design. Симпатично, но мне чуждо: карточки, тени… Я привык к панелям управления, где все по делу и визуально нет ничего лишнего.

Интерфейс Inspector v3 похож на Proxmox и состоит из двух панелей. Слева — список с поиском и фильтром по namespace, справа — кнопка Shell и детали: имя, статус, нода, IP, рестарты. Никаких анимаций, минимум цветов, только данные.

Темная тема — основная, светлая — опциональная и для скриншотов. Переключатель реализован через CSS-переменные, он меняет класс на body.

Критические баги сегодня

Сейчас Shell работает только в инспектор-поде, exec в целевой под не идет. Я вручную не перенес прокси-логику из v2, где exec можно было отдавать в любой под, и в v3 при замене xterm.js на ttyd логика потерялась. Планирую передавать namespace/pod в URL ttyd. В v3 я полностью переделал вызов терминала в вебе: в Inspector v2 подключение занимало полчаса, в v3 — пять секунд.

Сейчас в интерфейсе есть кнопка логов, а эндпойнта для них нет. Сделал кнопку, когда верстал, и отложил реализацию функционала. Планирую сделать эндпойнт /api/pods/{namespace}/{name}/logs и модальное окно.

Inspector v2 имел спидометры на Chart.js, а в v3 GPU-мониторинг пока что не выведен в интерфейс. В кластере есть DCGM, метрики собираются. Планирую сделать эндпойнт для /api/gpu и JS-модуль.

Ограничения, которые стоит знать

В Inspector v3 rewrite-target ломает WebSocket, так как NGINX переписывает URL. Для HTTP это нормально, для WebSocket — фатально, поскольку заголовки Upgrade и Connection теряются. Пока наладил костыль в виде отдельного Ingress-хоста для ttyd без rewrite-target. Работает стабильно.

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

Планы по развитию Inspector

Inspector v3 еще требует некоторых доделок: Shell в целевом поде, логи, перезапуск, GPU-метрики. После этого — безоговорочный production-ready. Кроме того, для себя на обозримую перспективу утвердил следующий план по добавлению фичей:

  • Управление ВМ: список, статус, ресурсы, выключение, перезагрузка, снапшоты. Планирую реализовать через Proxmoxer как клиент: Proxmox BMC — эмуляция IPMI, power cycle ноды через Proxmox API.

  • Больше Kubernetes-ресурсов: PVC, ConfigMaps, Secrets, StatefulSets, DaemonSets.

  • Переключатель между кластерами в сайдбаре, чтобы один Inspector управлял всем.

  • Авторизация через Keycloak (SSO/OIDC) по ролям: viewer, operator, admin. Middleware в FastAPI.

  • Helm-чарты: Chart, Jenkinsfile, values для трех кластеров Inspector.

  • PVC, ConfigMaps, Secrets, StatefulSets, DaemonSets — пять эндпойнтов, пять модулей.

  • Перезапуск пода по кнопке — POST-эндпойнт, кнопка с подтверждением.

  • Smoke-тесты для API.

  • README для новых пользователей.

После окончания всех работ над v3 также хочу переписать фронт на React v4 с TypeScript — но это не раньше, чем через полгода.

Вместо заключения

Я до сих пор иногда смотрю на код Inspector и думаю: «Боже, как криво тут написано». Но он работает и решает мою задачу.

Если вы сейчас читаете это и думаете «хочу так же, но...» — просто начните. Не с идеального плана. Со списанного ноутбука. С кривого скрипта. А если не получится — хотя бы расскажите, что не вышло. Думаю, здесь такое тоже оценят.

Выносить Inspector в open source не тороплюсь. Сначала надо добить до состояния «не стыдно», а потом уже идти на GitHub. Если интересно посмотреть код или предложить фичу — пишите в личку, добавлю в лист ожидания. Буду рад вопросам, критике, идеям по архитектуре в комментариях. 

Если вам интересна работа с инфраструктурами, обратите внимание на наши вакансии: