Боль
Если вы держите on‑prem кластер на десятки или сотни нод, дальше можно не объяснять. Обновили CNI, или ядро, или драйвер сетевой карты, или просто переехали частью нод в новый сегмент. Проходит какое‑то время, и начинают капать тикеты: «у нас иногда таймаутит между подами», «DNS иногда не резолвится», «health‑check то падает, то нет».
Ключевое слово тут «иногда». Проблема не глобальная, не про 100% трафика. Ломается конкретная пара нод или конкретный протокол, и ломается ровно так, чтобы спрятаться за агрегатом. 98% успешных проверок выглядят прекрасно. Ровно до той секунды, когда доходит: недостающие 2% это одна пара, один протокол, и так изо дня в день.
Привычный инструментарий в этот момент почти бесполезен:
kubectl execплюсpingилиcurlруками. Отлично, а какую пару проверять? Их сотни, и вы не знаете, какая именно сломалась.Node exporter и типовые дашборды покажут CPU, память и диск конкретной ноды. Про то, что нода A не достучалась до ноды B по UDP, там нет ни слова.
Логи CNI‑плагина. Если повезёт, найдётся что‑то релевантное. Постфактум и далеко не по каждой паре.
Хотелось получить инструмент, который постоянно и во всех направлениях гоняет между нодами живые пробы, а при сбое сразу говорит: вот пара, вот протокол, вот хоп.
Что уже есть: Goldpinger
Первое, что приходит в голову, это Goldpinger. Инструмент взрослый и обкатанный: DaemonSet, опрашивает своих собратьев по HTTP, отдаёт метрики и рисует граф связности в собственном web UI. Ставится за минуту и честно отвечает на вопрос, видит ли нода X ноду Y. Если у вас его нет, начните с него. Порог входа копеечный, польза сразу.
Но деградация бывает не бинарной, а частичной, да ещё и завязанной на конкретный протокол, и вот тут его модели перестаёт хватать. Основной меш между пирами у него HTTP, per‑peer ICMP и реактивного per‑hop трейсинга нет. А мне нужны были все пять протоколов отдельными проверками между каждой парой, плюс автоматический MTR прямо в момент сбоя.
Так появился kconmon‑ng, Kubernetes Node Connectivity Monitor, next generation (репозиторий, Apache 2.0).
В прошлый раз я писал в LinkedIn про измерительное ядро. С тех пор к нему приросла целая веб‑консоль, и большая часть статьи будет про неё.

Как оно устроено
Архитектурно это связка agent/controller. Но не классический full‑mesh HTTP‑опрос.
Controller (Deployment) держит реестр агентов с heartbeat‑eviction, следит за нодами через Kubernetes informer, отдаёт топологию по gRPC‑стриму и умеет leader election для HA (
controller.leaderElection: true).Agent (DaemonSet) получает от контроллера живой список пиров по gRPC. Никакого поллинга: контроллер сам пушит полный снапшот пиров при подключении и заново при каждом изменении топологии. Дальше агент гоняет между собой и всеми пирами пять типов проверок: TCP, UDP, ICMP, DNS, HTTP.
+-------------------------------------------+ | Controller (Deployment) | | agent registry (heartbeat eviction) | | node watcher (zone labels) | | topology API + gRPC event stream | | leader election (active/standby) | +---------------------+---------------------+ | gRPC stream: peer list, pushed on every change | +-------------------+ +-------------------+ +-------------------+ | Agent (node-1) | | Agent (node-2) | | Agent (node-3) | | TCP UDP ICMP | | TCP UDP ICMP | | TCP UDP ICMP | | DNS HTTP | | DNS HTTP | | DNS HTTP | | MTR on failure | | MTR on failure | | MTR on failure | | /metrics :8080 | | /metrics :8080 | | /metrics :8080 | +-------------------+ +-------------------+ +-------------------+ ^ ^ +------------------ probes between all pairs -----------------+
Каждый чекер отдаёт не бинарное жив/не жив, а свой набор метрик:
Чекер | Что меряет |
|---|---|
TCP | время коннекта и полный RTT по каждому пиру |
UDP | средний RTT, джиттер и packet loss на burst из нескольких пакетов |
ICMP | echo RTT и loss, IPv4 и IPv6 |
DNS | время резолва по паре (hostname, resolver), системный или явные upstream |
HTTP | пофазовый тайминг для заданных URL: DNS, connect, TLS, TTFB, total |
MTR | реактивный трейс при сбое, RTT и loss по каждому хопу |
TCP, UDP, ICMP и DNS работают из коробки с интервалом 5 секунд. HTTP по умолчанию выключен, потому что URL знаете только вы.
Главное отличие в диагностике: когда TCP, UDP или ICMP проба даёт сбой, агент сам запускает MTR для этой пары. Есть cooldown на пару, иначе один сломанный линк зальёт кластер трейсами. Результат уезжает в метрику kconmon_ng_mtr_hop_rtt_seconds с лейблами hop_number и hop_ip. Вместо «между node-5 и node-17 что‑то не так» вы сразу видите, на каком хопе начинаются потери.
Ещё несколько решений. Инцидент они не ловят, зато сильно экономят нервы в проде:
Zone auto‑discovery. Контроллер сам резолвит зону из label ноды и раздаёт её агентам при регистрации, так что каждая метрика несёт
source_zoneиdestination_zoneбез ручной прописки. Смена лейбла разлетается пирам сразу через full sync.Сброс stale‑гейджей. При изменении топологии per‑pair гейджи сбрасываются, а не остаются висеть призраком от ноды, которой уже нет.
Дерегистрация при shutdown. Роллинг самого kconmon‑ng не пишет фейковые потери в собственные метрики.
Строгая валидация конфига. Незнакомые ключи отклоняются, опечатка роняет старт явно. При hot‑reload невалидный конфиг просто не применяется, остаётся старый.
Self‑monitoring. Контроллер знает, сколько нод должно иметь агента (
kconmon_ng_controller_expected_agents, из числа schedulable‑нод). Если зарегистрированных меньше, вы получите отдельный алерт, а не тишину.
Консоль
Вот этого в прошлый раз не было совсем. Опциональный веб‑интерфейс, по умолчанию выключен, деплоится тем же чартом. Читает он тот же Prometheus и тот же контроллер, которые у вас уже есть: второго пути данных нет, агенты не меняются, метрики нигде не дублируются.
Двенадцать страниц: Обзор, Онлайн, Расследование, Матрица, Топология, MTR, Диагностика, Цели и расписания, Метрики, Оповещения, Консоль с PromQL dev‑tools и Настройки. Read‑only страницы работают вообще без базы, хватит одного URL Prometheus. История, авторизация, инциденты и правила алертинга уже просят PostgreSQL.
Водить вас по всем двенадцати я не буду. Лучше покажу маршрут, которым реально хожу во время инцидента.
1. Матрица: какая пара
Матрица N×N, по ячейке на каждую упорядоченную пару, раскраска по loss и latency. Сломанная нода читается красным столбцом. Односторонняя проблема оставляет одну‑единственную ячейку, а поломка на границе зон рисуется целым блоком.

С controller.events.enabled матрица и топология пушатся по WebSocket. Без него они поллят, и страница прямо пишет, в каком из двух режимов она сейчас, а не делает вид, что живая.
2. Расследование: что было вокруг излома
Кнопка расследования прямо в ячейке ведёт на /investigate, скоуп и окно уже зашиты в URL. Страница сводит девять источников таймлайна вокруг этого скоупа: события топологии, события Kubernetes, записи аудита, изменения MTR‑путей, запуски диагностики, окна работ, аннотации, вычисленные пересечения порогов и горящие алерты.
Дальше она ранжирует кандидатов в причины. Арифметика простая: вес класса, умноженный на линейное затухание по 300 секундам до onset. Веса такие:
3. Под пробой поехала инфраструктура: сменился маршрут (path‑change) или кластер поменял ноду или под (k8s). Ближе к сетевому симптому не бывает.
2. Изменился флот или его конфигурация: событие топологии или запись в аудите консоли. Правдоподобно, но на шаг дальше.
1. Окно работ. Оно объясняет деградацию, а не обвиняет кого‑то, и стоит в рейтинге ниже настоящих подозреваемых, чтобы никогда их не перебить.
0. Аннотации, запуски диагностики, пересечения порогов и горящие алерты. Первые два это то, что человек сделал по поводу проблемы. Порог сам и есть симптом. А горящий алерт всего лишь пересказывает его, причём обычно по тому же ряду, из которого порог и вычислен. Дай ему вес выше нуля, и страница начнёт показывать «причина вашей аварии: сообщение о вашей аварии». Именно так эти панели обычно и начинают врать.
Никакого ML. Константы лежат в одном модуле, и документация их цитирует, а не пересказывает.

Расследование сохраняется как инцидент, а пермалинк поднимает из сохранённой строки точный скоуп и окно. Ссылка физически не может разъехаться с инцидентом, который она называет.
3. Машина времени: как это выглядело в 02:14
Утренний разбор ночного инцидента всегда упирается в один и тот же вопрос: а что там, собственно, было? В консоли на этот случай есть кнопка Now в правом верхнем углу. Она открывает пикер: пресеты от «15m ago» до «24h ago», календарь и точное время. Выбираете момент, и каждая страница, которая что‑то читает, начинает отвечать про него, а не про сейчас: топология складывается из сохранённых событий, PromQL считается в точке t, лента событий превращается в скроллбэк. Выбранный момент живёт прямо в URL как ?at= с меткой RFC 3339, поэтому ссылку можно кинуть коллеге в инцидентный чат, и у него откроется то же самое прошлое.
Пока Машина времени включена, все мутирующие контролы задизейблены, а баннер сверху называет момент и держит кнопку Return to Live. Поменять флот из прошлого нельзя.

4. Оповещения: превратить находку в правило
Когда понятно, как выглядит поломка, хочется, чтобы в следующий раз она разбудила вас сама. /alerting собирает правило из шести типовых шаблонов (loss на паре, latency между зонами, сбои DNS, HTTP TTFB, пропавший агент, лежащий внешний таргет) либо из сырого PromQL.
Две вещи, которые тут важны:
Валидация запускает выражение, а не парсит его. В сборке сознательно нет зависимости от парсера prometheus/prometheus. POST /api/v1/alert-rules/preview выполняет выражение как instant query против вашего живого Prometheus и говорит, сколько рядов оно поймало. Ошибка рендера и ошибка запроса разъезжаются: «правило не собирается» и «правило сейчас ничего не матчит» это разные новости.
Консоль управляет, Prometheus вычисляет. Все включённые правила реконсайлятся server‑side apply в один объект PrometheusRule. Цель применения одна: дрейф это одно сравнение, частичного применения не бывает. И ничто в консоли не решает, что алерт сработал.
Объекты PrometheusRule, которые консоль не писала, показываются read‑only. Усыновить такой объект можно только явной копией, оригинал не мутируется никогда. Значит обе копии будут вычисляться, пока вы одну не удалите, и отчёт импорта пишет об этом прямым текстом.

Дрейф сначала фиксируется, потом чинится, причём в одном проходе. Отсюда штука, которая на первый взгляд выглядит багом: правило может честно показывать drift и свежий lastSyncedAt одновременно. Расхождение увидели и тут же исправили. Ошибки не роняют цикл, они приземляются на конкретное правило с закрытым классом причины (crd-missing, forbidden, other).
Остальное коротко
MTR Explorer. Каждый пройденный путь хешируется по содержимому и дедуплицируется на входе, так что история пути это список изменений, а не стена одинаковых трейсов. Три панели, клиентский diff между любыми двумя снапшотами, опциональное обогащение хопов через rDNS и MaxMind (выключено по умолчанию и это единственная часть, которая ходит за пределы кластера).
Диагностика, внешние таргеты и расписания. Прогнать пару по требованию прямо посреди инцидента, с историей запусков и пермалинками. Внешние таргеты агенты чекают непрерывно, причём CIDR‑allowlist проверяет агент, а не консоль: агент без явного опт‑ина просто отказывается от внешней работы.
Лента Онлайн и командная палитра. Виртуализированный поток событий с фильтрами по типу, severity и скоупу, пауза с буфером и учёт пропущенных событий.
⌘Kпо навигации, действиям и Машине времени, написана руками, без зависимостей.Auth, RBAC, аудит, вебхуки.
anonymous | local | header | oidc, 25 permissions на четыре встроенные роли (viewer,operator,alert-editor,admin) плюс кастомные, журнал аудита, API‑токены и исходящие вебхуки на переходы инцидентов и алертов. Доставки подписаныX-Kconmon-Signature: sha256=<hmac>по сырому телу, секрет каждого эндпоинта write‑only через API и лежит зашифрованным AES-256-GCM.Настройки. Конфигурация выгружается версионированным бандлом и импортируется сначала в dry‑run. Вебхуки экспортируются с флагом
hasSecretи без самого секрета, поэтому импортом их создать нельзя: запечатанный секрет из API не выходит.
Установка
Что понадобится: Kubernetes 1.31+ (CI гоняется на 1.36), Helm 4 (чарт публикуется как OCI‑артефакт, Helm ≥3.14 тоже работает) и Prometheus Operator, если хочется ServiceMonitor и правила алертинга из коробки. Что немаловажно, дополнительных capability агенту не нужно вовсе: ICMP и MTR ходят через непривилегированный ICMP‑сокет, который открывает sysctl net.ipv4.ping_group_range. Этот sysctl у kubelet в списке безопасных, так что чарт сам его и выставляет, заодно с RBAC на node watch для контроллера.
helm upgrade --install kconmon-ng oci://ghcr.io/esdmitrii/charts/kconmon-ng \ --version 2.0.3 \ --set serviceMonitor.enabled=true \ --set prometheusRule.enabled=true
Проверяем, что всё поднялось. Должен быть один controller pod и по одному agent pod на каждую ноду:
kubectl get pods -l app.kubernetes.io/name=kconmon-ng -o wide
И что агенты реально отдают метрики:
AGENT=$(kubectl get pods -l app.kubernetes.io/component=agent -o jsonpath='{.items[0].metadata.name}') kubectl port-forward "$AGENT" 8080 & curl -s http://localhost:8080/metrics | grep '^kconmon_ng' | head
Консоль это флаг на том же релизе, остальное в чарте не меняется:
helm upgrade --install kconmon-ng oci://ghcr.io/esdmitrii/charts/kconmon-ng \ --version 2.0.3 \ --set console.enabled=true \ --set console.prometheus.url=http://prometheus-operated.monitoring:9090 kubectl port-forward svc/kconmon-ng-console 8081:8080
На http://localhost:8081 вы получите read‑only страницы под анонимным viewer. Дальше всё включается по одному флагу. Истории, авторизации, инцидентам и правилам алертинга нужен database.existingSecret с PostgreSQL DSN. Realtime‑пуш живёт на controller.events.enabled=true. Алертингу, кроме console.alerting.enabled=true, понадобится ещё CRD PrometheusRule от Prometheus Operator. Каждая ручка описана прямо в charts/kconmon-ng/values.yaml.
Что чарт 2.0.3 делает за вас
Двойка это в основном про шаги, которые раньше делались руками. Любой секрет по‑прежнему можно принести своим existingSecret, а можно попросить чарт отрендерить его блоком secret:. Значения полей при этом пишутся дословно, поэтому плейсхолдер для всякого рода операторов и инжекторов${vault:...} доезжает байт в байт и разрешается на admission.
Своей базы и своего кеша чарт не ставит. Направьте database.existingSecret на Secret с DSN вида postgres://, а redis.existingSecret на Secret с redis://, и подойдёт то, что у вас уже крутится: RDS, StatefulSet, свой кластер CloudNativePG. GeoLite2 больше не надо подкладывать: при geoip.mode=auto рядом с консолью крутится сайдкар из образа geoipupdate от самой MaxMind, а консоль перечитывает оба файла и переоткрывает тот, что изменился. Свежая база подхватывается без рестарта.
Дашборды раскладываются ConfigMap’ами с меткой grafana_dashboard для сайдкара, поды едут на дефолтах restricted‑PSS, агент в том числе. Он дропает ALL наравне со всеми: ICMP и MTR работают через непривилегированный ICMP‑сокет, который открывает sysctl net.ipv4.ping_group_range, а тот у kubelet в списке безопасных. Namespace на restricted принимает DaemonSet как есть.
Метрики и PromQL
Все метрики идут с настраиваемым префиксом (kconmon_ng по умолчанию) и общим набором лейблов для парных проверок: source_node, destination_node, source_zone, destination_zone. Набор не менялся между релизами. Дашборды и recording rules, написанные ещё под 1.5.0, ведут себя ровно так же.
Что я держу под рукой:
# Пять худших пар по UDP loss прямо сейчас topk(5, kconmon_ng_udp_packet_loss_ratio) # p95 времени TCP-коннекта в разрезе пар зон histogram_quantile(0.95, sum by (le, source_zone, destination_zone) ( rate(kconmon_ng_tcp_connect_duration_seconds_bucket[5m]) ) ) # Пары, где TCP именно падает, а не просто тормозит rate(kconmon_ng_tcp_results_total{result="fail"}[5m]) > 0 # p99 резолва DNS по каждому резолверу histogram_quantile(0.99, sum by (le, resolver) (rate(kconmon_ng_dns_duration_seconds_bucket[5m])) ) # Где именно умирают пакеты на конкретной паре kconmon_ng_mtr_hop_rtt_seconds{source_node="node-1", destination_node="node-2"}
Из коробки при prometheusRule.enabled: true едут семь правил:
- alert: UDPLossHigh expr: kconmon_ng_udp_packet_loss_ratio > 0.5 for: 5m - alert: TCPChecksFailing expr: >- sum by (source_node, destination_node, source_zone, destination_zone) (rate(kconmon_ng_tcp_results_total{result="fail"}[5m])) / sum by (source_node, destination_node, source_zone, destination_zone) (rate(kconmon_ng_tcp_results_total[5m])) > 0.05 for: 5m - alert: PairWentSilent expr: >- sum by (source_node, destination_node) (rate(kconmon_ng_tcp_results_total[1h] offset 5m)) > 0 unless sum by (source_node, destination_node) (rate(kconmon_ng_tcp_results_total[5m])) > 0 for: 10m - alert: DNSChecksFailing expr: >- sum by (source_node, source_zone, host, resolver) (rate(kconmon_ng_dns_results_total{result="fail"}[5m])) / sum by (source_node, source_zone, host, resolver) (rate(kconmon_ng_dns_results_total[5m])) > 0.05 for: 5m - alert: ExternalChecksFailing expr: >- sum by (source_node, source_zone, target, target_kind, check_type) (rate(kconmon_ng_external_results_total{result="fail"}[5m])) / sum by (source_node, source_zone, target, target_kind, check_type) (rate(kconmon_ng_external_results_total[5m])) > 0.1 for: 5m - alert: KconmonAgentsMissing expr: >- (kconmon_ng_controller_expected_agents - kconmon_ng_controller_registered_agents > 0) and (kconmon_ng_controller_leader == 1) for: 10m - alert: KconmonControllerDown expr: absent(kconmon_ng_controller_leader == 1) for: 5m
Три правила *ChecksFailing смотрят на долю неудач, а не на сырой rate: одна вспышка держала бы rate положительным все пять минут окна и будила бы вас на ровном месте, а доля так и остаётся в пределах нормы, пока линк реально не сломался. Пороги: 5% внутри кластера (TCP, DNS) и 10% для внешних целей, до которых сеть уже не ваша.
PairWentSilent тут стоит особняком: он ловит не плохие цифры, а тишину, когда пара ещё час назад слала результаты, а теперь молчит, и остальные правила по ней тоже замолкают, будто всё хорошо.
Последние два следят за самим kconmon‑ng. Мониторинг, который замолчал, разбудит вас, а не будет тихо выглядеть здоровым. Полный список метрик с типами и лейблами лежит в docs/metrics.md.
И три готовых дашборда для Grafana в dashboards/: Overview (Connectivity Matrix, success rate и латентность по каждому протоколу, статус контроллера, счётчик срабатываний MTR), Node Detail (то же самое в разбивке по destination‑ноде) и Zone Heatmap (тепловая карта латентности и потерь между зонами).
for f in dashboards/*.json; do curl -s -X POST "http://localhost:3000/api/dashboards/db" \ -H "Content-Type: application/json" -u admin:admin \ -d "{\"dashboard\": $(cat "$f"), \"overwrite\": true}" done



Из терминала
kubectl-kconmon ходит в HTTP API контроллера через port‑forward на client‑go, так что посмотреть топологию и прогнать разовую проверку можно без браузера. Проверка со сбоем возвращает код выхода 2, отдельно от 1 для ошибок CLI и API. В пайплайн это ложится нормально. -o json отдаёт сырой результат как есть.
$ kubectl kconmon topology NODE ZONE READY AGENT AGENT IP node-1 us-east-1a yes node-1-kconmon-ng-agent-aaaaa 10.0.0.1 node-2 us-east-1b yes node-2-kconmon-ng-agent-bbbbb 10.0.0.2 node-3 us-east-1c no - - $ kubectl kconmon check node-1 node-2 --type udp OK udp node-1 -> node-2 (us-east-1a -> us-east-1b) duration=1.1ms sent=5 recv=5 loss=0% rtt=1.1ms jitter=240µs
Ставится через krew, плагин уже в официальном индексе:
kubectl krew install kconmon
Ломаем связность руками
Проверить, что детект сбоев, MTR и алерты работают, проще всего сломав сеть самому. В hack/README.md есть NetworkPolicy, которая режет весь трафик между агентами:
kubectl apply -f - <<'EOF' apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: block-agent-traffic namespace: default spec: podSelector: matchLabels: app.kubernetes.io/name: kconmon-ng app.kubernetes.io/component: agent policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app.kubernetes.io/component: controller - namespaceSelector: matchLabels: kubernetes.io/metadata.name: monitoring EOF
Исключение для namespace monitoring тут несущее, и это тот случай, где я наступил на грабли лично. Метрики потерь экспортируют сами агенты. Если политика заодно отрежет скрейпы Prometheus, ряды агентов протухнут минут через пять, и поломка, которую вы только что устроили, исчезнет со всех дашбордов. Хаос ослепит собственного наблюдателя. Проверено живьём: без исключения две ноды, где Prometheus не живёт, ушли в down в списке таргетов за один интервал скрейпа.
Через 10 или 30 секунд в логах агентов:
kubectl logs -l app.kubernetes.io/component=agent --since=1m | grep -E "check failed|triggering MTR"
Ожидаемо: check failed с type: tcp/udp/icmp и i/o timeout либо 100% loss, следом triggering MTR trace и MTR trace completed.
В Grafana на Overview просядут success rate по TCP, UDP и ICMP, покраснеют loss ratio, счётчик MTR Triggers уйдёт в ненулевое значение. В консоли та же поломка видна на /matrix красными ячейками, а /investigate по одной из этих пар покажет таймлайн сбоя вместе с MTR‑трейсом, который агент снял в момент срыва.
Убрать политику:
kubectl delete networkpolicy block-agent-traffic
Через минуту‑две всё вернётся в зелёное. Полный локальный стенд на Minikube с kube‑prometheus‑stack, консолью, PostgreSQL и проверкой round‑trip алертинга поднимается одной командой ./hack/local-test.sh up, и там же лежит пошаговый вариант руками.
Более длинный сценарий есть в docs/demo/breaking-cni.md: заблэкхолить UDP между двумя нодами и смотреть, как краснеет ровно одна ячейка матрицы, пока TCP и ICMP на той же паре остаются зелёными. Потом сломать TCP, ICMP и HTTP ещё в трёх местах одновременно и увидеть каждый отказ отдельно, в своей паре и своём протоколе.
Что kconmon‑ng не делает
Он не перехватывает реальный прод‑трафик. Это пробы между нодами. Телеметрия сервис‑меша и eBPF‑инструменты отвечают на другой вопрос и отвечают на него лучше.
Консоль это потребитель Prometheus, а не замена ему. Снесите консоль завтра, и все метрики, дашборды и правила продолжат работать.
В статье нет ни одного бенчмарка. Просто потому, что я не гонял таких, которые не стыдно опубликовать.
Всё, что добавляет консоль, по умолчанию выключено либо read‑only‑additive. Апгрейд релиза без изменения values рендерит те же манифесты.
Про тесты, раз уж речь о доверии к цифрам: во фронтенде 3593 теста в 120 файлах, 29 Go‑пакетов несут тесты, и CI гоняет их с race‑детектором на каждый PR, вместе с линтом и helm‑lint по набору CI‑профилей values. Тег v* кросс‑компилирует бинарники, публикует образы и чарт в GHCR и запускает e2e.
Что дальше
v2.0.0 был последней запланированной вехой консоли, так что она доделана по задумке, а не брошена посередине. Чего мне теперь не хватает? Чужих кластеров: флотов побольше, CNI поэкзотичнее, инсталляций с упором на IPv6.
Issues и PR открыты. Если запустите и оно соврёт вам про вашу сеть, мне правда интересно про это услышать.
Репозиторий: https://github.com/EsDmitrii/kconmon‑ng
Чарт:
oci://ghcr.io/esdmitrii/charts/kconmon-ng, версия 2.0.3Лицензия: Apache 2.0

