# Kind Clusters Dashboard — веб-интерфейс (FastAPI) для kind. # Создание кластеров — в браузере: http://127.0.0.1:8080 (порт: KIND_K8S_WEB_PORT; 6000 на хосте — ERR_UNSAFE_PORT в Chrome). # # Все операции с Compose только с явным выбором среды: # make docker up | make docker down | make docker logs | … # make podman up | make podman down | … # make docker rebuild / make podman rebuild — образ без кэша и пересоздание контейнера # make docker restart / make podman restart — перезапуск сервиса kind-k8s-web # Без префикса docker/podman цели up/down/restart/logs/ps/build/rebuild/check-docker/kubectl завершатся с подсказкой. # # Автор: Сергей Антропов — https://devops.org.ru KIND_K8S_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) SETUP_ENV_SCRIPT := $(KIND_K8S_DIR)/scripts/setup_env_interactive.py PYTHON ?= python3 ifneq (,$(filter podman,$(MAKECMDGOALS))) COMPOSE := podman compose # Сокет Podman на хосте (перекрывает CONTAINER_SOCKET из .env, если там путь Docker). PODMAN_HOST_SOCK := $(shell $(PYTHON) "$(KIND_K8S_DIR)/scripts/detect_podman_socket.py") # docker-compose из podman compose часто подставляет том из .env, игнорируя префикс VAR= в командной строке. # Файл .env.podman.override (см. цель _podman_env_override) задаёт CONTAINER_SOCKET после .env. PODMAN_COMPOSE_ENV_FILE := --env-file .env.podman.override # user: в контейнере для доступа к смонтированному podman.sock: # «podman compose» на macOS часто дергает docker-compose к API Podman — keep-id не срабатывает, uid с сокета даёт EACCES. # По умолчанию 0:0 (как у типичного docker.sock); переопределение: KIND_K8S_PODMAN_CONTAINER_UIDGID=501:20 # или KIND_K8S_PODMAN_USE_SOCKET_OWNER=1 (владелец по stat, как detect_podman_socket.py --print-owner). ifneq (,$(KIND_K8S_PODMAN_USE_SOCKET_OWNER)) _PODMAN_UIDGID := $(shell $(PYTHON) "$(KIND_K8S_DIR)/scripts/detect_podman_socket.py" --print-owner) else ifneq (,$(KIND_K8S_PODMAN_CONTAINER_UIDGID)) _PODMAN_UIDGID := $(KIND_K8S_PODMAN_CONTAINER_UIDGID) else _PODMAN_UIDGID := 0:0 endif endif _PODMAN_UID_FIRST := $(word 1,$(subst :, ,$(_PODMAN_UIDGID))) _PODMAN_HOME := $(if $(filter 0,$(_PODMAN_UID_FIRST)),/root,/tmp) ifneq (,$(findstring var/folders,$(PODMAN_HOST_SOCK))) $(error PODMAN_HOST_SOCK содержит /var/folders/ — этот API-сокет нельзя смонтировать в compose. Обновите репозиторий (scripts/detect_podman_socket.py) и удалите такой путь из .env) endif # Внутри контейнера — путь podman, не docker.sock; DOCKER_HOST должен указывать на тот же путь (API совместим). COMPOSE_PODMAN_ENV := KIND_K8S_CONTAINER_UIDGID=$(_PODMAN_UIDGID) KIND_K8S_CONTAINER_HOME=$(_PODMAN_HOME) CONTAINER_SOCKET=$(PODMAN_HOST_SOCK) CONTAINER_SOCKET_MOUNT_TARGET=/run/podman/podman.sock KIND_K8S_REMOTE_SOCKET_URI=unix:///run/podman/podman.sock COMPOSE_FILE_ARGS := -f docker-compose.yml -f docker-compose.podman.yml else ifneq (,$(filter docker,$(MAKECMDGOALS))) COMPOSE := docker compose # Сброс путей от Podman в .env: сокет в контейнере снова /var/run/docker.sock. COMPOSE_DOCKER_OVERRIDES := CONTAINER_SOCKET_MOUNT_TARGET=/var/run/docker.sock KIND_K8S_REMOTE_SOCKET_URI=unix:///var/run/docker.sock endif COMPOSE_PODMAN_ENV ?= COMPOSE_DOCKER_OVERRIDES ?= COMPOSE_FILE_ARGS ?= PODMAN_COMPOSE_ENV_FILE ?= .PHONY: help docker podman _require_runtime _podman_env_override up down restart logs ps setup clusters-dir check-docker build rebuild kubectl probe-sockets release # При «exec format error» у kind: make docker build COMPOSE_BUILD_FLAGS=--platform linux/arm64 COMPOSE_BUILD_FLAGS ?= # Для цели kubectl: имя кластера и аргументы kubectl после --kubeconfig (по умолчанию: get nodes). CLUSTER ?= KUBECTL_ARGS ?= get nodes # Сборка и push в Docker Hub (make release): linux/amd64 (x86_64) и linux/arm64 (Apple Silicon и др.). # linux/386 для kind в образе не поддерживается официальными бинарниками. # Перед push make release вызывает docker login docker.io (сохранение в ~/.docker/config.json). Строка [auth] … token у BuildKit — нормальный обмен по протоколу реестра после входа. # CI без интерактива: SKIP_RELEASE_DOCKER_LOGIN=1, если docker login уже выполнен или настроен credential helper. RELEASE_PLATFORMS ?= linux/amd64,linux/arm64 RELEASE_TAG ?= v1.0.0 DOCKERHUB_REPO ?= inecs/kind-cluster-dashboard KIND_VERSION ?= 0.24.0 HELM_VERSION ?= v3.16.3 KUBECTL_VERSION ?= SKIP_RELEASE_DOCKER_LOGIN ?= help: ## Справка по целям @echo "Веб-UI kind — только с выбором Docker или Podman в одной команде с целью:" @echo " make docker up или make podman up → http://127.0.0.1:\$${KIND_K8S_WEB_PORT:-8080}" @echo " make docker down / make podman down" @echo " make docker restart / make podman restart (перезапуск kind-k8s-web)" @echo " make docker logs / make podman logs (follow -f)" @echo " make docker ps / make podman ps (статус сервисов)" @echo " make docker build / make podman build" @echo " make docker rebuild / make podman rebuild (build --no-cache + up --force-recreate)" @echo " make docker check-docker / make podman check-docker" @echo " make docker kubectl CLUSTER=<имя> — kubectl в контейнере (см. KUBECTL_ARGS, по умолчанию get nodes)" @echo " make release — multi-arch buildx и push (по умолчанию DOCKERHUB_REPO=inecs/kind-cluster-dashboard, RELEASE_TAG=v1.0.0; см. README)" @echo "Без установки Compose: make setup, make clusters-dir (python3 для setup)." @grep -E '^[a-zA-Z0-9_-]+:.*?##' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?##"} {printf " \033[36m%-28s\033[0m %s\n", $$1, $$2}' docker: ## Маркер среды: задайте вторую цель (например: make docker up) @: podman: ## Маркер среды: задайте вторую цель (например: make podman up) @: # Общая проверка: цели up/down/logs/ps/build/rebuild/check-docker/kubectl — только make docker … / make podman … _require_runtime: @if [ -z "$(COMPOSE)" ]; then \ echo >&2 "Укажите среду в той же команде, что и цель:"; \ echo >&2 " make docker up | make podman up"; \ echo >&2 " make docker down | make docker restart | make docker logs | make docker ps | make docker build | make docker rebuild | make docker check-docker | make docker kubectl CLUSTER=…"; \ echo >&2 " (или то же с префиксом podman)"; \ exit 1; \ fi # Только для make podman …: перекрыть CONTAINER_SOCKET из .env (устаревший /var/folders/… ломает volume). _podman_env_override: @if [ -n "$(COMPOSE_PODMAN_ENV)" ]; then \ printf '%s\n' \ "CONTAINER_SOCKET=$(PODMAN_HOST_SOCK)" \ "KIND_K8S_CONTAINER_UIDGID=$(_PODMAN_UIDGID)" \ "KIND_K8S_CONTAINER_HOME=$(_PODMAN_HOME)" \ "CONTAINER_SOCKET_MOUNT_TARGET=/run/podman/podman.sock" \ "KIND_K8S_REMOTE_SOCKET_URI=unix:///run/podman/podman.sock" \ > "$(KIND_K8S_DIR)/.env.podman.override"; \ fi up: _require_runtime _podman_env_override clusters-dir build ## (с docker/podman) Поднять веб-UI в фоне cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) up -d kind-k8s-web down: _require_runtime _podman_env_override ## (с docker/podman) Остановить compose в этом каталоге cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) down restart: _require_runtime _podman_env_override ## (с docker/podman) Перезапустить контейнер kind-k8s-web cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) restart kind-k8s-web logs: _require_runtime _podman_env_override ## (с docker/podman) Логи kind-k8s-web (follow -f) cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) logs -f kind-k8s-web ps: _require_runtime _podman_env_override ## (с docker/podman) Статус контейнеров compose-проекта cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) ps probe-sockets: ## Проверить сокеты Docker/Podman (доступ без permission denied) @$(PYTHON) "$(KIND_K8S_DIR)/scripts/probe_container_sockets.py" setup: ## Интерактивно создать .env (scripts/setup_env_interactive.py; нужен python3 на хосте) @$(PYTHON) "$(SETUP_ENV_SCRIPT)" clusters-dir: ## Каталог clusters/ для тома (если ещё нет) @mkdir -p "$(KIND_K8S_DIR)/clusters" check-docker: _require_runtime _podman_env_override ## (с docker/podman) Проверить CLI и compose @case "$(COMPOSE)" in \ docker*) command -v docker >/dev/null 2>&1 || { echo >&2 "docker не найден в PATH."; exit 1; } ;; \ podman*) command -v podman >/dev/null 2>&1 || { echo >&2 "podman не найден в PATH."; exit 1; } ;; \ esac @$(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) version >/dev/null 2>&1 || { echo >&2 "Команда «$(COMPOSE) version» недоступна."; exit 1; } @echo "$(COMPOSE): OK" # kubectl в образе; путь к kubeconfig — как у веб-UI (подстановка server через host.docker.internal, см. kubeconfig_patch.py). kubectl: _require_runtime _podman_env_override ## (с docker/podman) kubectl в контейнере: CLUSTER=имя [KUBECTL_ARGS="get pods -A"] @if [ -z "$(CLUSTER)" ]; then \ echo >&2 "Задайте CLUSTER=<имя_кластера> (каталог в ./clusters/)."; \ echo >&2 "Пример: make docker kubectl CLUSTER=dev"; \ echo >&2 "Свои подкоманды: make docker kubectl CLUSTER=dev KUBECTL_ARGS=\"get pods -A\""; \ exit 1; \ fi cd "$(KIND_K8S_DIR)" && KC=$$($(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) exec -T kind-k8s-web python3 scripts/effective_kubeconfig_path.py $(CLUSTER) | tr -d '\r') && \ $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) exec kind-k8s-web kubectl --kubeconfig="$$KC" $(KUBECTL_ARGS) build: _require_runtime _podman_env_override clusters-dir ## (с docker/podman) Собрать образ kind-k8s-tools:local cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) build $(COMPOSE_BUILD_FLAGS) rebuild: _require_runtime _podman_env_override clusters-dir ## (с docker/podman) Пересобрать образ без кэша и пересоздать контейнер kind-k8s-web cd "$(KIND_K8S_DIR)" && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) build --no-cache $(COMPOSE_BUILD_FLAGS) && $(COMPOSE_PODMAN_ENV) $(COMPOSE_DOCKER_OVERRIDES) $(COMPOSE) $(COMPOSE_FILE_ARGS) $(PODMAN_COMPOSE_ENV_FILE) up -d --force-recreate kind-k8s-web # Публикация образа в Docker Hub (buildx, --push). Сначала интерактивный docker login (или SKIP_RELEASE_DOCKER_LOGIN=1). release: ## Multi-arch (amd64+arm64) сборка и push [DOCKERHUB_REPO=inecs/kind-cluster-dashboard] [RELEASE_TAG=v1.0.0] … [SKIP_RELEASE_DOCKER_LOGIN=1] @if [ -z "$(strip $(DOCKERHUB_REPO))" ]; then \ echo >&2 "Задайте DOCKERHUB_REPO=/<имя_репозитория> (в Makefile задано по умолчанию inecs/kind-cluster-dashboard)."; \ echo >&2 "Платформы: $(RELEASE_PLATFORMS) (amd64 — ПК x86_64, arm64 — Apple Silicon и серверы ARM)."; \ echo >&2 "Перед push make release выполнит: docker login docker.io (или задайте SKIP_RELEASE_DOCKER_LOGIN=1)."; \ exit 1; \ fi @command -v docker >/dev/null 2>&1 || { echo >&2 "Нужен docker в PATH."; exit 1; } @docker buildx version >/dev/null 2>&1 || { echo >&2 "Нужен Docker Buildx (docker buildx version)."; exit 1; } @if [ "$(strip $(SKIP_RELEASE_DOCKER_LOGIN))" != "1" ]; then \ echo "Вход в Docker Hub: docker login docker.io (учётные данные в ~/.docker/config.json; дальше BuildKit получит token у реестра — это норма)."; \ docker login docker.io || { echo >&2 "docker login docker.io завершился с ошибкой."; exit 1; }; \ fi @docker buildx inspect kind-k8s-release >/dev/null 2>&1 || docker buildx create --name kind-k8s-release --driver docker-container --use @docker buildx use kind-k8s-release cd "$(KIND_K8S_DIR)" && docker buildx build \ --platform "$(RELEASE_PLATFORMS)" \ --push \ -t "$(DOCKERHUB_REPO):$(RELEASE_TAG)" \ --build-arg "KIND_VERSION=$(KIND_VERSION)" \ --build-arg "HELM_VERSION=$(HELM_VERSION)" \ --build-arg "KUBECTL_VERSION=$(KUBECTL_VERSION)" \ -f Dockerfile \ .