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

推荐订阅源

博客园_首页
I
InfoQ
The Register - Security
The Register - Security
L
LangChain Blog
H
Help Net Security
The GitHub Blog
The GitHub Blog
S
Schneier on Security
博客园 - 【当耐特】
W
WeLiveSecurity
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
The Cloudflare Blog
H
Heimdal Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
雷峰网
雷峰网
N
Netflix TechBlog - Medium
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Visual Studio Blog
博客园 - 聂微东
PCI Perspectives
PCI Perspectives
Last Week in AI
Last Week in AI
A
Arctic Wolf
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
S
Secure Thoughts
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
SegmentFault 最新的问题
SecWiki News
SecWiki News
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Schneier on Security
Schneier on Security
P
Proofpoint News Feed
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AI
AI
Engineering at Meta
Engineering at Meta

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

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Inspector v3: как я сделал свой центр управления Kubernetes на старом ноутбуке
Артем Тетерятников · 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. Если интересно посмотреть код или предложить фичу — пишите в личку, добавлю в лист ожидания. Буду рад вопросам, критике, идеям по архитектуре в комментариях. 

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