38 KiB
Kind Clusters Dashboard — локальные кластеры Kubernetes (kind)
Проект сделан для тех, кому нужен локальный Kubernetes без облака и без ручной возни в терминале: kind остаётся движком, а поверх него — единая панель в браузере и REST API. В одном месте можно создавать и удалять кластеры, смотреть узлы и поды, ставить типовые вещи через Helm (ingress, мониторинг, metrics-server и др.), вести журнал операций и скачивать kubeconfig для работы с кластером с хоста или из других контейнеров. Удобно для обучения DevOps/Kubernetes, быстрой проверки манифестов и Helm-чартов, демо и песочниц, когда не хочется поднимать управляемый кластер и платить за API control plane.
Технически панель — это FastAPI в Docker-образе kind-k8s-tools:local, запуск через Makefile и Compose (Docker или Podman). На хосте по умолчанию интерфейс открывается на порту 8080 (переменная KIND_K8S_WEB_PORT в .env); внутри контейнера приложение слушает 6000. Порт 6000 наружу по умолчанию не пробрасываем: в Chrome и других браузерах на Chromium он считается небезопасным (ERR_UNSAFE_PORT). kind, kubectl и Helm ставятся в образ — на машине достаточно контейнерной среды и make. Данные кластеров и kubeconfig лежат на хосте в clusters/<имя>/.
Документация
| Ресурс | Описание |
|---|---|
| app/docs/api_routes.md | Описание REST API /api/v1/* с примерами JSON (для фронтенда и интеграций) |
| app/docs/screenshots.md | Скриншоты веб-интерфейса (светлая и тёмная тема); файлы в app/docs/images/ |
/docs (Swagger), /redoc, /api/v1/health |
На панели открываются в отдельном окне браузера (window.open); прямой URL — тот же порт, что и UI (по умолчанию 8080) |
Скриншоты (превью)
Полная галерея и подписи — в app/docs/screenshots.md. Ниже — главная панель в обеих темах.
| Светлая тема | Тёмная тема |
|---|---|
![]() |
![]() |
В корне репозитория — файл env.example: перечислены только имена переменных (без значений), для ориентира при ручной настройке .env. Полноценно создать .env можно интерактивно скриптом scripts/setup_env_interactive.py (make setup; в начале — выбор docker или podman, путь CONTAINER_SOCKET подставляется автоматически).
Зачем это нужно
- Быстро получить Kubernetes локально (интеграционные тесты, манифесты, обучение) — см. ввод о назначении проекта выше.
- Версия кластера и число worker-нод задаются в веб-UI (или через REST API / скрипты в контейнере).
- Количество кластеров не ограничено кодом (ограничения — ресурсы хоста и Docker).
- Артефакты на хосте:
clusters/<имя>/— том дляkubeconfig(доступен в контейнере как/work/clusters/<имя>/).
Веб-интерфейс
- Верхняя единая карточка: заголовок, краткое описание и строка состояния среды (
kind/kubectl/ Docker или Podman API). - Статистика: число кластеров в kind, локальных каталогов, сумма workers из
meta.json, счётчики фоновых заданий. - Создание кластера (
/cluster-create): форма с подсказкой теговkindest/node(GET /api/v1/versions), фоновое задание и опрос статуса (JSON в сворачиваемом блоке); блок «Последние задания» (журнал с диска) обновляется без полной перерисовки таблицы — новые записи добавляются сверху, раскрытый лог не сбрасывается при автообновлении. - Кластеры (
/clusters): сводка ресурсов узлов (донаты), таблица кластеров — старт/стоп, скачивание kubeconfig, модалки узлов/подов, ссылка на страницу кластера; с панели (/) — кнопка «Перейти к созданию кластера» (размер кнопки плавно уменьшается на узком экране). - Аддоны (
/cluster-addons): выбор кластера и установка/удаление через Helm в контейнере — ingress-nginx, kube-prometheus-stack (логин/пароль Grafana), metrics-server, Istio + Kiali (Kiali по умолчанию без формы входа,authпри необходимости в YAML values); журнал операции на странице (прогресс + вывод как при создании кластера), история вclusters/<имя>/helm_addon_log.json. Нужна пересборка образа после обновления Dockerfile (бинарникhelm). Таймаут операций:KIND_K8S_HELM_TIMEOUT_SEC(по умолчанию 900 с). - Последние задания: история в памяти процесса (до 200 записей; после перезапуска контейнера сбрасывается); кнопка «Очистить завершённые» вызывает
DELETE /api/v1/jobs(из памяти удаляются только завершённые задания). - Журнал (
/journal): три режима (по кластеру, развёртывание, Helm-аддоны), пагинация; на узком экране таблица записей превращается в карточки (как «Последние задания» на странице создания). - Документация (
/documentation): README иapp/docs/*.mdв браузере (Markdown); при загрузке и при переходе между файлами — полноэкранный спиннер (как на панели). - Автообновление таблиц и плашки среды каждые ~3,5 с (fetch к API без перезагрузки страницы).
- При активном задании — прогресс-бар, журнал (в т.ч. скачивание образа; для docker при поддержке CLI —
pull --progress=plain, см.KIND_K8S_DOCKER_PULL_PLAIN), опрос статуса чаще, чем общие таблицы; кнопка «Отменить» — прерывание с завершением текущей дочерней команды. - Уведомления (toast) при успехе/ошибке; в подвале — копирайт и ссылка на devops.org.ru.
Шапка: навигация в виде пилюль (стили .nav-pill); при ширине окна меньше ~920px — кнопка «гамбургер» и выезжающая панель (app/static/js/nav-mobile.js). Пункты Swagger, ReDoc и Health открывают страницу в отдельном именованном окне (~1240×840), чтобы не уходить с панели (см. скрипт в base.html).
Адаптивная вёрстка (кратко): донаты «Ресурсы узлов» переносятся на новые строки при ширине окна меньше ~710px; блок «Статистика» на главной — мини-карточки в две колонки при меньше ~520px; таблицы кластеров и «Последние задания» на /cluster-create при ширине меньше ~920px оформляются карточками; на /journal карточки записей — при меньше ~620px; в журнале и таблицах заданий колонка времени (UTC) — дата и время в две строки при меньше ~920px. При первой загрузке полноэкранный спиннер: главная, /clusters, страница кластера, /cluster-create, /documentation (app/static/style.css — .page-loading-overlay).
Структура фронтенда: app/templates/base.html (шапка и меню), app/templates/dashboard.html, app/templates/journal.html, app/templates/documentation.html, app/static/style.css, app/static/js/dashboard.js, app/static/js/journal.js, app/static/js/documentation.js, app/static/js/nav-mobile.js (префикс API: data-api-base на <body>, по умолчанию /api/v1).
Требования на хосте
| Компонент | Назначение |
|---|---|
| Docker + Compose v2 (или Podman + compose) | Сборка образа и запуск веб-сервиса |
| make | make docker up / make podman up и вспомогательные цели |
| python3 | Только для make setup (создание .env) |
На хост не нужны: kind, kubectl, Python приложения — всё это в образе kind-k8s-tools:local и выполняется в контейнере kind-k8s-web. Проверка API и узлов: веб-интерфейс (кластер → узлы/поды) или make docker kubectl CLUSTER=<имя> / make podman kubectl … (см. ниже) — kubectl вызывается через docker compose exec / podman compose exec внутри уже запущенного сервиса.
Файл clusters/<имя>/kubeconfig.host (или скачивание из веб-UI — кнопка со стрелкой вниз) для kubectl на хосте: https://<KIND_K8S_KUBECONFIG_CLIENT_HOST или localhost>:<порт> — см. app/kubeconfig_patch.py. Переменная позволяет задать IP/имя хоста, если localhost недоступен с вашей машины. Рядом в UI — вторая кнопка (иконка «пакет»): GET …/kubeconfig/docker — kubeconfig для kubectl из любого контейнера: https://host.docker.internal:<порт> (порт с проброса 6443 control plane на хост) и tls-server-name: localhost, как у процесса веб-приложения. На Linux в вашем контейнере добавьте extra_hosts: ["host.docker.internal:host-gateway"] (в compose — как у сервиса kind-k8s-web). Если docker port недоступен при генерации файла, подставляется запасной https://<имя>-control-plane:6443 (тогда нужна общая сеть с kind).
Смонтированы сокет Docker/Podman и каталог ./clusters → в контейнере /work/clusters. Каталог ./app монтируется в /opt/kind-k8s/app для разработки без пересборки образа. Файл ./README.md монтируется в /opt/kind-k8s/README.md (страница «Документация» и GET /api/v1/docs/readme без пересборки образа).
После создания кластера при KIND_K8S_PATCH_KUBECONFIG дополнительно пишется kubeconfig.host; скачивание через API каждый раз пересобирает файл с актуальным портом и хостом из KIND_K8S_KUBECONFIG_CLIENT_HOST.
kubectl из другого контейнера (ошибка localhost:<порт> / dev-control-plane: Name or service not known)
Файл kubeconfig.host рассчитан на запуск kubectl с хоста. Внутри другого контейнера 127.0.0.1 — не хост Docker, поэтому https://localhost:<порт> из kubeconfig.host там не работает. Имя …-control-plane резолвится только в той же сети, что и узел kind — из «чужой» сети контейнера DNS его не видит.
Вариант 1 (предпочтительно): скачать kubeconfig из UI (кнопка с иконкой пакета) или GET /api/v1/clusters/{name}/kubeconfig/docker. В файле уже https://host.docker.internal:<порт> и tls-server-name: localhost (как у kubectl внутри kind-k8s-web): трафик идёт на хост, затем в проброшенный порт API kind — без общей сети с кластером. В вашем контейнере на Linux добавьте в compose/run: extra_hosts: ["host.docker.internal:host-gateway"] (как в docker-compose.yml у kind-k8s-web). Шлюз переопределяется KIND_K8S_APISERVER_GATEWAY_HOST; SNI — KIND_K8S_KUBECONFIG_TLS_SERVER_NAME.
Вариант 2: общая сеть Docker с kind. Подключите контейнер к сети kind и задайте server: https://<имя_кластера>-control-plane:6443 (см. docker network ls / docker inspect …-control-plane).
Подробности — app/kubeconfig_patch.py.
Быстрый старт
Готовый образ с Docker Hub (без сборки, без файла .env)
Опубликованный образ: inecs/kind-cluster-dashboard:v1.0.0 (соответствует значениям по умолчанию в Makefile: DOCKERHUB_REPO=inecs/kind-cluster-dashboard, RELEASE_TAG=v1.0.0). Все теги: hub.docker.com/r/inecs/kind-cluster-dashboard/tags.
Сборка и отправка в Docker Hub (для сопровождения образа): linux/amd64 (ПК x86_64) и linux/arm64 (Apple Silicon и прочий ARM). Образ linux/386 (32-bit x86) для kind официальными бинарниками не поставляется, поэтому в релиз не входит.
docker login
# По умолчанию пушит inecs/kind-cluster-dashboard:v1.0.0 (см. Makefile):
make release
# Или явно: make release DOCKERHUB_REPO=inecs/kind-cluster-dashboard RELEASE_TAG=v1.0.0
# Необязательно: KIND_VERSION=0.24.0 HELM_VERSION=v3.16.3 KUBECTL_VERSION=v1.32.0
Другой образ/тег задайте в shell: KIND_K8S_HUB_IMAGE=логин/репозиторий:тег при вызове compose (в YAML ниже то же через подстановку).
mkdir -p clusters
# Docker (файл в репозитории: compose/docker-compose.hub.docker.yml; образ по умолчанию — v1.0.0):
docker compose -f compose/docker-compose.hub.docker.yml up -d
# Другой тег: KIND_K8S_HUB_IMAGE=inecs/kind-cluster-dashboard:latest docker compose -f compose/docker-compose.hub.docker.yml up -d
# Podman — путь к API-сокету только через переменные окружения (не через .env):
export CONTAINER_SOCKET="${XDG_RUNTIME_DIR}/podman/podman.sock"
podman compose -f compose/docker-compose.hub.podman.yml up -d
Браузер: http://127.0.0.1:8080 (в примерах ниже публикация 8080:6000; при необходимости поменяйте в YAML).
Ниже — полные примеры docker-compose: все переменные environment заданы явно теми же значениями, что использует приложение по умолчанию (см. docker-compose.yml, app/core/*.py, app/kubeconfig_patch.py). .env не нужен. Актуальные копии — в compose/docker-compose.hub.docker.yml и compose/docker-compose.hub.podman.yml.
Docker — вставьте в docker-compose.yml (или сохраните как отдельный файл):
services:
kind-k8s-web:
image: ${KIND_K8S_HUB_IMAGE:-inecs/kind-cluster-dashboard:v1.0.0}
container_name: kind-clusters-dashboard
user: "0:0"
volumes:
- ${KIND_K8S_CLUSTERS_DIR:-./clusters}:/work/clusters
- ${CONTAINER_SOCKET:-/var/run/docker.sock}:/var/run/docker.sock
ports:
- "8080:6000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
HOME: /root
DOCKER_HOST: unix:///var/run/docker.sock
KIND_K8S_IN_CONTAINER: "1"
KIND_K8S_WORKDIR: /work
KIND_K8S_PATCH_KUBECONFIG: "1"
KIND_K8S_KUBECONFIG_CLIENT_HOST: localhost
KIND_K8S_KUBECONFIG_TLS_SERVER_NAME: localhost
KIND_K8S_APISERVER_GATEWAY_HOST: host.docker.internal
CONTAINER_CLI: docker
KIND_K8S_SKIP_VERSION_LIST: "0"
KIND_K8S_VERSION_LIST_DISPLAY: "50"
KIND_K8S_HUB_TAGS_MAX_PAGES: "120"
KIND_K8S_DEBUG: "0"
KIND_K8S_JOB_LOG_MAX_LINES: "2500"
KIND_K8S_STREAM_PTY: "1"
KIND_K8S_DOCKER_PULL_PLAIN: "1"
KIND_K8S_JOB_API_LOG_MAX_LINES: "5000"
KIND_K8S_JOBS_JSON: /work/clusters/kind_k8s_jobs.json
KIND_K8S_README_PATH: /opt/kind-k8s/README.md
KIND_K8S_WAIT_NODES: "1"
KIND_K8S_WAIT_NODES_TIMEOUT_SEC: "300"
KIND_K8S_HELM_TIMEOUT_SEC: "900"
KIND_K8S_HELM_VERSIONS_CACHE_SEC: "600"
KIND_K8S_HELM_VERSIONS_MAX: "80"
KIND_K8S_CLUSTER_JOURNAL_MAX_ENTRIES: "500"
KIND_K8S_CLUSTER_JOURNAL_MAX_LOG_LINES: "2000"
KIND_K8S_HELM_ADDON_LOG_MAX_ENTRIES: "500"
KIND_K8S_APP_TITLE: "Kind Clusters Dashboard"
KIND_K8S_UVICORN_RELOAD: "0"
working_dir: /opt/kind-k8s/app
command: ["/opt/kind-k8s/run_uvicorn.sh"]
Podman — перед запуском: export CONTAINER_SOCKET="$XDG_RUNTIME_DIR/podman/podman.sock" (или фактический путь к podman.sock). При EACCES на сокет подберите user / UID:GID как в основном docker-compose.yml и Makefile для make podman up.
services:
kind-k8s-web:
image: ${KIND_K8S_HUB_IMAGE:-inecs/kind-cluster-dashboard:v1.0.0}
container_name: kind-clusters-dashboard
userns_mode: keep-id
user: "0:0"
volumes:
- ${KIND_K8S_CLUSTERS_DIR:-./clusters}:/work/clusters
- ${CONTAINER_SOCKET}:/run/podman/podman.sock
ports:
- "8080:6000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
HOME: /tmp
DOCKER_HOST: unix:///run/podman/podman.sock
KIND_K8S_IN_CONTAINER: "1"
KIND_K8S_WORKDIR: /work
KIND_K8S_PATCH_KUBECONFIG: "1"
KIND_K8S_KUBECONFIG_CLIENT_HOST: localhost
KIND_K8S_KUBECONFIG_TLS_SERVER_NAME: localhost
KIND_K8S_APISERVER_GATEWAY_HOST: host.docker.internal
CONTAINER_CLI: podman
KIND_K8S_SKIP_VERSION_LIST: "0"
KIND_K8S_VERSION_LIST_DISPLAY: "50"
KIND_K8S_HUB_TAGS_MAX_PAGES: "120"
KIND_K8S_DEBUG: "0"
KIND_K8S_JOB_LOG_MAX_LINES: "2500"
KIND_K8S_STREAM_PTY: "1"
KIND_K8S_DOCKER_PULL_PLAIN: "1"
KIND_K8S_JOB_API_LOG_MAX_LINES: "5000"
KIND_K8S_JOBS_JSON: /work/clusters/kind_k8s_jobs.json
KIND_K8S_README_PATH: /opt/kind-k8s/README.md
KIND_K8S_WAIT_NODES: "1"
KIND_K8S_WAIT_NODES_TIMEOUT_SEC: "300"
KIND_K8S_HELM_TIMEOUT_SEC: "900"
KIND_K8S_HELM_VERSIONS_CACHE_SEC: "600"
KIND_K8S_HELM_VERSIONS_MAX: "80"
KIND_K8S_CLUSTER_JOURNAL_MAX_ENTRIES: "500"
KIND_K8S_CLUSTER_JOURNAL_MAX_LOG_LINES: "2000"
KIND_K8S_HELM_ADDON_LOG_MAX_ENTRIES: "500"
KIND_K8S_APP_TITLE: "Kind Clusters Dashboard"
KIND_K8S_UVICORN_RELOAD: "0"
working_dir: /opt/kind-k8s/app
command: ["/opt/kind-k8s/run_uvicorn.sh"]
Запуск из репозитория (локальная сборка образа)
# Из корня клонированного репозитория Kind Clusters Dashboard (рядом с Makefile):
make setup # опционально: интерактивно создать .env (Enter — дефолты из скрипта)
make docker check-docker # или: make podman check-docker
make docker up # или: make podman up
# Браузер: http://127.0.0.1:8080 (порт: KIND_K8S_WEB_PORT в .env; не 6000 на хосте — Chrome ERR_UNSAFE_PORT)
Из родительского каталога: make -C <каталог-корня-репозитория> docker up (подставьте путь к каталогу с Makefile).
Логи, статус, перезапуск и остановка: make docker logs / make podman logs (follow), make docker ps / make podman ps, make docker restart / make podman restart (перезапуск сервиса kind-k8s-web), make docker down / make podman down.
Разработка UI и API без пересборки образа
В docker-compose.yml смонтированы ./app → /opt/kind-k8s/app и ./README.md → /opt/kind-k8s/README.md (только чтение).
По умолчанию (KIND_K8S_UVICORN_RELOAD=1) uvicorn запускается с --reload (см. scripts/run_uvicorn.sh) и перезапускает процесс при изменении *.py, *.html, *.css, *.js в app/. Пересобирать образ нужно после изменений Dockerfile, requirements.txt или scripts/run_uvicorn.sh.
Отключить reload: в .env задать KIND_K8S_UVICORN_RELOAD=0.
Дополнительно: CLI в одноразовом контейнере
Если нужен сценарий без UI (CI, скрипты):
docker compose run --rm --entrypoint python3 kind-k8s-web \
/opt/kind-k8s/app/create_cluster.py --non-interactive --name dev --kubernetes-version 1.29.4 --workers 2
docker compose run --rm --entrypoint python3 kind-k8s-web \
/opt/kind-k8s/app/delete_cluster.py --non-interactive --name dev --yes
Рабочий каталог сервиса в образе — /opt/kind-k8s/app; том clusters/ и сокет те же, что у docker compose up.
kubectl без установки на хост
Пока запущен веб-сервис (make docker up или make podman up), kubectl из образа:
# По умолчанию: get nodes (kubeconfig: /work/clusters/<имя>/kubeconfig внутри контейнера)
make docker kubectl CLUSTER=<имя_кластера>
# или: make podman kubectl CLUSTER=<имя_кластера>
make docker kubectl CLUSTER=<имя> KUBECTL_ARGS="get pods -A"
make docker kubectl CLUSTER=<имя> KUBECTL_ARGS="config view --minify"
Эквивалент вручную (из корня репозитория, Docker):
docker compose exec kind-k8s-web kubectl --kubeconfig=/work/clusters/<имя>/kubeconfig get nodes
После успешного kind create по умолчанию выполняется kubectl wait готовности нод (KIND_K8S_WAIT_NODES, KIND_K8S_WAIT_NODES_TIMEOUT_SEC в .env) — тоже внутри контейнера приложения.
Команды Makefile
| Цель | Описание |
|---|---|
make help |
Краткая справка |
make docker up / make podman up |
Поднять веб-UI (kind-k8s-web) |
make docker down / make podman down |
Остановить compose в каталоге репозитория |
make docker restart / make podman restart |
Перезапустить контейнер сервиса kind-k8s-web (compose restart) |
make docker logs / make podman logs |
Логи kind-k8s-web (stream, -f) |
make docker ps / make podman ps |
Статус контейнеров текущего compose-проекта |
make docker build / make podman build |
Собрать образ kind-k8s-tools:local |
make docker rebuild / make podman rebuild |
Пересборка образа без кэша (build --no-cache) и пересоздание контейнера (up -d --force-recreate) |
make docker check-docker / make podman check-docker |
Проверить выбранный CLI и compose version |
make docker kubectl CLUSTER=… / make podman kubectl CLUSTER=… |
kubectl в контейнере kind-k8s-web (опционально KUBECTL_ARGS="…"; по умолчанию get nodes). Сервис должен быть up. |
make setup |
Интерактивно создать .env (список переменных в scripts/setup_env_interactive.py) |
make clusters-dir |
Создать каталог clusters/ |
make docker … / make podman … |
Префикс обязателен для целей up, down, restart, logs, ps, build, rebuild, check-docker, kubectl |
Цели up, down, restart, logs, ps, build, rebuild, check-docker и kubectl без docker/podman в той же команде завершатся с подсказкой.
Переменные окружения
Файл .env в корне репозитория подхватывает Compose. Создать его: make setup или вручную по списку в scripts/setup_env_interactive.py. Файл .env в git не коммитится (см. .gitignore).
Переменные DOCKER_HOST, KIND_K8S_IN_CONTAINER, KIND_K8S_WORKDIR в контейнере задаются литералами в docker-compose.yml, а не из .env.
| Переменная | Где используется | Назначение |
|---|---|---|
KIND_VERSION |
build-arg | Версия бинарника kind при сборке образа |
KUBECTL_VERSION |
build-arg | Версия kubectl в образе; пусто в compose → в Dockerfile подставляется stable.txt при сборке; make setup предлагает закреплённый тег |
KIND_K8S_WEB_PORT |
ports | Порт на хосте для веб-UI (по умолчанию 8080; в контейнере публикация идёт на процесс на 6000) |
KIND_K8S_WEB_HOST |
локальный uvicorn / Settings | Хост привязки при запуске вне compose (в контейнере задаётся entrypoint) |
KIND_K8S_UVICORN_RELOAD |
контейнер | 1 (по умолчанию) — hot-reload при правках в ./app; 0 — без reload |
KIND_K8S_APP_TITLE |
контейнер / Settings | Заголовок OpenAPI и HTML; пустое значение из compose не ломает приложение (env_ignore_empty, fallback) |
KIND_K8S_WAIT_NODES |
контейнер | 0 — не ждать Ready нод после create |
KIND_K8S_WAIT_NODES_TIMEOUT_SEC |
контейнер | Таймаут kubectl wait (секунды) |
CONTAINER_SOCKET |
volume | Путь к сокету на хосте; при make podman … перед compose пишется .env.podman.override (в .gitignore) с актуальным путём из scripts/detect_podman_socket.py, чтобы перекрыть .env: иначе старый docker-compose часто подставляет том из .env (в т.ч. неверный сокет /var/folders/…/podman-machine-default-api.sock) и падает с operation not supported |
CONTAINER_SOCKET_MOUNT_TARGET |
volume | Путь внутри контейнера: Docker — /var/run/docker.sock; Podman — /run/podman/podman.sock |
KIND_K8S_REMOTE_SOCKET_URI |
контейнер DOCKER_HOST |
URI API (совпадает с точкой монтирования), например unix:///run/podman/podman.sock для Podman |
KIND_K8S_PATCH_KUBECONFIG |
контейнер | Патч server в kubeconfig для хоста; по умолчанию включено (1 в compose и в make setup) |
CONTAINER_CLI |
контейнер | CLI для docker port / podman port (docker или podman) |
KIND_K8S_CONTAINER_UIDGID |
compose user |
uid:gid процесса в контейнере; для Docker обычно 0:0; для rootless Podman — $(id -u):$(id -g) (пишет make setup) |
KIND_K8S_CONTAINER_HOME |
контейнер HOME |
Для не-root в образе без /home/<uid>: /tmp (Podman); для root — /root |
KIND_K8S_SKIP_VERSION_LIST |
контейнер | Не ходить в Docker Hub за тегами |
KIND_K8S_VERSION_LIST_DISPLAY |
контейнер | Сколько строк показывать в интерактивном CLI при выборе версии (веб-UI выводит полный список из API) |
KIND_K8S_HUB_TAGS_MAX_PAGES |
контейнер | Сколько страниц Docker Hub обходить при сборе тегов (старые 1.19.x часто на поздних страницах; в коде по умолчанию 120, максимум 500) |
KIND_K8S_DEBUG |
контейнер | 1/true/yes/да — уровень DEBUG в логах |
KIND_K8S_JOB_LOG_MAX_LINES |
приложение | Сколько строк журнала хранить в памяти на задание (старые вытесняются); по умолчанию 2500 |
KIND_K8S_JOB_API_LOG_MAX_LINES |
приложение | Сколько строк отдавать в GET /api/v1/jobs/{id} (хвост); по умолчанию 5000, максимум 20000 |
KIND_K8S_JOBS_JSON |
приложение | Путь к JSON с историей заданий; пусто — clusters/kind_k8s_jobs.json под KIND_K8S_WORKDIR |
KIND_K8S_STREAM_PTY |
приложение | 1 (по умолчанию) — для kind и podman pull псевдо-TTY; 0 — только pipe |
KIND_K8S_DOCKER_PULL_PLAIN |
приложение | 1 (по умолчанию) — если в выводе docker pull --help есть --progress, используется docker pull --progress=plain без PTY; иначе обычный pull. 0 — никогда не добавлять флаг |
KIND_K8S_README_PATH |
контейнер / приложение | Абсолютный путь к README.md для страницы /documentation; если пусто — используется README.md рядом с каталогом app/ (в образе: /opt/kind-k8s/README.md) |
KIND_K8S_WORKDIR |
локальный запуск | Корень данных на машине разработчика без compose |
COMPOSE_BUILD_FLAGS |
Makefile | Например make docker build COMPOSE_BUILD_FLAGS=--platform linux/arm64 (то же для make docker rebuild) |
Podman (пример rootless)
export CONTAINER_SOCKET="$XDG_RUNTIME_DIR/podman/podman.sock"
make podman up
Доступ к сокету (permission denied на /var/run/docker.sock внутри контейнера): у rootless Podman сокет обычно принадлежит вашему пользователю, а не «root» из контейнера. В .env для Podman нужны KIND_K8S_CONTAINER_UIDGID=$(id -u):$(id -g) и KIND_K8S_CONTAINER_HOME=/tmp — при make setup с выбором Podman скрипт записывает их сам. Команды make podman … подмешивают docker-compose.podman.yml (userns_mode: keep-id) и дублируют uid/gid в окружении: без keep-id rootless Podman часто даёт permission denied на смонтированный сокет даже при верном user:. Вручную: podman compose -f docker-compose.yml -f docker-compose.podman.yml up -d и те же переменные в .env или в shell. На системах с SELinux при отказе доступа к сокету попробуйте CONTAINER_SOCKET_VOLUME_OPTS=:Z в .env.
Структура репозитория (основное)
| Путь | Назначение |
|---|---|
Makefile |
Запуск веб-UI; префикс docker или podman обязателен; цели up, down, restart, logs, rebuild, build и др. Для Podman: --env-file .env.podman.override (нужен compose с поддержкой --env-file, обычно ≥ 1.28). |
.env.podman.override |
Создаётся целью make podman …: CONTAINER_SOCKET, KIND_K8S_* для сокета; перекрывает устаревшие значения в .env. |
scripts/setup_env_interactive.py |
Интерактивное создание .env (все ключи и дефолты внутри скрипта) |
scripts/run_uvicorn.sh |
Точка входа контейнера: uvicorn с опциональным --reload |
Dockerfile |
Образ: kind, kubectl, docker-cli, FastAPI |
requirements.txt |
pip-зависимости веб-приложения |
docker-compose.yml |
Сервис kind-k8s-web, тома ./clusters, ./app, ./README.md, сокет |
docker-compose.podman.yml |
Только для Podman: userns_mode: keep-id (подмешивается в make podman …) |
app/main.py |
FastAPI: главная /, создание кластера /cluster-create, /documentation, редирект /ui, монтирование /static |
app/api/v1/ |
REST API: router.py, endpoints/ (health, versions, docs_readme, clusters) |
app/core/ |
Жизненный цикл кластеров, задания, настройки, блокировки (kind_guard), пути |
app/models/schemas.py |
Pydantic-схемы запросов/ответов API |
app/templates/ |
Jinja2: base.html, dashboard.html, clusters.html, cluster_create.html, cluster_detail.html, cluster_edit.html, cluster_addons.html, journal.html, documentation.html |
app/static/ |
style.css, js/dashboard.js, js/documentation.js, js/vendor/ (marked, DOMPurify для README в UI) |
app/docs/ |
api_routes.md (описание REST API) |
app/create_cluster.py, delete_cluster.py, cluster_status.py |
CLI и переиспользование из API / compose run |
В UI и API список версий kindest/node по умолчанию тянется с Docker Hub (нужна сеть). В изолированной среде: KIND_K8S_SKIP_VERSION_LIST=1 — версию вводят вручную.
Где лежат данные на хосте
clusters/<имя>/kind-config.yamlclusters/<имя>/kubeconfigclusters/<имя>/meta.json
Содержимое clusters/*/ не коммитится; в репозитории есть clusters/.gitkeep.
Git и артефакты
В .gitignore: .env, каталоги в clusters/ (кроме .gitkeep), __pycache__/ и *.pyc, .DS_Store.
Ограничения
- Образ
kindest/node:v…должен быть доступен для pull. - На Windows без WSL удобнее WSL2 + Docker Desktop.
- kubectl на хосте не обязателен: используйте веб-UI или
make docker kubectl/make podman kubectl(см. выше). - История заданий в UI/API хранится в памяти (до 200 записей); после перезапуска контейнера очищается. Завершённые записи можно удалить из памяти кнопкой на панели или
DELETE /api/v1/jobs. - При
exec format errorу kind пересоберите образ:make docker rebuild COMPOSE_BUILD_FLAGS=--platform linux/arm64(илиmake podman …, илиmake docker buildбез--no-cache, илиlinux/amd64).
Автор: Сергей Антропов — devops.org.ru

