From c24f0c6b51b1928c1b43ddef868296a833d4b23c Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Sat, 4 Apr 2026 05:43:10 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F:=20README,=20api=5Froutes,=20?= =?UTF-8?q?=D1=83=D0=BA=D0=B0=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20app/do?= =?UTF-8?q?cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: веб-UI, структура static/templates, нет env.example, make setup, KIND_K8S_WEB_HOST, jobs в памяти, .gitignore, ссылки на /docs и ReDoc. - api_routes: сводная таблица маршрутов, UI/статика, поведение jobs (лимит 200), уточнение stats, коды 400 для kubeconfig/workloads/delete. - app/docs/README.md: навигация по документации приложения. --- README.md | 92 ++++++++++++++++++++++++++++-------------- app/docs/README.md | 11 +++++ app/docs/api_routes.md | 67 ++++++++++++++++++++++++++---- 3 files changed, 132 insertions(+), 38 deletions(-) create mode 100644 app/docs/README.md diff --git a/README.md b/README.md index b3e1f1c..3f0bfdc 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,57 @@ # kind-k8s-develop — локальные кластеры Kubernetes (kind) -Образ **kind-k8s-tools:local** и **Makefile** поднимают **веб-интерфейс** (FastAPI) на порту **6000** на хосте: через браузер создаёте и удаляете кластеры, смотрите статистику. **kubeconfig** сохраняется в `clusters/<имя>/`. На хосте достаточно **Docker** (или Podman) и **make**; **kind** и **kubectl** — внутри контейнера. +Образ **kind-k8s-tools:local** и **Makefile** поднимают **веб-интерфейс** (FastAPI) на порту **6000** на хосте (или значении **`KIND_K8S_WEB_PORT`** в `.env`): через браузер создаёте и удаляете кластеры, смотрите статистику и вывод `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 (для фронтенда и интеграций) | +| **[app/docs/README.md](app/docs/README.md)** | Указатель по каталогу `app/docs/` | +| **`/docs`** (Swagger) и **`/redoc`** | Интерактивная OpenAPI-документация на том же порту, что и UI | + +Шаблона **`env.example`** в репозитории нет: переменные для `.env` задаются интерактивно скриптом **`scripts/setup_env_interactive.py`** (`make setup`). + ## Зачем это нужно - Быстро получить Kubernetes без облака (интеграционные тесты, манифесты, обучение). -- Версия кластера и число worker-нод задаются **в веб-UI** (или при необходимости скриптами/API). +- Версия кластера и число worker-нод задаются **в веб-UI** (или через REST API / скрипты в контейнере). - Количество кластеров **не ограничено** кодом (ограничения — ресурсы хоста и Docker). - Артефакты на хосте: `clusters/<имя>/` — удобно указать путь к `kubeconfig` в приложении или в `kubectl`. +## Веб-интерфейс + +- Плашка **состояния среды**: наличие `kind`/`kubectl`, доступность Docker/Podman API по сокету. +- **Статистика**: число кластеров в kind, локальных каталогов, сумма workers из `meta.json`, счётчики фоновых заданий. +- **Создание кластера**: форма с подсказкой тегов `kindest/node` (`GET /api/v1/versions`), фоновое задание и опрос статуса (JSON в сворачиваемом блоке). +- **Таблица кластеров**: признаки регистрации в kind и наличия kubeconfig, скачивание kubeconfig, просмотр узлов/подов в модальном окне, удаление. +- **Последние задания** создания (данные в памяти процесса; после перезапуска контейнера история сбрасывается). +- Кнопки обновления, опциональное **автообновление каждые 15 с**, уведомления (toast) при успехе/ошибке. + +**Структура фронтенда:** `app/templates/base.html` (шапка, ссылки на Swagger/ReDoc/Health), `app/templates/dashboard.html`, стили `app/static/style.css`, логика `app/static/js/dashboard.js` (базовый префикс API из `data-api-base` на ``, по умолчанию `/api/v1`). + ## Требования на хосте | Компонент | Назначение | |-----------|------------| | **Docker** + **Compose v2** (или **Podman** + compose) | Сборка образа и запуск веб-сервиса | | **make** | `make docker up` / `make podman up` и вспомогательные цели | +| **python3** | Только для **`make setup`** (создание `.env`) | | **kubectl** (опционально) | Проверка API с хоста: `kubectl --kubeconfig=clusters/<имя>/kubeconfig get nodes` | -**На хост не ставятся:** Python, kind — всё в образе. +**В образ не обязательно ставить на хост:** kind, Python приложения — они внутри контейнера. -Смонтированы **сокет** Docker/Podman и каталог **`./clusters`** → в контейнере `/work/clusters`. +Смонтированы **сокет** Docker/Podman и каталог **`./clusters`** → в контейнере **`/work/clusters`**. Каталог **`./app`** монтируется в **`/opt/kind-k8s/app`** для разработки без пересборки образа. -После создания кластера из UI kubeconfig **патчится** на `https://127.0.0.1:<порт>` для доступа с хоста, см. `kubeconfig_patch.py`. +После создания кластера kubeconfig по умолчанию **патчится** на `https://127.0.0.1:<порт>` для доступа с хоста (`KIND_K8S_PATCH_KUBECONFIG`, см. `app/kubeconfig_patch.py`). ## Быстрый старт ```bash cd kind-k8s-develop -make setup # опционально: интерактивно .env (скрипт в scripts/; Enter — дефолты как в compose; нужен python3) +make setup # опционально: интерактивно создать .env (Enter — дефолты из скрипта) make docker check-docker # или: make podman check-docker make docker up # или: make podman up # Браузер: http://127.0.0.1:6000 (порт: KIND_K8S_WEB_PORT в .env) @@ -39,13 +61,11 @@ make docker up # или: make podman up **Логи и остановка:** `make docker logs` / `make podman logs`, `make docker down` / `make podman down`. -Описание REST API и примеры JSON: **`app/docs/api_routes.md`**, интерактивно: **`/docs`** на том же порту. - ### Разработка UI и API без пересборки образа -В **`docker-compose.yml`** каталог **`./app`** смонтирован в контейнер как **`/opt/kind-k8s/app`** — исправления в Python, шаблонах и `static/` на хосте сразу видны внутри сервиса. +В **`docker-compose.yml`** каталог **`./app`** смонтирован в контейнер как **`/opt/kind-k8s/app`**. -По умолчанию (**`KIND_K8S_UVICORN_RELOAD=1`**) uvicorn запускается с **`--reload`** и перезапускает процесс при изменении `*.py`, `*.html`, `*.css`, `*.js` в `app/`. Пересобирать образ нужно только после изменений **Dockerfile**, **`requirements.txt`** или скрипта **`scripts/run_uvicorn.sh`**. +По умолчанию (**`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`**. @@ -75,7 +95,7 @@ docker compose run --rm --entrypoint python3 kind-k8s-web \ | `make docker logs` / `make podman logs` | Логи `kind-k8s-web` | | `make docker compose-build` / `make podman compose-build` | Собрать образ `kind-k8s-tools:local` | | `make docker check-docker` / `make podman check-docker` | Проверить выбранный CLI и `compose version` | -| `make setup` | Интерактивно заполнить `.env`: переменные и подсказки в `scripts/setup_env_interactive.py`, дефолты как в `docker-compose` | +| `make setup` | Интерактивно создать `.env` (список переменных в `scripts/setup_env_interactive.py`) | | `make clusters-dir` | Создать каталог `clusters/` | | `make docker …` / `make podman …` | Префикс **обязателен** для целей `up`, `down`, `logs`, `compose-build`, `check-docker` | @@ -83,24 +103,28 @@ docker compose run --rm --entrypoint python3 kind-k8s-web \ ## Переменные окружения -Файл **`.env`** в корне репозитория подхватывает Compose. Создать его можно командой **`make setup`** (опрос по списку в **`scripts/setup_env_interactive.py`**) или вручную. +Файл **`.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 в образе; в Dockerfile без build-arg — `stable.txt`; `make setup` предлагает закреплённый тег | +| **`KUBECTL_VERSION`** | build-arg | Версия kubectl в образе; пусто в compose → в Dockerfile подставляется `stable.txt` при сборке; `make setup` предлагает закреплённый тег | | **`KIND_K8S_WEB_PORT`** | ports | Порт **на хосте** для веб-UI (в контейнере 6000) | +| **`KIND_K8S_WEB_HOST`** | локальный uvicorn / Settings | Хост привязки при запуске вне compose (в контейнере задаётся entrypoint) | | **`KIND_K8S_UVICORN_RELOAD`** | контейнер | `1` (по умолчанию) — hot-reload при правках в `./app`; `0` — без reload | -| **`KIND_K8S_APP_TITLE`** | контейнер | Заголовок в OpenAPI и HTML | +| **`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` | +| **`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`** | контейнер | Сколько тегов показывать в списке | +| **`KIND_K8S_VERSION_LIST_DISPLAY`** | контейнер | Сколько тегов отдавать в API/UI | | **`KIND_K8S_HUB_TAGS_MAX_PAGES`** | контейнер | Лимит страниц API Hub | -| **`KIND_K8S_DEBUG`** | контейнер | Уровень DEBUG в логах | +| **`KIND_K8S_DEBUG`** | контейнер | `1`/`true`/`yes`/`да` — уровень DEBUG в логах | +| **`KIND_K8S_WORKDIR`** | локальный запуск | Корень данных на машине разработчика без compose | | **`COMPOSE_BUILD_FLAGS`** | Makefile | Например `make docker compose-build COMPOSE_BUILD_FLAGS=--platform linux/arm64` | ## Podman (пример rootless) @@ -110,21 +134,24 @@ export CONTAINER_SOCKET="$XDG_RUNTIME_DIR/podman/podman.sock" make podman up ``` -## Файлы +## Структура репозитория (основное) | Путь | Назначение | |------|------------| -| `Makefile` | Запуск веб-UI и вспомогательные цели | -| `scripts/setup_env_interactive.py` | Интерактивное создание `.env` (список переменных и дефолты внутри скрипта) | -| `scripts/run_uvicorn.sh` | Точка входа веб-сервиса: uvicorn с опциональным `--reload` | +| `Makefile` | Запуск веб-UI; префикс `docker` или `podman` обязателен для compose-целей | +| `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/main.py` | FastAPI, дашборд | -| `app/api/v1/` | REST API | -| `app/core/` | Логика кластеров, задания, блокировки | -| `app/docs/api_routes.md` | Описание маршрутов с примерами JSON | -| `app/create_cluster.py`, `delete_cluster.py`, … | Используются веб-API и при ручном `compose run` | +| `requirements.txt` | pip-зависимости веб-приложения | +| `docker-compose.yml` | Сервис `kind-k8s-web`, тома `./clusters`, `./app`, сокет | +| `app/main.py` | FastAPI: дашборд `/`, редирект `/ui`, монтирование `/static` | +| `app/api/v1/` | REST API: `router.py`, `endpoints/` (`health`, `versions`, `clusters`) | +| `app/core/` | Жизненный цикл кластеров, задания, настройки, блокировки (`kind_guard`), пути | +| `app/models/schemas.py` | Pydantic-схемы запросов/ответов API | +| `app/templates/` | Jinja2: `base.html`, `dashboard.html` | +| `app/static/` | `style.css`, `js/dashboard.js` | +| `app/docs/` | `api_routes.md`, `README.md` | +| `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`** — версию вводят вручную. @@ -136,9 +163,14 @@ make podman up Содержимое `clusters/*/` не коммитится; в репозитории есть `clusters/.gitkeep`. +## Git и артефакты + +В **`.gitignore`**: `.env`, каталоги в `clusters/` (кроме `.gitkeep`), **`__pycache__/`** и `*.pyc`, `.DS_Store`. + ## Ограничения - Образ `kindest/node:v…` должен быть доступен для pull. - На **Windows** без WSL удобнее WSL2 + Docker Desktop. - Для проверки с хоста нужен отдельный **kubectl** (в образе kubectl только внутри контейнера). +- История заданий создания в UI/API хранится в памяти (до **200** записей); после перезапуска контейнера очищается. - При **`exec format error`** у kind пересоберите образ: `make docker compose-build COMPOSE_BUILD_FLAGS=--platform linux/arm64` (или `make podman …`, или `linux/amd64`). diff --git a/app/docs/README.md b/app/docs/README.md new file mode 100644 index 0000000..e7f723e --- /dev/null +++ b/app/docs/README.md @@ -0,0 +1,11 @@ +# Документация приложения (app/docs) + +Каталог описаний для разработчиков и интеграции с UI. + +| Файл | Назначение | +|------|------------| +| [api_routes.md](api_routes.md) | Полное описание REST API `/api/v1/*` с примерами JSON (ориентир для фронтенда и клиентов). | + +После запуска сервиса доступны интерактивно: **Swagger** — `/docs`, **ReDoc** — `/redoc` (тот же порт, что и веб-UI). + +**Автор:** Сергей Антропов — [devops.org.ru](https://devops.org.ru) diff --git a/app/docs/api_routes.md b/app/docs/api_routes.md index 7a50693..e012625 100644 --- a/app/docs/api_routes.md +++ b/app/docs/api_routes.md @@ -3,7 +3,47 @@ **Базовый префикс:** `/api/v1` **Автор:** Сергей Антропов — [devops.org.ru](https://devops.org.ru) -Интерактивная документация OpenAPI: после запуска `make docker up` откройте [http://127.0.0.1:6000/docs](http://127.0.0.1:6000/docs). +## Как смотреть документацию + +| Способ | URL / путь | +|--------|------------| +| Swagger UI (OpenAPI) | `http://127.0.0.1:<порт>/docs` (порт по умолчанию **6000**, см. `KIND_K8S_WEB_PORT`) | +| ReDoc | `http://127.0.0.1:<порт>/redoc` | +| Этот файл | `app/docs/api_routes.md` в репозитории | + +## Веб-интерфейс и статика (не JSON) + +| Маршрут | Описание | +|---------|----------| +| `GET /` | HTML-панель: статус среды (kind, kubectl, Docker/Podman), статистика, форма создания кластера, таблицы кластеров и заданий, модальное окно «узлы / поды», ссылки на API. | +| `GET /ui` | Редирект **307** на `/` (удобный ярлык). | +| `GET /static/…` | CSS (`style.css`), скрипт панели (`js/dashboard.js`); базовый URL API задаётся атрибутом `data-api-base` на `` (по умолчанию `/api/v1`). | + +Шаблоны: `app/templates/base.html` (шапка, навигация), `app/templates/dashboard.html` (контент панели). + +--- + +## Сводка маршрутов API + +| Метод | Путь | Кратко | +|-------|------|--------| +| GET | `/api/v1/health` | Среда: kind, kubectl, движок контейнеров | +| GET | `/api/v1/versions` | Теги `kindest/node` (Docker Hub) или пусто при `KIND_K8S_SKIP_VERSION_LIST` | +| GET | `/api/v1/stats` | Сводка для дашборда | +| GET | `/api/v1/clusters` | Список кластеров | +| POST | `/api/v1/clusters` | Создание в фоне (**202** + `job_id`) | +| GET | `/api/v1/clusters/{name}` | Детали + `kubectl get nodes` при наличии kubeconfig | +| GET | `/api/v1/clusters/{name}/kubeconfig` | Скачать файл kubeconfig | +| GET | `/api/v1/clusters/{name}/workloads` | Узлы и поды (`kubectl`) | +| DELETE | `/api/v1/clusters/{name}` | Удалить кластер и данные в `clusters/` | +| GET | `/api/v1/jobs` | Последние задания создания | +| GET | `/api/v1/jobs/{job_id}` | Статус одного задания | + +### Фоновые задания (jobs) + +- Хранятся **только в памяти** процесса uvicorn; после перезапуска контейнера история обнуляется. +- В памяти держится не более **200** записей; при превышении старые задания вытесняются (`app/core/job_store.py`). +- Создание кластера: `POST /api/v1/clusters` → опрос `GET /api/v1/jobs/{job_id}` (как в веб-UI). --- @@ -82,13 +122,15 @@ } ``` -Поле `total_workers_from_meta` может быть `null`, если ни в одном `meta.json` нет `worker_nodes`. +- `total_workers_from_meta` может быть `null`, если ни в одном `meta.json` нет `worker_nodes`. +- `jobs_total` — число заданий в текущей памяти процесса (не более 200). +- `jobs_recent_failed` — сколько заданий в этом хранилище сейчас в статусе `failed` (не «последние N», а счётчик по всему снимку). --- ## GET /api/v1/clusters -Список имён: объединение `kind get clusters` и подкаталогов `clusters/*`. +Список: объединение `kind get clusters` и подкаталогов `clusters/*` (без дубликатов). **Пример ответа 200 (массив):** @@ -113,9 +155,9 @@ ## GET /api/v1/jobs -Список последних фоновых заданий (создание кластера), от новых к старым. Данные только в памяти процесса. +Список последних фоновых заданий (создание кластера), от новых к старым. -**Query:** `limit` (1–200, по умолчанию 30). +**Query:** `limit` (1–200, по умолчанию **30**). **Пример ответа 200 (массив `JobView`):** @@ -141,6 +183,8 @@ **Ошибка 404:** файла нет в `clusters/{name}/`. +**Ошибка 400:** некорректное имя кластера. + --- ## GET /api/v1/clusters/{name}/workloads @@ -160,7 +204,9 @@ } ``` -Если kubeconfig нет: `"error": "Нет сохранённого kubeconfig..."`, остальные поля подов/узлов — `null`. +Если kubeconfig нет: `error` — строка вида **`Нет сохранённого kubeconfig в clusters/<имя>/`**, поля вывода kubectl могут быть `null`. + +**Ошибка 400:** некорректное имя кластера. --- @@ -186,7 +232,7 @@ ## POST /api/v1/clusters -Создание кластера **в фоне** (ответ 202). +Создание кластера **в фоне** (ответ **202**). **Тело запроса (JSON):** @@ -208,6 +254,8 @@ } ``` +**Ошибка 400:** невалидное имя кластера или тело не проходит валидацию Pydantic. + **Ошибка 409 (кластер уже есть в kind):** ```json @@ -283,8 +331,11 @@ } ``` +**Ошибка 400:** некорректное имя кластера. +**Ошибка 500:** логическая ошибка удаления (тело с `detail`). + --- ## GET / -HTML-дашборд (не JSON): форма создания, таблица кластеров, ссылки на Swagger. +HTML-дашборд (не JSON): см. раздел «Веб-интерфейс и статика» выше.