909129d8ed
- Страница /cluster/<имя>: сводка, ресурсы узлов полосами, отдельные карточки на каждый kubectl get … -o json, рестарт пода. - API: overview с блоками k8s_*, POST pods/restart, расширенный набор ресурсов (-A). - Панель: спиннер загрузки, правки дашборда и стилей; документация api_routes, compose и прочие сопутствующие изменения.
210 lines
21 KiB
Markdown
210 lines
21 KiB
Markdown
# Kind Clusters Dashboard — локальные кластеры Kubernetes (kind)
|
||
|
||
Образ **kind-k8s-tools:local** и **Makefile** поднимают **веб-интерфейс** (FastAPI) на порту **8080** на хосте по умолчанию (или **`KIND_K8S_WEB_PORT`** в `.env`; внутри контейнера приложение слушает **6000**). Порт **6000 на хосте** не используем по умолчанию: Chrome и другие браузеры на Chromium отдают **ERR_UNSAFE_PORT**. Через браузер создаёте и удаляете кластеры, смотрите статистику и вывод `kubectl`. **kubeconfig** сохраняется в `clusters/<имя>/`. На хосте достаточно **Docker** (или Podman) и **make**; **kind** и **kubectl** — внутри контейнера.
|
||
|
||
**Автор:** Сергей Антропов — [devops.org.ru](https://devops.org.ru)
|
||
|
||
## Документация
|
||
|
||
| Ресурс | Описание |
|
||
|--------|----------|
|
||
| **[app/docs/api_routes.md](app/docs/api_routes.md)** | Описание REST API `/api/v1/*` с примерами JSON (для фронтенда и интеграций) |
|
||
| **`/docs`** (Swagger), **`/redoc`**, **`/api/v1/health`** | На панели открываются в **отдельном окне** браузера (`window.open`); прямой URL — тот же порт, что и UI (по умолчанию **8080**) |
|
||
|
||
Шаблона **`env.example`** в репозитории нет: переменные для `.env` задаются интерактивно скриптом **`scripts/setup_env_interactive.py`** (`make setup`).
|
||
|
||
## Зачем это нужно
|
||
|
||
- Быстро получить Kubernetes без облака (интеграционные тесты, манифесты, обучение).
|
||
- Версия кластера и число worker-нод задаются **в веб-UI** (или через REST API / скрипты в контейнере).
|
||
- Количество кластеров **не ограничено** кодом (ограничения — ресурсы хоста и Docker).
|
||
- Артефакты на хосте: `clusters/<имя>/` — том для `kubeconfig` (доступен в контейнере как `/work/clusters/<имя>/`).
|
||
|
||
## Веб-интерфейс
|
||
|
||
- Верхняя **единая карточка**: заголовок, краткое описание и строка **состояния среды** (`kind` / `kubectl` / Docker или Podman API).
|
||
- **Статистика**: число кластеров в kind, локальных каталогов, сумма workers из `meta.json`, счётчики фоновых заданий.
|
||
- **Создание кластера**: форма с подсказкой тегов `kindest/node` (`GET /api/v1/versions`), фоновое задание и опрос статуса (JSON в сворачиваемом блоке).
|
||
- **Таблица кластеров**: признаки регистрации в kind и наличия kubeconfig, **старт** и **стоп** узлов (фоновые задания с тем же журналом, что и при создании), скачивание kubeconfig, просмотр узлов/подов в модальном окне, удаление.
|
||
- **Последние задания**: история в памяти процесса (до **200** записей; после перезапуска контейнера сбрасывается); кнопка **«Очистить завершённые»** вызывает **`DELETE /api/v1/jobs`** (из памяти удаляются только завершённые задания).
|
||
- **Автообновление** таблиц и плашки среды каждые ~3,5 с (fetch к API без перезагрузки страницы).
|
||
- При активном задании — **прогресс-бар**, **журнал** (в т.ч. скачивание образа; для **docker** при поддержке CLI — **`pull --progress=plain`**, см. **`KIND_K8S_DOCKER_PULL_PLAIN`**), опрос статуса чаще, чем общие таблицы; кнопка **«Отменить»** — прерывание с завершением текущей дочерней команды.
|
||
- Уведомления (toast) при успехе/ошибке; в подвале — копирайт и ссылка на **devops.org.ru**.
|
||
|
||
**Шапка:** навигация в виде **пилюль** (стили `.nav-pill`); пункты **Swagger**, **ReDoc** и **Health** открывают страницу в **отдельном именованном окне** (~1240×840), чтобы не уходить с панели (см. скрипт в `base.html`).
|
||
|
||
**Структура фронтенда:** `app/templates/base.html` (шапка и меню), `app/templates/dashboard.html`, `app/static/style.css`, `app/static/js/dashboard.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` недоступен с вашей машины.
|
||
|
||
Смонтированы **сокет** 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`**.
|
||
|
||
## Быстрый старт
|
||
|
||
```bash
|
||
# Из корня клонированного репозитория 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, скрипты):
|
||
|
||
```bash
|
||
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** из образа:
|
||
|
||
```bash
|
||
# По умолчанию: 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**):
|
||
|
||
```bash
|
||
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 | Сокет Docker/Podman на хосте |
|
||
| **`KIND_K8S_PATCH_KUBECONFIG`** | контейнер | Патч `server` в kubeconfig для хоста; по умолчанию **включено** (`1` в compose и в `make setup`) |
|
||
| **`CONTAINER_CLI`** | контейнер | CLI для `docker port` / `podman port` (`docker` или `podman`) |
|
||
| **`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)
|
||
|
||
```bash
|
||
export CONTAINER_SOCKET="$XDG_RUNTIME_DIR/podman/podman.sock"
|
||
make podman up
|
||
```
|
||
|
||
## Структура репозитория (основное)
|
||
|
||
| Путь | Назначение |
|
||
|------|------------|
|
||
| `Makefile` | Запуск веб-UI; префикс `docker` или `podman` обязателен; цели `up`, `down`, `restart`, `logs`, `rebuild`, `build` и др. |
|
||
| `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`, сокет |
|
||
| `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`, `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.yaml`
|
||
- `clusters/<имя>/kubeconfig`
|
||
- `clusters/<имя>/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`).
|