# kind-k8s-develop — локальные кластеры 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 (для фронтенда и интеграций) | | **[app/docs/README.md](app/docs/README.md)** | Указатель по каталогу `app/docs/` | | **`/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` в приложении или в `kubectl`. ## Веб-интерфейс - Верхняя **единая карточка**: заголовок, краткое описание и строка **состояния среды** (`kind` / `kubectl` / Docker или Podman API). - **Статистика**: число кластеров в kind, локальных каталогов, сумма workers из `meta.json`, счётчики фоновых заданий. - **Создание кластера**: форма с подсказкой тегов `kindest/node` (`GET /api/v1/versions`), фоновое задание и опрос статуса (JSON в сворачиваемом блоке). - **Таблица кластеров**: признаки регистрации в kind и наличия kubeconfig, скачивание kubeconfig, просмотр узлов/подов в модальном окне, удаление. - **Последние задания** создания (данные в памяти процесса; после перезапуска контейнера история сбрасывается). - **Автообновление** таблиц и плашки среды каждые ~3,5 с (fetch к API без перезагрузки страницы). - При создании кластера — **прогресс-бар** и текст текущего этапа; кнопка **«Отменить создание»** (между этапами; шаг `kind create` до конца не прерывается). - Уведомления (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` на ``, по умолчанию `/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` | **В образ не обязательно ставить на хост:** kind, Python приложения — они внутри контейнера. Смонтированы **сокет** Docker/Podman и каталог **`./clusters`** → в контейнере **`/work/clusters`**. Каталог **`./app`** монтируется в **`/opt/kind-k8s/app`** для разработки без пересборки образа. После создания кластера kubeconfig по умолчанию **патчится** на `https://127.0.0.1:<порт>` для доступа с хоста (`KIND_K8S_PATCH_KUBECONFIG`, см. `app/kubeconfig_patch.py`). ## Быстрый старт ```bash cd kind-k8s-develop 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 kind-k8s-develop docker up`. **Логи, статус и остановка:** `make docker logs` / `make podman logs` (follow), `make docker ps` / `make podman ps`, `make docker down` / `make podman down`. ### Разработка UI и API без пересборки образа В **`docker-compose.yml`** каталог **`./app`** смонтирован в контейнер как **`/opt/kind-k8s/app`**. По умолчанию (**`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`. После успешного `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 logs` / `make podman logs` | Логи `kind-k8s-web` (stream, `-f`) | | `make docker ps` / `make podman ps` | Статус контейнеров текущего compose-проекта | | `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`) | | `make clusters-dir` | Создать каталог `clusters/` | | `make docker …` / `make podman …` | Префикс **обязателен** для целей `up`, `down`, `logs`, `ps`, `compose-build`, `check-docker` | Цели `up`, `down`, `logs`, `ps`, `compose-build` и `check-docker` **без** `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`** | контейнер | Сколько тегов отдавать в API/UI | | **`KIND_K8S_HUB_TAGS_MAX_PAGES`** | контейнер | Лимит страниц API Hub | | **`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) ```bash export CONTAINER_SOCKET="$XDG_RUNTIME_DIR/podman/podman.sock" make podman up ``` ## Структура репозитория (основное) | Путь | Назначение | |------|------------| | `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`, сокет | | `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`** — версию вводят вручную. ## Где лежат данные на хосте - `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** (в образе kubectl только внутри контейнера). - История заданий создания в UI/API хранится в памяти (до **200** записей); после перезапуска контейнера очищается. - При **`exec format error`** у kind пересоберите образ: `make docker compose-build COMPOSE_BUILD_FLAGS=--platform linux/arm64` (или `make podman …`, или `linux/amd64`).