ZX
1 пост
Как я поздравил?
Мне тут в коментах написали про майнеры
специально билд v1.10.15 добавляет в раздел Уязвимости таб Вредоносное где можно это проверить
Один статический бинарник размером всего ~22MB показывает, что на хосте на самом деле происходит — конфиги, сервисы, контейнеры, firewall, сертификаты — и даёт это править из браузера. а Хаб собирает много хостов в одно окно.
Кому нужен NetKnownsThat и зачем
У любого, кто хоть раз администрировал Linux-сервер дольше пары месяцев, есть одна и та же привычка: время от времени садиться и вручную обходить хост. `ss -tlnp`, чтобы вспомнить, что вообще слушает сеть. `nginx -T`, чтобы понять, какой конфиг реально загружен. `docker ps`, чтобы свериться, какие контейнеры живы. Заглянуть в `/etc/letsencrypt`, прикинуть на глаз, не горит ли что-то с сертификатами. Это не проверка — это ритуал, который держится на памяти конкретного человека о конкретном сервере. Стоит этому человеку уйти в отпуск, смениться, забыть — и ритуал прерывается молча, без предупреждения.
NetKnownsThat — это попытка превратить этот ритуал в инструмент, который помнит вместо человека и сверяет то, что человек обычно сверяет на глаз и не всегда полностью.
Что он в действительности даёт
Не мониторинг в привычном смысле — не графики нагрузки и не алерты по CPU. Ближе к техническому аудиту, который можно запускать когда угодно и который не забывает ни одного шага. Разница принципиальна: типичный дашборд показывает, что написано в конфигурации. NetKnownsThat показывает, где написанное разошлось с происходящим на самом деле — порт объявлен, но никто его не слушает; порт слушается, но нигде не объявлен; контейнер публикует адрес в обход firewall, потому что Docker и `ufw` живут в разных цепочках `iptables` и обычно не в курсе друг о друге; сертификат в файле обновился, а сервис так и отдаёт клиентам старый, потому что забыли `reload`.
Каждая находка приходит не голой строкой, а с объяснением, что произошло, и конкретным действием, которым это чинится — часто прямо тут же, без второй вкладки с `man`. Отдельно — карта того, как трафик реально течёт по хосту: от внешней сети через слушатель, пул, бэкенд-адрес, до контейнера и docker-сети, — собранная не по красоте, а по колонкам, чтобы не прыгать между сканами и правда читаться как маршрут запроса, а не как случайный граф.
Для кого это имеет смысл
Для одного человека, который отвечает за несколько серверов сам.** Классический сценарий — свои проекты, клиентские VPS, что угодно, где нет отдельной команды эксплуатации и весь контекст держится в голове одного человека. Именно этот сценарий рвётся первым: слишком много деталей, слишком легко забыть свериться с одним из них. `nkt` в этом случае — не замена компетенции, а внешняя память, которая не устаёт и не отвлекается.
Для небольших команд и агентств, которые ведут чужую инфраструктуру.** Здесь ключевой момент — режим хаба: один центр, из которого видно панель по каждому клиентскому серверу, без необходимости разворачивать полноценный стек на каждом отдельно и без единого общего аккаунта на все машины сразу. У каждого хоста остаётся собственный набор правил и собственные ограничения — хаб не может обойти то, что хост сам себе запретил, а значит компрометация центральной точки не превращается автоматически в потерю контроля над всем парком разом.
Для тех, кто уже настроил TLS и firewall и хочет знать, что это по-прежнему работает, а не просто было настроено однажды.** Продление сертификата, которое тихо сломалось полгода назад; правило firewall, разрешающее порт, который давно никто не слушает; сервис, объявленный в конфиге, но никогда фактически не поднятый, — такие вещи не кричат о себе, пока их прицельно не спросить.
Для тех, кому нужна проверка в CI или cron, а не картинка.** `nkt scan` — это разовый прогон с кодом выхода `2` при критичных находках, без браузера и без интерфейса вообще. Естественная точка для пайплайна деплоя: собрали, выкатили, прогнали — и если что-то разошлось с ожиданиями, сборка падает раньше, чем об этом узнают пользователи.
Для тех, кто работает по SSH и не хочет открывать браузер ради одного взгляда на сервер.** Терминальный интерфейс поверх `tview` — тот же набор экранов, что и в вебе, живущий в том же самом бинарнике, без отдельного проброса портов и без второго окна.
Для кого это лишнее
Честности ради стоит сказать и обратное. Огромному парку из сотен и тысяч однотипных машин нужна оркестрация и автоматизация масштаба, которую `nkt` не строил и строить не собирался — у хаба нет ни распределённого реестра, ни кластерной модели, только простой список хостов на одной SQLite-базе. Командам, уже полностью переехавшим на Kubernetes, здесь тоже почти нечего взять: инструмент разбирает `nginx`/`haproxy`/`docker-compose`/LXD/libvirt — классический стек виртуальных машин и контейнеров на голом Linux, а не манифесты кластера. А там, где по контракту или по регламенту нужен формальный аудиторский след корпоративного уровня — SOC2, PCI DSS с полным набором отчётности, — журнал действий `nkt` даёт честную историю «кто, что и когда изменил», но не заменяет собой выделенный compliance-инструмент, для этого не спроектированный.
Между этими двумя краями — между голым `ssh` и `grep` с одной стороны и тяжёлым энтерпрайз-стеком мониторинга с другой — и живёт тот, кому NetKnownsThat действительно пригодится: тот, кому нужна честная картина происходящего на сервере прямо сейчас, без разворачивания Prometheus, Grafana и ELK ради одного вопроса — «а точно ли Redis не торчит наружу?».
Дисклеймер. ELM327 — зарегистрированный продукт компании ELM Electronics. Эта серия описывает самостоятельную реализацию совместимого командного интерфейса в учебных целях и не является оригинальной микросхемой ELM Electronics. Проект не предназначен для коммерческого использования под именем «ELM327».
Если вы когда-нибудь приезжали на техосмотр или к механику, вы, скорее всего, видели, как он достаёт небольшое устройство, вставляет его под торпедо и смотрит в ноутбук. Этот разъём — OBD-II (On-Board Diagnostics, версия II). С 1996 года он обязателен для всех легковых автомобилей, продаваемых в США, с 2001 — для бензиновых, а с 2004 — для дизельных легковых в Европе (стандарт EOBD). Сегодня OBD-II / EOBD присутствует в подавляющем большинстве легковых автомобилей на ключевых рынках. Грузовики, автобусы и спецтехника — отдельная история: у них другие разъёмы (9-pin Deutsch), другие протоколы (J1939), другое напряжение борт-сети (24 В) и другие требования к защите.
Через этот разъём можно прочитать коды ошибок (те самые «check engine»), посмотреть живые данные с датчиков — обороты двигателя, температуру охлаждающей жидкости, давление турбины, лямбда-зонды, скорость — и даже сбросить ошибки после ремонта.
Звучит просто. Но внутри — несколько несовместимых друг с другом протоколов, которые появлялись в разное время у разных производителей. CAN, K-line, J1850, ISO 9141 — каждый требует своего физического уровня, своей процедуры инициализации, своего формата кадров.
Именно поэтому появился ELM327.
ELM327 — это микросхема канадской компании ELM Electronics, выпущенная в начале 2000-х. Внутри — маленький микроконтроллер с прошивкой, которая:
Принимает простые текстовые команды через UART — так называемые AT-команды (по аналогии с модемами Hayes)
Транслирует их в нужный протокол шины автомобиля
Возвращает ответ тоже в виде читаемых ASCII-строк
Диагностическая программа на телефоне или ноутбуке не знает ничего про CAN или K-line. Она просто отправляет строку 0100\r (запрос PID 00, поддерживаемые параметры) и получает обратно что-то вроде 41 00 BE 3E B8 10\r\n>. ELM327 берёт на себя всю грязную работу с шиной.
Команды ELM327 — это отдельный небольшой язык. Несколько примеров:
Команда Смысл ATZ Сброс адаптера ATI Версия прошивки ATSP6 Установить протокол ISO 15765-4 CAN 11-бит 500 кбит/с ATH1 Включить заголовки фреймов 0100 Отправить OBD запрос: поддерживаемые PID 01-20
Всего спецификация ELM327 v2.3 определяет более 100 AT-команд и 13 протоколов шин. Наша реализация охватывает набор команд, достаточный для прохождения ELM Scan Adapter Validator и работы целевых диагностических приложений.
Поищите «ELM327 Bluetooth» на любом маркетплейсе. Дешёвые Bluetooth-адаптеры стоят на порядок дешевле профессиональных устройств (OBDLink, Kiwi), но значительная часть из них — клоны на дешёвых микроконтроллерах с частичной реализацией протокола. Это не оригинальная микросхема ELM Electronics.
Типичные проблемы клонов:
Поддерживают только CAN, на K-line машинах не работают вообще
Сообщают версию ELM327 v1.5 или v2.1, хотя реализация неполная
AT-команды возвращают мусор или не работают вовсе
Теряют соединение по Bluetooth под нагрузкой
Не сохраняют настройки между сессиями
Профессиональные адаптеры (Kiwi 3, OBDLink MX+) существенно дороже, но содержат нормальную прошивку. Их недостаток: прошивку нельзя изменить под свои нужды.
Причина 1: Понять, как это работает
Диагностика — это не магия. Это конкретные протоколы с конкретными тайм-аутами и процедурами. Написав адаптер с нуля, вы будете точно знать, почему машина не отвечает, почему инициализация занимает 3 секунды, что значит «BUS INIT: ERROR».
Причина 2: Полный контроль
Хотите поддержку нестандартных PID? Особый формат вывода? Логирование на SD-карту? Всё это невозможно в закрытых устройствах, но тривиально, когда прошивка ваша.
Причина 3: Bluetooth-адаптер со своей идентификацией
Если вы разрабатываете диагностическое приложение, вам нужен адаптер с предсказуемым поведением. Свой адаптер = отсутствие сюрпризов от обновлений стороннего firmware.
Причина 4: Учебная задача мирового класса
Реализация ELM327 затрагивает практически все аспекты embedded-разработки: прерывания, UART, CAN, bit-banging, EEPROM, конечные автоматы, тайминги на уровне микросекунд, совместимость протоколов. Лучшей учебной задачи для начинающего embedded-разработчика трудно придумать.
В этой серии мы разберём готовую рабочую ELM327 v2.3-совместимую прошивку, написанную на C для микроконтроллера PIC18F25K80 в среде MPLAB X / XC8.
Прошивка реализует:
ELM327 v2.3-совместимый набор AT-команд, достаточный для прохождения ELM Scan Adapter Validator и работы целевых приложений
ISO 15765-4 (CAN OBD) — 11/29-бит, 500/250 кбит/с, включая ISO-TP многофреймовый обмен
ISO 9141-2 — 5-baud slow init, K-line
ISO 14230-4 (KWP2000) — fast init и 5-baud init
SAE J1850 PWM и VPW — логика протокола реализована в коде; требует отдельного физического уровня (MC33390 и аналоги), не входит в базовый BOM
SAE J1939 — программная поддержка CAN 29-бит кадров; для грузовиков нужна аппаратная версия под 24 В и 9-pin Deutsch разъём
Bluetooth через модуль HC-05 с автонастройкой (Android/Windows; для iOS нужен BLE или Wi-Fi модуль)
Полное сохранение настроек в EEPROM
Диагностические счётчики и отладочные команды
Прошивка проходит тест совместимости ELM Scan Adapter Validator (ELM 2.3). В последней главе подробно разберём результаты тестирования, что именно проверялось и какие незначительные расхождения с оригинальным поведением остаются (они не мешают работе целевых приложений).
Серия рассчитана на начинающих embedded-разработчиков. Мы предполагаем, что вы:
Знаете основы C (указатели, структуры, битовые операции)
Слышали про микроконтроллеры и регистры периферии
Имеете общее представление о том, что такое UART и SPI
Знания CAN, K-line, J1850, ISO-TP не требуются — мы объясним каждый протокол с нуля, начиная с физического уровня.
Готовый dev-kit (собранная плата с PIC18F25K80, TJA1050, K-line драйвером и HC-05) доступен для приобретения — подробности в комментариях к статье.
Компонент Примечание PIC18F25K80 Основной МК. Доступен в DIP-28 PIC18F26K80 Альтернатива с большей Flash TJA1050 CAN трансивер Драйвер K-line (LIN) Например, L9637D или TJA1020 HC-05 Bluetooth модуль PICkit 3/4 Программатор MPLAB X + XC8 Бесплатный компилятор OBD-II J1962 male connector или пиг-тейл Для подключения к машине
# Статья Тема 0 Эта статья Обзор серии 1 Глава 1 Выбор железа и конфигурация МК 2 Глава 2 Архитектура прошивки: главный цикл, ISR, структуры 3 Глава 3 UART и Bluetooth: буферизация и автонастройка HC-05 4 Глава 4 Таймеры, EEPROM и системные утилиты 5 Глава 5 CAN шина: инициализация, отправка, приём, фильтры 6 Глава 6 ISO-TP: многофреймовый обмен поверх CAN 7 Глава 7 K-line: ISO 9141 и KWP2000 от физики до протокола 8 Глава 8 J1850 PWM и VPW 9 Глава 9 Диспетчер протоколов и OBD-запросы 10 Глава 10 Парсер AT-команд 11 Глава 11 Тестирование совместимости с ELM327
Полный исходный код со схемами и проектом MPLAB X будет опубликован в последней статье серии.
Каждый, кто работает с Kubernetes, знает эту боль. Утро начинается с того, что нужно подключиться к базе данных в production для дебага, потом к Redis в staging для проверки кэша, затем к RabbitMQ для мониторинга очередей, и наконец к API-сервису для тестирования нового эндпоинта.
И вот уже восемь открытых терминалов, в каждом — свой kubectl port-forward. Окна перемешиваются, названия похожи, и найти нужный терминал становится квестом.
Ну да да, можно использовать Tmux, но это не сильно облегчает процесс.
С какими проблемами я столкнулся
Потеря контекста — когда одно из соединений падает, приходится перебирать все терминалы, чтобы найти нужный. На это уходит драгоценное время.
Ручная работа при смене контекста — переключил kubectl context на другой кластер, и всё нужно перенастраивать заново. Каждый раз одни и те же команды.
Отсутствие истории — вернулся после обеда, половина соединений отвалилась. Какие именно порты были нужны? Приходится вспоминать или искать в истории bash.
Сложность передачи знаний — коллега просит команду для подключения к сервису. Диктуешь по буквам namespace, имя пода, номера портов. Это неэффективно и приводит к ошибкам.
Я решил, что пора автоматизировать этот процесс и написать специализированный инструмент, исходник на GitHub PortFwd
Прежде чем писать своё, я изучил, что уже есть на рынке. Оказалось, что решения существуют, но каждое имеет свои плюсы и минусы.
kubectl port-forward
Встроенный инструмент, который работает надёжно и стабильно. Но у него принципиальное ограничение: один терминал — одно соединение. При работе с микросервисной архитектурой, где нужно одновременно держать открытыми 5-10 соединений, это превращается в хаос.
kubefwd
Интересный проект, который автоматически форвардит все сервисы из namespace. Звучит удобно, но есть серьёзные проблемы. Во-первых, он требует права sudo, потому что модифицирует /etc/hosts. Во-вторых, это слишком инвазивно — я хочу контролировать, какие именно сервисы форвардить, а не получать всё скопом.
Lens
Красивый графический интерфейс с множеством функций для работы с Kubernetes. Но это Electron-приложение, которое потребляет более 500 МБ оперативной памяти и 2% CPU даже в режиме простоя. Для такой простой задачи, как управление port-forward, это явный overkill. Плюс Lens — это комбайн, а мне нужен специализированный инструмент.
k9s
Отличный TUI для работы с Kubernetes, который я сам активно использую. Но port-forward там — не основная функция. Управлять множеством одновременных соединений неудобно, нет сохранения сессий, нет группировки.
После этого анализа я понял, что нужно писать своё решение. Лёгкое, специализированное, без Electron'а.
kftRay
Отдельно рассмотрю и сравню проект https://kftray.app/
Сравнение этих двух инструментов интересно тем, что они решают одну и ту же задачу — управление пробросом портов (port-forwarding) в Kubernetes — но делают это с совершенно разных позиций.
Характеристика kftray (Rust) portFwd (Go) Язык программирования Rust (фреймворк Tauri) Go Интерфейс Полноценный GUI (графическое окно в трее) CLI (командная строка) + кастомный UI Сложность Высокая (визуальное управление множеством конфигов) Минималистичная (быстрый запуск из терминала) Кроссплатформенность Отличная (Windows, macOS, Linux) Отличная (нативный бинарник) Целевая аудитория Те, кто хочет «настроить и забыть» через интерфейс Те, кто предпочитает терминал и скорость
Почему для такого проекта Go может быть лучше?
Несмотря на то, что kftray на Rust выглядит очень современно, использование Go для portFwd дает несколько фундаментальных преимуществ в контексте Kubernetes:
Нативная экосистема Kubernetes: Весь Kubernetes (и kubectl) написан на Go. Используя Go, разработчик portFwd применяет те же самые библиотеки (client-go), которые используют создатели K8s. Это гарантирует 100% совместимость с конфигами kubeconfig, контекстами и методами аутентификации.
Стабильность сетевых соединений: В Go работа с сетью и многопоточностью (Goroutines) реализована «из коробки» очень эффективно. Проброс портов — это постоянное ожидание трафика и пересылка пакетов. Горутины позволяют обрабатывать сотни таких соединений с минимальным потреблением памяти и без риска «уронить» поток.
Скорость компиляции и деплоя: Если вам нужно быстро внести правку в логику проброса портов, Go скомпилирует бинарник за секунды. Rust (особенно с тяжелым Tauri/GUI) собирается значительно дольше.
Размер и простота: Для системной утилиты, которая должна просто «перекидывать байты» из кластера на локальную машину, Go предлагает более простой и читаемый код. В Rust-проекте (как kftray) много времени уходит на управление памятью и согласование типов интерфейса, в то время как в Go-проекте фокус остается на сетевой логике.
Плюсы portFwd (на Go)
Минимализм: Идеально подходит для автоматизации и скриптов.
Низкий порог входа: Если вы захотите доработать инструмент под себя, разобраться в коде на Go будет в разы проще, чем в Rust-коде с GUI-обвязкой.
Работа с контекстами: Go-библиотеки лучше всего справляются с переключением между десятками кластеров и сложными методами SSO-логина в облака (AWS/GCP/Azure).
Плюсы kftray (на Rust)
Визуальный комфорт: Если у вас 20+ портов, управлять ими через иконку в трее удобнее, чем держать открытыми 5 вкладок терминала.
Безопасность памяти: Rust исключает целый класс ошибок сегментации, что важно для долгоживущих фоновых процессов.
Итог
Если вам нужен инструмент для ежедневной рутины с красивыми кнопками — выбирайте kftray. Если вам нужна надежная, быстрая и легкая утилита, которая работает так же, как сам Kubernetes — portFwd на Go будет более естественным и предсказуемым выбором.
Прежде чем писать реализацию, важно понять, как port-forward работает под капотом. Без этого понимания легко написать код, который будет падать в неожиданных местах.
Архитектура соединения
Когда вы выполняете kubectl port-forward pod/nginx 8080:80, данные проходят через несколько компонентов (см. рис. 1). Клиент устанавливает соединение с API Server, который проксирует запрос на Kubelet ноды, где запущен под. Kubelet, в свою очередь, использует nsenter для доступа к network namespace контейнера и пересылает данные процессу внутри.
рис. 1: Архитектура port-forward в Kubernetes
Протокол SPDY
Для мультиплексирования потоков Kubernetes использует протокол SPDY — предшественник HTTP/2. Это позволяет передавать несколько потоков данных через одно TCP-соединение. Процесс установки соединения выглядит так:
Клиент отправляет HTTP POST запрос на эндпоинт /api/v1/namespaces/{ns}/pods/{pod}/portforward
В заголовке указывается Upgrade: SPDY/3.1, что инициирует переключение протокола
API Server устанавливает SPDY-соединение и создаёт потоки для данных и ошибок
Kubelet получает запрос и проксирует данные в контейнер
Критический нюанс: Service vs Pod
Это один из самых важных моментов, который я понял не сразу. Хотя Kubernetes API формально поддерживает эндпоинт /services/{service}/portforward, на практике kubectl резолвит сервис в под и подключается напрямую к поду.
Почему так? Потому что port-forward работает на уровне конкретного контейнера через Kubelet, а не через механизм балансировки kube-proxy. Это значит, что нам тоже придётся находить backing pod для сервиса и резолвить targetPort.
После изучения теории я спроектировал архитектуру PortFwd. Приложение разделено на несколько слоёв, каждый из которых отвечает за свою область.
Слой Kubernetes (k8s/client.go)
Этот компонент инкапсулирует всю работу с Kubernetes API. Он отвечает за получение списка namespace'ов, подов и сервисов, а также за резолвинг сервисов в поды. Использует официальную библиотеку client-go.
Менеджер соединений (portforward/manager.go)
Центральный компонент, который управляет жизненным циклом всех port-forward соединений. Он создаёт SPDY-транспорт, отслеживает статус каждого соединения, обрабатывает ошибки и уведомляет UI об изменениях через callback.
Слой UI (ui/app.go, views.go, styles.go)
Терминальный интерфейс построен на фреймворке Bubble Tea с использованием библиотеки Lipgloss для стилизации. Bubble Tea реализует Elm Architecture: Model (состояние), Update (обработка событий), View (рендеринг).
Конфигурация (config/state.go)
Компонент для сохранения и восстановления сессий. При выходе из приложения текущие соединения сохраняются в YAML-файл, а при следующем запуске — восстанавливаются.
Инициализация клиента
Для работы с кластером нужно инициализировать клиент. Я использовал паттерн из официальных примеров client-go: сначала пробуем получить in-cluster конфиг (если приложение запущено внутри пода), а если не получается — читаем kubeconfig файл.
Ключевой момент — использование clientcmd.BuildConfigFromFlags, которая автоматически учитывает текущий context из kubeconfig. Это позволяет приложению работать с тем же кластером, что и kubectl.
Резолвинг Service в Pod
Когда пользователь выбирает сервис для port-forward, нам нужно найти backing pod. Алгоритм следующий:
Получаем сервис — делаем GET-запрос к API для получения спецификации сервиса
Резолвим targetPort — сервис может иметь port: 80, но контейнер слушает на targetPort: 8000. Нужно использовать именно targetPort
Строим label selector — из поля spec.selector сервиса формируем строку для поиска подов
Находим Running под — получаем список подов по selector и выбираем первый со статусом Running
Обработка Named Ports
Отдельная сложность — именованные порты. Сервис может указывать targetPort: http вместо числа. В этом случае нужно найти порт с таким именем в спецификации контейнера пода. Без этой обработки приложение будет пытаться подключиться к неправильному порту.
Это была одна из главных граблей в разработке. Я потратил несколько часов, разбираясь, почему соединение отказывает, пока не понял, что приложение стучится в порт 80, а процесс слушает на 8000.
Создание транспорта
Для установки port-forward соединения используется несколько компонентов из client-go:
spdy.RoundTripperFor — создаёт HTTP transport с поддержкой SPDY upgrade
spdy.NewDialer — создаёт dialer, который умеет устанавливать SPDY-соединения
portforward.NewOnAddresses — собственно создаёт port-forwarder
Важный момент: я использую NewOnAddresses с параметром []string{"127.0.0.1"}, а не стандартный New. Это заставляет слушать только на IPv4. По умолчанию forwarder слушает и на IPv4, и на IPv6, что на некоторых системах (особенно macOS) вызывает проблемы с dual-stack.
Управление жизненным циклом
Каждое соединение имеет несколько каналов для управления:
stopChan — сигнал остановки, закрытие этого канала прерывает ForwardPorts
readyChan — сигнал готовности, сообщает что туннель установлен
context с cancelFunc — для graceful shutdown
При запуске соединения мы ждём либо сигнала готовности (соединение установлено), либо ошибки, либо таймаута (30 секунд). После установки соединения переходим в режим ожидания завершения.
Почему Bubble Tea?
Bubble Tea от Charm — это фреймворк для построения терминальных интерфейсов на Go, вдохновлённый Elm Architecture. Он предоставляет чистую модель программирования:
Model — иммутабельное состояние приложения (текущий view, список соединений, выбранный элемент)
Update — чистая функция, которая принимает сообщение и возвращает новое состояние
View — чистая функция, которая рендерит состояние в строку для терминала
Этот подход делает код предсказуемым и легко тестируемым. Состояние всегда консистентно, потому что изменяется только через Update.
Стилизация с Lipgloss
Для визуального оформления я использую Lipgloss — библиотеку для стилизации терминального вывода. Она позволяет задавать цвета, отступы, границы, выравнивание — всё, что нужно для красивого интерфейса.
Я выбрал цветовую схему в стиле киберпанка: неоновый зелёный для активных соединений, красный для ошибок, жёлтый для предупреждений. Это делает статусы соединений мгновенно различимыми.
Связь UI и Manager
Менеджер соединений работает асинхронно — соединения устанавливаются и падают в фоне. Чтобы UI узнавал об изменениях, я использую callback-паттерн. При инициализации UI устанавливает callback в менеджере, и при любом изменении состояния соединения менеджер вызывает этот callback, который отправляет сообщение в Bubble Tea.
Зачем это нужно
Одна из главных фич PortFwd — сохранение сессий между запусками. Вы настроили 5 port-forward соединений, закрыли приложение, а при следующем запуске — все соединения автоматически восстанавливаются.
Формат хранения
Состояние сохраняется в YAML-файл по пути ~/.config/portfwd/state.yaml. Для каждого соединения сохраняются: namespace, тип ресурса (pod или service), имя ресурса, локальный и удалённый порты, и флаг wasActive — было ли соединение активно при сохранении.
Флаг wasActive важен: если пользователь вручную остановил соединение перед выходом, при следующем запуске оно должно остаться остановленным, а не переподключаться автоматически.
Логика восстановления
При запуске приложение читает файл состояния и для каждого сохранённого соединения проверяет:
Если wasActive: false — добавляем как остановленное, не подключаемся
Если wasActive: true — проверяем доступность ресурса (существует ли под/сервис)
Если ресурс доступен — автоматически переподключаемся
Если ресурс недоступен — добавляем как остановленное с ошибкой
Эта секция — самая ценная часть статьи. Здесь собраны реальные проблемы, на которые я потратил часы отладки.
Проблема 1: Зависание при выходе
Симптом: после нажатия q приложение не завершается, висит бесконечно.
Причина: callback onChange вызывает program.Send() в Bubble Tea. После вызова tea.Quit программа закрывает канал сообщений, и Send() блокируется навечно, ожидая возможности отправить сообщение.
Решение: отключать callback до остановки соединений. Сначала делаем m.onChange = nil, потом останавливаем соединения. Тогда при изменении состояния соединения callback не вызывается и deadlock не возникает.
Проблема 2: connection refused при подключении к сервису
Симптом: при port-forward к сервису получаем ошибку connection refused внутри контейнера.
Причина: я использовал port сервиса (например, 80) вместо targetPort (например, 8000). Приложение в контейнере слушало на 8000, а мы стучались в 80.
Решение: полный резолвинг targetPort, включая обработку named ports. Нужно всегда использовать targetPort, а не port сервиса.
Проблема 3: panic при повторном закрытии канала
Симптом: panic: close of closed channel при быстром двойном нажатии на «Stop».
Причина: канал stopChan закрывается дважды. В Go закрытие уже закрытого канала вызывает panic.
Решение: использовать sync.Once для защиты операции закрытия. Once гарантирует, что функция выполнится только один раз, даже при конкурентных вызовах.
Проблема 4: IPv6 connection refused
Симптом: на некоторых системах получаем ошибку IPv6 dial tcp6 [::1]:80: connection refused.
Причина: по умолчанию port-forwarder слушает и на IPv4, и на IPv6. На системах с определёнными сетевыми настройками это вызывает проблемы.
Решение: явно указывать только IPv4 через NewOnAddresses с параметром []string{"127.0.0.1"}.
Проблема 5: соединения не восстанавливаются при рестарте
Симптом: при запуске приложения список соединений пустой, хотя файл состояния существует.
Причина: я вызывал saveSessionState() после StopAll(). К моменту сохранения все соединения уже были удалены из менеджера.
Решение: изменить порядок: сначала сохраняем состояние (когда соединения ещё существуют), потом останавливаем их.
Альтернативы, от которых я отказался
WebSocket вместо SPDY: Kubernetes поддерживает оба протокола, но client-go имеет готовую и протестированную реализацию именно для SPDY. Писать свой WebSocket-клиент — лишняя работа без явных преимуществ.
Прямое подключение к Kubelet: Теоретически можно обойти API Server. Но это требует отдельной аутентификации, обхода сетевых ограничений и не даёт никаких преимуществ для нашей задачи.
Производительность
Я измерил потребление ресурсов PortFwd в сравнении с альтернативами при 5 активных соединениях в режиме простоя:
PortFwd: ~30 МБ RAM, ~0.1% CPU
Lens: ~500 МБ RAM, ~2% CPU
5 отдельных kubectl: ~100 МБ RAM, ~0.5% CPU
PortFwd потребляет в 15 раз меньше памяти, чем Lens, и запускается мгновенно (менее 100 мс против ~5 секунд у Lens).
Функциональность
✅ Единое окно для всех соединений
✅ Интерактивный выбор: namespace → тип ресурса → pod/service → порты
✅ Автоматический резолвинг targetPort для сервисов
✅ Сохранение и восстановление сессий между запусками
✅ Отдельные логи для каждого соединения
✅ Graceful shutdown без zombie-процессов
✅ Справка по горячим клавишам
Горячие клавиши
↑/↓ или j/k — навигация по списку
n — создать новое соединение
d — отключить выбранное соединение
D — отключить все соединения
r — переподключить выбранное
l — показать логи соединения
x — удалить остановленное соединение
? — показать справку
q — выход
Что получилось
Я создал специализированный инструмент, который решает конкретную проблему — управление множеством port-forward соединений в Kubernetes. PortFwd легковесный, быстрый и делает одну вещь хорошо.
Что можно улучшить
Профили — сохранение наборов соединений для разных окружений (dev, staging, prod) с быстрым переключением
Multi-cluster — работа с несколькими кластерами одновременно в одном окне
Auto-reconnect — автоматическое переподключение при потере связи без участия пользователя
Import — импорт соединений из kubectl команд или YAML-манифестов
Уроки, которые я извлёк
Читай исходники kubectl — это лучшая документация по работе с Kubernetes API. Официальная документация не покрывает многие нюансы.
Тестируй на разных системах — dual-stack IPv4/IPv6 ведёт себя по-разному на Linux и macOS. То, что работает у тебя, может сломаться у пользователя.
sync.Once — твой друг — каналы в Go закрываются только один раз, и это легко забыть при конкурентном доступе.
Порядок операций критичен — особенно при graceful shutdown. Сначала сохраняй состояние, потом останавливай процессы.
Буду рад звёздам на GitHub, issue с багами и pull request'ам с улучшениями. Спасибо за внимание!
Я написал первую строку кода в 10 лет, ИИ написала мне полностью рабочую программу в 2025
Технические аспекты: инструмент или «костыль»?
С технической стороны, ИИ-инструменты вроде Copilot, ChatGPT и прочих для написания кода — это не магические «чёрные ящики», а сложные модели, обученные на огромных объёмах открытого кода и документации. Они:
Ускоряют рутинные задачи — генерация шаблонного кода, написание тестов, документация.
Помогают искать решения — предлагают варианты, которые программист может адаптировать.
Снижают порог входа — новички быстрее осваивают синтаксис и паттерны.
Но есть и риски: ИИ может предлагать устаревшие, небезопасные или некорректные решения, как вверху. Бездумное копирование сгенерированного кода без понимания его работы ведёт к хрупким, плохо поддерживаемым системам. ИИ — это не замена программисту, а инструмент, эффективность которого зависит от квалификации пользователя.
Моральные аспекты: где грань между помощью и обманом?
1. Авторство и интеллектуальная собственность
ИИ обучается на открытых репозиториях, что вызывает вопросы: не является ли его вывод плагиатом? Пока код не копируется дословно, а переосмысливается — это ближе к тому, как учится человек. Однако ответственность за конечный продукт несёт программист, а не ИИ.
2. Честность и профессиональная этика
Использование ИИ становится «читерством», если:
· Выдаётся сгенерированный код за полностью свой.
· Маскируется некомпетентность.
· Нарушаются правила конкурсов или экзаменов.
В обычной работе, где цель — эффективный и качественный продукт, использование ИИ этично, если оно прозрачно для команды и заказчика.
3. Потеря навыков
Опасение, что ИИ сделает программистов ненужными, преувеличено. Скорее, смещается фокус навыков: меньше рутинного кодинга, больше — архитектурного мышления, анализа требований, проверки и адаптации результатов ИИ.
Запретить или адаптироваться?
Запрет ИИ в программировании напоминает историю с запретом калькуляторов в на экзамене. Да, бездумное использование вредит, но разумное — освобождает время для сложных задач. Вместо запретов нужны:
1. Новые стандарты образования — учить не только писать код, но и эффективно взаимодействовать с ИИ, критически оценивать его предложения.
2. Корпоративные политики — определить, где использование ИИ допустимо, а где требует обязательной проверки.
3. Этические кодексы — закрепить прозрачность в использовании ИИ-инструментов.
Заключение: ИИ — это новый «компилятор»
Раньше программисты писали на ассемблере, потом появились языки высокого уровня и компиляторы. Это не сделало разработку «чит-кодом», а подняло её на новый уровень. ИИ — следующая ступень абстракции.
Запрещать ИИ — значит отрицать эволюцию инструментов. Задача профессионала — не отвергать технологии, а интегрировать их с сохранением ответственности, критического мышления и этических принципов. ИИ не заменит программиста, но программист, использующий ИИ, заменит того, кто его игнорирует.
Вопрос не в том, «читерство» ли это, а в том, как мы, как сообщество, адаптируем нашу профессию к новым реалиям, сохранив её суть: создание качественных, безопасных и полезных цифровых решений.
Привет, Пикабу! Сижу, наблюдаю, как нейросеть за пять секунд пишет код, на который я в своё время потратил бы вечер, пачку печенек и серию нервных тиков. И знаете что? Я не грущу. Я… восхищаюсь.
Моя работа теперь — не писать код, а быть продвинутым заказчиком для безмозглого (пока что) железа. Перешёл из разряда «рабочий у станка» в «начальник цеха, который тыкает пальцем в планшет и говорит: “Сделай красиво”».
Мои новые профессиональные навыки:
1. Искусство надиктовывания. Раньше думал о синтаксисе, теперь думаю, как объяснить задачу так, чтобы ИИ не сделал калькулятор вместо интернет-магазина. «Нет, дружок, “сделай как у Amazon” — это не конкретно. Вот тебе 15 уточняющих пунктов».
2. Величайший верификатор. Я — тот самый человек, который смотрит на идеальный сгенерированный код и говорит: «А вот здесь, милый, у тебя вечная петля. Учи матчасть!» Чувствую себя сэнсеем, проверяющим ученика.
3. Мастер кнопки «Обновить». Когда ИИ выдаёт дичь, я не плачу. Я с загадочной улыбкой жму F5 и говорю: «Давай ещё разок, только с чувством, с толком, с расстановкой».
Обнаружил, что теперь у меня в два раза больше времени! Раньше тратил его на гугление ошибок, а теперь — на гугление мемов, который гуглит ошибки за меня. Круг замкнулся. Все счастливы.
Коллега пытался устроить соревнование: он против ChatGPT в написании скрипта. ИИ сделал за 10 секунд. Коллега — за полчаса, но с тремя чашками кофе и самоутверждением. Победила дружба. И кофе.
Следующий этап карьеры — стать «шаманом промптов». Буду водить курсы: «Как шепнуть нейросети на ушко, чтобы она сделала всё правильно и принесла вам кофе». Дорого беру.
А если серьёзно, я теперь не «программист-кодер». Я — «программист-стратег, дирижёр цифрового оркестра и ловец глюков». Звучит солиднее, да?
В общем, я не умираю. Я эволюционирую. Просто моя эволюция теперь измеряется не в строках кода, а в креативности запросов и скорости удаления дичи из ответов ИИ.
И да, этот пост, конечно же, писал я. А то вдруг подумаете, что я уже делегировал и это машина. Пока не делегировал. Но идея хорошая…
Ща спрошу у ИИ, как он сам к этому относится.
