Привет, Хабр! Меня зовут Артём, в 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, дашборд, — а также темную/светлую тему. Уже прилично.

Дальше понеслось: 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 — это костыли. Код расползался, а я при этом смотрел еще и в сторону управления, а не просто мониторинга.

Параллельно я запустил 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.
Теперь — подробнее об отдельных частях финального проекта.

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, без сборки, и этого мне достаточно. Правишь файл — обновляешь страницу.

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. Если интересно посмотреть код или предложить фичу — пишите в личку, добавлю в лист ожидания. Буду рад вопросам, критике, идеям по архитектуре в комментариях.
Если вам интересна работа с инфраструктурами, обратите внимание на наши вакансии:


























