From c1655d26745e53a0ab1b99f32b07c2fe6c1763db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 01:36:54 +0300 Subject: [PATCH 01/41] =?UTF-8?q?chore:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20=D1=81=202.0.0?= =?UTF-8?q?=20=D0=BD=D0=B0=203.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Обновлена версия в README.md - Обновлена версия во всех файлах docs/ - Обновлена версия в dockerfiles/README.md - Обновлена версия в roles/*/QUICKSTART.md - Подготовка к версии 3.0.0 с Kubernetes поддержкой Автор: Сергей Антропов Сайт: https://devops.org.ru --- README.md | 2 +- dockerfiles/README.md | 2 +- docs/creating-roles.md | 2 +- docs/dockerfiles.md | 2 +- docs/examples.md | 2 +- docs/getting-started.md | 2 +- docs/monitoring.md | 2 +- docs/platform-support.md | 2 +- roles/devops/QUICKSTART.md | 2 +- roles/ping/QUICKSTART.md | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index fbbc4d4..d98292c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## 📋 Описание diff --git a/dockerfiles/README.md b/dockerfiles/README.md index ce0f097..5bcbabb 100644 --- a/dockerfiles/README.md +++ b/dockerfiles/README.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## 🐳 Обзор diff --git a/docs/creating-roles.md b/docs/creating-roles.md index a62a3fd..34d6577 100644 --- a/docs/creating-roles.md +++ b/docs/creating-roles.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## 🚀 Быстрый старт diff --git a/docs/dockerfiles.md b/docs/dockerfiles.md index 59544b0..63f6f0e 100644 --- a/docs/dockerfiles.md +++ b/docs/dockerfiles.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## 🐳 Обзор diff --git a/docs/examples.md b/docs/examples.md index b02bb59..4a85b4d 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## Быстрый старт diff --git a/docs/getting-started.md b/docs/getting-started.md index a1462b7..a1e0e2b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## 🚀 Установка и настройка diff --git a/docs/monitoring.md b/docs/monitoring.md index 7d47bdc..d7d96bc 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## 🔍 Диагностика Docker diff --git a/docs/platform-support.md b/docs/platform-support.md index 680916b..9ce3091 100644 --- a/docs/platform-support.md +++ b/docs/platform-support.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## Описание diff --git a/roles/devops/QUICKSTART.md b/roles/devops/QUICKSTART.md index ee40527..91583a2 100644 --- a/roles/devops/QUICKSTART.md +++ b/roles/devops/QUICKSTART.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## Что делает роль diff --git a/roles/ping/QUICKSTART.md b/roles/ping/QUICKSTART.md index 2af14c3..6e367fd 100644 --- a/roles/ping/QUICKSTART.md +++ b/roles/ping/QUICKSTART.md @@ -2,7 +2,7 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru -**Версия:** 2.0.0 +**Версия:** 3.0.0 ## Что делает роль? From 881502ad697a5e7a5b24fe2977a2f2d2ee2f5d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 03:30:58 +0300 Subject: [PATCH 02/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=BA=D1=83=20Kubernetes=20Kind=20=D0=BA=D0=BB=D0=B0=D1=81?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создан новый Docker образ k8s для работы с Kind, kubectl, Helm, Istio CLI - Добавлены команды make k8s: create, destroy, stop, start, status, config, nodes, addon, shell - Добавлена поддержка пресетов Kubernetes в molecule/presets/k8s/ - Создан скрипт create_k8s_cluster.py для автоматического создания кластеров и установки аддонов - Добавлена документация docs/kubernetes-kind.md - Команды kubectl выполняются внутри контейнера k8s, не требуют локальной установки --- .gitignore | 3 + Makefile | 248 +++++++++++++- README.md | 7 + dockerfiles/k8s/Dockerfile | 73 +++++ docs/kubernetes-kind.md | 297 +++++++++++++++++ molecule/default/create.yml | 137 +++++++- molecule/default/create.yml.bak | 428 +++++++++++++++++++++++++ molecule/default/destroy.yml | 23 +- molecule/presets/examples/standart.yml | 14 + molecule/presets/k8s/k8s-minimal.yml | 42 +++ molecule/presets/k8s/kubernetes.yml | 59 ++++ scripts/create_k8s_cluster.py | 161 ++++++++++ 12 files changed, 1487 insertions(+), 5 deletions(-) create mode 100644 dockerfiles/k8s/Dockerfile create mode 100644 docs/kubernetes-kind.md create mode 100644 molecule/default/create.yml.bak create mode 100644 molecule/presets/k8s/k8s-minimal.yml create mode 100644 molecule/presets/k8s/kubernetes.yml create mode 100755 scripts/create_k8s_cluster.py diff --git a/.gitignore b/.gitignore index ea9bb01..0b461c0 100644 --- a/.gitignore +++ b/.gitignore @@ -180,3 +180,6 @@ cython_debug/ # Cursor IDE .cursor/ +# Kubernetes kubeconfig +kubeconfig + diff --git a/Makefile b/Makefile index 182ef84..548be9d 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ VERSION ?= 0.1.0 AUTHOR ?= "Сергей Антропов" SITE ?= "https://devops.org.ru" DOCKER_IMAGE ?= inecs/ansible-lab:ansible-controller-latest +DOCKER_K8S_IMAGE ?= inecs/ansible-lab:k8s-latest DOCKER_DIND_IMAGE ?= docker:27-dind CONTAINER_NAME ?= ansible-controller @@ -40,7 +41,7 @@ DOCKER_BUILDX_BUILDER ?= multiarch-builder # Базовые образы и их теги BASE_IMAGES := altlinux/p9 astralinux/astra-1.7 redos/redos:9 registry.access.redhat.com/ubi8/ubi centos:7 quay.io/centos/centos:8 quay.io/centos/centos:stream9 almalinux:8 rockylinux:8 ubuntu:20.04 ubuntu:22.04 ubuntu:24.04 debian:9 debian:10 debian:11 debian:bookworm -.PHONY: role vault git docker presets controller help update-playbooks generate-docs setup-cicd list create delete +.PHONY: role vault git docker presets controller k8s help update-playbooks generate-docs setup-cicd list create delete # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С РОЛЯМИ @@ -904,6 +905,8 @@ docker-get-base-tag: TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ansible-controller) \ TAG="latest";; \ + k8s) \ + TAG="latest";; \ *) \ echo "❌ Неизвестный образ: $(IMAGE)"; \ exit 1;; \ @@ -1012,6 +1015,238 @@ controller: echo " 💡 Удаляет: контейнеры и сети";; \ esac +# ============================================================================= +# КОМАНДЫ ДЛЯ РАБОТЫ С KUBERNETES KIND +# ============================================================================= +k8s: + @case "$(word 2, $(MAKECMDGOALS))" in \ + create) \ + echo "☸️ Создание Kind кластера..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + PRESET=k8s-minimal; \ + echo "📋 Используется preset по умолчанию: $$PRESET (минимальный без аддонов)"; \ + else \ + PRESET=$$PRESET_ARG; \ + echo "📋 Используется preset: $$PRESET"; \ + fi; \ + if [ ! -f "molecule/presets/k8s/$$PRESET.yml" ]; then \ + echo "❌ Ошибка: Пресет '$$PRESET' не найден!"; \ + echo "💡 Доступные пресеты:"; \ + ls -1 molecule/presets/k8s/*.yml 2>/dev/null | sed 's|molecule/presets/k8s/||g' | sed 's|\.yml||g' | sed 's/^/ - /' || echo " - k8s-minimal"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET; \ + docker run -d --name $$CONTAINER_NAME --rm \ + -v "$(PWD):/workspace" -w /workspace \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -u root \ + -e ANSIBLE_FORCE_COLOR=1 \ + -e MOLECULE_PRESET=$$PRESET \ + -e MOLECULE_EPHEMERAL_DIRECTORY=/tmp/molecule_workspace \ + $(DOCKER_K8S_IMAGE) \ + /bin/bash -c 'sleep infinity'; \ + echo "🚀 Запуск создания кластера..."; \ + docker exec $$CONTAINER_NAME bash -c "cd /workspace && python3 /workspace/scripts/create_k8s_cluster.py molecule/presets/k8s/$$PRESET.yml $$CONTAINER_NAME"; \ + echo "✅ Kind кластер создан"; \ + echo "💡 Для подключения используйте: make k8s kubeconfig"; \ + echo "💡 Для остановки используйте: make k8s stop";; \ + destroy) \ + echo "🗑️ Удаление Kind кластера..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + PRESET=$${PRESET_ARG:-k8s-minimal}; \ + CONTAINER_NAME=k8s-$$PRESET; \ + if docker ps | grep -q $$CONTAINER_NAME; then \ + docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ + else \ + echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ + fi; \ + docker rm -f $$CONTAINER_NAME 2>/dev/null || true; \ + echo "✅ Kind кластер удален";; \ + stop) \ + echo "🛑 Остановка Kind кластера..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s stop kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if docker ps | grep -q $$CONTAINER_NAME; then \ + docker exec $$CONTAINER_NAME bash -c "kind get clusters | xargs -I {} kind stop cluster --name {}" 2>/dev/null || true; \ + echo "✅ Kind кластер остановлен"; \ + else \ + echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ + fi; \ + echo "💡 Кластер остановлен, но не удален"; \ + echo "💡 Для перезапуска: make k8s start $$PRESET_ARG"; \ + echo "💡 Для полного удаления: make k8s destroy $$PRESET_ARG";; \ + start) \ + echo "🚀 Запуск Kind кластера..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s start kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + docker exec $$CONTAINER_NAME bash -c "kind get clusters | xargs -I {} kind start cluster --name {}" 2>/dev/null || true; \ + echo "✅ Kind кластер запущен";; \ + status) \ + echo "📊 Статус Kind кластеров:"; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s status kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if docker ps | grep -q $$CONTAINER_NAME; then \ + docker exec $$CONTAINER_NAME bash -c "kind get clusters" 2>/dev/null || echo " Нет кластеров"; \ + docker exec $$CONTAINER_NAME bash -c "kind get clusters | while read cluster; do echo \"Кластер: \$$cluster\"; kubectl --context kind-\$$cluster get nodes 2>/dev/null || true; done" 2>/dev/null || true; \ + else \ + echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + fi;; \ + config) \ + echo "📋 Получение kubeconfig..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s config kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + KUBECONFIG_FILE="$$(pwd)/kubeconfig"; \ + docker exec $$CONTAINER_NAME bash -c "kind get kubeconfig" > $$KUBECONFIG_FILE 2>/dev/null || true; \ + if [ -f $$KUBECONFIG_FILE ] && [ -s $$KUBECONFIG_FILE ]; then \ + echo "✅ kubeconfig сохранен в: $$KUBECONFIG_FILE"; \ + echo ""; \ + echo "💡 Для использования:"; \ + echo " export KUBECONFIG=$$KUBECONFIG_FILE"; \ + echo " kubectl get nodes"; \ + echo ""; \ + echo "💡 Или для однократного использования:"; \ + echo " kubectl --kubeconfig=$$KUBECONFIG_FILE get nodes"; \ + else \ + echo "❌ Не удалось получить kubeconfig"; \ + rm -f $$KUBECONFIG_FILE; \ + fi;; \ + addon) \ + echo "📦 Установка аддона..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + MANIFEST_ARG="$(word 4, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ + exit 1; \ + fi; \ + if [ -z "$$MANIFEST_ARG" ]; then \ + echo "❌ Ошибка: Укажите URL манифеста"; \ + echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ + echo "📥 Установка аддона из $$MANIFEST_ARG..."; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ + echo "✅ Аддон установлен";; \ + nodes) \ + echo "🖥️ Просмотр узлов кластера..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s nodes kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify get nodes";; \ + shell) \ + echo "🐚 Открытие shell в контейнере..."; \ + PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ + if [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите пресет"; \ + echo "💡 Пример: make k8s shell kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if docker ps | grep -q $$CONTAINER_NAME; then \ + docker exec -it $$CONTAINER_NAME bash; \ + else \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + fi;; \ + *) \ + echo "☸️ Доступные команды:"; \ + echo ""; \ + echo " make k8s create [preset] - создать Kind кластер"; \ + echo " 💡 Без параметра: используется k8s-minimal (без аддонов)"; \ + echo " 💡 С параметром: используется указанный пресет"; \ + echo " 💡 Кластер НЕ удаляется автоматически"; \ + echo ""; \ + echo " make k8s destroy [preset] - удалить Kind кластер полностью"; \ + echo " 💡 Удалит: кластер и контейнер ansible-controller"; \ + echo ""; \ + echo " make k8s stop [cluster] - остановить Kind кластер (без удаления)"; \ + echo " 💡 Можно указать имя кластера или остановить все"; \ + echo " 💡 Для перезапуска: make k8s start"; \ + echo ""; \ + echo " make k8s start [cluster] - запустить остановленный кластер"; \ + echo " 💡 Можно указать имя кластера или запустить все"; \ + echo ""; \ + echo " make k8s status [cluster] - показать статус кластеров"; \ + echo " 💡 Можно указать имя конкретного кластера"; \ + echo ""; \ + echo " make k8s config [cluster] - получить kubeconfig для подключения"; \ + echo " 💡 Сохраняет: kubeconfig в корне проекта"; \ + echo " 💡 Можно указать имя конкретного кластера"; \ + echo ""; \ + echo " make k8s addon [preset] [url] - установить аддон из манифеста"; \ + echo " 💡 Требует: пресет и URL манифеста"; \ + echo " 💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ + echo ""; \ + echo " make k8s nodes [preset] - показать узлы кластера"; \ + echo " 💡 Требует: пресет"; \ + echo " 💡 Пример: make k8s nodes kubernetes"; \ + echo ""; \ + echo " make k8s shell [preset] - открыть shell в контейнере"; \ + echo " 💡 Для: ручного управления kubectl/kind"; \ + echo " 💡 Пример: make k8s shell kubernetes"; \ + echo ""; \ + echo "💡 Примеры:"; \ + echo " make k8s create # создать минимальный кластер"; \ + echo " make k8s create kubernetes # создать кластер с аддонами"; \ + echo " make k8s nodes kubernetes # показать узлы кластера"; \ + echo " make k8s config kubernetes # получить kubeconfig для кластера"; \ + echo " export KUBECONFIG=kubeconfig # использовать конфиг"; \ + echo " kubectl get nodes # проверить узлы"; \ + echo " make k8s addon kubernetes https://example.com/manifest.yaml # установить аддон"; \ + echo " make k8s stop kubernetes # остановить кластер"; \ + echo " make k8s start kubernetes # запустить кластер"; \ + echo " make k8s destroy kubernetes # удалить кластер с пресетом kubernetes";; \ + esac + # ============================================================================= # СПРАВКА # ============================================================================= @@ -1105,6 +1340,17 @@ help: @echo " make controller run - запустить ansible-controller" @echo " make controller stop - остановить ansible-controller" @echo "" + @echo "☸️ KUBERNETES (Kind кластеры):" + @echo " make k8s create [preset] - создать Kind кластер (по умолчанию: k8s-minimal)" + @echo " make k8s destroy [preset] - удалить Kind кластер" + @echo " make k8s start [preset] - запустить Kind кластер" + @echo " make k8s stop [preset] - остановить Kind кластер" + @echo " make k8s status [preset] - показать статус кластера" + @echo " make k8s nodes [preset] - показать узлы кластера" + @echo " make k8s config [preset] - получить kubeconfig для подключения" + @echo " make k8s addon [preset] [url] - установить аддон из манифеста" + @echo " make k8s shell [preset] - открыть shell в контейнере k8s" + @echo "" @echo "💡 ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ:" @echo " make presets list # показать все preset'ы" @echo " make presets test PRESET=etcd-patroni # тест с etcd-patroni" diff --git a/README.md b/README.md index d98292c..02fef60 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ AnsibleLab - это универсальная система для разра - **Автоматическая проверка** синтаксиса Ansible ролей - **Управление секретами** через Ansible Vault - **Готовые Docker образы** для разных ОС +- **Kubernetes Kind кластеры** для тестирования в среде Kubernetes +- **Автоматическая установка аддонов** (Istio, Prometheus, Grafana, Kiali и другие) ## 📁 Структура проекта @@ -501,6 +503,10 @@ make custom-images # справка по собственным - **[docs/dockerfiles.md](docs/dockerfiles.md)** - Полная документация по Docker образам +### Kubernetes + +- **[docs/kubernetes-kind.md](docs/kubernetes-kind.md)** - Документация по работе с Kind кластерами + ## 🐳 Docker образы Проект использует готовые Docker образы для различных ОС: @@ -620,6 +626,7 @@ MIT License - ✅ Управление секретами через Ansible Vault - ✅ Готовые Docker образы для разных ОС - ✅ CI/CD интеграция +- ✅ Kubernetes Kind кластеры для тестирования --- diff --git a/dockerfiles/k8s/Dockerfile b/dockerfiles/k8s/Dockerfile new file mode 100644 index 0000000..34f7fba --- /dev/null +++ b/dockerfiles/k8s/Dockerfile @@ -0,0 +1,73 @@ +# Kubernetes Kind Container - Multi-Arch +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru + +ARG TARGETARCH + +FROM ubuntu:22.04 + +# Обновляем систему +RUN apt-get update && apt-get upgrade -y && apt-get clean + +# Устанавливаем базовые пакеты +RUN apt-get install -y \ + wget \ + curl \ + git \ + vim \ + bash \ + ca-certificates \ + python3 \ + python3-yaml \ + file \ + apt-transport-https \ + gnupg \ + lsb-release \ + && apt-get clean + +# Устанавливаем Docker CLI +RUN DOCKER_VERSION=20.10.24 && \ + if [ "${TARGETARCH}" = "amd64" ]; then \ + wget -O /tmp/docker-cli.tgz "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" && \ + tar -xz -C /tmp -f /tmp/docker-cli.tgz && \ + mv /tmp/docker/docker /usr/local/bin/ && \ + rm -rf /tmp/docker-cli.tgz /tmp/docker; \ + else \ + wget -O /tmp/docker-cli.tgz "https://download.docker.com/linux/static/stable/aarch64/docker-${DOCKER_VERSION}.tgz" && \ + tar -xz -C /tmp -f /tmp/docker-cli.tgz && \ + mv /tmp/docker/docker /usr/local/bin/ && \ + rm -rf /tmp/docker-cli.tgz /tmp/docker; \ + fi && \ + chmod +x /usr/local/bin/docker + +# Устанавливаем kubectl +RUN if [ "${TARGETARCH}" = "amd64" ]; then \ + wget -O kubectl "https://dl.k8s.io/release/v1.34.1/bin/linux/amd64/kubectl"; \ + else \ + wget -O kubectl "https://dl.k8s.io/release/v1.34.1/bin/linux/arm64/kubectl"; \ + fi && \ + chmod +x kubectl && \ + mv kubectl /usr/local/bin/ + +# Устанавливаем Helm +RUN wget https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 -O - | bash + +# Устанавливаем Kind v0.30.0 +RUN if [ "${TARGETARCH}" = "amd64" ]; then \ + wget -O /usr/local/bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.30.0/kind-linux-amd64"; \ + else \ + wget -O /usr/local/bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.30.0/kind-linux-arm64"; \ + fi && \ + chmod +x /usr/local/bin/kind && \ + ls -lh /usr/local/bin/kind && \ + file /usr/local/bin/kind + +# Устанавливаем Istio CLI +RUN ARCH=$(echo ${TARGETARCH} | sed 's/amd64/x86_64/; s/arm64/aarch64/') && \ + ISTIO_VERSION=1.22.1 && \ + wget -qO- https://istio.io/downloadIstio | ISTIO_VERSION=${ISTIO_VERSION} TARGET_ARCH=${ARCH} sh - && \ + mv istio-${ISTIO_VERSION}/bin/istioctl /usr/local/bin/ && \ + rm -rf istio-${ISTIO_VERSION} + +# Команда по умолчанию +CMD ["sleep", "infinity"] diff --git a/docs/kubernetes-kind.md b/docs/kubernetes-kind.md new file mode 100644 index 0000000..87b7b95 --- /dev/null +++ b/docs/kubernetes-kind.md @@ -0,0 +1,297 @@ +# Kubernetes Kind Кластеры + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Описание + +Проект поддерживает автоматическое создание и управление Kubernetes кластерами на базе [Kind](https://kind.sigs.k8s.io/) для тестирования в изолированной лабораторной среде. + +## Возможности + +- Создание Kind кластеров с настраиваемым количеством worker-узлов +- Автоматическая установка аддонов: + - Ingress NGINX Controller + - Metrics Server + - Istio Service Mesh + - Kiali (визуализация Istio) + - Prometheus Stack (Prometheus + Grafana) +- Настройка портов для внешнего доступа к аддонам +- Интеграция с Docker контейнерами в одной лабораторной сети + +## Команды + +### Создание кластера + +```bash +# Создание минимального кластера (без аддонов) +make k8s create + +# Создание кластера с полным набором аддонов +make k8s create kubernetes + +# Использование пользовательского пресета +make k8s create my-custom-preset +``` + +### Управление кластером + +```bash +# Удаление кластера +make k8s destroy [preset] + +# Остановка кластера (без удаления) +make k8s stop [cluster] + +# Запуск остановленного кластера +make k8s start [cluster] + +# Проверка статуса кластера +make k8s status [cluster] + +# Получение kubeconfig для подключения +make k8s config [cluster] + +# Открытие shell в контейнере +make k8s shell +``` + +## Конфигурация + +### Пресеты Kubernetes хранятся в `molecule/presets/k8s/` + +#### Минимальный кластер (`k8s-minimal.yml`) + +```yaml +kind_clusters: + - name: minimal + workers: 0 # Только control-plane узел + api_port: 6443 +``` + +#### Полный кластер с аддонами (`kubernetes.yml`) + +```yaml +kind_clusters: + - name: lab + workers: 2 + api_port: 6443 + addons: + ingress_nginx: true + metrics_server: true + istio: true + kiali: true + prometheus_stack: true + ingress_host_http_port: 8081 + ingress_host_https_port: 8443 + # Порты для внешнего доступа к аддонам + addon_ports: + prometheus: 9090 + grafana: 3000 + kiali: 20001 +``` + +## Доступ к аддонам + +После создания кластера с аддонами, они доступны на следующих портах: + +### Prometheus +```bash +# Web UI доступна на порту 9090 +http://localhost:9090 +``` + +### Grafana +```bash +# Web UI доступна на порту 3000 +http://localhost:3000 + +# Пароль администратора +kubectl get secret -n monitoring monitoring-grafana \ + -o jsonpath="{.data.admin-password}" | base64 -d + +# Логин: admin +# Пароль: (получен выше) +``` + +### Kiali +```bash +# Web UI доступна на порту 20001 +http://localhost:20001 + +# Аутентификация: anonymous (отключена по умолчанию) +``` + +### Istio Ingress +```bash +# HTTP доступен на порту 8081 +http://localhost:8081 + +# HTTPS доступен на порту 8443 +https://localhost:8443 +``` + +## Примеры использования + +### Создание и настройка кластера + +```bash +# 1. Создать кластер с аддонами +make k8s create kubernetes + +# 2. Проверить статус +make k8s status + +# 3. Получить kubeconfig +make k8s config lab + +# 4. Использовать kubeconfig +export KUBECONFIG=kubeconfig +kubectl get nodes +kubectl get pods -A + +# 5. Открыть Grafana в браузере +open http://localhost:3000 +# Логин: admin +# Пароль: (получить командой выше) +``` + +### Управление кластером + +```bash +# Остановить кластер (без удаления) +make k8s stop lab + +# Запустить остановленный кластер +make k8s start lab + +# Проверить конкретный кластер +make k8s status lab + +# Получить kubeconfig для конкретного кластера +make k8s config lab + +# Удалить кластер +make k8s destroy kubernetes +``` + +### Работа внутри контейнера + +```bash +# Открыть shell в контейнере ansible-controller +make k8s shell + +# Внутри контейнера: +kind get clusters +kubectl --context kind-lab get nodes +istioctl --context kind-lab proxy-status +kubectl --context kind-lab get pods -n monitoring +``` + +## Архитектура + +``` +┌─────────────────────────────────────────────────────────┐ +│ Docker Network: labnet │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Ubuntu22 │ │ Debian12 │ │ Ansible │ │ +│ │ Container │ │ Container │ │ Controller │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────────────────────────────────────────┐ │ +│ │ Kind Cluster: "lab" │ │ +│ │ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ Control Plane│ │ Worker 1 │ │ │ +│ │ │ Port 6443 │ │ │ │ │ +│ │ └──────────────┘ └──────────────┘ │ │ +│ │ ┌──────────────┐ │ │ +│ │ │ Worker 2 │ │ │ +│ │ └──────────────┘ │ │ +│ │ │ │ +│ │ NodePort Services: │ │ +│ │ - Prometheus :9090 │ │ +│ │ - Grafana :3000 │ │ +│ │ - Kiali :20001 │ │ +│ │ - Ingress :8081 (HTTP), :8443 (HTTPS) │ │ +│ └──────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────┘ +``` + +## Настройка портов аддонов + +Вы можете настроить порты для внешнего доступа в пресете: + +```yaml +kind_clusters: + - name: lab + workers: 2 + addons: + prometheus_stack: true + kiali: true + addon_ports: + prometheus: 9090 # Prometheus UI + grafana: 3000 # Grafana UI + kiali: 20001 # Kiali UI +``` + +## Best Practices + +1. **Минимальные ресурсы:** Для быстрого тестирования используйте `workers: 0` (только control-plane) +2. **Production-like:** Для реалистичных тестов используйте `workers: 2-3` +3. **Аддоны:** Включайте только необходимые аддоны для уменьшения времени создания +4. **Изоляция:** Каждый preset может иметь свой уникальный кластер с разными настройками +5. **Порты:** Используйте разные порты для разных кластеров, если запускаете несколько + +## Troubleshooting + +### Кластер не создается + +```bash +# Проверить логи +docker logs ansible-controller + +# Проверить доступное место на диске +df -h + +# Проверить Docker ресурсы +docker system df +``` + +### Проблемы с аддонами + +```bash +# Проверить статус подов +kubectl get pods -A + +# Проверить сервисы +kubectl get svc -A + +# Проверить порты +kubectl get svc -n monitoring +``` + +### Проблемы с Istio + +```bash +# Переустановить Istio +istioctl uninstall -y --context kind-lab +istioctl install -y --set profile=demo --context kind-lab +``` + +### Проблемы с Prometheus Stack + +```bash +# Переустановить +helm uninstall monitoring -n monitoring +helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ + --namespace monitoring --kube-context kind-lab +``` + +## Дополнительные ресурсы + +- [Kind Documentation](https://kind.sigs.k8s.io/docs/) +- [Istio Documentation](https://istio.io/latest/docs/) +- [Kiali Documentation](https://kiali.io/documentation/) +- [Prometheus Operator](https://prometheus-operator.dev/) diff --git a/molecule/default/create.yml b/molecule/default/create.yml index c405b91..4ad0db2 100644 --- a/molecule/default/create.yml +++ b/molecule/default/create.yml @@ -4,7 +4,8 @@ vars: # Получаем preset из переменной окружения или используем default preset_name: "{{ lookup('env', 'MOLECULE_PRESET') | default('default') }}" - preset_file: "/workspace/molecule/presets/{{ preset_name }}.yml" + # Проверяем сначала в папке k8s, затем в основной папке presets + preset_file: "{{ '/workspace/molecule/presets/k8s/' + preset_name + '.yml' if (preset_name in ['k8s-minimal', 'kubernetes', 'k8s-full'] or preset_name.startswith('k8s-')) else '/workspace/molecule/presets/' + preset_name + '.yml' }}" # Fallback значения если preset файл не найден docker_network: labnet @@ -30,6 +31,7 @@ - name: u1 family: debian groups: [test] + kind_clusters: [] tasks: # - name: Install required collections @@ -281,4 +283,135 @@ - Groups: {{ groups_map.keys() | list | join(', ') }} - Systemd nodes: {{ hosts | selectattr('type','undefined') | list | length }} - DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - - DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} \ No newline at end of file + - DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} + + # ---------- Kind clusters (если определены) ---------- + - name: Create kind cluster configs + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + mkdir -p /ansible/.kind; + cat > /ansible/.kind/{{ item.name }}.yaml < 0 + + - name: Create kind clusters + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + set -e; + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + if kind get clusters | grep -qx "$$n"; then + echo "[kind] cluster $$n already exists"; + else + echo "[kind] creating $$n"; + kind create cluster --name "$$n" --config "/ansible/.kind/$$n.yaml"; + fi + done + ' + when: (kind_clusters | default([])) | length > 0 + + - name: Install Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack (per cluster, if enabled) + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + set -e; + helm repo add kiali https://kiali.org/helm-charts >/dev/null 2>&1 || true; + helm repo add prometheus-community https://prometheus-community.github.io/helm-charts >/dev/null 2>&1 || true; + helm repo update >/dev/null 2>&1 || true; + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + # ingress-nginx + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("ingress_nginx", False) | to_json }}; then + echo "[addons] ingress-nginx on $$n"; + kubectl --context kind-$$n apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml || true; + kubectl --context kind-$$n -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s || true; + fi + # metrics-server + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("metrics_server", False) | to_json }}; then + echo "[addons] metrics-server on $$n"; + kubectl --context kind-$$n apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml || true; + kubectl --context kind-$$n -n kube-system patch deploy metrics-server -p \ + "{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"metrics-server\",\"args\":[\"--kubelet-insecure-tls\",\"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\"]}]}}}}}" || true; + fi + # istio (demo profile) + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("istio", False) | to_json }}; then + echo "[addons] istio (demo profile) on $$n"; + istioctl install -y --set profile=demo --context kind-$$n; + kubectl --context kind-$$n -n istio-system rollout status deploy/istiod --timeout=180s || true; + kubectl --context kind-$$n -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s || true; + fi + # kiali (server chart, anonymous auth) — требует istio/metrics + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("kiali", False) | to_json }}; then + echo "[addons] kiali on $$n"; + kubectl --context kind-$$n create ns istio-system >/dev/null 2>&1 || true; + helm upgrade --install kiali-server kiali/kiali-server \ + --namespace istio-system --kube-context kind-$$n \ + --set auth.strategy=anonymous --wait --timeout 180s; + fi + # kube-prometheus-stack (Prometheus + Grafana) + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("prometheus_stack", False) | to_json }}; then + echo "[addons] kube-prometheus-stack on $$n"; + kubectl --context kind-$$n create ns monitoring >/dev/null 2>&1 || true; + helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ + --namespace monitoring --kube-context kind-$$n \ + --set grafana.adminPassword=admin \ + --set grafana.defaultDashboardsTimezone=browser \ + --wait --timeout 600s; + # дождаться графаны + kubectl --context kind-$$n -n monitoring rollout status deploy/monitoring-grafana --timeout=300s || true; + fi + done + ' + when: (kind_clusters | default([])) | length > 0 + + - name: Setup NodePort for addons + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + {% for cluster in kind_clusters | default([]) %} + {% if cluster.addon_ports is defined %} + if [ "$$n" = "{{ cluster.name }}" ]; then + {% if cluster.addon_ports.prometheus is defined %} + echo "[ports] Prometheus: {{ cluster.addon_ports.prometheus }}"; + kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.prometheus }}}]' 2>/dev/null || true; + {% endif %} + {% if cluster.addon_ports.grafana is defined %} + echo "[ports] Grafana: {{ cluster.addon_ports.grafana }}"; + kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-grafana --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.grafana }}}]' 2>/dev/null || true; + {% endif %} + {% if cluster.addon_ports.kiali is defined %} + echo "[ports] Kiali: {{ cluster.addon_ports.kiali }}"; + kubectl --context kind-{{ cluster.name }} patch svc -n istio-system kiali --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.kiali }}}]' 2>/dev/null || true; + {% endif %} + fi + {% endif %} + {% endfor %} + done + ' + when: (kind_clusters | default([])) | length > 0 \ No newline at end of file diff --git a/molecule/default/create.yml.bak b/molecule/default/create.yml.bak new file mode 100644 index 0000000..4dc04c6 --- /dev/null +++ b/molecule/default/create.yml.bak @@ -0,0 +1,428 @@ +--- +- hosts: localhost + gather_facts: false + vars: + # Получаем preset из переменной окружения или используем default + preset_name: "{{ lookup('env', 'MOLECULE_PRESET') | default('default') }}" + # Проверяем сначала в папке k8s, затем в основной папке presets + preset_file: "{{ '/workspace/molecule/presets/k8s/' + preset_name + '.yml' if (preset_name in ['k8s-minimal', 'kubernetes', 'k8s-full'] or preset_name.startswith('k8s-')) else '/workspace/molecule/presets/' + preset_name + '.yml' }}" + + # Fallback значения если preset файл не найден + docker_network: labnet + generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini" + images: + alt: "inecs/ansible-lab:alt-linux-latest" + astra: "inecs/ansible-lab:astra-linux-latest" + rhel: "inecs/ansible-lab:rhel-latest" + centos: "inecs/ansible-lab:centos-latest" + alma: "inecs/ansible-lab:alma-latest" + rocky: "inecs/ansible-lab:rocky-latest" + redos: "inecs/ansible-lab:redos-latest" + ubuntu: "inecs/ansible-lab:ubuntu-latest" + debian: "inecs/ansible-lab:debian-latest" + systemd_defaults: + privileged: true + command: "/sbin/init" + volumes: + - "/sys/fs/cgroup:/sys/fs/cgroup:rw" + tmpfs: ["/run", "/run/lock"] + capabilities: ["SYS_ADMIN"] + hosts: + - name: u1 + family: debian + groups: [test] + kind_clusters: [] + + tasks: +# - name: Install required collections +# command: ansible-galaxy collection install -r /workspace/requirements.yml +# delegate_to: localhost +# ignore_errors: true +# register: collections_install +# changed_when: false +# run_once: true +# become: true +# vars: +# ansible_python_interpreter: /usr/bin/python3 +# environment: +# ANSIBLE_COLLECTIONS_PATH: /usr/share/ansible/collections + + # Определяем архитектуру системы для корректной загрузки образов + - name: Detect system architecture + shell: | + arch=$(uname -m) + case $arch in + x86_64) echo "linux/amd64" ;; + aarch64|arm64) echo "linux/arm64" ;; + armv7l) echo "linux/arm/v7" ;; + *) echo "linux/amd64" ;; + esac + register: detected_platform + changed_when: false + + - name: Set ansible_architecture variable + set_fact: + ansible_architecture: "{{ detected_platform.stdout }}" + + - name: Load preset configuration + include_vars: "{{ preset_file }}" + when: preset_file is file + ignore_errors: true + + # Фильтрация хостов по поддерживаемым платформам + - name: Filter hosts by supported platforms + set_fact: + filtered_hosts: "{{ filtered_hosts | default([]) + [item] }}" + loop: "{{ hosts }}" + when: | + item.supported_platforms is not defined or + ansible_architecture in item.supported_platforms + + - name: Update hosts list with filtered results + set_fact: + hosts: "{{ filtered_hosts | default(hosts) }}" + + - name: Display filtered hosts + debug: + msg: "Platform {{ ansible_architecture }}: {{ hosts | length }} hosts will be deployed" + + - name: Ensure network exists + community.docker.docker_network: + name: "{{ docker_network }}" + state: present + + # SYSTEMD nodes + - name: Pull systemd images with correct platform + command: "docker pull --platform {{ ansible_architecture }} {{ images[item.family] }}" + loop: "{{ hosts | selectattr('type','undefined') | list }}" + loop_control: { label: "{{ item.name }}" } + when: item.family is defined and images[item.family] is defined + register: pull_result + ignore_errors: yes + + - name: Display pull results + debug: + msg: "Pulled {{ item.item.name }}: {{ 'OK' if (item.rc is defined and item.rc == 0) else 'SKIPPED (not available for this platform)' }}" + loop: "{{ pull_result.results | default([]) }}" + loop_control: + label: "{{ item.item.name }}" + + - name: Start systemd nodes + community.docker.docker_container: + name: "{{ item.name }}" + image: "{{ images[item.family] }}" + networks: + - name: "{{ docker_network }}" + privileged: "{{ systemd_defaults.privileged }}" + command: "{{ systemd_defaults.command }}" + volumes: "{{ systemd_defaults.volumes | default([]) + (item.volumes | default([])) }}" + tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}" + capabilities: "{{ systemd_defaults.capabilities | default([]) }}" + published_ports: "{{ item.publish | default([]) }}" + env: "{{ item.env | default({}) }}" + # Специальные настройки для Astra Linux и RedOS (для совместимости с amd64 базовыми образами) + security_opts: "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}" + platform: "{{ 'linux/amd64' if item.family in ['astra', 'redos'] else omit }}" + state: started + restart_policy: unless-stopped + loop: "{{ hosts | selectattr('type','undefined') | list }}" + loop_control: { label: "{{ item.name }}" } + when: item.family is defined and images[item.family] is defined + + # Ожидание стабилизации контейнеров + - name: Wait for containers to be ready + pause: + seconds: 5 + when: hosts | length > 0 + + # Создание tmp директории в контейнерах + - name: Create Ansible tmp directory in containers + community.docker.docker_container_exec: + container: "{{ item.name }}" + command: "mkdir -p /tmp/.ansible-tmp && chmod 755 /tmp/.ansible-tmp" + loop: "{{ hosts | selectattr('type','undefined') | list }}" + loop_control: { label: "{{ item.name }}" } + when: item.family is defined and images[item.family] is defined + ignore_errors: true + retries: 3 + delay: 2 + + # DinD nodes + - name: Start DinD nodes (docker:27-dind) + community.docker.docker_container: + name: "{{ item.name }}" + image: "docker:27-dind" + networks: + - name: "{{ docker_network }}" + privileged: true + env: + DOCKER_TLS_CERTDIR: "" + published_ports: "{{ item.publish | default([]) }}" + volumes: "{{ (item.volumes | default([])) + [item.name + '-docker:/var/lib/docker'] }}" + state: started + restart_policy: unless-stopped + loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}" + loop_control: { label: "{{ item.name }}" } + + # DOoD nodes (mount docker.sock) + - name: Start DOoD nodes (systemd + docker.sock mount) + community.docker.docker_container: + name: "{{ item.name }}" + image: "{{ images[item.family] }}" + networks: + - name: "{{ docker_network }}" + privileged: "{{ systemd_defaults.privileged }}" + command: "{{ systemd_defaults.command }}" + volumes: "{{ (systemd_defaults.volumes | default([])) + ['/var/run/docker.sock:/var/run/docker.sock'] + (item.volumes | default([])) }}" + tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}" + capabilities: "{{ systemd_defaults.capabilities | default([]) }}" + published_ports: "{{ item.publish | default([]) }}" + env: "{{ item.env | default({}) }}" + state: started + restart_policy: unless-stopped + loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list }}" + loop_control: { label: "{{ item.name }}" } + when: item.family is defined and images[item.family] is defined + + # Build groups map + - name: Initialize groups map + set_fact: + groups_map: {} + + - name: Append hosts to groups + set_fact: + groups_map: "{{ groups_map | combine({ item_group: (groups_map[item_group] | default([])) + [item_name] }) }}" + loop: "{{ hosts | subelements('groups', skip_missing=True) }}" + loop_control: + label: "{{ item.0.name }}" + vars: + item_name: "{{ item.0.name }}" + item_group: "{{ item.1 }}" + + # Render inventory + - name: Render inventory ini + set_fact: + inv_content: | + [all:vars] + ansible_connection=community.docker.docker + ansible_remote_tmp=/tmp/.ansible-tmp + + {% for group, members in (groups_map | dictsort) %} + [{{ group }}] + {% for h in members %}{{ h }} + {% endfor %} + + {% endfor %} + [all] + {% for h in hosts %}{{ h.name }} + {% endfor %} + + {# Группа с Debian-based системами (Debian, Ubuntu, Alt) - используем /usr/bin/python3 #} + {% set debian_hosts = [] %} + {% for h in hosts %} + {% if h.family in ['ubuntu', 'debian', 'alt'] %} + {% set _ = debian_hosts.append(h.name) %} + {% endif %} + {% endfor %} + {% if debian_hosts %} + [debian_family:vars] + ansible_python_interpreter=/usr/bin/python3 + + [debian_family] + {% for h in debian_hosts %}{{ h }} + {% endfor %} + {% endif %} + + {# Группа с RHEL-based системами (RHEL, CentOS, Alma, Rocky, RedOS) #} + {% set rhel_hosts = [] %} + {% for h in hosts %} + {% if h.family in ['rhel', 'centos', 'alma', 'rocky', 'redos'] %} + {% set _ = rhel_hosts.append(h.name) %} + {% endif %} + {% endfor %} + {% if rhel_hosts %} + [rhel_family:vars] + ansible_python_interpreter=/usr/bin/python3 + + [rhel_family] + {% for h in rhel_hosts %}{{ h }} + {% endfor %} + {% endif %} + + {# Astra Linux - используем /usr/bin/python3 #} + {% set astra_hosts = [] %} + {% for h in hosts %} + {% if h.family == 'astra' %} + {% set _ = astra_hosts.append(h.name) %} + {% endif %} + {% endfor %} + {% if astra_hosts %} + [astra_family:vars] + ansible_python_interpreter=/usr/bin/python3 + + [astra_family] + {% for h in astra_hosts %}{{ h }} + {% endfor %} + {% endif %} + + {# Глобальный fallback для остальных хостов #} + [unmatched_hosts:vars] + ansible_python_interpreter=auto_silent + + - name: Ensure inventory directory exists + file: + path: "{{ generated_inventory | dirname }}" + state: directory + mode: "0755" + + - name: Write inventory file + copy: + dest: "{{ generated_inventory }}" + content: "{{ inv_content }}" + mode: "0644" + + - name: Display inventory summary + debug: + msg: | + 📋 Inventory Summary: + - Total hosts: {{ hosts | length }} + - Groups: {{ groups_map.keys() | list | join(', ') }} + - Systemd nodes: {{ hosts | selectattr('type','undefined') | list | length }} + - DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} + - DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} + + # ---------- Kind clusters (если определены) ---------- + - name: Prepare kind cluster configs + set_fact: + kind_config_content: | + kind: Cluster + apiVersion: kind.x-k8s.io/v1alpha4 + nodes: + - role: control-plane + {% if (item.addons|default({})).ingress_nginx|default(false) %} + extraPortMappings: + - containerPort: 80 + hostPort: {{ item.ingress_host_http_port | default(8081) }} + protocol: TCP + - containerPort: 443 + hostPort: {{ item.ingress_host_https_port | default(8443) }} + protocol: TCP + {% endif %} + {% for i in range(item.workers | default(0)) %} + - role: worker + {% endfor %} + networking: + apiServerAddress: "0.0.0.0" + apiServerPort: {{ item.api_port | default(0) }} + loop: "{{ kind_clusters | default([]) }}" + when: (kind_clusters | default([])) | length > 0 + + - name: Create kind cluster configs + community.docker.docker_container_exec: + container: "{{ ansible_controller_container | default('ansible-controller') }}" + command: > + bash -c " + mkdir -p /ansible/.kind; + echo '{{ kind_config_content }}' > /ansible/.kind/{{ item.name }}.yaml + " + loop: "{{ kind_clusters | default([]) }}" + when: (kind_clusters | default([])) | length > 0 + + - name: Create kind clusters + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + set -e; + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + if kind get clusters | grep -qx "$$n"; then + echo "[kind] cluster $$n already exists"; + else + echo "[kind] creating $$n"; + kind create cluster --name "$$n" --config "/ansible/.kind/$$n.yaml"; + fi + done + ' + when: (kind_clusters | default([])) | length > 0 + + - name: Install Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack (per cluster, if enabled) + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + set -e; + helm repo add kiali https://kiali.org/helm-charts >/dev/null 2>&1 || true; + helm repo add prometheus-community https://prometheus-community.github.io/helm-charts >/dev/null 2>&1 || true; + helm repo update >/dev/null 2>&1 || true; + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + # ingress-nginx + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("ingress_nginx", False) | to_json }}; then + echo "[addons] ingress-nginx on $$n"; + kubectl --context kind-$$n apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml || true; + kubectl --context kind-$$n -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s || true; + fi + # metrics-server + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("metrics_server", False) | to_json }}; then + echo "[addons] metrics-server on $$n"; + kubectl --context kind-$$n apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml || true; + kubectl --context kind-$$n -n kube-system patch deploy metrics-server -p \ + "{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"metrics-server\",\"args\":[\"--kubelet-insecure-tls\",\"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\"]}]}}}}}" || true; + fi + # istio (demo profile) + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("istio", False) | to_json }}; then + echo "[addons] istio (demo profile) on $$n"; + istioctl install -y --set profile=demo --context kind-$$n; + kubectl --context kind-$$n -n istio-system rollout status deploy/istiod --timeout=180s || true; + kubectl --context kind-$$n -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s || true; + fi + # kiali (server chart, anonymous auth) — требует istio/metrics + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("kiali", False) | to_json }}; then + echo "[addons] kiali on $$n"; + kubectl --context kind-$$n create ns istio-system >/dev/null 2>&1 || true; + helm upgrade --install kiali-server kiali/kiali-server \ + --namespace istio-system --kube-context kind-$$n \ + --set auth.strategy=anonymous --wait --timeout 180s; + fi + # kube-prometheus-stack (Prometheus + Grafana) + if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("prometheus_stack", False) | to_json }}; then + echo "[addons] kube-prometheus-stack on $$n"; + kubectl --context kind-$$n create ns monitoring >/dev/null 2>&1 || true; + helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ + --namespace monitoring --kube-context kind-$$n \ + --set grafana.adminPassword=admin \ + --set grafana.defaultDashboardsTimezone=browser \ + --wait --timeout 600s; + # дождаться графаны + kubectl --context kind-$$n -n monitoring rollout status deploy/monitoring-grafana --timeout=300s || true; + fi + done + ' + when: (kind_clusters | default([])) | length > 0 + + - name: Setup NodePort for addons + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + {% for cluster in kind_clusters | default([]) %} + {% if cluster.addon_ports is defined %} + if [ "$$n" = "{{ cluster.name }}" ]; then + {% if cluster.addon_ports.prometheus is defined %} + echo "[ports] Prometheus: {{ cluster.addon_ports.prometheus }}"; + kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.prometheus }}}]' 2>/dev/null || true; + {% endif %} + {% if cluster.addon_ports.grafana is defined %} + echo "[ports] Grafana: {{ cluster.addon_ports.grafana }}"; + kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-grafana --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.grafana }}}]' 2>/dev/null || true; + {% endif %} + {% if cluster.addon_ports.kiali is defined %} + echo "[ports] Kiali: {{ cluster.addon_ports.kiali }}"; + kubectl --context kind-{{ cluster.name }} patch svc -n istio-system kiali --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.kiali }}}]' 2>/dev/null || true; + {% endif %} + fi + {% endif %} + {% endfor %} + done + ' + when: (kind_clusters | default([])) | length > 0 \ No newline at end of file diff --git a/molecule/default/destroy.yml b/molecule/default/destroy.yml index f319182..f1b0aad 100644 --- a/molecule/default/destroy.yml +++ b/molecule/default/destroy.yml @@ -4,7 +4,8 @@ vars: # Получаем preset из переменной окружения или используем default preset_name: "{{ lookup('env', 'MOLECULE_PRESET') | default('default') }}" - preset_file: "/workspace/molecule/presets/{{ preset_name }}.yml" + # Проверяем сначала в папке k8s, затем в основной папке presets + preset_file: "{{ '/workspace/molecule/presets/k8s/' + preset_name + '.yml' if (preset_name in ['k8s-minimal', 'kubernetes', 'k8s-full'] or preset_name.startswith('k8s-')) else '/workspace/molecule/presets/' + preset_name + '.yml' }}" # Fallback значения если preset файл не найден docker_network: labnet @@ -12,6 +13,7 @@ - name: u1 family: debian groups: [test] + kind_clusters: [] tasks: - name: Load preset configuration @@ -74,10 +76,27 @@ # Используем переменную hosts из загруженного пресета hosts: "{{ hosts }}" + - name: Remove kind clusters + community.docker.docker_container_exec: + container: ansible-controller + command: > + bash -lc ' + set -e; + for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do + if kind get clusters | grep -qx "$$n"; then + echo "[kind] deleting $$n"; + kind delete cluster --name "$$n" || true; + fi + done + ' + when: (kind_clusters | default([])) | length > 0 + ignore_errors: true + - name: Display cleanup summary debug: msg: | 🧹 Cleanup Summary: - Removed containers: {{ hosts | length }} - Removed DinD volumes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - - Network: {{ docker_network }} \ No newline at end of file + - Network: {{ docker_network }} + - Removed kind clusters: {{ kind_clusters | default([]) | length }} \ No newline at end of file diff --git a/molecule/presets/examples/standart.yml b/molecule/presets/examples/standart.yml index 6044d61..d334b65 100644 --- a/molecule/presets/examples/standart.yml +++ b/molecule/presets/examples/standart.yml @@ -33,6 +33,20 @@ systemd_defaults: tmpfs: ["/run", "/run/lock"] capabilities: ["SYS_ADMIN"] +# Kind кластеры (опционально) +# kind_clusters: +# - name: lab +# workers: 2 +# api_port: 6443 +# addons: +# ingress_nginx: true +# metrics_server: true +# istio: true +# kiali: true +# prometheus_stack: true +# ingress_host_http_port: 8081 +# ingress_host_https_port: 8443 + hosts: # Стандартный набор - 3 хоста - name: u1 diff --git a/molecule/presets/k8s/k8s-minimal.yml b/molecule/presets/k8s/k8s-minimal.yml new file mode 100644 index 0000000..5408b26 --- /dev/null +++ b/molecule/presets/k8s/k8s-minimal.yml @@ -0,0 +1,42 @@ +--- +#description: Минимальный Kind кластер без аддонов +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru + +docker_network: labnet +generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini" + +# systemd-ready образы +images: + alt: "inecs/ansible-lab:alt-linux-latest" + astra: "inecs/ansible-lab:astra-linux-latest" + rhel: "inecs/ansible-lab:rhel-latest" + centos7: "inecs/ansible-lab:centos7-latest" + centos8: "inecs/ansible-lab:centos8-latest" + centos9: "inecs/ansible-lab:centos9-latest" + alma: "inecs/ansible-lab:alma-latest" + rocky: "inecs/ansible-lab:rocky-latest" + redos: "inecs/ansible-lab:redos-latest" + ubuntu20: "inecs/ansible-lab:ubuntu20-latest" + ubuntu22: "inecs/ansible-lab:ubuntu22-latest" + ubuntu24: "inecs/ansible-lab:ubuntu24-latest" + debian9: "inecs/ansible-lab:debian9-latest" + debian10: "inecs/ansible-lab:debian10-latest" + debian11: "inecs/ansible-lab:debian11-latest" + debian12: "inecs/ansible-lab:debian12-latest" + +systemd_defaults: + privileged: true + command: "/sbin/init" + volumes: + - "/sys/fs/cgroup:/sys/fs/cgroup:rw" + tmpfs: ["/run", "/run/lock"] + capabilities: ["SYS_ADMIN"] + +# Минимальный Kind кластер без аддонов +kind_clusters: + - name: minimal + workers: 0 # Только control-plane + api_port: 6443 + +hosts: [] diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml new file mode 100644 index 0000000..dc6737c --- /dev/null +++ b/molecule/presets/k8s/kubernetes.yml @@ -0,0 +1,59 @@ +--- +#description: Пресет для тестирования с Kubernetes Kind кластером +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru + +docker_network: labnet +generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini" + +# systemd-ready образы +images: + alt: "inecs/ansible-lab:alt-linux-latest" + astra: "inecs/ansible-lab:astra-linux-latest" + rhel: "inecs/ansible-lab:rhel-latest" + centos7: "inecs/ansible-lab:centos7-latest" + centos8: "inecs/ansible-lab:centos8-latest" + centos9: "inecs/ansible-lab:centos9-latest" + alma: "inecs/ansible-lab:alma-latest" + rocky: "inecs/ansible-lab:rocky-latest" + redos: "inecs/ansible-lab:redos-latest" + ubuntu20: "inecs/ansible-lab:ubuntu20-latest" + ubuntu22: "inecs/ansible-lab:ubuntu22-latest" + ubuntu24: "inecs/ansible-lab:ubuntu24-latest" + debian9: "inecs/ansible-lab:debian9-latest" + debian10: "inecs/ansible-lab:debian10-latest" + debian11: "inecs/ansible-lab:debian11-latest" + debian12: "inecs/ansible-lab:debian12-latest" + +systemd_defaults: + privileged: true + command: "/sbin/init" + volumes: + - "/sys/fs/cgroup:/sys/fs/cgroup:rw" + tmpfs: ["/run", "/run/lock"] + capabilities: ["SYS_ADMIN"] + +# Kind кластеры с полным набором аддонов +kind_clusters: + - name: lab + workers: 2 + api_port: 6443 + addons: + ingress_nginx: true + metrics_server: true + istio: true + kiali: true + prometheus_stack: true + ingress_host_http_port: 8081 + ingress_host_https_port: 8443 + # Порты для доступа к аддонам извне + # Документация: https://devops.org.ru + # Prometheus: http://localhost:9090 + # Grafana: http://localhost:3000 (admin/admin) + # Kiali: http://localhost:20001 + addon_ports: + prometheus: 9090 + grafana: 3000 + kiali: 20001 + +hosts: [] diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py new file mode 100755 index 0000000..78c534f --- /dev/null +++ b/scripts/create_k8s_cluster.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +""" +Скрипт для создания Kind кластеров +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess +import os + +def run_cmd(cmd): + """Выполнить команду""" + print(f"[run] {cmd}") + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode != 0: + print(f"[error] {result.stderr}") + sys.exit(1) + print(result.stdout) + return result.stdout + +def main(): + if len(sys.argv) < 3: + print("Usage: create_k8s_cluster.py ") + sys.exit(1) + + preset_file = sys.argv[1] + container_name = sys.argv[2] + + print(f"📋 Читаю пресет: {preset_file}") + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + + kind_clusters = preset.get('kind_clusters', []) + if not kind_clusters: + print("⚠️ В пресете не определены kind кластеры") + sys.exit(0) + + os.makedirs("/ansible/.kind", exist_ok=True) + + for cluster in kind_clusters: + name = cluster['name'] + config_file = f"/ansible/.kind/{name}.yaml" + + print(f"\n☸️ Создание конфигурации для кластера: {name}") + + # Создаем конфигурацию Kind + config = { + 'kind': 'Cluster', + 'apiVersion': 'kind.x-k8s.io/v1alpha4', + 'nodes': [ + {'role': 'control-plane'} + ], + 'networking': { + 'apiServerAddress': '0.0.0.0', + 'apiServerPort': cluster.get('api_port', 0) + } + } + + # Добавляем extraPortMappings для ingress если нужно + if cluster.get('addons', {}).get('ingress_nginx'): + config['nodes'][0]['extraPortMappings'] = [ + { + 'containerPort': 80, + 'hostPort': cluster.get('ingress_host_http_port', 8081), + 'protocol': 'TCP' + }, + { + 'containerPort': 443, + 'hostPort': cluster.get('ingress_host_https_port', 8443), + 'protocol': 'TCP' + } + ] + + # Добавляем worker nodes + workers = cluster.get('workers', 0) + for i in range(workers): + config['nodes'].append({'role': 'worker'}) + + # Записываем конфигурацию + with open(config_file, 'w') as f: + yaml.dump(config, f) + + print(f"✅ Конфигурация сохранена: {config_file}") + + # Проверяем существование кластера + result = subprocess.run(f"kind get clusters", shell=True, capture_output=True, text=True) + existing = result.stdout.strip().split('\n') if result.returncode == 0 else [] + + if name in existing: + print(f"⚠️ Кластер '{name}' уже существует, пропускаю") + else: + print(f"🚀 Создание кластера: {name}") + run_cmd(f"kind create cluster --name {name} --config {config_file}") + + # Устанавливаем аддоны + addons = cluster.get('addons', {}) + if not addons: + continue + + print(f"\n📦 Установка аддонов для кластера: {name}") + + if addons.get('ingress_nginx'): + print(" - Installing ingress-nginx") + run_cmd(f"kubectl --context kind-{name} apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml") + run_cmd(f"kubectl --context kind-{name} -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s") + + if addons.get('metrics_server'): + print(" - Installing metrics-server") + run_cmd(f"kubectl --context kind-{name} apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml") + patch_json = '{"spec":{"template":{"spec":{"containers":[{"name":"metrics-server","args":["--kubelet-insecure-tls","--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname"]}]}}}}' + run_cmd(f"kubectl --context kind-{name} -n kube-system patch deploy metrics-server -p '{patch_json}'") + + if addons.get('istio'): + print(" - Installing Istio") + run_cmd(f"istioctl install -y --set profile=demo --context kind-{name}") + run_cmd(f"kubectl --context kind-{name} -n istio-system rollout status deploy/istiod --timeout=180s") + run_cmd(f"kubectl --context kind-{name} -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s") + + if addons.get('kiali'): + print(" - Installing Kiali") + run_cmd(f"kubectl --context kind-{name} create ns istio-system") + run_cmd(f"helm upgrade --install kiali-server kiali/kiali-server --namespace istio-system --kube-context kind-{name} --set auth.strategy=anonymous --wait --timeout 180s") + + if addons.get('prometheus_stack'): + print(" - Installing Prometheus Stack") + run_cmd(f"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts") + run_cmd(f"helm repo update") + run_cmd(f"kubectl --context kind-{name} create ns monitoring") + run_cmd(f"helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --kube-context kind-{name} --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s") + run_cmd(f"kubectl --context kind-{name} -n monitoring rollout status deploy/monitoring-grafana --timeout=300s") + + # Настраиваем NodePort для аддонов + addon_ports = cluster.get('addon_ports', {}) + if addon_ports: + print("\n🔌 Настройка NodePort для аддонов") + + if 'prometheus' in addon_ports: + port = addon_ports['prometheus'] + print(f" - Prometheus: {port}") + patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' + run_cmd(f"kubectl --context kind-{name} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='{patch_json}'") + + if 'grafana' in addon_ports: + port = addon_ports['grafana'] + print(f" - Grafana: {port}") + patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' + run_cmd(f"kubectl --context kind-{name} patch svc -n monitoring monitoring-grafana --type='json' -p='{patch_json}'") + + if 'kiali' in addon_ports: + port = addon_ports['kiali'] + print(f" - Kiali: {port}") + patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' + run_cmd(f"kubectl --context kind-{name} patch svc -n istio-system kiali --type='json' -p='{patch_json}'") + + print(f"✅ Кластер '{name}' готов!") + + print("\n🎉 Все кластеры созданы!") + +if __name__ == '__main__': + main() From 714ca43d38e9fbc21abc42c757306d37ce95845a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 03:33:47 +0300 Subject: [PATCH 03/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D1=81=20=D0=BC=D0=B0=D0=BD=D0=B8=D1=84=D0=B5=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=D0=B8,=20Helm=20=D0=B8=20Helm=20=D1=80=D0=B5=D0=BF=D0=BE?= =?UTF-8?q?=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D1=8F=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены команды make k8s manifest для работы с манифестами YAML - Добавлены команды make k8s helm для управления Helm чартами - Добавлены команды make k8s helmrepo для управления Helm репозиториями - Создана подробная документация docs/kubernetes-commands.md - Обновлена справка в Makefile Поддерживаемые операции: - manifest: apply, delete, update - helm: apply, delete, update, rollback, list, status - helmrepo: add, list, delete, update, packages --- Makefile | 164 +++++++++++++++ docs/kubernetes-commands.md | 395 ++++++++++++++++++++++++++++++++++++ 2 files changed, 559 insertions(+) create mode 100644 docs/kubernetes-commands.md diff --git a/Makefile b/Makefile index 548be9d..8b3a87b 100644 --- a/Makefile +++ b/Makefile @@ -1197,6 +1197,158 @@ k8s: echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ fi;; \ + manifest) \ + echo "📄 Работа с манифестами..."; \ + MANIFEST_CMD="$(word 3, $(MAKECMDGOALS))"; \ + PRESET_ARG="$(word 4, $(MAKECMDGOALS))"; \ + MANIFEST_ARG="$(word 5, $(MAKECMDGOALS))"; \ + if [ -z "$$MANIFEST_CMD" ] || [ -z "$$PRESET_ARG" ] || [ -z "$$MANIFEST_ARG" ]; then \ + echo "❌ Ошибка: Укажите команду, пресет и путь к манифесту"; \ + echo "💡 Пример: make k8s manifest apply kubernetes https://example.com/manifest.yaml"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ + case "$$MANIFEST_CMD" in \ + apply) \ + echo "📥 Применение манифеста: $$MANIFEST_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG";; \ + delete) \ + echo "🗑️ Удаление ресурсов из манифеста: $$MANIFEST_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG";; \ + update) \ + echo "🔄 Обновление манифеста: $$MANIFEST_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG --force";; \ + *) \ + echo "❌ Неизвестная команда: $$MANIFEST_CMD"; \ + echo "💡 Доступные команды: apply, delete, update"; \ + exit 1;; \ + esac;; \ + helm) \ + echo "📦 Работа с Helm..."; \ + HELM_CMD="$(word 3, $(MAKECMDGOALS))"; \ + PRESET_ARG="$(word 4, $(MAKECMDGOALS))"; \ + RELEASE_ARG="$(word 5, $(MAKECMDGOALS))"; \ + CHART_ARG="$(word 6, $(MAKECMDGOALS))"; \ + if [ -z "$$HELM_CMD" ] || [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите команду и пресет"; \ + echo "💡 Пример: make k8s helm list kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ + case "$$HELM_CMD" in \ + apply) \ + if [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя релиза и чарт"; \ + echo "💡 Пример: make k8s helm apply kubernetes my-release stable/nginx-ingress"; \ + exit 1; \ + fi; \ + echo "📦 Установка Helm чарта: $$CHART_ARG как $$RELEASE_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + delete) \ + if [ -z "$$RELEASE_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя релиза"; \ + echo "💡 Пример: make k8s helm delete kubernetes my-release"; \ + exit 1; \ + fi; \ + echo "🗑️ Удаление Helm релиза: $$RELEASE_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm uninstall $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + update) \ + if [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя релиза и чарт"; \ + echo "💡 Пример: make k8s helm update kubernetes my-release stable/nginx-ingress"; \ + exit 1; \ + fi; \ + echo "🔄 Обновление Helm релиза: $$RELEASE_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + rollback) \ + if [ -z "$$RELEASE_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя релиза"; \ + echo "💡 Пример: make k8s helm rollback kubernetes my-release"; \ + exit 1; \ + fi; \ + echo "⏪ Откат Helm релиза: $$RELEASE_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm rollback $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + list) \ + echo "📋 Список Helm релизов:"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm list --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy --all-namespaces 2>&1 | grep -v '^WARNING' || true";; \ + status) \ + if [ -z "$$RELEASE_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя релиза"; \ + echo "💡 Пример: make k8s helm status kubernetes my-release"; \ + exit 1; \ + fi; \ + echo "📊 Статус Helm релиза: $$RELEASE_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm status $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + *) \ + echo "❌ Неизвестная команда: $$HELM_CMD"; \ + echo "💡 Доступные команды: apply, delete, update, rollback, list, status"; \ + exit 1;; \ + esac;; \ + helmrepo) \ + echo "🏪 Работа с Helm репозиториями..."; \ + REPO_CMD="$(word 3, $(MAKECMDGOALS))"; \ + PRESET_ARG="$(word 4, $(MAKECMDGOALS))"; \ + NAME_ARG="$(word 5, $(MAKECMDGOALS))"; \ + URL_ARG="$(word 6, $(MAKECMDGOALS))"; \ + if [ -z "$$REPO_CMD" ] || [ -z "$$PRESET_ARG" ]; then \ + echo "❌ Ошибка: Укажите команду и пресет"; \ + echo "💡 Пример: make k8s helmrepo list kubernetes"; \ + exit 1; \ + fi; \ + CONTAINER_NAME=k8s-$$PRESET_ARG; \ + if ! docker ps | grep -q $$CONTAINER_NAME; then \ + echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ + echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ + exit 1; \ + fi; \ + case "$$REPO_CMD" in \ + add) \ + if [ -z "$$NAME_ARG" ] || [ -z "$$URL_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя и URL репозитория"; \ + echo "💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ + exit 1; \ + fi; \ + echo "➕ Добавление Helm репозитория: $$NAME_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "helm repo add $$NAME_ARG $$URL_ARG 2>&1 | grep -v '^WARNING' || true; helm repo update";; \ + list) \ + echo "📋 Список Helm репозиториев:"; \ + docker exec $$CONTAINER_NAME bash -c "helm repo list 2>&1 | grep -v '^WARNING' || true";; \ + delete) \ + if [ -z "$$NAME_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя репозитория"; \ + echo "💡 Пример: make k8s helmrepo delete kubernetes stable"; \ + exit 1; \ + fi; \ + echo "🗑️ Удаление Helm репозитория: $$NAME_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "helm repo remove $$NAME_ARG 2>&1 | grep -v '^WARNING' || true";; \ + update) \ + echo "🔄 Обновление Helm репозиториев"; \ + docker exec $$CONTAINER_NAME bash -c "helm repo update 2>&1 | grep -v '^WARNING' || true";; \ + packages) \ + if [ -z "$$NAME_ARG" ]; then \ + echo "❌ Ошибка: Укажите имя репозитория"; \ + echo "💡 Пример: make k8s helmrepo packages kubernetes stable"; \ + exit 1; \ + fi; \ + echo "📦 Пакеты в репозитории: $$NAME_ARG"; \ + docker exec $$CONTAINER_NAME bash -c "helm search repo $$NAME_ARG 2>&1 | grep -v '^WARNING' || true";; \ + *) \ + echo "❌ Неизвестная команда: $$REPO_CMD"; \ + echo "💡 Доступные команды: add, list, delete, update, packages"; \ + exit 1;; \ + esac;; \ *) \ echo "☸️ Доступные команды:"; \ echo ""; \ @@ -1234,6 +1386,18 @@ k8s: echo " 💡 Для: ручного управления kubectl/kind"; \ echo " 💡 Пример: make k8s shell kubernetes"; \ echo ""; \ + echo " make k8s manifest [cmd] [preset] [url] - работа с манифестами"; \ + echo " 💡 Команды: apply, delete, update"; \ + echo " 💡 Пример: make k8s manifest apply kubernetes https://example.com/deploy.yaml"; \ + echo ""; \ + echo " make k8s helm [cmd] [preset] [release] [chart] - работа с Helm"; \ + echo " 💡 Команды: apply, delete, update, rollback, list, status"; \ + echo " 💡 Пример: make k8s helm apply kubernetes nginx stable/nginx-ingress"; \ + echo ""; \ + echo " make k8s helmrepo [cmd] [preset] [name] [url] - работа с Helm репозиториями"; \ + echo " 💡 Команды: add, list, delete, update, packages"; \ + echo " 💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ + echo ""; \ echo "💡 Примеры:"; \ echo " make k8s create # создать минимальный кластер"; \ echo " make k8s create kubernetes # создать кластер с аддонами"; \ diff --git a/docs/kubernetes-commands.md b/docs/kubernetes-commands.md new file mode 100644 index 0000000..5ca5dfb --- /dev/null +++ b/docs/kubernetes-commands.md @@ -0,0 +1,395 @@ +# Команды для работы с Kubernetes + +Автор: Сергей Антропов +Сайт: https://devops.org.ru + +## Содержание + +- [Работа с манифестами](#работа-с-манифестами) +- [Работа с Helm](#работа-с-helm) +- [Работа с Helm репозиториями](#работа-с-helm-репозиториями) + +## Работа с манифестами + +Команды для применения, удаления и обновления манифестов YAML в кластере. + +### Синтаксис + +```bash +make k8s manifest [команда] [пресет] [URL_или_путь_к_файлу] +``` + +### Команды + +#### `apply` - Применение манифеста + +Применяет манифест YAML к кластеру. + +```bash +make k8s manifest apply kubernetes https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml +``` + +**Примеры:** +```bash +# Применение манифеста из URL +make k8s manifest apply kubernetes https://example.com/deploy.yaml + +# Применение локального манифеста +make k8s manifest apply kubernetes ./manifests/my-app.yaml +``` + +#### `delete` - Удаление ресурсов + +Удаляет ресурсы из кластера по манифесту. + +```bash +make k8s manifest delete kubernetes https://example.com/deploy.yaml +``` + +#### `update` - Обновление манифеста + +Обновляет ресурсы в кластере, используя манифест. + +```bash +make k8s manifest update kubernetes https://example.com/deploy.yaml +``` + +### Примеры использования + +```bash +# Установка NGINX Ingress Controller +make k8s manifest apply kubernetes https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml + +# Удаление ресурсов +make k8s manifest delete kubernetes https://example.com/deploy.yaml + +# Обновление конфигурации +make k8s manifest update kubernetes https://example.com/deploy.yaml --force +``` + +## Работа с Helm + +Команды для установки, обновления и управления Helm чартами в кластере. + +### Синтаксис + +```bash +make k8s helm [команда] [пресет] [релиз] [чант] +``` + +### Команды + +#### `apply` - Установка/обновление чарта + +Устанавливает новый релиз или обновляет существующий. + +```bash +make k8s helm apply kubernetes my-nginx nginx/nginx-ingress +``` + +**Параметры:** +- `пресет` - имя пресета кластера +- `релиз` - имя релиза (например: my-nginx) +- `чарт` - имя чарта (например: nginx/nginx-ingress) + +**Примеры:** +```bash +# Установка nginx-ingress +make k8s helm apply kubernetes nginx-ingress nginx/nginx-ingress + +# Установка с указанием репозитория +make k8s helm apply kubernetes prometheus prometheus-community/prometheus +``` + +#### `delete` - Удаление релиза + +Удаляет Helm релиз из кластера. + +```bash +make k8s helm delete kubernetes my-nginx +``` + +**Примеры:** +```bash +# Удаление релиза +make k8s helm delete kubernetes nginx-ingress + +# Удаление с флагом --keep-history +# (не поддерживается в текущей реализации) +``` + +#### `update` - Обновление релиза + +Обновляет существующий Helm релиз. + +```bash +make k8s helm update kubernetes my-nginx nginx/nginx-ingress +``` + +#### `rollback` - Откат релиза + +Откатывает релиз к предыдущей версии. + +```bash +make k8s helm rollback kubernetes my-nginx +``` + +**Примеры:** +```bash +# Откат к предыдущей ревизии +make k8s helm rollback kubernetes my-nginx + +# Откат к конкретной ревизии (не поддерживается в текущей реализации) +``` + +#### `list` - Список релизов + +Показывает список всех установленных Helm релизов. + +```bash +make k8s helm list kubernetes +``` + +**Пример вывода:** +``` +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +nginx-ingress default 1 2024-01-15 12:00:00.000000 +0000 UTC deployed nginx-ingress-0.1.0 1.0.0 +``` + +#### `status` - Статус релиза + +Показывает подробную информацию о статусе релиза. + +```bash +make k8s helm status kubernetes my-nginx +``` + +### Примеры использования + +```bash +# 1. Установка NGINX Ingress Controller +make k8s helm apply kubernetes nginx-ingress nginx/nginx-ingress + +# 2. Проверка статуса +make k8s helm status kubernetes nginx-ingress + +# 3. Обновление релиза +make k8s helm update kubernetes nginx-ingress nginx/nginx-ingress + +# 4. Просмотр списка релизов +make k8s helm list kubernetes + +# 5. Откат релиза +make k8s helm rollback kubernetes nginx-ingress + +# 6. Удаление релиза +make k8s helm delete kubernetes nginx-ingress +``` + +## Работа с Helm репозиториями + +Команды для управления Helm репозиториями. + +### Синтаксис + +```bash +make k8s helmrepo [команда] [пресет] [имя] [URL] +``` + +### Команды + +#### `add` - Добавление репозитория + +Добавляет новый Helm репозиторий. + +```bash +make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable +``` + +**Примеры:** +```bash +# Добавление официального репозитория Helm +make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable + +# Добавление репозитория Bitnami +make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami + +# Добавление репозитория NGINX +make k8s helmrepo add kubernetes nginx https://helm.nginx.com/stable + +# Добавление репозитория Prometheus +make k8s helmrepo add kubernetes prometheus-community https://prometheus-community.github.io/helm-charts + +# Добавление пользовательского репозитория +make k8s helmrepo add kubernetes my-repo https://charts.example.com +``` + +#### `list` - Список репозиториев + +Показывает список всех добавленных Helm репозиториев. + +```bash +make k8s helmrepo list kubernetes +``` + +**Пример вывода:** +``` +NAME URL +stable https://charts.helm.sh/stable +bitnami https://charts.bitnami.com/bitnami +nginx https://helm.nginx.com/stable +``` + +#### `delete` - Удаление репозитория + +Удаляет Helm репозиторий. + +```bash +make k8s helmrepo delete kubernetes stable +``` + +**Примеры:** +```bash +# Удаление репозитория +make k8s helmrepo delete kubernetes stable +``` + +#### `update` - Обновление репозиториев + +Обновляет информацию о всех Helm репозиториях. + +```bash +make k8s helmrepo update kubernetes +``` + +**Примеры:** +```bash +# Обновление всех репозиториев +make k8s helmrepo update kubernetes +``` + +#### `packages` - Список пакетов + +Показывает список пакетов в указанном репозитории. + +```bash +make k8s helmrepo packages kubernetes stable +``` + +**Примеры:** +```bash +# Просмотр пакетов в репозитории stable +make k8s helmrepo packages kubernetes stable + +# Просмотр пакетов в репозитории nginx +make k8s helmrepo packages kubernetes nginx + +# Поиск конкретного пакета +make k8s helmrepo packages kubernetes stable | grep nginx +``` + +### Примеры использования + +```bash +# 1. Добавление нескольких репозиториев +make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable +make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami +make k8s helmrepo add kubernetes nginx https://helm.nginx.com/stable + +# 2. Просмотр списка репозиториев +make k8s helmrepo list kubernetes + +# 3. Обновление репозиториев +make k8s helmrepo update kubernetes + +# 4. Поиск пакетов в репозитории +make k8s helmrepo packages kubernetes stable + +# 5. Удаление репозитория +make k8s helmrepo delete kubernetes my-custom-repo + +# 6. Установка пакета из добавленного репозитория +make k8s helm apply kubernetes my-nginx nginx/nginx-ingress +``` + +## Полный рабочий пример + +```bash +# 1. Создание кластера +make k8s create kubernetes + +# 2. Добавление Helm репозиториев +make k8s helmrepo add kubernetes nginx https://helm.nginx.com/stable +make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami + +# 3. Обновление репозиториев +make k8s helmrepo update kubernetes + +# 4. Просмотр доступных пакетов +make k8s helmrepo packages kubernetes nginx + +# 5. Установка NGINX Ingress Controller +make k8s helm apply kubernetes nginx-ingress nginx/nginx-ingress + +# 6. Проверка статуса +make k8s helm status kubernetes nginx-ingress + +# 7. Просмотр списка релизов +make k8s helm list kubernetes + +# 8. Применение манифеста +make k8s manifest apply kubernetes https://example.com/deploy.yaml + +# 9. Откат релиза (если нужно) +make k8s helm rollback kubernetes nginx-ingress + +# 10. Удаление релиза +make k8s helm delete kubernetes nginx-ingress + +# 11. Удаление репозитория +make k8s helmrepo delete kubernetes nginx + +# 12. Удаление кластера +make k8s destroy kubernetes +``` + +## Обработка ошибок + +### Ошибка: Контейнер не запущен + +``` +❌ Контейнер k8s-kubernetes не запущен +💡 Запустите: make k8s create kubernetes +``` + +**Решение:** Запустите кластер перед выполнением команд. + +### Ошибка: Неизвестная команда + +``` +❌ Неизвестная команда: unknown +💡 Доступные команды: apply, delete, update +``` + +**Решение:** Используйте правильную команду из списка доступных. + +### Ошибка: Не указаны параметры + +``` +❌ Ошибка: Укажите имя релиза и чарт +💡 Пример: make k8s helm apply kubernetes my-release stable/nginx-ingress +``` + +**Решение:** Укажите все необходимые параметры команды. + +## Дополнительная информация + +- Все команды kubectl и helm выполняются внутри контейнера `k8s-[пресет]` +- Вам не нужно устанавливать kubectl или helm локально +- Подключение к кластеру происходит через имя узла control-plane +- Используется флаг `--insecure-skip-tls-verify` для обхода проблем с сертификатами + +## Автор + +Сергей Антропов +Сайт: https://devops.org.ru From 1b6c83d94176df4e58d898f9f1ca6a7b4ffa7bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 03:37:12 +0300 Subject: [PATCH 04/41] =?UTF-8?q?docs:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D1=80=D1=83=D0=BA=D0=BE=D0=B2=D0=BE=D0=B4=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=BE=20=D0=BF=D0=BE=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B5=20?= =?UTF-8?q?=D1=81=20Kubernetes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создано подробное руководство docs/kubernetes-full-guide.md - Описаны все аспекты работы с Kubernetes кластерами - Добавлены примеры использования манифестов, Helm, Ingress - Подробно описана работа с мониторингом (Prometheus, Grafana) - Документирована работа с Service Mesh (Istio, Kiali) - Добавлены примеры полных развертываний - Включены разделы по безопасности и отладке - Документация на русском языке с большим количеством примеров --- docs/kubernetes-full-guide.md | 906 ++++++++++++++++++++++++++++++++++ 1 file changed, 906 insertions(+) create mode 100644 docs/kubernetes-full-guide.md diff --git a/docs/kubernetes-full-guide.md b/docs/kubernetes-full-guide.md new file mode 100644 index 0000000..4cc43b5 --- /dev/null +++ b/docs/kubernetes-full-guide.md @@ -0,0 +1,906 @@ +# Полное руководство по работе с Kubernetes кластерами + +Автор: Сергей Антропов +Сайт: https://devops.org.ru + +## Содержание + +- [Введение](#введение) +- [Создание кластера](#создание-кластера) +- [Управление кластером](#управление-кластером) +- [Работа с манифестами](#работа-с-манифестами) +- [Работа с Helm](#работа-с-helm) +- [Настройка Ingress](#настройка-ingress) +- [Мониторинг и аддоны](#мониторинг-и-аддоны) +- [Service Mesh с Istio](#service-mesh-с-istio) +- [Примеры полных развертываний](#примеры-полных-развертываний) + +## Введение + +AnsibleLab предоставляет полную поддержку создания и управления локальными Kubernetes кластерами на основе Kind (Kubernetes in Docker). Kind позволяет запускать Kubernetes кластеры внутри Docker контейнеров, что идеально подходит для разработки, тестирования и обучения. + +### Основные возможности + +- Создание многоузловых Kubernetes кластеров +- Установка и управление Helm чартами +- Работа с манифестами YAML +- Настройка Ingress контроллеров +- Установка систем мониторинга (Prometheus, Grafana) +- Развертывание Service Mesh (Istio, Kiali) +- Изоляция сети и безопасность + +### Преимущества + +- Не требует установки локального Kubernetes +- Быстрое создание и удаление кластеров +- Поддержка многоузловых конфигураций +- Полная совместимость с production окружениями +- Контейнеризация всех инструментов + +## Создание кластера + +### Базовое создание + +Простое создание минимального кластера без дополнительных компонентов: + +```bash +make k8s create +``` + +Создаст кластер с пресетом `k8s-minimal` - один control-plane узел без дополнительных компонентов. + +### Создание полнофункционального кластера + +Создание кластера с предустановленными компонентами: + +```bash +make k8s create kubernetes +``` + +Этот пресет создает кластер со следующими компонентами: +- 1 control-plane узел +- 2 worker узла +- Ingress NGINX Controller +- Metrics Server +- Istio Service Mesh +- Kiali для визуализации Istio +- Prometheus Stack (Prometheus + Grafana) + +### Конфигурация пресета + +Пресеты находятся в `molecule/presets/k8s/`. Пример конфигурации: + +```yaml +kind_clusters: + - name: lab + workers: 2 + api_port: 6443 + addons: + ingress_nginx: true + metrics_server: true + istio: true + kiali: true + prometheus_stack: true + ingress_host_http_port: 8081 + ingress_host_https_port: 8443 + addon_ports: + prometheus: 9090 + grafana: 3000 + kiali: 20001 +``` + +### Проверка статуса кластера + +```bash +# Показать узлы кластера +make k8s nodes kubernetes + +# Вывод: +# NAME STATUS ROLES AGE VERSION +# lab-control-plane Ready control-plane 5m v1.34.0 +# lab-worker Ready 4m v1.34.0 +# lab-worker2 Ready 4m v1.34.0 + +# Показать подробный статус +make k8s status kubernetes +``` + +### Подключение к кластеру + +```bash +# Получить kubeconfig +make k8s config kubernetes + +# Использовать kubeconfig +export KUBECONFIG=$(pwd)/kubeconfig +kubectl get nodes +``` + +## Управление кластером + +### Остановка и запуск + +```bash +# Остановить кластер (без удаления) +make k8s stop kubernetes + +# Запустить остановленный кластер +make k8s start kubernetes + +# Перезапустить кластер +make k8s stop kubernetes && make k8s start kubernetes +``` + +### Удаление кластера + +```bash +# Полное удаление кластера и контейнера +make k8s destroy kubernetes +``` + +### Получение shell в контейнере + +```bash +# Открыть интерактивный shell +make k8s shell kubernetes + +# Теперь доступны все команды kubectl, helm, kind внутри контейнера +kubectl get nodes +helm list +kind get clusters +``` + +## Работа с манифестами + +### Применение манифеста + +Применение манифеста из URL: + +```bash +make k8s manifest apply kubernetes https://example.com/deploy.yaml +``` + +Применение локального манифеста: + +```bash +# Создайте файл example-deployment.yaml +cat > example-deployment.yaml < wordpress.yaml < app.yaml < + +# Просмотр логов с follow +kubectl --kubeconfig kubeconfig logs -f + +# Выполнить команду в поде +kubectl --kubeconfig kubeconfig exec -it -- /bin/sh + +# Описание ресурса +kubectl --kubeconfig kubeconfig describe pod +kubectl --kubeconfig kubeconfig describe service +``` + +### Масштабирование + +```bash +# Масштабировать Deployment +kubectl --kubeconfig kubeconfig scale deployment --replicas=5 + +# Автомасштабирование (требует metrics-server) +kubectl --kubeconfig kubeconfig autoscale deployment \ + --cpu-percent=70 --min=2 --max=10 +``` + +### Экспорт конфигурации + +```bash +# Экспортировать ресурс в YAML +kubectl --kubeconfig kubeconfig get deployment -o yaml > exported.yaml + +# Экспортировать все ресурсы namespace +kubectl --kubeconfig kubeconfig get all -n -o yaml > all-resources.yaml +``` + +## Безопасность + +### Использование Secrets + +```bash +# Создать Secret из файла +kubectl --kubeconfig kubeconfig create secret generic my-secret \ + --from-file=username=./username.txt \ + --from-file=password=./password.txt + +# Создать Secret из литерала +kubectl --kubeconfig kubeconfig create secret generic my-secret \ + --from-literal=username=admin \ + --from-literal=password=secret123 +``` + +### Network Policies + +Создайте файл `network-policy.yaml`: + +```yaml +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: test-network-policy + namespace: default +spec: + podSelector: + matchLabels: + role: db + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: + matchLabels: + role: api + ports: + - protocol: TCP + port: 5432 + egress: + - to: + - podSelector: + matchLabels: + role: api + ports: + - protocol: TCP + port: 5432 +``` + +Примените: + +```bash +make k8s manifest apply kubernetes ./network-policy.yaml +``` + +## Заключение + +AnsibleLab предоставляет полный набор инструментов для работы с Kubernetes кластерами локально. Вы можете: + +- Создавать и управлять кластерами +- Устанавливать и настраивать приложения +- Работать с мониторингом и Service Mesh +- Тестировать перед развертыванием в production + +Все инструменты работают внутри Docker контейнеров, что обеспечивает изоляцию и переносимость. + +## Автор + +Сергей Антропов +Сайт: https://devops.org.ru From 60c2623fbc72ffc326a47a92147ae9b8facfdee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:15:44 +0300 Subject: [PATCH 05/41] =?UTF-8?q?refactor:=20=D1=83=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B8=20Kind=20=D0=B8=D0=B7=20cr?= =?UTF-8?q?eate.yml=20=D0=B8=20destroy.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалены все задачи по созданию Kind кластеров из create.yml - Удалены все задачи по удалению Kind кластеров из destroy.yml - Добавлены комментарии о том, что все операции с Kind выполняются через make k8s - Теперь Kind кластеры полностью управляются через Python скрипт create_k8s_cluster.py --- molecule/default/create.yml | 132 +---------------------------------- molecule/default/destroy.yml | 17 +---- 2 files changed, 4 insertions(+), 145 deletions(-) diff --git a/molecule/default/create.yml b/molecule/default/create.yml index 4ad0db2..4e25d02 100644 --- a/molecule/default/create.yml +++ b/molecule/default/create.yml @@ -285,133 +285,5 @@ - DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} - # ---------- Kind clusters (если определены) ---------- - - name: Create kind cluster configs - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - mkdir -p /ansible/.kind; - cat > /ansible/.kind/{{ item.name }}.yaml < 0 - - - name: Create kind clusters - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - set -e; - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - if kind get clusters | grep -qx "$$n"; then - echo "[kind] cluster $$n already exists"; - else - echo "[kind] creating $$n"; - kind create cluster --name "$$n" --config "/ansible/.kind/$$n.yaml"; - fi - done - ' - when: (kind_clusters | default([])) | length > 0 - - - name: Install Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack (per cluster, if enabled) - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - set -e; - helm repo add kiali https://kiali.org/helm-charts >/dev/null 2>&1 || true; - helm repo add prometheus-community https://prometheus-community.github.io/helm-charts >/dev/null 2>&1 || true; - helm repo update >/dev/null 2>&1 || true; - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - # ingress-nginx - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("ingress_nginx", False) | to_json }}; then - echo "[addons] ingress-nginx on $$n"; - kubectl --context kind-$$n apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml || true; - kubectl --context kind-$$n -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s || true; - fi - # metrics-server - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("metrics_server", False) | to_json }}; then - echo "[addons] metrics-server on $$n"; - kubectl --context kind-$$n apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml || true; - kubectl --context kind-$$n -n kube-system patch deploy metrics-server -p \ - "{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"metrics-server\",\"args\":[\"--kubelet-insecure-tls\",\"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\"]}]}}}}}" || true; - fi - # istio (demo profile) - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("istio", False) | to_json }}; then - echo "[addons] istio (demo profile) on $$n"; - istioctl install -y --set profile=demo --context kind-$$n; - kubectl --context kind-$$n -n istio-system rollout status deploy/istiod --timeout=180s || true; - kubectl --context kind-$$n -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s || true; - fi - # kiali (server chart, anonymous auth) — требует istio/metrics - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("kiali", False) | to_json }}; then - echo "[addons] kiali on $$n"; - kubectl --context kind-$$n create ns istio-system >/dev/null 2>&1 || true; - helm upgrade --install kiali-server kiali/kiali-server \ - --namespace istio-system --kube-context kind-$$n \ - --set auth.strategy=anonymous --wait --timeout 180s; - fi - # kube-prometheus-stack (Prometheus + Grafana) - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("prometheus_stack", False) | to_json }}; then - echo "[addons] kube-prometheus-stack on $$n"; - kubectl --context kind-$$n create ns monitoring >/dev/null 2>&1 || true; - helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ - --namespace monitoring --kube-context kind-$$n \ - --set grafana.adminPassword=admin \ - --set grafana.defaultDashboardsTimezone=browser \ - --wait --timeout 600s; - # дождаться графаны - kubectl --context kind-$$n -n monitoring rollout status deploy/monitoring-grafana --timeout=300s || true; - fi - done - ' - when: (kind_clusters | default([])) | length > 0 - - - name: Setup NodePort for addons - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - {% for cluster in kind_clusters | default([]) %} - {% if cluster.addon_ports is defined %} - if [ "$$n" = "{{ cluster.name }}" ]; then - {% if cluster.addon_ports.prometheus is defined %} - echo "[ports] Prometheus: {{ cluster.addon_ports.prometheus }}"; - kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.prometheus }}}]' 2>/dev/null || true; - {% endif %} - {% if cluster.addon_ports.grafana is defined %} - echo "[ports] Grafana: {{ cluster.addon_ports.grafana }}"; - kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-grafana --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.grafana }}}]' 2>/dev/null || true; - {% endif %} - {% if cluster.addon_ports.kiali is defined %} - echo "[ports] Kiali: {{ cluster.addon_ports.kiali }}"; - kubectl --context kind-{{ cluster.name }} patch svc -n istio-system kiali --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.kiali }}}]' 2>/dev/null || true; - {% endif %} - fi - {% endif %} - {% endfor %} - done - ' - when: (kind_clusters | default([])) | length > 0 \ No newline at end of file + # ---------- Kind clusters создаются через Python скрипт create_k8s_cluster.py ---------- + # Все задачи по созданию кластеров и установке аддонов выполняются через make k8s create \ No newline at end of file diff --git a/molecule/default/destroy.yml b/molecule/default/destroy.yml index f1b0aad..b29137f 100644 --- a/molecule/default/destroy.yml +++ b/molecule/default/destroy.yml @@ -76,21 +76,8 @@ # Используем переменную hosts из загруженного пресета hosts: "{{ hosts }}" - - name: Remove kind clusters - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - set -e; - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - if kind get clusters | grep -qx "$$n"; then - echo "[kind] deleting $$n"; - kind delete cluster --name "$$n" || true; - fi - done - ' - when: (kind_clusters | default([])) | length > 0 - ignore_errors: true + # ---------- Kind clusters удаляются через make k8s destroy ---------- + # Все задачи по удалению кластеров выполняются через команду make k8s destroy - name: Display cleanup summary debug: From 4ed9c2e0eb19450af64d820bbe8120c58c13d454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:23:43 +0300 Subject: [PATCH 06/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B8=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B8=D0=B7=20=D0=BF=D1=80=D0=B5=D1=81=D0=B5?= =?UTF-8?q?=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлено создание контейнеров из секции hosts в create_k8s_cluster.py - Добавлено удаление контейнеров в команде make k8s destroy - Создан скрипт scripts/delete_hosts.py для удаления контейнеров - Контейнеры автоматически создаются в Docker сети из пресета - Контейнеры удаляются вместе с Kind кластером при make k8s destroy --- Makefile | 9 +- molecule/default/create.yml | 3 - molecule/default/create.yml.bak | 428 ---------------------------- molecule/default/destroy.yml | 3 - molecule/presets/k8s/kubernetes.yml | 9 +- scripts/create_k8s_cluster.py | 69 ++++- scripts/delete_hosts.py | 44 +++ 7 files changed, 127 insertions(+), 438 deletions(-) delete mode 100644 molecule/default/create.yml.bak create mode 100644 scripts/delete_hosts.py diff --git a/Makefile b/Makefile index 8b3a87b..36249aa 100644 --- a/Makefile +++ b/Makefile @@ -1052,17 +1052,22 @@ k8s: echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ - echo "🗑️ Удаление Kind кластера..."; \ + echo "🗑️ Удаление Kind кластера и контейнеров..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ PRESET=$${PRESET_ARG:-k8s-minimal}; \ CONTAINER_NAME=k8s-$$PRESET; \ if docker ps | grep -q $$CONTAINER_NAME; then \ + echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ else \ echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ fi; \ docker rm -f $$CONTAINER_NAME 2>/dev/null || true; \ - echo "✅ Kind кластер удален";; \ + echo "🗑️ Удаление контейнеров из пресета..."; \ + if [ -f "molecule/presets/k8s/$$PRESET.yml" ]; then \ + python3 scripts/delete_hosts.py molecule/presets/k8s/$$PRESET.yml 2>/dev/null || true; \ + fi; \ + echo "✅ Удаление завершено";; \ stop) \ echo "🛑 Остановка Kind кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ diff --git a/molecule/default/create.yml b/molecule/default/create.yml index 4e25d02..2ce934b 100644 --- a/molecule/default/create.yml +++ b/molecule/default/create.yml @@ -284,6 +284,3 @@ - Systemd nodes: {{ hosts | selectattr('type','undefined') | list | length }} - DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} - - # ---------- Kind clusters создаются через Python скрипт create_k8s_cluster.py ---------- - # Все задачи по созданию кластеров и установке аддонов выполняются через make k8s create \ No newline at end of file diff --git a/molecule/default/create.yml.bak b/molecule/default/create.yml.bak deleted file mode 100644 index 4dc04c6..0000000 --- a/molecule/default/create.yml.bak +++ /dev/null @@ -1,428 +0,0 @@ ---- -- hosts: localhost - gather_facts: false - vars: - # Получаем preset из переменной окружения или используем default - preset_name: "{{ lookup('env', 'MOLECULE_PRESET') | default('default') }}" - # Проверяем сначала в папке k8s, затем в основной папке presets - preset_file: "{{ '/workspace/molecule/presets/k8s/' + preset_name + '.yml' if (preset_name in ['k8s-minimal', 'kubernetes', 'k8s-full'] or preset_name.startswith('k8s-')) else '/workspace/molecule/presets/' + preset_name + '.yml' }}" - - # Fallback значения если preset файл не найден - docker_network: labnet - generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini" - images: - alt: "inecs/ansible-lab:alt-linux-latest" - astra: "inecs/ansible-lab:astra-linux-latest" - rhel: "inecs/ansible-lab:rhel-latest" - centos: "inecs/ansible-lab:centos-latest" - alma: "inecs/ansible-lab:alma-latest" - rocky: "inecs/ansible-lab:rocky-latest" - redos: "inecs/ansible-lab:redos-latest" - ubuntu: "inecs/ansible-lab:ubuntu-latest" - debian: "inecs/ansible-lab:debian-latest" - systemd_defaults: - privileged: true - command: "/sbin/init" - volumes: - - "/sys/fs/cgroup:/sys/fs/cgroup:rw" - tmpfs: ["/run", "/run/lock"] - capabilities: ["SYS_ADMIN"] - hosts: - - name: u1 - family: debian - groups: [test] - kind_clusters: [] - - tasks: -# - name: Install required collections -# command: ansible-galaxy collection install -r /workspace/requirements.yml -# delegate_to: localhost -# ignore_errors: true -# register: collections_install -# changed_when: false -# run_once: true -# become: true -# vars: -# ansible_python_interpreter: /usr/bin/python3 -# environment: -# ANSIBLE_COLLECTIONS_PATH: /usr/share/ansible/collections - - # Определяем архитектуру системы для корректной загрузки образов - - name: Detect system architecture - shell: | - arch=$(uname -m) - case $arch in - x86_64) echo "linux/amd64" ;; - aarch64|arm64) echo "linux/arm64" ;; - armv7l) echo "linux/arm/v7" ;; - *) echo "linux/amd64" ;; - esac - register: detected_platform - changed_when: false - - - name: Set ansible_architecture variable - set_fact: - ansible_architecture: "{{ detected_platform.stdout }}" - - - name: Load preset configuration - include_vars: "{{ preset_file }}" - when: preset_file is file - ignore_errors: true - - # Фильтрация хостов по поддерживаемым платформам - - name: Filter hosts by supported platforms - set_fact: - filtered_hosts: "{{ filtered_hosts | default([]) + [item] }}" - loop: "{{ hosts }}" - when: | - item.supported_platforms is not defined or - ansible_architecture in item.supported_platforms - - - name: Update hosts list with filtered results - set_fact: - hosts: "{{ filtered_hosts | default(hosts) }}" - - - name: Display filtered hosts - debug: - msg: "Platform {{ ansible_architecture }}: {{ hosts | length }} hosts will be deployed" - - - name: Ensure network exists - community.docker.docker_network: - name: "{{ docker_network }}" - state: present - - # SYSTEMD nodes - - name: Pull systemd images with correct platform - command: "docker pull --platform {{ ansible_architecture }} {{ images[item.family] }}" - loop: "{{ hosts | selectattr('type','undefined') | list }}" - loop_control: { label: "{{ item.name }}" } - when: item.family is defined and images[item.family] is defined - register: pull_result - ignore_errors: yes - - - name: Display pull results - debug: - msg: "Pulled {{ item.item.name }}: {{ 'OK' if (item.rc is defined and item.rc == 0) else 'SKIPPED (not available for this platform)' }}" - loop: "{{ pull_result.results | default([]) }}" - loop_control: - label: "{{ item.item.name }}" - - - name: Start systemd nodes - community.docker.docker_container: - name: "{{ item.name }}" - image: "{{ images[item.family] }}" - networks: - - name: "{{ docker_network }}" - privileged: "{{ systemd_defaults.privileged }}" - command: "{{ systemd_defaults.command }}" - volumes: "{{ systemd_defaults.volumes | default([]) + (item.volumes | default([])) }}" - tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}" - capabilities: "{{ systemd_defaults.capabilities | default([]) }}" - published_ports: "{{ item.publish | default([]) }}" - env: "{{ item.env | default({}) }}" - # Специальные настройки для Astra Linux и RedOS (для совместимости с amd64 базовыми образами) - security_opts: "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}" - platform: "{{ 'linux/amd64' if item.family in ['astra', 'redos'] else omit }}" - state: started - restart_policy: unless-stopped - loop: "{{ hosts | selectattr('type','undefined') | list }}" - loop_control: { label: "{{ item.name }}" } - when: item.family is defined and images[item.family] is defined - - # Ожидание стабилизации контейнеров - - name: Wait for containers to be ready - pause: - seconds: 5 - when: hosts | length > 0 - - # Создание tmp директории в контейнерах - - name: Create Ansible tmp directory in containers - community.docker.docker_container_exec: - container: "{{ item.name }}" - command: "mkdir -p /tmp/.ansible-tmp && chmod 755 /tmp/.ansible-tmp" - loop: "{{ hosts | selectattr('type','undefined') | list }}" - loop_control: { label: "{{ item.name }}" } - when: item.family is defined and images[item.family] is defined - ignore_errors: true - retries: 3 - delay: 2 - - # DinD nodes - - name: Start DinD nodes (docker:27-dind) - community.docker.docker_container: - name: "{{ item.name }}" - image: "docker:27-dind" - networks: - - name: "{{ docker_network }}" - privileged: true - env: - DOCKER_TLS_CERTDIR: "" - published_ports: "{{ item.publish | default([]) }}" - volumes: "{{ (item.volumes | default([])) + [item.name + '-docker:/var/lib/docker'] }}" - state: started - restart_policy: unless-stopped - loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}" - loop_control: { label: "{{ item.name }}" } - - # DOoD nodes (mount docker.sock) - - name: Start DOoD nodes (systemd + docker.sock mount) - community.docker.docker_container: - name: "{{ item.name }}" - image: "{{ images[item.family] }}" - networks: - - name: "{{ docker_network }}" - privileged: "{{ systemd_defaults.privileged }}" - command: "{{ systemd_defaults.command }}" - volumes: "{{ (systemd_defaults.volumes | default([])) + ['/var/run/docker.sock:/var/run/docker.sock'] + (item.volumes | default([])) }}" - tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}" - capabilities: "{{ systemd_defaults.capabilities | default([]) }}" - published_ports: "{{ item.publish | default([]) }}" - env: "{{ item.env | default({}) }}" - state: started - restart_policy: unless-stopped - loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list }}" - loop_control: { label: "{{ item.name }}" } - when: item.family is defined and images[item.family] is defined - - # Build groups map - - name: Initialize groups map - set_fact: - groups_map: {} - - - name: Append hosts to groups - set_fact: - groups_map: "{{ groups_map | combine({ item_group: (groups_map[item_group] | default([])) + [item_name] }) }}" - loop: "{{ hosts | subelements('groups', skip_missing=True) }}" - loop_control: - label: "{{ item.0.name }}" - vars: - item_name: "{{ item.0.name }}" - item_group: "{{ item.1 }}" - - # Render inventory - - name: Render inventory ini - set_fact: - inv_content: | - [all:vars] - ansible_connection=community.docker.docker - ansible_remote_tmp=/tmp/.ansible-tmp - - {% for group, members in (groups_map | dictsort) %} - [{{ group }}] - {% for h in members %}{{ h }} - {% endfor %} - - {% endfor %} - [all] - {% for h in hosts %}{{ h.name }} - {% endfor %} - - {# Группа с Debian-based системами (Debian, Ubuntu, Alt) - используем /usr/bin/python3 #} - {% set debian_hosts = [] %} - {% for h in hosts %} - {% if h.family in ['ubuntu', 'debian', 'alt'] %} - {% set _ = debian_hosts.append(h.name) %} - {% endif %} - {% endfor %} - {% if debian_hosts %} - [debian_family:vars] - ansible_python_interpreter=/usr/bin/python3 - - [debian_family] - {% for h in debian_hosts %}{{ h }} - {% endfor %} - {% endif %} - - {# Группа с RHEL-based системами (RHEL, CentOS, Alma, Rocky, RedOS) #} - {% set rhel_hosts = [] %} - {% for h in hosts %} - {% if h.family in ['rhel', 'centos', 'alma', 'rocky', 'redos'] %} - {% set _ = rhel_hosts.append(h.name) %} - {% endif %} - {% endfor %} - {% if rhel_hosts %} - [rhel_family:vars] - ansible_python_interpreter=/usr/bin/python3 - - [rhel_family] - {% for h in rhel_hosts %}{{ h }} - {% endfor %} - {% endif %} - - {# Astra Linux - используем /usr/bin/python3 #} - {% set astra_hosts = [] %} - {% for h in hosts %} - {% if h.family == 'astra' %} - {% set _ = astra_hosts.append(h.name) %} - {% endif %} - {% endfor %} - {% if astra_hosts %} - [astra_family:vars] - ansible_python_interpreter=/usr/bin/python3 - - [astra_family] - {% for h in astra_hosts %}{{ h }} - {% endfor %} - {% endif %} - - {# Глобальный fallback для остальных хостов #} - [unmatched_hosts:vars] - ansible_python_interpreter=auto_silent - - - name: Ensure inventory directory exists - file: - path: "{{ generated_inventory | dirname }}" - state: directory - mode: "0755" - - - name: Write inventory file - copy: - dest: "{{ generated_inventory }}" - content: "{{ inv_content }}" - mode: "0644" - - - name: Display inventory summary - debug: - msg: | - 📋 Inventory Summary: - - Total hosts: {{ hosts | length }} - - Groups: {{ groups_map.keys() | list | join(', ') }} - - Systemd nodes: {{ hosts | selectattr('type','undefined') | list | length }} - - DinD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - - DOoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} - - # ---------- Kind clusters (если определены) ---------- - - name: Prepare kind cluster configs - set_fact: - kind_config_content: | - kind: Cluster - apiVersion: kind.x-k8s.io/v1alpha4 - nodes: - - role: control-plane - {% if (item.addons|default({})).ingress_nginx|default(false) %} - extraPortMappings: - - containerPort: 80 - hostPort: {{ item.ingress_host_http_port | default(8081) }} - protocol: TCP - - containerPort: 443 - hostPort: {{ item.ingress_host_https_port | default(8443) }} - protocol: TCP - {% endif %} - {% for i in range(item.workers | default(0)) %} - - role: worker - {% endfor %} - networking: - apiServerAddress: "0.0.0.0" - apiServerPort: {{ item.api_port | default(0) }} - loop: "{{ kind_clusters | default([]) }}" - when: (kind_clusters | default([])) | length > 0 - - - name: Create kind cluster configs - community.docker.docker_container_exec: - container: "{{ ansible_controller_container | default('ansible-controller') }}" - command: > - bash -c " - mkdir -p /ansible/.kind; - echo '{{ kind_config_content }}' > /ansible/.kind/{{ item.name }}.yaml - " - loop: "{{ kind_clusters | default([]) }}" - when: (kind_clusters | default([])) | length > 0 - - - name: Create kind clusters - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - set -e; - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - if kind get clusters | grep -qx "$$n"; then - echo "[kind] cluster $$n already exists"; - else - echo "[kind] creating $$n"; - kind create cluster --name "$$n" --config "/ansible/.kind/$$n.yaml"; - fi - done - ' - when: (kind_clusters | default([])) | length > 0 - - - name: Install Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack (per cluster, if enabled) - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - set -e; - helm repo add kiali https://kiali.org/helm-charts >/dev/null 2>&1 || true; - helm repo add prometheus-community https://prometheus-community.github.io/helm-charts >/dev/null 2>&1 || true; - helm repo update >/dev/null 2>&1 || true; - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - # ingress-nginx - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("ingress_nginx", False) | to_json }}; then - echo "[addons] ingress-nginx on $$n"; - kubectl --context kind-$$n apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml || true; - kubectl --context kind-$$n -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s || true; - fi - # metrics-server - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("metrics_server", False) | to_json }}; then - echo "[addons] metrics-server on $$n"; - kubectl --context kind-$$n apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml || true; - kubectl --context kind-$$n -n kube-system patch deploy metrics-server -p \ - "{\"spec\":{\"template\":{\"spec\":{\"containers\":[{\"name\":\"metrics-server\",\"args\":[\"--kubelet-insecure-tls\",\"--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname\"]}]}}}}}" || true; - fi - # istio (demo profile) - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("istio", False) | to_json }}; then - echo "[addons] istio (demo profile) on $$n"; - istioctl install -y --set profile=demo --context kind-$$n; - kubectl --context kind-$$n -n istio-system rollout status deploy/istiod --timeout=180s || true; - kubectl --context kind-$$n -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s || true; - fi - # kiali (server chart, anonymous auth) — требует istio/metrics - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("kiali", False) | to_json }}; then - echo "[addons] kiali on $$n"; - kubectl --context kind-$$n create ns istio-system >/dev/null 2>&1 || true; - helm upgrade --install kiali-server kiali/kiali-server \ - --namespace istio-system --kube-context kind-$$n \ - --set auth.strategy=anonymous --wait --timeout 180s; - fi - # kube-prometheus-stack (Prometheus + Grafana) - if {{ (kind_clusters | items2dict(key_name="name", value_name="addons")).get(n, {}).get("prometheus_stack", False) | to_json }}; then - echo "[addons] kube-prometheus-stack on $$n"; - kubectl --context kind-$$n create ns monitoring >/dev/null 2>&1 || true; - helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ - --namespace monitoring --kube-context kind-$$n \ - --set grafana.adminPassword=admin \ - --set grafana.defaultDashboardsTimezone=browser \ - --wait --timeout 600s; - # дождаться графаны - kubectl --context kind-$$n -n monitoring rollout status deploy/monitoring-grafana --timeout=300s || true; - fi - done - ' - when: (kind_clusters | default([])) | length > 0 - - - name: Setup NodePort for addons - community.docker.docker_container_exec: - container: ansible-controller - command: > - bash -lc ' - for n in {{ (kind_clusters | default([]) | map(attribute="name") | list) | map('quote') | join(' ') }}; do - {% for cluster in kind_clusters | default([]) %} - {% if cluster.addon_ports is defined %} - if [ "$$n" = "{{ cluster.name }}" ]; then - {% if cluster.addon_ports.prometheus is defined %} - echo "[ports] Prometheus: {{ cluster.addon_ports.prometheus }}"; - kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.prometheus }}}]' 2>/dev/null || true; - {% endif %} - {% if cluster.addon_ports.grafana is defined %} - echo "[ports] Grafana: {{ cluster.addon_ports.grafana }}"; - kubectl --context kind-{{ cluster.name }} patch svc -n monitoring monitoring-grafana --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.grafana }}}]' 2>/dev/null || true; - {% endif %} - {% if cluster.addon_ports.kiali is defined %} - echo "[ports] Kiali: {{ cluster.addon_ports.kiali }}"; - kubectl --context kind-{{ cluster.name }} patch svc -n istio-system kiali --type='json' -p='[{"op": "replace", "path": "/spec/type", "value":"NodePort"},{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{{ cluster.addon_ports.kiali }}}]' 2>/dev/null || true; - {% endif %} - fi - {% endif %} - {% endfor %} - done - ' - when: (kind_clusters | default([])) | length > 0 \ No newline at end of file diff --git a/molecule/default/destroy.yml b/molecule/default/destroy.yml index b29137f..587b37d 100644 --- a/molecule/default/destroy.yml +++ b/molecule/default/destroy.yml @@ -76,9 +76,6 @@ # Используем переменную hosts из загруженного пресета hosts: "{{ hosts }}" - # ---------- Kind clusters удаляются через make k8s destroy ---------- - # Все задачи по удалению кластеров выполняются через команду make k8s destroy - - name: Display cleanup summary debug: msg: | diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index dc6737c..16bd82b 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -56,4 +56,11 @@ kind_clusters: grafana: 3000 kiali: 20001 -hosts: [] +hosts: + # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) + - name: u1 + family: ubuntu22 + groups: [test, web] + - name: u2 + family: debian12 + groups: [test, web] diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 78c534f..8fcb450 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -31,9 +31,76 @@ def main(): with open(preset_file, 'r') as f: preset = yaml.safe_load(f) + # Создаем Docker сеть если её нет + docker_network = preset.get('docker_network', 'labnet') + print(f"\n🌐 Проверка Docker сети: {docker_network}") + result = subprocess.run(f"docker network ls --format '{{{{.Name}}}}' | grep -x {docker_network}", + shell=True, capture_output=True, text=True) + if not result.stdout.strip(): + print(f"📡 Создание Docker сети: {docker_network}") + run_cmd(f"docker network create {docker_network}") + else: + print(f"✅ Сеть {docker_network} уже существует") + + # Получаем конфигурацию для hosts + hosts = preset.get('hosts', []) + images = preset.get('images', {}) + systemd_defaults = preset.get('systemd_defaults', {}) + + # Создаем контейнеры если определены hosts + if hosts: + print(f"\n🐳 Создание контейнеров (всего: {len(hosts)})") + for host in hosts: + host_name = host['name'] + family = host['family'] + + # Проверяем существование контейнера + result = subprocess.run(f"docker ps -a --format '{{{{.Names}}}}' | grep -x {host_name}", + shell=True, capture_output=True, text=True) + if result.stdout.strip(): + print(f"⚠️ Контейнер '{host_name}' уже существует, удаляем старый") + run_cmd(f"docker rm -f {host_name}") + + # Получаем образ + image = images.get(family, f"inecs/ansible-lab:{family}-latest") + + # Формируем команду docker run + cmd_parts = [ + "docker run -d", + f"--name {host_name}", + f"--network {docker_network}", + "--restart=unless-stopped" + ] + + # Добавляем systemd настройки + if systemd_defaults.get('privileged'): + cmd_parts.append("--privileged") + + for vol in systemd_defaults.get('volumes', []): + cmd_parts.append(f"-v {vol}") + + for tmpfs in systemd_defaults.get('tmpfs', []): + cmd_parts.append(f"--tmpfs {tmpfs}") + + if systemd_defaults.get('capabilities'): + for cap in systemd_defaults['capabilities']: + cmd_parts.append(f"--cap-add {cap}") + + cmd_parts.append(image) + + # Добавляем command в конец если задан + if systemd_defaults.get('command'): + cmd_parts.append(systemd_defaults['command']) + + cmd = " ".join(cmd_parts) + print(f"🚀 Создание контейнера: {host_name}") + run_cmd(cmd) + print(f"✅ Контейнер '{host_name}' создан") + kind_clusters = preset.get('kind_clusters', []) if not kind_clusters: - print("⚠️ В пресете не определены kind кластеры") + print("\n⚠️ В пресете не определены kind кластеры") + print("✅ Создание контейнеров завершено") sys.exit(0) os.makedirs("/ansible/.kind", exist_ok=True) diff --git a/scripts/delete_hosts.py b/scripts/delete_hosts.py new file mode 100644 index 0000000..865dc1a --- /dev/null +++ b/scripts/delete_hosts.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Скрипт для удаления контейнеров из секции hosts пресета +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess + +def main(): + if len(sys.argv) < 2: + print("Usage: delete_hosts.py ") + sys.exit(1) + + preset_file = sys.argv[1] + + print(f"📋 Читаю пресет: {preset_file}") + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + + hosts = preset.get('hosts', []) + if not hosts: + print("⚠️ В пресете нет контейнеров для удаления") + sys.exit(0) + + print(f"🗑️ Удаление контейнеров (всего: {len(hosts)})") + for host in hosts: + host_name = host['name'] + + # Проверяем существование контейнера + result = subprocess.run(f"docker ps -a --format '{{{{.Names}}}}' | grep -x {host_name}", + shell=True, capture_output=True, text=True) + if result.stdout.strip(): + print(f"🗑️ Удаление контейнера: {host_name}") + subprocess.run(f"docker rm -f {host_name}", shell=True, capture_output=True, text=True) + print(f"✅ Контейнер '{host_name}' удален") + else: + print(f"⚠️ Контейнер '{host_name}' не найден") + + print("✅ Удаление завершено") + +if __name__ == "__main__": + main() From 377e15119aa821b27ef60d05c95b2903f7d78816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:26:20 +0300 Subject: [PATCH 07/41] =?UTF-8?q?refactor:=20=D0=B7=D0=B0=D0=BF=D1=83?= =?UTF-8?q?=D1=81=D0=BA=20Python=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=20k8s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Команда destroy теперь запускает delete_hosts.py через контейнер k8s - Все Python скрипты выполняются внутри Docker контейнера для единообразия - Если контейнер не запущен, скрипты выполняются на хосте (fallback) --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 36249aa..c84b45c 100644 --- a/Makefile +++ b/Makefile @@ -1065,7 +1065,11 @@ k8s: docker rm -f $$CONTAINER_NAME 2>/dev/null || true; \ echo "🗑️ Удаление контейнеров из пресета..."; \ if [ -f "molecule/presets/k8s/$$PRESET.yml" ]; then \ - python3 scripts/delete_hosts.py molecule/presets/k8s/$$PRESET.yml 2>/dev/null || true; \ + if docker ps | grep -q $$CONTAINER_NAME; then \ + docker exec $$CONTAINER_NAME bash -c "python3 /workspace/scripts/delete_hosts.py /workspace/molecule/presets/k8s/$$PRESET.yml" 2>/dev/null || true; \ + else \ + python3 scripts/delete_hosts.py molecule/presets/k8s/$$PRESET.yml 2>/dev/null || true; \ + fi; \ fi; \ echo "✅ Удаление завершено";; \ stop) \ From 591cdaf8318379ca9c771558c16c340742cbcf52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:28:19 +0300 Subject: [PATCH 08/41] =?UTF-8?q?refactor:=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=20Kubernetes?= =?UTF-8?q?=20=D0=B2=20k8s-controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Все команды make k8s теперь используют единый контейнер k8s-controller - Упрощена логика именования контейнеров - Контейнер k8s-controller используется для всех операций с Kubernetes --- Makefile | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index c84b45c..2efbc30 100644 --- a/Makefile +++ b/Makefile @@ -1036,7 +1036,7 @@ k8s: ls -1 molecule/presets/k8s/*.yml 2>/dev/null | sed 's|molecule/presets/k8s/||g' | sed 's|\.yml||g' | sed 's/^/ - /' || echo " - k8s-minimal"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET; \ + CONTAINER_NAME=k8s-controller; \ docker run -d --name $$CONTAINER_NAME --rm \ -v "$(PWD):/workspace" -w /workspace \ -v /var/run/docker.sock:/var/run/docker.sock \ @@ -1055,7 +1055,7 @@ k8s: echo "🗑️ Удаление Kind кластера и контейнеров..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ PRESET=$${PRESET_ARG:-k8s-minimal}; \ - CONTAINER_NAME=k8s-$$PRESET; \ + CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ @@ -1080,7 +1080,7 @@ k8s: echo "💡 Пример: make k8s stop kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec $$CONTAINER_NAME bash -c "kind get clusters | xargs -I {} kind stop cluster --name {}" 2>/dev/null || true; \ echo "✅ Kind кластер остановлен"; \ @@ -1098,7 +1098,7 @@ k8s: echo "💡 Пример: make k8s start kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ @@ -1114,7 +1114,7 @@ k8s: echo "💡 Пример: make k8s status kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec $$CONTAINER_NAME bash -c "kind get clusters" 2>/dev/null || echo " Нет кластеров"; \ docker exec $$CONTAINER_NAME bash -c "kind get clusters | while read cluster; do echo \"Кластер: \$$cluster\"; kubectl --context kind-\$$cluster get nodes 2>/dev/null || true; done" 2>/dev/null || true; \ @@ -1130,7 +1130,7 @@ k8s: echo "💡 Пример: make k8s config kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ @@ -1165,7 +1165,7 @@ k8s: echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ @@ -1183,7 +1183,7 @@ k8s: echo "💡 Пример: make k8s nodes kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ @@ -1199,7 +1199,7 @@ k8s: echo "💡 Пример: make k8s shell kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ docker exec -it $$CONTAINER_NAME bash; \ else \ @@ -1216,7 +1216,7 @@ k8s: echo "💡 Пример: make k8s manifest apply kubernetes https://example.com/manifest.yaml"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ @@ -1249,7 +1249,7 @@ k8s: echo "💡 Пример: make k8s helm list kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ @@ -1316,7 +1316,7 @@ k8s: echo "💡 Пример: make k8s helmrepo list kubernetes"; \ exit 1; \ fi; \ - CONTAINER_NAME=k8s-$$PRESET_ARG; \ + CONTAINER_NAME=k8s-controller; \ if ! docker ps | grep -q $$CONTAINER_NAME; then \ echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ From 5c8862e9bf1d67fb676113dbbb94ae840f212025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:34:59 +0300 Subject: [PATCH 09/41] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20kubectl=20=D0=BA=20API?= =?UTF-8?q?=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D1=83=20Kind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен --validate=false для отключения валидации при установке аддонов - kubectl теперь подключается напрямую к control-plane узлу - Контейнер k8s-controller автоматически подключается к сети kind - Все kubectl команды используют --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify --- scripts/create_k8s_cluster.py | 38 ++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 8fcb450..3ca5e40 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -159,6 +159,20 @@ def main(): else: print(f"🚀 Создание кластера: {name}") run_cmd(f"kind create cluster --name {name} --config {config_file}") + + # Подключаем контейнер k8s-controller к сети kind + print(f"🔗 Подключение контейнера к сети kind...") + result = subprocess.run(f"docker network inspect kind", shell=True, capture_output=True, text=True) + if result.returncode == 0: + # Получаем имя контейнера из аргументов (второй аргумент) + controller_name = sys.argv[2] if len(sys.argv) > 2 else "k8s-controller" + result = subprocess.run(f"docker network connect kind {controller_name}", shell=True, capture_output=True, text=True) + if result.returncode == 0: + print(f"✅ Контейнер {controller_name} подключен к сети kind") + else: + print(f"⚠️ Не удалось подключить контейнер к сети kind: {result.stderr}") + else: + print(f"⚠️ Сеть kind не найдена") # Устанавливаем аддоны addons = cluster.get('addons', {}) @@ -169,33 +183,33 @@ def main(): if addons.get('ingress_nginx'): print(" - Installing ingress-nginx") - run_cmd(f"kubectl --context kind-{name} apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml") - run_cmd(f"kubectl --context kind-{name} -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify apply --validate=false -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n ingress-nginx rollout status deploy/ingress-nginx-controller --timeout=180s") if addons.get('metrics_server'): print(" - Installing metrics-server") - run_cmd(f"kubectl --context kind-{name} apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify apply --validate=false -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml") patch_json = '{"spec":{"template":{"spec":{"containers":[{"name":"metrics-server","args":["--kubelet-insecure-tls","--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname"]}]}}}}' - run_cmd(f"kubectl --context kind-{name} -n kube-system patch deploy metrics-server -p '{patch_json}'") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n kube-system patch deploy metrics-server -p '{patch_json}'") if addons.get('istio'): print(" - Installing Istio") run_cmd(f"istioctl install -y --set profile=demo --context kind-{name}") - run_cmd(f"kubectl --context kind-{name} -n istio-system rollout status deploy/istiod --timeout=180s") - run_cmd(f"kubectl --context kind-{name} -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n istio-system rollout status deploy/istiod --timeout=180s") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s") if addons.get('kiali'): print(" - Installing Kiali") - run_cmd(f"kubectl --context kind-{name} create ns istio-system") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns istio-system") run_cmd(f"helm upgrade --install kiali-server kiali/kiali-server --namespace istio-system --kube-context kind-{name} --set auth.strategy=anonymous --wait --timeout 180s") if addons.get('prometheus_stack'): print(" - Installing Prometheus Stack") run_cmd(f"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts") run_cmd(f"helm repo update") - run_cmd(f"kubectl --context kind-{name} create ns monitoring") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns monitoring") run_cmd(f"helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --kube-context kind-{name} --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s") - run_cmd(f"kubectl --context kind-{name} -n monitoring rollout status deploy/monitoring-grafana --timeout=300s") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n monitoring rollout status deploy/monitoring-grafana --timeout=300s") # Настраиваем NodePort для аддонов addon_ports = cluster.get('addon_ports', {}) @@ -206,19 +220,19 @@ def main(): port = addon_ports['prometheus'] print(f" - Prometheus: {port}") patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' - run_cmd(f"kubectl --context kind-{name} patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='{patch_json}'") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='{patch_json}'") if 'grafana' in addon_ports: port = addon_ports['grafana'] print(f" - Grafana: {port}") patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' - run_cmd(f"kubectl --context kind-{name} patch svc -n monitoring monitoring-grafana --type='json' -p='{patch_json}'") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify patch svc -n monitoring monitoring-grafana --type='json' -p='{patch_json}'") if 'kiali' in addon_ports: port = addon_ports['kiali'] print(f" - Kiali: {port}") patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' - run_cmd(f"kubectl --context kind-{name} patch svc -n istio-system kiali --type='json' -p='{patch_json}'") + run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify patch svc -n istio-system kiali --type='json' -p='{patch_json}'") print(f"✅ Кластер '{name}' готов!") From 33e329c091e3081fbbf765cec630067822a9d1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:38:57 +0300 Subject: [PATCH 10/41] =?UTF-8?q?refactor:=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=D1=81=20=D0=BF=D0=BE=D1=80=D1=82=D0=BE=D0=B2=20ing?= =?UTF-8?q?ress=20=D0=B2=20addon=5Fports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалены ingress_host_http_port и ingress_host_https_port - Все порты (ingress, prometheus, grafana, kiali) теперь в addon_ports - Обновлен скрипт create_k8s_cluster.py для работы с новой структурой - Все порты теперь пробрасываются через extraPortMappings в конфигурации Kind - Порты теперь видны в контейнерах control-plane и worker --- molecule/presets/k8s/kubernetes.yml | 6 ++- scripts/create_k8s_cluster.py | 58 ++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index 16bd82b..0a94f56 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -44,14 +44,16 @@ kind_clusters: istio: true kiali: true prometheus_stack: true - ingress_host_http_port: 8081 - ingress_host_https_port: 8443 # Порты для доступа к аддонам извне # Документация: https://devops.org.ru + # Ingress HTTP: http://localhost:8081 + # Ingress HTTPS: https://localhost:8443 # Prometheus: http://localhost:9090 # Grafana: http://localhost:3000 (admin/admin) # Kiali: http://localhost:20001 addon_ports: + ingress_http: 8081 + ingress_https: 8443 prometheus: 9090 grafana: 3000 kiali: 20001 diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 3ca5e40..426a99e 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -124,20 +124,50 @@ def main(): } } - # Добавляем extraPortMappings для ingress если нужно - if cluster.get('addons', {}).get('ingress_nginx'): - config['nodes'][0]['extraPortMappings'] = [ - { - 'containerPort': 80, - 'hostPort': cluster.get('ingress_host_http_port', 8081), - 'protocol': 'TCP' - }, - { - 'containerPort': 443, - 'hostPort': cluster.get('ingress_host_https_port', 8443), - 'protocol': 'TCP' - } - ] + # Добавляем extraPortMappings для всех портов из addon_ports + addon_ports = cluster.get('addon_ports', {}) + port_mappings = [] + + # Ingress порты + if addon_ports.get('ingress_http'): + port_mappings.append({ + 'containerPort': 80, + 'hostPort': addon_ports['ingress_http'], + 'protocol': 'TCP' + }) + if addon_ports.get('ingress_https'): + port_mappings.append({ + 'containerPort': 443, + 'hostPort': addon_ports['ingress_https'], + 'protocol': 'TCP' + }) + + # Prometheus порт + if addon_ports.get('prometheus'): + port_mappings.append({ + 'containerPort': 9090, + 'hostPort': addon_ports['prometheus'], + 'protocol': 'TCP' + }) + + # Grafana порт + if addon_ports.get('grafana'): + port_mappings.append({ + 'containerPort': 3000, + 'hostPort': addon_ports['grafana'], + 'protocol': 'TCP' + }) + + # Kiali порт + if addon_ports.get('kiali'): + port_mappings.append({ + 'containerPort': 20001, + 'hostPort': addon_ports['kiali'], + 'protocol': 'TCP' + }) + + if port_mappings: + config['nodes'][0]['extraPortMappings'] = port_mappings # Добавляем worker nodes workers = cluster.get('workers', 0) From 791504abf64856b2e4d13169d634b1621395b84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 08:52:43 +0300 Subject: [PATCH 11/41] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=83=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20Istio,=20Kiali=20=D0=B8=20Prometheus?= =?UTF-8?q?=20Stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Istio теперь использует исправленный kubeconfig с IP control-plane узла - Helm команды теперь используют исправленный kubeconfig - Создание namespace сделано идемпотентным (не прерывает скрипт при существовании) - Все аддоны теперь должны устанавливаться корректно --- scripts/create_k8s_cluster.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 426a99e..8f8c03b 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -224,21 +224,33 @@ def main(): if addons.get('istio'): print(" - Installing Istio") - run_cmd(f"istioctl install -y --set profile=demo --context kind-{name}") + # Генерируем kubeconfig и заменяем 0.0.0.0 на IP control-plane узла + run_cmd(f"kind get kubeconfig --name {name} > /tmp/istio-kubeconfig-{name}.yaml") + # Получаем IP control-plane узла и заменяем в kubeconfig + result = subprocess.run(f"docker inspect {name}-control-plane --format='{{{{.NetworkSettings.Networks.kind.IPAddress}}}}'", + shell=True, capture_output=True, text=True) + if result.returncode == 0: + control_plane_ip = result.stdout.strip() + # Заменяем 0.0.0.0 на IP control-plane + subprocess.run(f"sed -i 's/0\\.0\\.0\\.0:6443/{control_plane_ip}:6443/g' /tmp/istio-kubeconfig-{name}.yaml", shell=True) + # Устанавливаем Istio используя kubeconfig + run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml istioctl install -y --set profile=demo") run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n istio-system rollout status deploy/istiod --timeout=180s") run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n istio-system rollout status deploy/istio-ingressgateway --timeout=180s") if addons.get('kiali'): print(" - Installing Kiali") - run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns istio-system") - run_cmd(f"helm upgrade --install kiali-server kiali/kiali-server --namespace istio-system --kube-context kind-{name} --set auth.strategy=anonymous --wait --timeout 180s") + subprocess.run(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns istio-system", shell=True, capture_output=True) + # Используем исправленный kubeconfig + run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install kiali-server kiali/kiali-server --namespace istio-system --set auth.strategy=anonymous --wait --timeout 180s") if addons.get('prometheus_stack'): print(" - Installing Prometheus Stack") run_cmd(f"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts") run_cmd(f"helm repo update") - run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns monitoring") - run_cmd(f"helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --kube-context kind-{name} --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s") + subprocess.run(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns monitoring", shell=True, capture_output=True) + # Используем исправленный kubeconfig + run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s") run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n monitoring rollout status deploy/monitoring-grafana --timeout=300s") # Настраиваем NodePort для аддонов From e2b9d94075870b798619b3e088be13f870e3c6d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:07:00 +0300 Subject: [PATCH 12/41] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BF=D0=BE=D1=80=D1=82=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20NodePort=20=D0=B8=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20Helm=20=D1=80=D0=B5?= =?UTF-8?q?=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Изменены порты Prometheus, Grafana и Kiali на NodePort диапазон (30000-32767) - Добавлены Helm репозитории для Kiali и Prometheus - Исправлена логика extraPortMappings для правильного маппинга портов - Prometheus: 30900, Grafana: 30000, Kiali: 32001 --- molecule/presets/k8s/kubernetes.yml | 12 ++++++------ scripts/create_k8s_cluster.py | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index 0a94f56..8a00d1d 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -48,15 +48,15 @@ kind_clusters: # Документация: https://devops.org.ru # Ingress HTTP: http://localhost:8081 # Ingress HTTPS: https://localhost:8443 - # Prometheus: http://localhost:9090 - # Grafana: http://localhost:3000 (admin/admin) - # Kiali: http://localhost:20001 + # Prometheus: http://localhost:30900 + # Grafana: http://localhost:30000 (admin/admin) + # Kiali: http://localhost:32001 addon_ports: ingress_http: 8081 ingress_https: 8443 - prometheus: 9090 - grafana: 3000 - kiali: 20001 + prometheus: 30900 + grafana: 30000 + kiali: 32001 hosts: # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 8f8c03b..b853893 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -142,26 +142,26 @@ def main(): 'protocol': 'TCP' }) - # Prometheus порт + # Prometheus порт - мапим NodePort на hostPort if addon_ports.get('prometheus'): port_mappings.append({ - 'containerPort': 9090, + 'containerPort': addon_ports['prometheus'], 'hostPort': addon_ports['prometheus'], 'protocol': 'TCP' }) - # Grafana порт + # Grafana порт - мапим NodePort на hostPort if addon_ports.get('grafana'): port_mappings.append({ - 'containerPort': 3000, + 'containerPort': addon_ports['grafana'], 'hostPort': addon_ports['grafana'], 'protocol': 'TCP' }) - # Kiali порт + # Kiali порт - мапим NodePort на hostPort if addon_ports.get('kiali'): port_mappings.append({ - 'containerPort': 20001, + 'containerPort': addon_ports['kiali'], 'hostPort': addon_ports['kiali'], 'protocol': 'TCP' }) @@ -241,13 +241,17 @@ def main(): if addons.get('kiali'): print(" - Installing Kiali") subprocess.run(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns istio-system", shell=True, capture_output=True) + # Добавляем Helm репозиторий Kiali + run_cmd(f"helm repo add kiali https://kiali.org/helm-charts") + run_cmd(f"helm repo update") # Используем исправленный kubeconfig run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install kiali-server kiali/kiali-server --namespace istio-system --set auth.strategy=anonymous --wait --timeout 180s") if addons.get('prometheus_stack'): print(" - Installing Prometheus Stack") - run_cmd(f"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts") - run_cmd(f"helm repo update") + # Добавляем Helm репозиторий Prometheus + subprocess.run(f"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts", shell=True, capture_output=True) + subprocess.run(f"helm repo update", shell=True, capture_output=True) subprocess.run(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify create ns monitoring", shell=True, capture_output=True) # Используем исправленный kubeconfig run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s") From 87002cb9b3ef382f641888504a6a5db33b5ac500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:11:32 +0300 Subject: [PATCH 13/41] =?UTF-8?q?refactor:=20=D0=B8=D1=81=D0=BF=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20Cluste?= =?UTF-8?q?rIP=20=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20NodePort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Убрана логика патчинга NodePort для Prometheus, Grafana и Kiali - Сервисы теперь остаются с типом ClusterIP - Порты пробрасываются через extraPortMappings в Kind конфиге - Использованы стандартные порты: Prometheus 9090, Grafana 3000, Kiali 20001 - Более простой и надежный подход без ограничений NodePort диапазона --- molecule/presets/k8s/kubernetes.yml | 12 ++++----- scripts/create_k8s_cluster.py | 41 +++++++---------------------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index 8a00d1d..0a94f56 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -48,15 +48,15 @@ kind_clusters: # Документация: https://devops.org.ru # Ingress HTTP: http://localhost:8081 # Ingress HTTPS: https://localhost:8443 - # Prometheus: http://localhost:30900 - # Grafana: http://localhost:30000 (admin/admin) - # Kiali: http://localhost:32001 + # Prometheus: http://localhost:9090 + # Grafana: http://localhost:3000 (admin/admin) + # Kiali: http://localhost:20001 addon_ports: ingress_http: 8081 ingress_https: 8443 - prometheus: 30900 - grafana: 30000 - kiali: 32001 + prometheus: 9090 + grafana: 3000 + kiali: 20001 hosts: # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index b853893..e497d34 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -142,27 +142,27 @@ def main(): 'protocol': 'TCP' }) - # Prometheus порт - мапим NodePort на hostPort + # Prometheus порт - пробрасываем изнутри кластера (9090) на внешний порт if addon_ports.get('prometheus'): port_mappings.append({ - 'containerPort': addon_ports['prometheus'], - 'hostPort': addon_ports['prometheus'], + 'containerPort': 9090, # Внутренний порт Prometheus + 'hostPort': addon_ports['prometheus'], # Внешний порт на хосте 'protocol': 'TCP' }) - # Grafana порт - мапим NodePort на hostPort + # Grafana порт - пробрасываем изнутри кластера (3000) на внешний порт if addon_ports.get('grafana'): port_mappings.append({ - 'containerPort': addon_ports['grafana'], - 'hostPort': addon_ports['grafana'], + 'containerPort': 3000, # Внутренний порт Grafana + 'hostPort': addon_ports['grafana'], # Внешний порт на хосте 'protocol': 'TCP' }) - # Kiali порт - мапим NodePort на hostPort + # Kiali порт - пробрасываем изнутри кластера (20001) на внешний порт if addon_ports.get('kiali'): port_mappings.append({ - 'containerPort': addon_ports['kiali'], - 'hostPort': addon_ports['kiali'], + 'containerPort': 20001, # Внутренний порт Kiali + 'hostPort': addon_ports['kiali'], # Внешний порт на хосте 'protocol': 'TCP' }) @@ -257,29 +257,6 @@ def main(): run_cmd(f"KUBECONFIG=/tmp/istio-kubeconfig-{name}.yaml helm upgrade --install monitoring prometheus-community/kube-prometheus-stack --namespace monitoring --set grafana.adminPassword=admin --set grafana.defaultDashboardsTimezone=browser --wait --timeout 600s") run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify -n monitoring rollout status deploy/monitoring-grafana --timeout=300s") - # Настраиваем NodePort для аддонов - addon_ports = cluster.get('addon_ports', {}) - if addon_ports: - print("\n🔌 Настройка NodePort для аддонов") - - if 'prometheus' in addon_ports: - port = addon_ports['prometheus'] - print(f" - Prometheus: {port}") - patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' - run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify patch svc -n monitoring monitoring-kube-prom-prometheus --type='json' -p='{patch_json}'") - - if 'grafana' in addon_ports: - port = addon_ports['grafana'] - print(f" - Grafana: {port}") - patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' - run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify patch svc -n monitoring monitoring-grafana --type='json' -p='{patch_json}'") - - if 'kiali' in addon_ports: - port = addon_ports['kiali'] - print(f" - Kiali: {port}") - patch_json = f'[{{"op": "replace", "path": "/spec/type", "value":"NodePort"}},{{"op": "replace", "path": "/spec/ports/0/nodePort", "value":{port}}}]' - run_cmd(f"kubectl --server=https://{name}-control-plane:6443 --insecure-skip-tls-verify patch svc -n istio-system kiali --type='json' -p='{patch_json}'") - print(f"✅ Кластер '{name}' готов!") print("\n🎉 Все кластеры созданы!") From d48c273e50025ecd5860b16935393657ce4af44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:13:22 +0300 Subject: [PATCH 14/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=BE=D1=80=D1=82=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20Metrics=20Server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен проброс порта 4443 для Metrics Server - Metrics Server теперь доступен на http://localhost:4443 - Обновлена документация с информацией о порте --- molecule/presets/k8s/kubernetes.yml | 10 ++++++---- scripts/create_k8s_cluster.py | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index 0a94f56..0c22283 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -46,17 +46,19 @@ kind_clusters: prometheus_stack: true # Порты для доступа к аддонам извне # Документация: https://devops.org.ru - # Ingress HTTP: http://localhost:8081 - # Ingress HTTPS: https://localhost:8443 + # Ingress HTTP: http://localhost:80 + # Ingress HTTPS: https://localhost:443 # Prometheus: http://localhost:9090 # Grafana: http://localhost:3000 (admin/admin) # Kiali: http://localhost:20001 + # Metrics Server: http://localhost:4443 addon_ports: - ingress_http: 8081 - ingress_https: 8443 + ingress_http: 80 + ingress_https: 443 prometheus: 9090 grafana: 3000 kiali: 20001 + metrics_server: 4443 hosts: # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index e497d34..6a4b1d4 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -166,6 +166,14 @@ def main(): 'protocol': 'TCP' }) + # Metrics Server порт - пробрасываем изнутри кластера (4443) на внешний порт + if addon_ports.get('metrics_server'): + port_mappings.append({ + 'containerPort': 4443, # Внутренний порт Metrics Server + 'hostPort': addon_ports['metrics_server'], # Внешний порт на хосте + 'protocol': 'TCP' + }) + if port_mappings: config['nodes'][0]['extraPortMappings'] = port_mappings From 69b547dda6c839538f09c9e414a9c61e1e826537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:25:59 +0300 Subject: [PATCH 15/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20port-forward=20=D0=B4=D0=BB=D1=8F=20=D0=BA?= =?UTF-8?q?=D0=BB=D0=B0=D1=81=D1=82=D0=B5=D1=80=D0=B0=20Kubernetes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создан скрипт scripts/portforward.py для управления port-forward - Добавлены команды в Makefile: - make k8s portforward create - создать port-forward для всех сервисов - make k8s portforward list - показать активные port-forward - make k8s portforward clear - очистить все port-forward - make k8s portforward delete PORT - удалить конкретный port-forward - Сервисы остаются с типом ClusterIP - Port-forward автоматически создается для Prometheus, Grafana, Kiali и Metrics Server - Порты: Prometheus 9090, Grafana 3000, Kiali 20001, Metrics Server 4443 --- Makefile | 31 +++++ molecule/presets/k8s/kubernetes.yml | 22 ++- scripts/portforward.py | 200 ++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+), 13 deletions(-) create mode 100755 scripts/portforward.py diff --git a/Makefile b/Makefile index 2efbc30..1694773 100644 --- a/Makefile +++ b/Makefile @@ -1358,6 +1358,37 @@ k8s: echo "💡 Доступные команды: add, list, delete, update, packages"; \ exit 1;; \ esac;; \ + portforward) \ + PORTFWD_CMD="$(word 3, $(MAKECMDGOALS))"; \ + PORT_ARG="$(word 4, $(MAKECMDGOALS))"; \ + if [ -z "$$PORTFWD_CMD" ]; then \ + echo "❌ Ошибка: Укажите команду"; \ + echo "💡 Пример: make k8s portforward create"; \ + exit 1; \ + fi; \ + case "$$PORTFWD_CMD" in \ + create) \ + echo "🔌 Создание port-forward..."; \ + python3 scripts/portforward.py create;; \ + list) \ + echo "📋 Список активных port-forward..."; \ + python3 scripts/portforward.py list;; \ + clear) \ + echo "🗑️ Очистка всех port-forward..."; \ + python3 scripts/portforward.py clear;; \ + delete) \ + if [ -z "$$PORT_ARG" ]; then \ + echo "❌ Ошибка: Укажите порт"; \ + echo "💡 Пример: make k8s portforward delete 3000"; \ + exit 1; \ + fi; \ + echo "🗑️ Удаление port-forward на порту $$PORT_ARG..."; \ + python3 scripts/portforward.py delete $$PORT_ARG;; \ + *) \ + echo "❌ Неизвестная команда: $$PORTFWD_CMD"; \ + echo "💡 Доступные команды: create, list, clear, delete"; \ + exit 1;; \ + esac;; \ *) \ echo "☸️ Доступные команды:"; \ echo ""; \ diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index 0c22283..0284f48 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -45,26 +45,22 @@ kind_clusters: kiali: true prometheus_stack: true # Порты для доступа к аддонам извне - # Документация: https://devops.org.ru - # Ingress HTTP: http://localhost:80 - # Ingress HTTPS: https://localhost:443 + # Ingress HTTP: http://localhost:8081 + # Ingress HTTPS: https://localhost:8443 # Prometheus: http://localhost:9090 # Grafana: http://localhost:3000 (admin/admin) # Kiali: http://localhost:20001 # Metrics Server: http://localhost:4443 addon_ports: - ingress_http: 80 - ingress_https: 443 + ingress_http: 8081 + ingress_https: 8443 prometheus: 9090 grafana: 3000 kiali: 20001 metrics_server: 4443 -hosts: - # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) - - name: u1 - family: ubuntu22 - groups: [test, web] - - name: u2 - family: debian12 - groups: [test, web] +hosts: [] +# # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) +# - name: u1 +# family: ubuntu22 +# groups: [test, web] diff --git a/scripts/portforward.py b/scripts/portforward.py new file mode 100755 index 0000000..6eb046d --- /dev/null +++ b/scripts/portforward.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +Скрипт для управления port-forward для Kubernetes сервисов +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess +import os +import signal +import time + +def get_cluster_name(): + """Получаем имя кластера из preset файла""" + preset_file = "molecule/presets/k8s/kubernetes.yml" + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + return preset['kind_clusters'][0]['name'] + +def run_cmd(cmd): + """Выполняет команду и возвращает результат""" + print(f"[run] {cmd}") + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode != 0: + print(f"[error] {result.stderr}") + else: + print(result.stdout) + return result + +def get_portforward_pids(): + """Получает PID процессов port-forward""" + result = subprocess.run("ps aux | grep 'kubectl.*port-forward' | grep -v grep", shell=True, capture_output=True, text=True) + pids = [] + for line in result.stdout.split('\n'): + if line.strip(): + pids.append(int(line.split()[1])) + return pids + +def list_portforwards(): + """Показывает список всех активных port-forward""" + pids = get_portforward_pids() + if not pids: + print("❌ Нет активных port-forward") + return + + print("📋 Активные port-forward:") + result = subprocess.run("ps aux | grep 'kubectl.*port-forward' | grep -v grep", shell=True, capture_output=True, text=True) + for line in result.stdout.split('\n'): + if line.strip(): + print(f" {line}") + +def clear_portforwards(): + """Завершает все процессы port-forward""" + pids = get_portforward_pids() + if not pids: + print("❌ Нет активных port-forward") + return + + print(f"🗑️ Завершение {len(pids)} процессов port-forward...") + for pid in pids: + try: + os.kill(pid, signal.SIGTERM) + print(f"✅ Процесс {pid} завершен") + except ProcessLookupError: + print(f"⚠️ Процесс {pid} уже не существует") + + # Ждем завершения процессов + time.sleep(2) + + # Принудительно убиваем оставшиеся + remaining_pids = get_portforward_pids() + if remaining_pids: + print("⚠️ Принудительное завершение оставшихся процессов...") + for pid in remaining_pids: + try: + os.kill(pid, signal.SIGKILL) + print(f"✅ Процесс {pid} принудительно завершен") + except ProcessLookupError: + pass + +def create_portforwards(): + """Создает port-forward для всех сервисов из preset""" + # Загружаем preset + preset_file = "molecule/presets/k8s/kubernetes.yml" + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + + cluster_name = preset['kind_clusters'][0]['name'] + addon_ports = preset['kind_clusters'][0].get('addon_ports', {}) + + print(f"🔌 Создание port-forward для кластера: {cluster_name}") + + # Prometheus + if addon_ports.get('prometheus'): + port = addon_ports['prometheus'] + print(f" - Prometheus: localhost:{port} -> monitoring/monitoring-kube-prometheus-prometheus:9090") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "monitoring", + "svc/monitoring-kube-prometheus-prometheus", + f"{port}:9090" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Grafana + if addon_ports.get('grafana'): + port = addon_ports['grafana'] + print(f" - Grafana: localhost:{port} -> monitoring/monitoring-grafana:80") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "monitoring", + "svc/monitoring-grafana", + f"{port}:80" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Kiali + if addon_ports.get('kiali'): + port = addon_ports['kiali'] + print(f" - Kiali: localhost:{port} -> istio-system/kiali:20001") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "istio-system", + "svc/kiali", + f"{port}:20001" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Metrics Server + if addon_ports.get('metrics_server'): + port = addon_ports['metrics_server'] + print(f" - Metrics Server: localhost:{port} -> kube-system/metrics-server:4443") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "kube-system", + "svc/metrics-server", + f"{port}:4443" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + time.sleep(2) + print("✅ Port-forward создан") + list_portforwards() + +def delete_portforward(port): + """Удаляет port-forward для конкретного порта""" + pids = get_portforward_pids() + if not pids: + print(f"❌ Нет активных port-forward на порту {port}") + return + + # Находим процесс с нужным портом + result = subprocess.run(f"ps aux | grep 'kubectl.*port-forward.*:{port}' | grep -v grep", shell=True, capture_output=True, text=True) + if not result.stdout.strip(): + print(f"❌ Не найден port-forward на порту {port}") + return + + # Извлекаем PID + pid = int(result.stdout.split()[1]) + print(f"🗑️ Завершение port-forward на порту {port} (PID: {pid})...") + try: + os.kill(pid, signal.SIGTERM) + print(f"✅ Port-forward на порту {port} завершен") + except ProcessLookupError: + print(f"⚠️ Процесс {pid} уже не существует") + +def main(): + if len(sys.argv) < 2: + print("Usage: portforward.py [port]") + sys.exit(1) + + command = sys.argv[1] + + if command == "create": + create_portforwards() + elif command == "list": + list_portforwards() + elif command == "clear": + clear_portforwards() + elif command == "delete": + if len(sys.argv) < 3: + print("Usage: portforward.py delete ") + sys.exit(1) + port = sys.argv[2] + delete_portforward(port) + else: + print(f"❌ Неизвестная команда: {command}") + sys.exit(1) + +if __name__ == "__main__": + main() From ebed1f76ab5cb089544bd18a5bf5513d51b5e97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:28:07 +0300 Subject: [PATCH 16/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=D0=B0=20recreate=20=D0=B4=D0=BB=D1=8F=20port-forward?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена функция recreate_portforwards() в scripts/portforward.py - Команда recreate удаляет все существующие port-forward и создает их заново - Добавлена команда в Makefile: make k8s portforward recreate - Полезно для переподключения после перезапуска контейнера или кластера --- Makefile | 5 ++++- scripts/portforward.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1694773..9ceb4a8 100644 --- a/Makefile +++ b/Makefile @@ -1376,6 +1376,9 @@ k8s: clear) \ echo "🗑️ Очистка всех port-forward..."; \ python3 scripts/portforward.py clear;; \ + recreate) \ + echo "🔄 Пересоздание port-forward..."; \ + python3 scripts/portforward.py recreate;; \ delete) \ if [ -z "$$PORT_ARG" ]; then \ echo "❌ Ошибка: Укажите порт"; \ @@ -1386,7 +1389,7 @@ k8s: python3 scripts/portforward.py delete $$PORT_ARG;; \ *) \ echo "❌ Неизвестная команда: $$PORTFWD_CMD"; \ - echo "💡 Доступные команды: create, list, clear, delete"; \ + echo "💡 Доступные команды: create, list, clear, recreate, delete"; \ exit 1;; \ esac;; \ *) \ diff --git a/scripts/portforward.py b/scripts/portforward.py index 6eb046d..23d4cd5 100755 --- a/scripts/portforward.py +++ b/scripts/portforward.py @@ -173,9 +173,16 @@ def delete_portforward(port): except ProcessLookupError: print(f"⚠️ Процесс {pid} уже не существует") +def recreate_portforwards(): + """Пересоздает port-forward: удаляет существующие и создает заново""" + print("🔄 Пересоздание port-forward...") + clear_portforwards() + time.sleep(1) + create_portforwards() + def main(): if len(sys.argv) < 2: - print("Usage: portforward.py [port]") + print("Usage: portforward.py [port]") sys.exit(1) command = sys.argv[1] @@ -186,6 +193,8 @@ def main(): list_portforwards() elif command == "clear": clear_portforwards() + elif command == "recreate": + recreate_portforwards() elif command == "delete": if len(sys.argv) < 3: print("Usage: portforward.py delete ") From 06bfc00b5af24752898b27e80b9aff372c1a509d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:34:59 +0300 Subject: [PATCH 17/41] =?UTF-8?q?fix:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20extraPortMappings=20=D0=B4=D0=BB=D1=8F=20=D0=B0?= =?UTF-8?q?=D0=B4=D0=B4=D0=BE=D0=BD=D0=BE=D0=B2,=20port-forward=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=20=D0=B2=20?= =?UTF-8?q?=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9=20=D1=81?= =?UTF-8?q?=D0=BA=D1=80=D0=B8=D0=BF=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/create_k8s_cluster.py | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 6a4b1d4..8eefbd2 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -142,40 +142,7 @@ def main(): 'protocol': 'TCP' }) - # Prometheus порт - пробрасываем изнутри кластера (9090) на внешний порт - if addon_ports.get('prometheus'): - port_mappings.append({ - 'containerPort': 9090, # Внутренний порт Prometheus - 'hostPort': addon_ports['prometheus'], # Внешний порт на хосте - 'protocol': 'TCP' - }) - - # Grafana порт - пробрасываем изнутри кластера (3000) на внешний порт - if addon_ports.get('grafana'): - port_mappings.append({ - 'containerPort': 3000, # Внутренний порт Grafana - 'hostPort': addon_ports['grafana'], # Внешний порт на хосте - 'protocol': 'TCP' - }) - - # Kiali порт - пробрасываем изнутри кластера (20001) на внешний порт - if addon_ports.get('kiali'): - port_mappings.append({ - 'containerPort': 20001, # Внутренний порт Kiali - 'hostPort': addon_ports['kiali'], # Внешний порт на хосте - 'protocol': 'TCP' - }) - - # Metrics Server порт - пробрасываем изнутри кластера (4443) на внешний порт - if addon_ports.get('metrics_server'): - port_mappings.append({ - 'containerPort': 4443, # Внутренний порт Metrics Server - 'hostPort': addon_ports['metrics_server'], # Внешний порт на хосте - 'protocol': 'TCP' - }) - - if port_mappings: - config['nodes'][0]['extraPortMappings'] = port_mappings + # Не добавляем extraPortMappings для портов аддонов - используем port-forward # Добавляем worker nodes workers = cluster.get('workers', 0) From 604c7816beffb4f9d4ffc7f53f3d05431929bc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:39:20 +0300 Subject: [PATCH 18/41] =?UTF-8?q?feat:=20port-forward=20=D1=82=D0=B5=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D1=8C=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=20=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D0=BE=D0=BC=20=D0=BA=D0=BE=D0=BC=D0=BF=D1=8C=D1=8E=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Скрипт получает kubeconfig из контейнера k8s-controller - Заменяет server 0.0.0.0 на localhost для локального доступа - Port-forward запускается на хосте через локальный kubectl - Все аддоны доступны на localhost: Grafana 3000, Prometheus 9090, Kiali 20001 --- scripts/portforward.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/scripts/portforward.py b/scripts/portforward.py index 23d4cd5..afca259 100755 --- a/scripts/portforward.py +++ b/scripts/portforward.py @@ -80,7 +80,7 @@ def clear_portforwards(): pass def create_portforwards(): - """Создает port-forward для всех сервисов из preset""" + """Создает port-forward для всех сервисов из preset на локальном компьютере""" # Загружаем preset preset_file = "molecule/presets/k8s/kubernetes.yml" with open(preset_file, 'r') as f: @@ -89,7 +89,29 @@ def create_portforwards(): cluster_name = preset['kind_clusters'][0]['name'] addon_ports = preset['kind_clusters'][0].get('addon_ports', {}) + # Получаем kubeconfig из контейнера k8s-controller print(f"🔌 Создание port-forward для кластера: {cluster_name}") + print("📋 Получение kubeconfig из контейнера k8s-controller...") + + # Копируем kubeconfig из контейнера + result = subprocess.run( + f"docker exec k8s-controller kind get kubeconfig --name {cluster_name}", + shell=True, capture_output=True, text=True + ) + + if result.returncode != 0: + print(f"❌ Ошибка получения kubeconfig: {result.stderr}") + return + + # Сохраняем kubeconfig во временный файл + kubeconfig_file = "/tmp/kubeconfig-lab.yaml" + with open(kubeconfig_file, 'w') as f: + f.write(result.stdout) + + # Меняем server с 0.0.0.0 на localhost для локального доступа + subprocess.run(f"sed -i.bak 's|server: https://0.0.0.0:6443|server: https://localhost:6443|g' {kubeconfig_file}", shell=True) + + print("✅ Kubeconfig подготовлен") # Prometheus if addon_ports.get('prometheus'): @@ -97,8 +119,7 @@ def create_portforwards(): print(f" - Prometheus: localhost:{port} -> monitoring/monitoring-kube-prometheus-prometheus:9090") subprocess.Popen([ "kubectl", - "--server=https://{}-control-plane:6443".format(cluster_name), - "--insecure-skip-tls-verify", + f"--kubeconfig={kubeconfig_file}", "port-forward", "-n", "monitoring", "svc/monitoring-kube-prometheus-prometheus", @@ -111,8 +132,7 @@ def create_portforwards(): print(f" - Grafana: localhost:{port} -> monitoring/monitoring-grafana:80") subprocess.Popen([ "kubectl", - "--server=https://{}-control-plane:6443".format(cluster_name), - "--insecure-skip-tls-verify", + f"--kubeconfig={kubeconfig_file}", "port-forward", "-n", "monitoring", "svc/monitoring-grafana", @@ -125,8 +145,7 @@ def create_portforwards(): print(f" - Kiali: localhost:{port} -> istio-system/kiali:20001") subprocess.Popen([ "kubectl", - "--server=https://{}-control-plane:6443".format(cluster_name), - "--insecure-skip-tls-verify", + f"--kubeconfig={kubeconfig_file}", "port-forward", "-n", "istio-system", "svc/kiali", @@ -139,8 +158,7 @@ def create_portforwards(): print(f" - Metrics Server: localhost:{port} -> kube-system/metrics-server:4443") subprocess.Popen([ "kubectl", - "--server=https://{}-control-plane:6443".format(cluster_name), - "--insecure-skip-tls-verify", + f"--kubeconfig={kubeconfig_file}", "port-forward", "-n", "kube-system", "svc/metrics-server", From 44fff158ceb3b9eea65c0470475b948ca2c256f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:40:47 +0300 Subject: [PATCH 19/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BF=D0=BE=D1=80=D1=82=D1=8B=20Ing?= =?UTF-8?q?ress=20(80,=20443)=20=D0=B2=20port-forward?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен port-forward для Ingress HTTP на порт 8081 (localhost:8081 -> ingress-nginx-controller:80) - Добавлен port-forward для Ingress HTTPS на порт 8443 (localhost:8443 -> ingress-nginx-controller:443) - Теперь все аддоны доступны через port-forward: Ingress, Prometheus, Grafana, Kiali, Metrics Server --- scripts/portforward.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/portforward.py b/scripts/portforward.py index afca259..bf2d993 100755 --- a/scripts/portforward.py +++ b/scripts/portforward.py @@ -113,6 +113,32 @@ def create_portforwards(): print("✅ Kubeconfig подготовлен") + # Ingress HTTP (80) + if addon_ports.get('ingress_http'): + port = addon_ports['ingress_http'] + print(f" - Ingress HTTP: localhost:{port} -> ingress-nginx-controller:80") + subprocess.Popen([ + "kubectl", + f"--kubeconfig={kubeconfig_file}", + "port-forward", + "-n", "ingress-nginx", + "svc/ingress-nginx-controller", + f"{port}:80" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Ingress HTTPS (443) + if addon_ports.get('ingress_https'): + port = addon_ports['ingress_https'] + print(f" - Ingress HTTPS: localhost:{port} -> ingress-nginx-controller:443") + subprocess.Popen([ + "kubectl", + f"--kubeconfig={kubeconfig_file}", + "port-forward", + "-n", "ingress-nginx", + "svc/ingress-nginx-controller", + f"{port}:443" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # Prometheus if addon_ports.get('prometheus'): port = addon_ports['prometheus'] From ab1231fce5078867e5d7bbabdfbbcad79542cb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:45:57 +0300 Subject: [PATCH 20/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20port-forward=20(=D0=BD=D0=B5=20=D0=B8?= =?UTF-8?q?=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F,=20=D1=82=D1=80=D0=B5=D0=B1=D1=83=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dockerfiles/k8s-portforward/Dockerfile | 43 ++++++ .../k8s-portforward/portforward-container.py | 145 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 dockerfiles/k8s-portforward/Dockerfile create mode 100644 dockerfiles/k8s-portforward/portforward-container.py diff --git a/dockerfiles/k8s-portforward/Dockerfile b/dockerfiles/k8s-portforward/Dockerfile new file mode 100644 index 0000000..3f21cb7 --- /dev/null +++ b/dockerfiles/k8s-portforward/Dockerfile @@ -0,0 +1,43 @@ +# Kubernetes Port-Forward Container +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru + +ARG TARGETARCH + +FROM ubuntu:22.04 + +# Обновляем систему +RUN apt-get update && apt-get upgrade -y && apt-get clean + +# Устанавливаем базовые пакеты + socat для форвардинга портов +RUN apt-get install -y \ + wget \ + curl \ + bash \ + ca-certificates \ + python3 \ + python3-yaml \ + socat \ + netcat-openbsd \ + && apt-get clean + +# Устанавливаем kubectl +RUN if [ "${TARGETARCH}" = "amd64" ]; then \ + wget -O kubectl "https://dl.k8s.io/release/v1.34.1/bin/linux/amd64/kubectl"; \ + else \ + wget -O kubectl "https://dl.k8s.io/release/v1.34.1/bin/linux/arm64/kubectl"; \ + fi && \ + chmod +x kubectl && \ + mv kubectl /usr/local/bin/ + +# Создаем рабочий каталог +WORKDIR /portforward + +# Копируем скрипт порт-форвардинга +COPY portforward-container.py /portforward/portforward-container.py + +# Делаем скрипт исполняемым +RUN chmod +x /portforward/portforward-container.py + +# Команда по умолчанию +CMD ["python3", "/portforward/portforward-container.py"] diff --git a/dockerfiles/k8s-portforward/portforward-container.py b/dockerfiles/k8s-portforward/portforward-container.py new file mode 100644 index 0000000..3713077 --- /dev/null +++ b/dockerfiles/k8s-portforward/portforward-container.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +""" +Скрипт для работы внутри контейнера k8s-portforward +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess +import time +import os +import signal +import json + +def run_kubectl_portforward(cluster_name, namespace, service, remote_port, local_port): + """Запускает kubectl port-forward внутри контейнера""" + cmd = [ + "kubectl", + f"--server=https://{cluster_name}-control-plane:6443", + "--insecure-skip-tls-verify", + "port-forward", + "-n", namespace, + service, + f"{local_port}:{remote_port}" + ] + + print(f"[portforward] Запуск: {' '.join(cmd)}") + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + return process + +def main(): + """Главная функция""" + # Загружаем preset + preset_file = "/workspace/molecule/presets/k8s/kubernetes.yml" + + if not os.path.exists(preset_file): + print(f"❌ Файл {preset_file} не найден") + print("💡 Убедитесь, что workspace подключен в контейнер") + sys.exit(1) + + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + + cluster_name = preset['kind_clusters'][0]['name'] + addon_ports = preset['kind_clusters'][0].get('addon_ports', {}) + + print(f"🔌 Запуск порт-форвардинга для кластера: {cluster_name}") + + processes = [] + + # Ingress HTTP + if addon_ports.get('ingress_http'): + port = addon_ports['ingress_http'] + print(f" - Ingress HTTP: {port} -> ingress-nginx-controller:80") + proc = run_kubectl_portforward( + cluster_name, "ingress-nginx", + "svc/ingress-nginx-controller", 80, port + ) + processes.append(proc) + + # Ingress HTTPS + if addon_ports.get('ingress_https'): + port = addon_ports['ingress_https'] + print(f" - Ingress HTTPS: {port} -> ingress-nginx-controller:443") + proc = run_kubectl_portforward( + cluster_name, "ingress-nginx", + "svc/ingress-nginx-controller", 443, port + ) + processes.append(proc) + + # Prometheus + if addon_ports.get('prometheus'): + port = addon_ports['prometheus'] + print(f" - Prometheus: {port} -> monitoring-kube-prometheus-prometheus:9090") + proc = run_kubectl_portforward( + cluster_name, "monitoring", + "svc/monitoring-kube-prometheus-prometheus", 9090, port + ) + processes.append(proc) + + # Grafana + if addon_ports.get('grafana'): + port = addon_ports['grafana'] + print(f" - Grafana: {port} -> monitoring-grafana:80") + proc = run_kubectl_portforward( + cluster_name, "monitoring", + "svc/monitoring-grafana", 80, port + ) + processes.append(proc) + + # Kiali + if addon_ports.get('kiali'): + port = addon_ports['kiali'] + print(f" - Kiali: {port} -> kiali:20001") + proc = run_kubectl_portforward( + cluster_name, "istio-system", + "svc/kiali", 20001, port + ) + processes.append(proc) + + # Metrics Server + if addon_ports.get('metrics_server'): + port = addon_ports['metrics_server'] + print(f" - Metrics Server: {port} -> metrics-server:4443") + proc = run_kubectl_portforward( + cluster_name, "kube-system", + "svc/metrics-server", 4443, port + ) + processes.append(proc) + + print("✅ Все порты запущены. Ожидание завершения...") + print("💡 Контейнер будет работать, пока все port-forward активны") + + # Ожидание завершения процессов + try: + while True: + time.sleep(1) + # Проверяем, что все процессы еще работают + alive = [p for p in processes if p.poll() is None] + if not alive: + print("⚠️ Все port-forward завершились") + break + except KeyboardInterrupt: + print("\n🛑 Получен сигнал завершения...") + + # Завершаем все процессы + print("🗑️ Завершение port-forward...") + for proc in processes: + if proc.poll() is None: + proc.terminate() + try: + proc.wait(timeout=5) + except subprocess.TimeoutExpired: + proc.kill() + + print("✅ Завершено") + +if __name__ == "__main__": + main() From 3238b3903ac3941734b1dcbaf2ca977507450a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:48:05 +0300 Subject: [PATCH 21/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD?= =?UTF-8?q?=D0=B5=D1=80=20k8s-portforward=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=80=D1=82-=D1=84=D0=BE=D1=80=D0=B2=D0=B0=D1=80=D0=B4?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создан Dockerfile для контейнера порт-форвардинга - Скрипт portforward-container.py работает внутри контейнера - Скрипт portforward_container.py управляет контейнером с хоста - Контейнер автоматически пробрасывает порты через -p - Контейнер подключается к сети Kind для доступа к кластеру - Добавлен k8s-portforward в docker-get-base-tag --- Makefile | 16 +- .../k8s-portforward/portforward-container.py | 0 scripts/portforward_container.py | 151 ++++++++++++++++++ 3 files changed, 160 insertions(+), 7 deletions(-) mode change 100644 => 100755 dockerfiles/k8s-portforward/portforward-container.py create mode 100755 scripts/portforward_container.py diff --git a/Makefile b/Makefile index 9ceb4a8..f23ba57 100644 --- a/Makefile +++ b/Makefile @@ -905,13 +905,15 @@ docker-get-base-tag: TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ansible-controller) \ TAG="latest";; \ - k8s) \ - TAG="latest";; \ - *) \ - echo "❌ Неизвестный образ: $(IMAGE)"; \ - exit 1;; \ - esac; \ - echo "$$TAG" + k8s) \ + TAG="latest";; \ + k8s-portforward) \ + TAG="latest";; \ + *) \ + echo "❌ Неизвестный образ: $(IMAGE)"; \ + exit 1;; \ +esac; \ +echo "$$TAG" # Сборка одного образа с multi-arch docker-build-image: diff --git a/dockerfiles/k8s-portforward/portforward-container.py b/dockerfiles/k8s-portforward/portforward-container.py old mode 100644 new mode 100755 diff --git a/scripts/portforward_container.py b/scripts/portforward_container.py new file mode 100755 index 0000000..b2236d5 --- /dev/null +++ b/scripts/portforward_container.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +""" +Скрипт для управления контейнером k8s-portforward +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess +import re +import os + +CONTAINER_NAME = "k8s-portforward" +IMAGE_NAME = "inecs/ansible-lab:k8s-portforward-latest" + +def get_ports_from_preset(): + """Получает список портов из preset""" + preset_file = "molecule/presets/k8s/kubernetes.yml" + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + + addon_ports = preset['kind_clusters'][0].get('addon_ports', {}) + return addon_ports + +def get_port_mappings(ports): + """Генерирует список портов для -p""" + port_list = [] + + # Контейнер должен быть в той же Docker сети, что и Kind + # Пробрасываем порты как HOST_PORT:CONTAINER_PORT + if ports.get('ingress_http'): + port_list.append(f"{ports['ingress_http']}:{ports['ingress_http']}") + if ports.get('ingress_https'): + port_list.append(f"{ports['ingress_https']}:{ports['ingress_https']}") + if ports.get('prometheus'): + port_list.append(f"{ports['prometheus']}:{ports['prometheus']}") + if ports.get('grafana'): + port_list.append(f"{ports['grafana']}:{ports['grafana']}") + if ports.get('kiali'): + port_list.append(f"{ports['kiali']}:{ports['kiali']}") + if ports.get('metrics_server'): + port_list.append(f"{ports['metrics_server']}:{ports['metrics_server']}") + + return port_list + +def start_container(): + """Запускает контейнер port-forward""" + print("🚀 Запуск контейнера k8s-portforward...") + + # Получаем порты из preset + ports = get_ports_from_preset() + port_mappings = get_port_mappings(ports) + + if not port_mappings: + print("❌ Нет портов для проброса") + return + + # Формируем команду docker run + cmd = [ + "docker", "run", + "-d", + "--name", CONTAINER_NAME, + "--rm", + "--network", "kind", # Подключаемся к сети Kind + "-v", f"{os.getcwd()}:/workspace", + ] + + # Добавляем порты через -p + for port_map in port_mappings: + cmd.extend(["-p", port_map]) + + cmd.append(IMAGE_NAME) + + print(f"📋 Команда: {' '.join(cmd)}") + + # Удаляем старый контейнер если существует + subprocess.run(["docker", "rm", "-f", CONTAINER_NAME], + capture_output=True, stderr=subprocess.DEVNULL) + + # Запускаем новый контейнер + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode == 0: + print(f"✅ Контейнер {CONTAINER_NAME} запущен") + print(f"📋 Проброшены порты: {', '.join(port_mappings)}") + else: + print(f"❌ Ошибка: {result.stderr}") + return + + # Показываем логи + print("\n📋 Логи контейнера (первые 20 строк):") + subprocess.run(["docker", "logs", "--tail", "20", CONTAINER_NAME]) + +def stop_container(): + """Останавливает контейнер port-forward""" + print("🛑 Остановка контейнера k8s-portforward...") + result = subprocess.run(["docker", "stop", CONTAINER_NAME], + capture_output=True, text=True) + + if result.returncode == 0: + print(f"✅ Контейнер {CONTAINER_NAME} остановлен") + else: + print(f"⚠️ {result.stderr}") + +def restart_container(): + """Перезапускает контейнер port-forward""" + print("🔄 Перезапуск контейнера k8s-portforward...") + stop_container() + start_container() + +def status_container(): + """Показывает статус контейнера""" + print("📊 Статус контейнера k8s-portforward:") + result = subprocess.run(["docker", "ps", "-a", "--filter", f"name={CONTAINER_NAME}", + "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}"], + capture_output=True, text=True) + + if result.returncode == 0: + print(result.stdout) + else: + print("❌ Ошибка при получении статуса") + +def logs_container(): + """Показывает логи контейнера""" + print("📋 Логи контейнера k8s-portforward:") + subprocess.run(["docker", "logs", "-f", CONTAINER_NAME]) + +def main(): + if len(sys.argv) < 2: + print("Usage: portforward_container.py ") + sys.exit(1) + + command = sys.argv[1] + + if command == "start": + start_container() + elif command == "stop": + stop_container() + elif command == "restart": + restart_container() + elif command == "status": + status_container() + elif command == "logs": + logs_container() + else: + print(f"❌ Неизвестная команда: {command}") + print("💡 Доступные команды: start, stop, restart, status, logs") + sys.exit(1) + +if __name__ == "__main__": + main() From 69a589974e73ac1a8bd03cb03c8324d0d438901e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:48:51 +0300 Subject: [PATCH 22/41] =?UTF-8?q?wip:=20=D0=BF=D0=BE=D0=BF=D1=8B=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20port-forward=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20k8s-co?= =?UTF-8?q?ntroller=20(=D1=82=D1=80=D0=B5=D0=B1=D1=83=D0=B5=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/portforward_container.py | 50 +++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/scripts/portforward_container.py b/scripts/portforward_container.py index b2236d5..2be80f7 100755 --- a/scripts/portforward_container.py +++ b/scripts/portforward_container.py @@ -44,9 +44,17 @@ def get_port_mappings(ports): return port_list def start_container(): - """Запускает контейнер port-forward""" + """Запускает контейнер port-forward через k8s-controller""" print("🚀 Запуск контейнера k8s-portforward...") + # Проверяем, что k8s-controller запущен + result = subprocess.run(["docker", "ps", "--filter", "name=k8s-controller", "--format", "{{.Names}}"], + capture_output=True, text=True) + if "k8s-controller" not in result.stdout: + print("❌ Контейнер k8s-controller не запущен") + print("💡 Запустите: make k8s create kubernetes") + return + # Получаем порты из preset ports = get_ports_from_preset() port_mappings = get_port_mappings(ports) @@ -73,12 +81,10 @@ def start_container(): print(f"📋 Команда: {' '.join(cmd)}") - # Удаляем старый контейнер если существует - subprocess.run(["docker", "rm", "-f", CONTAINER_NAME], - capture_output=True, stderr=subprocess.DEVNULL) - - # Запускаем новый контейнер - result = subprocess.run(cmd, capture_output=True, text=True) + # Выполняем команду через k8s-controller + cmd_str = ' '.join(['"' + arg.replace('"', '\\"') + '"' for arg in cmd]) + result = subprocess.run(["docker", "exec", "k8s-controller", "sh", "-c", cmd_str], + capture_output=True, text=True) if result.returncode == 0: print(f"✅ Контейнер {CONTAINER_NAME} запущен") @@ -94,8 +100,18 @@ def start_container(): def stop_container(): """Останавливает контейнер port-forward""" print("🛑 Остановка контейнера k8s-portforward...") - result = subprocess.run(["docker", "stop", CONTAINER_NAME], + + # Проверяем, что k8s-controller запущен + result = subprocess.run(["docker", "ps", "--filter", "name=k8s-controller", "--format", "{{.Names}}"], capture_output=True, text=True) + if "k8s-controller" not in result.stdout: + print("⚠️ Контейнер k8s-controller не запущен, пытаемся остановить локально...") + result = subprocess.run(["docker", "stop", CONTAINER_NAME], + capture_output=True, text=True) + else: + # Останавливаем через k8s-controller + result = subprocess.run(["docker", "exec", "k8s-controller", "docker", "stop", CONTAINER_NAME], + capture_output=True, text=True) if result.returncode == 0: print(f"✅ Контейнер {CONTAINER_NAME} остановлен") @@ -111,9 +127,21 @@ def restart_container(): def status_container(): """Показывает статус контейнера""" print("📊 Статус контейнера k8s-portforward:") - result = subprocess.run(["docker", "ps", "-a", "--filter", f"name={CONTAINER_NAME}", - "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}"], - capture_output=True, text=True) + + # Проверяем, что k8s-controller запущен + result = subprocess.run(["docker", "ps", "--filter", "name=k8s-controller", "--format", "{{.Names}}"], + capture_output=True, text=True) + if "k8s-controller" not in result.stdout: + print("⚠️ Контейнер k8s-controller не запущен, проверяем локально...") + result = subprocess.run(["docker", "ps", "-a", "--filter", f"name={CONTAINER_NAME}", + "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}"], + capture_output=True, text=True) + else: + # Проверяем через k8s-controller + result = subprocess.run(["docker", "exec", "k8s-controller", "docker", "ps", "-a", + "--filter", f"name={CONTAINER_NAME}", + "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}"], + capture_output=True, text=True) if result.returncode == 0: print(result.stdout) From 6d55f924e50c9537507cbf027c99cbb1dfa3f9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:49:50 +0300 Subject: [PATCH 23/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4?= =?UTF-8?q?=D1=8B=20=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80?= =?UTF-8?q?=D0=BE=D0=BC=20port-forward=20=D0=B2=20make=20k8s=20portforward?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены команды: container-start, container-stop, container-restart, container-status, container-logs - Локальные команды: create, list, clear, recreate, delete - Команды контейнера работают через k8s-controller - Обновлена справка с описанием всех команд --- Makefile | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index f23ba57..5e21ffc 100644 --- a/Makefile +++ b/Makefile @@ -1370,7 +1370,7 @@ k8s: fi; \ case "$$PORTFWD_CMD" in \ create) \ - echo "🔌 Создание port-forward..."; \ + echo "🔌 Создание port-forward (локально)..."; \ python3 scripts/portforward.py create;; \ list) \ echo "📋 Список активных port-forward..."; \ @@ -1389,9 +1389,25 @@ k8s: fi; \ echo "🗑️ Удаление port-forward на порту $$PORT_ARG..."; \ python3 scripts/portforward.py delete $$PORT_ARG;; \ + container-start) \ + echo "🚀 Запуск контейнера k8s-portforward..."; \ + python3 scripts/portforward_container.py start;; \ + container-stop) \ + echo "🛑 Остановка контейнера k8s-portforward..."; \ + python3 scripts/portforward_container.py stop;; \ + container-restart) \ + echo "🔄 Перезапуск контейнера k8s-portforward..."; \ + python3 scripts/portforward_container.py restart;; \ + container-status) \ + echo "📊 Статус контейнера k8s-portforward..."; \ + python3 scripts/portforward_container.py status;; \ + container-logs) \ + echo "📋 Логи контейнера k8s-portforward..."; \ + python3 scripts/portforward_container.py logs;; \ *) \ echo "❌ Неизвестная команда: $$PORTFWD_CMD"; \ - echo "💡 Доступные команды: create, list, clear, recreate, delete"; \ + echo "💡 Локальные команды: create, list, clear, recreate, delete"; \ + echo "💡 Команды контейнера: container-start, container-stop, container-restart, container-status, container-logs"; \ exit 1;; \ esac;; \ *) \ @@ -1439,11 +1455,16 @@ k8s: echo " 💡 Команды: apply, delete, update, rollback, list, status"; \ echo " 💡 Пример: make k8s helm apply kubernetes nginx stable/nginx-ingress"; \ echo ""; \ - echo " make k8s helmrepo [cmd] [preset] [name] [url] - работа с Helm репозиториями"; \ - echo " 💡 Команды: add, list, delete, update, packages"; \ - echo " 💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ - echo ""; \ - echo "💡 Примеры:"; \ + echo " make k8s helmrepo [cmd] [preset] [name] [url] - работа с Helm репозиториями"; \ + echo " 💡 Команды: add, list, delete, update, packages"; \ + echo " 💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ + echo ""; \ + echo " make k8s portforward [cmd] - управление port-forward"; \ + echo " 💡 Локальные команды: create, list, clear, recreate, delete [port]"; \ + echo " 💡 Команды контейнера: container-start, container-stop, container-restart, container-status, container-logs"; \ + echo " 💡 Пример: make k8s portforward create"; \ + echo ""; \ + echo "💡 Примеры:"; \ echo " make k8s create # создать минимальный кластер"; \ echo " make k8s create kubernetes # создать кластер с аддонами"; \ echo " make k8s nodes kubernetes # показать узлы кластера"; \ From e3d393ef91aee483e5526048f5a36900292732da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:51:35 +0300 Subject: [PATCH 24/41] =?UTF-8?q?refactor:=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BA=D0=BE=D0=BC=D0=B0=D0=BD=D0=B4=D1=8B=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=D0=B0=20?= =?UTF-8?q?port-forward,=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D1=82=D1=81=D1=8F=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=BE=D0=B4=D1=85=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалены команды container-start, container-stop, container-restart, container-status, container-logs - Оставлены только локальные команды: create, list, clear, recreate, delete - Port-forward работает локально через Python и kubectl на хосте - Упрощена справка --- Makefile | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 5e21ffc..d71c743 100644 --- a/Makefile +++ b/Makefile @@ -1370,7 +1370,7 @@ k8s: fi; \ case "$$PORTFWD_CMD" in \ create) \ - echo "🔌 Создание port-forward (локально)..."; \ + echo "🔌 Создание port-forward..."; \ python3 scripts/portforward.py create;; \ list) \ echo "📋 Список активных port-forward..."; \ @@ -1389,25 +1389,9 @@ k8s: fi; \ echo "🗑️ Удаление port-forward на порту $$PORT_ARG..."; \ python3 scripts/portforward.py delete $$PORT_ARG;; \ - container-start) \ - echo "🚀 Запуск контейнера k8s-portforward..."; \ - python3 scripts/portforward_container.py start;; \ - container-stop) \ - echo "🛑 Остановка контейнера k8s-portforward..."; \ - python3 scripts/portforward_container.py stop;; \ - container-restart) \ - echo "🔄 Перезапуск контейнера k8s-portforward..."; \ - python3 scripts/portforward_container.py restart;; \ - container-status) \ - echo "📊 Статус контейнера k8s-portforward..."; \ - python3 scripts/portforward_container.py status;; \ - container-logs) \ - echo "📋 Логи контейнера k8s-portforward..."; \ - python3 scripts/portforward_container.py logs;; \ *) \ echo "❌ Неизвестная команда: $$PORTFWD_CMD"; \ - echo "💡 Локальные команды: create, list, clear, recreate, delete"; \ - echo "💡 Команды контейнера: container-start, container-stop, container-restart, container-status, container-logs"; \ + echo "💡 Доступные команды: create, list, clear, recreate, delete"; \ exit 1;; \ esac;; \ *) \ @@ -1459,10 +1443,9 @@ k8s: echo " 💡 Команды: add, list, delete, update, packages"; \ echo " 💡 Пример: make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable"; \ echo ""; \ - echo " make k8s portforward [cmd] - управление port-forward"; \ - echo " 💡 Локальные команды: create, list, clear, recreate, delete [port]"; \ - echo " 💡 Команды контейнера: container-start, container-stop, container-restart, container-status, container-logs"; \ - echo " 💡 Пример: make k8s portforward create"; \ + echo " make k8s portforward [cmd] - управление port-forward"; \ + echo " 💡 Команды: create, list, clear, recreate, delete [port]"; \ + echo " 💡 Пример: make k8s portforward create"; \ echo ""; \ echo "💡 Примеры:"; \ echo " make k8s create # создать минимальный кластер"; \ From 02eab55d73425f542eeb92fa04adce3f24a8e009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:55:49 +0300 Subject: [PATCH 25/41] =?UTF-8?q?feat:=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20port-f?= =?UTF-8?q?orward=20=D0=BF=D1=80=D0=B8=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B8/=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BB=D0=B0=D1=81=D1=82=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Port-forward создается автоматически при make k8s create - Port-forward очищается автоматически при make k8s destroy --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index d71c743..1e26b59 100644 --- a/Makefile +++ b/Makefile @@ -1051,6 +1051,8 @@ k8s: echo "🚀 Запуск создания кластера..."; \ docker exec $$CONTAINER_NAME bash -c "cd /workspace && python3 /workspace/scripts/create_k8s_cluster.py molecule/presets/k8s/$$PRESET.yml $$CONTAINER_NAME"; \ echo "✅ Kind кластер создан"; \ + echo "🔌 Автоматическое создание port-forward..."; \ + python3 scripts/portforward.py create || echo "⚠️ Не удалось создать port-forward автоматически"; \ echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ @@ -1058,6 +1060,8 @@ k8s: PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ PRESET=$${PRESET_ARG:-k8s-minimal}; \ CONTAINER_NAME=k8s-controller; \ + echo "🔌 Очистка port-forward..."; \ + python3 scripts/portforward.py clear || echo "⚠️ Не удалось очистить port-forward"; \ if docker ps | grep -q $$CONTAINER_NAME; then \ echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ From 3b8c6e52ea09bd94347ef4f45b7c9a2054901871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:58:50 +0300 Subject: [PATCH 26/41] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20Kubernetes=20Kind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен раздел 'Требования к системе' (Python 3, kubectl) - Описан автоматический port-forward - Добавлены все команды Helm (apply, delete, update, rollback, list, status) - Добавлены команды управления Helm репозиториями - Добавлены команды работы с манифестами - Обновлены примеры использования - Добавлен раздел Troubleshooting для port-forward - Обновлена архитектура с учетом port-forward - Добавлены порты Ingress и Metrics Server --- docs/kubernetes-kind.md | 389 +++++++++++++++++++++++++++++++++------- 1 file changed, 323 insertions(+), 66 deletions(-) diff --git a/docs/kubernetes-kind.md b/docs/kubernetes-kind.md index 87b7b95..3b86b93 100644 --- a/docs/kubernetes-kind.md +++ b/docs/kubernetes-kind.md @@ -7,17 +7,60 @@ Проект поддерживает автоматическое создание и управление Kubernetes кластерами на базе [Kind](https://kind.sigs.k8s.io/) для тестирования в изолированной лабораторной среде. +## Требования к системе + +Для работы с Kubernetes кластерами **на локальной машине** требуется установить: + +### 1. Python 3 + +Port-forward управляется Python скриптом. Установите Python 3: + +**macOS:** +```bash +brew install python3 +``` + +**Ubuntu/Debian:** +```bash +sudo apt update && sudo apt install -y python3 python3-pip +``` + +**CentOS/RHEL:** +```bash +sudo yum install -y python3 python3-pip +``` + +### 2. kubectl + +`kubectl` используется для port-forward и работы с кластером. Установите kubectl: + +**macOS:** +```bash +brew install kubectl +``` + +**Linux:** +```bash +curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +chmod +x kubectl +sudo mv kubectl /usr/local/bin/ +``` + +**Проверка установки:** +```bash +python3 --version +kubectl version --client +``` + ## Возможности -- Создание Kind кластеров с настраиваемым количеством worker-узлов -- Автоматическая установка аддонов: - - Ingress NGINX Controller - - Metrics Server - - Istio Service Mesh - - Kiali (визуализация Istio) - - Prometheus Stack (Prometheus + Grafana) -- Настройка портов для внешнего доступа к аддонам -- Интеграция с Docker контейнерами в одной лабораторной сети +- ✅ Создание Kind кластеров с настраиваемым количеством worker-узлов +- ✅ Автоматическая установка аддонов (Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack) +- ✅ **Автоматический port-forward** для доступа к аддонам +- ✅ Работа с Helm (установка, удаление, обновление чартов) +- ✅ Работа с Kubernetes манифестами +- ✅ Управление Docker контейнерами в лабораторной сети +- ✅ Интеграция с Istio Service Mesh ## Команды @@ -34,26 +77,148 @@ make k8s create kubernetes make k8s create my-custom-preset ``` +**Что происходит при создании:** +1. Создается Docker контейнер `k8s-controller` с Kind, kubectl, Helm, Istio CLI +2. Создаются Docker контейнеры из раздела `hosts` пресета (если есть) +3. Создается Kind кластер с настройками из пресета +4. Устанавливаются аддоны (Ingress, Metrics Server, Istio, Kiali, Prometheus) +5. **Автоматически создается port-forward** для доступа к сервисам + ### Управление кластером ```bash -# Удаление кластера +# Удаление кластера (с автоматической очисткой port-forward) make k8s destroy [preset] # Остановка кластера (без удаления) -make k8s stop [cluster] +make k8s stop [preset] # Запуск остановленного кластера -make k8s start [cluster] +make k8s start [preset] # Проверка статуса кластера -make k8s status [cluster] +make k8s status [preset] # Получение kubeconfig для подключения -make k8s config [cluster] +make k8s config [preset] -# Открытие shell в контейнере -make k8s shell +# Открытие shell в контейнере k8s-controller +make k8s shell [preset] + +# Просмотр узлов кластера +make k8s nodes [preset] +``` + +### Port-Forward (Проброс портов) + +Port-forward **создается автоматически** при создании кластера и **удаляется автоматически** при его уничтожении. + +```bash +# Создать port-forward для всех сервисов из preset +make k8s portforward create [preset] + +# Просмотреть активные port-forward +make k8s portforward list + +# Удалить конкретный port-forward по порту +make k8s portforward delete [port] + +# Удалить все port-forward +make k8s portforward clear + +# Пересоздать все port-forward (очистить + создать) +make k8s portforward recreate +``` + +**Порты по умолчанию:** +- **Prometheus:** 9090 +- **Grafana:** 3000 +- **Kiali:** 20001 +- **Ingress HTTP:** 8081 +- **Ingress HTTPS:** 8443 +- **Metrics Server:** 4443 + +### Установка аддонов из манифестов + +```bash +# Установить аддон из URL манифеста +make k8s addon [preset] [manifest_url] + +# Пример: установить MetalLB +make k8s addon kubernetes https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml +``` + +### Работа с манифестами + +```bash +# Применить манифест +make k8s manifest apply [preset] [manifest_url_or_path] + +# Удалить ресурсы из манифеста +make k8s manifest delete [preset] [manifest_url_or_path] + +# Обновить манифест (apply с --force) +make k8s manifest update [preset] [manifest_url_or_path] + +# Примеры: +make k8s manifest apply kubernetes https://example.com/app.yaml +make k8s manifest apply kubernetes ./manifests/my-app.yaml +make k8s manifest delete kubernetes ./manifests/my-app.yaml +``` + +### Работа с Helm + +#### Установка и управление релизами + +```bash +# Установить Helm chart +make k8s helm apply [preset] [release_name] [chart_name] + +# Удалить Helm release +make k8s helm delete [preset] [release_name] + +# Обновить Helm release +make k8s helm update [preset] [release_name] [chart_name] + +# Откатить Helm release к предыдущей версии +make k8s helm rollback [preset] [release_name] + +# Список всех Helm releases +make k8s helm list [preset] + +# Статус конкретного Helm release +make k8s helm status [preset] [release_name] + +# Примеры: +make k8s helm apply kubernetes my-app bitnami/nginx +make k8s helm status kubernetes my-app +make k8s helm list kubernetes +make k8s helm rollback kubernetes my-app +``` + +#### Управление Helm репозиториями + +```bash +# Добавить Helm репозиторий +make k8s helmrepo add [preset] [repo_name] [repo_url] + +# Список Helm репозиториев +make k8s helmrepo list [preset] + +# Удалить Helm репозиторий +make k8s helmrepo delete [preset] [repo_name] + +# Обновить Helm репозитории +make k8s helmrepo update [preset] + +# Список пакетов (charts) в репозитории +make k8s helmrepo packages [preset] [repo_name] + +# Примеры: +make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami +make k8s helmrepo list kubernetes +make k8s helmrepo packages kubernetes bitnami +make k8s helmrepo update kubernetes ``` ## Конфигурация @@ -82,18 +247,19 @@ kind_clusters: istio: true kiali: true prometheus_stack: true - ingress_host_http_port: 8081 - ingress_host_https_port: 8443 - # Порты для внешнего доступа к аддонам + # Порты для port-forward addon_ports: + ingress_http: 8081 + ingress_https: 8443 prometheus: 9090 grafana: 3000 kiali: 20001 + metrics_server: 4443 ``` ## Доступ к аддонам -После создания кластера с аддонами, они доступны на следующих портах: +После создания кластера с аддонами, они доступны через **автоматический port-forward**: ### Prometheus ```bash @@ -136,68 +302,112 @@ https://localhost:8443 ### Создание и настройка кластера ```bash -# 1. Создать кластер с аддонами +# 1. Создать кластер с аддонами (port-forward создастся автоматически!) make k8s create kubernetes # 2. Проверить статус -make k8s status +make k8s status kubernetes -# 3. Получить kubeconfig -make k8s config lab +# 3. Проверить port-forward +make k8s portforward list -# 4. Использовать kubeconfig +# 4. Получить kubeconfig +make k8s config kubernetes + +# 5. Использовать kubeconfig export KUBECONFIG=kubeconfig kubectl get nodes kubectl get pods -A -# 5. Открыть Grafana в браузере +# 6. Открыть Grafana в браузере open http://localhost:3000 # Логин: admin # Пароль: (получить командой выше) ``` -### Управление кластером +### Работа с Helm ```bash -# Остановить кластер (без удаления) -make k8s stop lab +# 1. Добавить репозиторий +make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami -# Запустить остановленный кластер -make k8s start lab +# 2. Посмотреть доступные charts +make k8s helmrepo packages kubernetes bitnami -# Проверить конкретный кластер -make k8s status lab +# 3. Установить nginx +make k8s helm apply kubernetes nginx bitnami/nginx -# Получить kubeconfig для конкретного кластера -make k8s config lab +# 4. Проверить статус +make k8s helm status kubernetes nginx -# Удалить кластер +# 5. Посмотреть все релизы +make k8s helm list kubernetes + +# 6. Откатить релиз +make k8s helm rollback kubernetes nginx + +# 7. Удалить релиз +make k8s helm delete kubernetes nginx +``` + +### Работа с манифестами + +```bash +# 1. Применить манифест из URL +make k8s manifest apply kubernetes https://k8s.io/examples/application/deployment.yaml + +# 2. Применить манифест из файла +make k8s manifest apply kubernetes ./manifests/my-app.yaml + +# 3. Обновить манифест +make k8s manifest update kubernetes ./manifests/my-app.yaml + +# 4. Удалить ресурсы +make k8s manifest delete kubernetes ./manifests/my-app.yaml +``` + +### Управление портами + +```bash +# Если нужно пересоздать port-forward +make k8s portforward recreate + +# Посмотреть активные port-forward +make k8s portforward list + +# Удалить конкретный порт (например, 3000 для Grafana) +make k8s portforward delete 3000 + +# Удалить все port-forward +make k8s portforward clear +``` + +### Полное удаление кластера + +```bash +# Удалит кластер, контейнеры и все port-forward make k8s destroy kubernetes ``` ### Работа внутри контейнера ```bash -# Открыть shell в контейнере ansible-controller -make k8s shell +# Открыть shell в контейнере k8s-controller +make k8s shell kubernetes # Внутри контейнера: kind get clusters kubectl --context kind-lab get nodes istioctl --context kind-lab proxy-status kubectl --context kind-lab get pods -n monitoring +helm list --all-namespaces ``` ## Архитектура ``` ┌─────────────────────────────────────────────────────────┐ -│ Docker Network: labnet │ -│ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Ubuntu22 │ │ Debian12 │ │ Ansible │ │ -│ │ Container │ │ Container │ │ Controller │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ Docker Network: kind │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ Kind Cluster: "lab" │ │ @@ -209,19 +419,36 @@ kubectl --context kind-lab get pods -n monitoring │ │ │ Worker 2 │ │ │ │ │ └──────────────┘ │ │ │ │ │ │ -│ │ NodePort Services: │ │ +│ │ ClusterIP Services: │ │ │ │ - Prometheus :9090 │ │ │ │ - Grafana :3000 │ │ │ │ - Kiali :20001 │ │ -│ │ - Ingress :8081 (HTTP), :8443 (HTTPS) │ │ +│ │ - Ingress :80 (HTTP), :443 (HTTPS) │ │ │ └──────────────────────────────────────────────────┘ │ │ │ +│ ┌──────────────┐ │ +│ │ k8s-controller│ ← kubectl port-forward │ +│ │ (Kind, Helm) │ │ +│ └──────────────┘ │ +└─────────────────────────────────────────────────────────┘ + │ + │ kubectl port-forward + │ (localhost) + ▼ +┌─────────────────────────────────────────────────────────┐ +│ Локальная машина │ +│ │ +│ • localhost:9090 → Prometheus │ +│ • localhost:3000 → Grafana │ +│ • localhost:20001 → Kiali │ +│ • localhost:8081 → Ingress HTTP │ +│ • localhost:8443 → Ingress HTTPS │ └─────────────────────────────────────────────────────────┘ ``` ## Настройка портов аддонов -Вы можете настроить порты для внешнего доступа в пресете: +Вы можете настроить порты для port-forward в пресете: ```yaml kind_clusters: @@ -231,26 +458,48 @@ kind_clusters: prometheus_stack: true kiali: true addon_ports: - prometheus: 9090 # Prometheus UI - grafana: 3000 # Grafana UI - kiali: 20001 # Kiali UI + prometheus: 9090 # Port-forward для Prometheus + grafana: 3000 # Port-forward для Grafana + kiali: 20001 # Port-forward для Kiali + ingress_http: 8081 # Port-forward для Ingress HTTP + ingress_https: 8443 # Port-forward для Ingress HTTPS + metrics_server: 4443 # Port-forward для Metrics Server ``` ## Best Practices -1. **Минимальные ресурсы:** Для быстрого тестирования используйте `workers: 0` (только control-plane) -2. **Production-like:** Для реалистичных тестов используйте `workers: 2-3` -3. **Аддоны:** Включайте только необходимые аддоны для уменьшения времени создания -4. **Изоляция:** Каждый preset может иметь свой уникальный кластер с разными настройками -5. **Порты:** Используйте разные порты для разных кластеров, если запускаете несколько +1. **Порты:** Port-forward создается автоматически при создании кластера - не нужно делать это вручную +2. **Локальные зависимости:** Установите Python 3 и kubectl на локальной машине для работы port-forward +3. **Минимальные ресурсы:** Для быстрого тестирования используйте `workers: 0` (только control-plane) +4. **Production-like:** Для реалистичных тестов используйте `workers: 2-3` +5. **Аддоны:** Включайте только необходимые аддоны для уменьшения времени создания +6. **Удаление:** Используйте `make k8s destroy` - он автоматически очистит port-forward +7. **Helm:** Используйте официальные Helm charts из проверенных репозиториев +8. **Изоляция:** Каждый preset может иметь свой уникальный кластер с разными настройками ## Troubleshooting +### Port-forward не работает + +```bash +# Проверить, что порт не занят +lsof -i :3000 + +# Пересоздать port-forward +make k8s portforward recreate + +# Проверить активные port-forward +make k8s portforward list + +# Проверить, что kubectl установлен локально +kubectl version --client +``` + ### Кластер не создается ```bash # Проверить логи -docker logs ansible-controller +docker logs k8s-controller # Проверить доступное место на диске df -h @@ -268,25 +517,32 @@ kubectl get pods -A # Проверить сервисы kubectl get svc -A -# Проверить порты -kubectl get svc -n monitoring +# Проверить логи +kubectl logs -n monitoring -l app.kubernetes.io/name=grafana ``` ### Проблемы с Istio ```bash -# Переустановить Istio -istioctl uninstall -y --context kind-lab -istioctl install -y --set profile=demo --context kind-lab +# Открыть shell в контейнере +make k8s shell kubernetes + +# Внутри контейнера: +istioctl proxy-status --context kind-lab +istioctl proxy-config --context kind-lab ``` -### Проблемы с Prometheus Stack +### Python не установлен ```bash -# Переустановить -helm uninstall monitoring -n monitoring -helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ - --namespace monitoring --kube-context kind-lab +# macOS +brew install python3 + +# Ubuntu/Debian +sudo apt update && sudo apt install -y python3 python3-pip + +# Проверка +python3 --version ``` ## Дополнительные ресурсы @@ -295,3 +551,4 @@ helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \ - [Istio Documentation](https://istio.io/latest/docs/) - [Kiali Documentation](https://kiali.io/documentation/) - [Prometheus Operator](https://prometheus-operator.dev/) +- [Helm Documentation](https://helm.sh/docs/) From 4ca882b5f79b436e6754b1831da5eedb74064a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 09:59:41 +0300 Subject: [PATCH 27/41] =?UTF-8?q?docs:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=B0=20=D0=BA=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=D0=BC=20=D0=B8=20=D0=BA=D1=80=D0=BE=D1=81=D1=81=D0=BF=D0=BB?= =?UTF-8?q?=D0=B0=D1=82=D1=84=D0=BE=D1=80=D0=BC=D0=B5=D0=BD=D0=BD=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен раздел 'Доступ к приложениям' - Описан доступ через Ingress с примерами hosts файла - Описан доступ через ClusterIP с ручным port-forward - Добавлен раздел 'Кроссплатформенность' для macOS и Linux - Добавлены примеры работы с пользовательскими приложениями --- docs/kubernetes-kind.md | 156 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/docs/kubernetes-kind.md b/docs/kubernetes-kind.md index 3b86b93..5ea794a 100644 --- a/docs/kubernetes-kind.md +++ b/docs/kubernetes-kind.md @@ -297,6 +297,128 @@ http://localhost:8081 https://localhost:8443 ``` +## Доступ к приложениям + +### Доступ через Ingress + +Если ваше приложение использует Ingress с доменным именем: + +1. **Создайте манифест с Ingress:** + +```yaml +# my-app-ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: my-app-ingress +spec: + rules: + - host: my-app.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: my-app-service + port: + number: 80 +``` + +2. **Примените манифест:** + +```bash +make k8s manifest apply kubernetes my-app-ingress.yaml +``` + +3. **Добавьте домен в hosts файл:** + +**macOS/Linux:** +```bash +echo "127.0.0.1 my-app.local" | sudo tee -a /etc/hosts +``` + +4. **Откройте приложение через Ingress порт:** + +```bash +# HTTP через Ingress +curl http://my-app.local:8081 +# или в браузере +open http://my-app.local:8081 + +# HTTPS через Ingress +curl -k https://my-app.local:8443 +``` + +**Важно:** Используйте порт Ingress (8081 для HTTP, 8443 для HTTPS) вместе с доменным именем! + +### Доступ через ClusterIP (port-forward) + +Если ваше приложение имеет сервис с типом ClusterIP: + +1. **Установите приложение (например, через Helm):** + +```bash +make k8s helm apply kubernetes my-app bitnami/nginx +``` + +2. **Узнайте имя сервиса:** + +```bash +# Откройте shell в контейнере +make k8s shell kubernetes + +# Посмотрите сервисы +kubectl get svc -A + +# Выход из контейнера +exit +``` + +3. **Создайте port-forward вручную:** + +```bash +# Экспортируйте kubeconfig +export KUBECONFIG=$(pwd)/kubeconfig + +# Создайте port-forward +# Синтаксис: kubectl port-forward -n svc/ : +kubectl port-forward -n default svc/my-app-service 8080:80 +``` + +4. **Откройте приложение:** + +```bash +# Доступно на localhost:8080 +curl http://localhost:8080 +open http://localhost:8080 +``` + +**Примечание:** Port-forward запустится в фоне. Для остановки нажмите `Ctrl+C` или найдите процесс и завершите его: + +```bash +# Найти процесс port-forward +ps aux | grep "kubectl port-forward" + +# Завершить процесс (замените PID) +kill +``` + +### Автоматический port-forward для пользовательских приложений + +Если вы хотите, чтобы port-forward создавался автоматически, добавьте порт в пресет: + +```yaml +# molecule/presets/k8s/kubernetes.yml +addon_ports: + # ... существующие порты аддонов + my_app: 8080 # Ваш порт +``` + +Затем в коде `scripts/portforward.py` добавьте логику для этих портов. Однако, это требует знания имени сервиса и namespace заранее. + +**Рекомендация:** Для пользовательских приложений используйте ручной port-forward или Ingress. + ## Примеры использования ### Создание и настройка кластера @@ -466,6 +588,39 @@ kind_clusters: metrics_server: 4443 # Port-forward для Metrics Server ``` +## Кроссплатформенность + +Port-forward работает одинаково на **macOS** и **Linux**: + +### macOS + +```bash +# Проверка установки +python3 --version +kubectl version --client + +# Работа с port-forward +make k8s portforward create +make k8s portforward list +``` + +### Linux + +```bash +# Проверка установки +python3 --version +kubectl version --client + +# Работа с port-forward (то же самое!) +make k8s portforward create +make k8s portforward list +``` + +**Важно:** Port-forward работает идентично на обеих платформах, так как использует стандартные утилиты: +- `kubectl` (кроссплатформенный) +- Python 3 (кроссплатформенный) +- `kubectl port-forward` команда (кроссплатформенная) + ## Best Practices 1. **Порты:** Port-forward создается автоматически при создании кластера - не нужно делать это вручную @@ -476,6 +631,7 @@ kind_clusters: 6. **Удаление:** Используйте `make k8s destroy` - он автоматически очистит port-forward 7. **Helm:** Используйте официальные Helm charts из проверенных репозиториев 8. **Изоляция:** Каждый preset может иметь свой уникальный кластер с разными настройками +9. **Ingress vs ClusterIP:** Используйте Ingress для доступа по доменным именам, ClusterIP + port-forward для прямого доступа к сервисам ## Troubleshooting From fd80db220a101f3a8a41934feec3ce7c8c154c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 10:03:59 +0300 Subject: [PATCH 28/41] =?UTF-8?q?feat:=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D1=83?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20Ingres?= =?UTF-8?q?s=20=D1=85=D0=BE=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20=D0=B2=20/etc/?= =?UTF-8?q?hosts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен скрипт scripts/manage_hosts.py для управления /etc/hosts - Автоматическое добавление Ingress хостов при создании кластера - Автоматическое удаление Ingress хостов при удалении кластера - Записи помечаются меткой #k8s для разделения со статическими - Команды: add, remove, cleanup, list --- Makefile | 4 + scripts/manage_hosts.py | 190 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100755 scripts/manage_hosts.py diff --git a/Makefile b/Makefile index 1e26b59..38c6cc8 100644 --- a/Makefile +++ b/Makefile @@ -1053,6 +1053,8 @@ k8s: echo "✅ Kind кластер создан"; \ echo "🔌 Автоматическое создание port-forward..."; \ python3 scripts/portforward.py create || echo "⚠️ Не удалось создать port-forward автоматически"; \ + echo "📝 Добавление Ingress хостов в /etc/hosts..."; \ + sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось добавить Ingress хосты (требуется sudo)"; \ echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ @@ -1062,6 +1064,8 @@ k8s: CONTAINER_NAME=k8s-controller; \ echo "🔌 Очистка port-forward..."; \ python3 scripts/portforward.py clear || echo "⚠️ Не удалось очистить port-forward"; \ + echo "📝 Удаление Ingress хостов из /etc/hosts..."; \ + sudo python3 scripts/manage_hosts.py remove 2>/dev/null || echo "⚠️ Не удалось удалить Ingress хосты (требуется sudo)"; \ if docker ps | grep -q $$CONTAINER_NAME; then \ echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ diff --git a/scripts/manage_hosts.py b/scripts/manage_hosts.py new file mode 100755 index 0000000..bd91433 --- /dev/null +++ b/scripts/manage_hosts.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +""" +Скрипт для управления записями в /etc/hosts для Ingress ресурсов +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import subprocess +import re +import os + +HOSTS_FILE = "/etc/hosts" +K8S_MARKER = "#k8s" + +def is_root(): + """Проверяет, запущен ли скрипт от root""" + return os.geteuid() == 0 + +def get_ingress_hosts(): + """Получает список Ingress хостов из кластера""" + # Получаем kubeconfig из контейнера k8s-controller + result = subprocess.run( + "docker exec k8s-controller kind get kubeconfig", + shell=True, capture_output=True, text=True + ) + + if result.returncode != 0: + print("⚠️ Кластер недоступен") + return [] + + # Сохраняем kubeconfig во временный файл + import tempfile + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.yaml') as f: + f.write(result.stdout) + kubeconfig_file = f.name + + try: + # Получаем все Ingress ресурсы + result = subprocess.run( + f"kubectl --kubeconfig {kubeconfig_file} get ingress --all-namespaces -o jsonpath='{{range .items}}{{range .spec.rules}}{{.host}}{{\"\\n\"}}{{end}}{{end}}'", + shell=True, capture_output=True, text=True + ) + + hosts = [] + if result.returncode == 0: + hosts = [h.strip() for h in result.stdout.split('\n') if h.strip()] + + return hosts + finally: + # Удаляем временный файл + os.unlink(kubeconfig_file) + +def get_current_k8s_entries(): + """Получает текущие k8s записи из /etc/hosts""" + if not os.path.exists(HOSTS_FILE): + return [] + + entries = [] + with open(HOSTS_FILE, 'r') as f: + for line in f: + if K8S_MARKER in line: + # Извлекаем IP и hostname + parts = line.split() + if len(parts) >= 2: + ip = parts[0] + hostname = parts[1] + entries.append((ip, hostname)) + + return entries + +def remove_k8s_entries(): + """Удаляет все записи с меткой #k8s из /etc/hosts""" + if not os.path.exists(HOSTS_FILE): + print("⚠️ Файл /etc/hosts не существует") + return + + print("🗑️ Удаление k8s записей из /etc/hosts...") + + # Читаем файл + with open(HOSTS_FILE, 'r') as f: + lines = f.readlines() + + # Фильтруем строки с меткой #k8s + new_lines = [] + removed_count = 0 + + for line in lines: + if K8S_MARKER in line: + removed_count += 1 + print(f" ❌ Удалено: {line.strip()}") + else: + new_lines.append(line) + + # Записываем обратно + with open(HOSTS_FILE, 'w') as f: + f.writelines(new_lines) + + print(f"✅ Удалено {removed_count} записей") + +def add_ingress_entries(): + """Добавляет записи Ingress в /etc/hosts""" + if not is_root(): + print("❌ Ошибка: скрипт должен быть запущен от root") + print("💡 Используйте: sudo python3 scripts/manage_hosts.py add") + sys.exit(1) + + # Проверяем доступность кластера + result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True) + if result.returncode != 0: + print("❌ Контейнер k8s-controller не запущен") + return + + print("🔍 Поиск Ingress ресурсов в кластере...") + hosts = get_ingress_hosts() + + if not hosts: + print("ℹ️ Не найдено Ingress ресурсов в кластере") + return + + print(f"📋 Найдено {len(hosts)} Ingress хостов: {', '.join(hosts)}") + + # Получаем текущие записи + current_entries = get_current_k8s_entries() + current_hosts = [e[1] for e in current_entries] + + # Удаляем старые записи + if current_entries: + print("🗑️ Удаление старых k8s записей...") + remove_k8s_entries() + + # Добавляем новые записи + print("➕ Добавление новых записей в /etc/hosts...") + + with open(HOSTS_FILE, 'a') as f: + for host in hosts: + entry = f"127.0.0.1 {host} {K8S_MARKER}\n" + f.write(entry) + print(f" ✅ Добавлено: 127.0.0.1 {host}") + + print(f"✅ Добавлено {len(hosts)} записей в /etc/hosts") + +def cleanup_k8s_entries(): + """Очищает k8s записи если кластер недоступен""" + print("🔍 Проверка доступности кластера...") + + # Проверяем доступность контейнера + result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True) + if result.returncode == 0: + print("✅ Кластер доступен") + return + + print("⚠️ Кластер недоступен, очищаем k8s записи...") + remove_k8s_entries() + +def list_k8s_entries(): + """Выводит список текущих k8s записей""" + entries = get_current_k8s_entries() + if not entries: + print("ℹ️ Нет k8s записей в /etc/hosts") + return + + print("📋 Текущие k8s записи в /etc/hosts:") + for ip, hostname in entries: + print(f" {ip} {hostname}") + +def main(): + if len(sys.argv) < 2: + print("Usage: manage_hosts.py ") + print(" add - добавить Ingress хосты в /etc/hosts") + print(" remove - удалить все k8s записи из /etc/hosts") + print(" cleanup - удалить k8s записи если кластер недоступен") + print(" list - показать текущие k8s записи") + sys.exit(1) + + command = sys.argv[1] + + if command == "add": + add_ingress_entries() + elif command == "remove": + remove_k8s_entries() + elif command == "cleanup": + cleanup_k8s_entries() + elif command == "list": + list_k8s_entries() + else: + print(f"❌ Неизвестная команда: {command}") + sys.exit(1) + +if __name__ == '__main__': + main() From 9677aea28193ed4b60228342b76a61cb94b1365a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 10:06:41 +0300 Subject: [PATCH 29/41] =?UTF-8?q?feat:=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BE?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20Ingres?= =?UTF-8?q?s=20=D1=85=D0=BE=D1=81=D1=82=D0=BE=D0=B2=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлен вызов manage_hosts.py после: - make k8s manifest apply - make k8s addon - make k8s helm apply - make k8s helm update - Создан тестовый манифест grafana-ingress - Автоматическое обновление /etc/hosts при любых изменениях Ingress --- Makefile | 16 ++++++++++++---- manifests/test-grafana-ingress.yaml | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 manifests/test-grafana-ingress.yaml diff --git a/Makefile b/Makefile index 38c6cc8..16cc8c7 100644 --- a/Makefile +++ b/Makefile @@ -1184,7 +1184,9 @@ k8s: CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ echo "📥 Установка аддона из $$MANIFEST_ARG..."; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ - echo "✅ Аддон установлен";; \ + echo "✅ Аддон установлен"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ nodes) \ echo "🖥️ Просмотр узлов кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ @@ -1236,7 +1238,9 @@ k8s: case "$$MANIFEST_CMD" in \ apply) \ echo "📥 Применение манифеста: $$MANIFEST_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ delete) \ echo "🗑️ Удаление ресурсов из манифеста: $$MANIFEST_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG";; \ @@ -1274,7 +1278,9 @@ k8s: exit 1; \ fi; \ echo "📦 Установка Helm чарта: $$CHART_ARG как $$RELEASE_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ delete) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ @@ -1290,7 +1296,9 @@ k8s: exit 1; \ fi; \ echo "🔄 Обновление Helm релиза: $$RELEASE_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ rollback) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ diff --git a/manifests/test-grafana-ingress.yaml b/manifests/test-grafana-ingress.yaml new file mode 100644 index 0000000..37122ca --- /dev/null +++ b/manifests/test-grafana-ingress.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: grafana-ingress + namespace: monitoring + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: grafana.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: monitoring-grafana + port: + number: 80 From 6ef4090fb26e3e75380a627e38bf6252b7f002ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 10:14:00 +0300 Subject: [PATCH 30/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20Ingress=20?= =?UTF-8?q?=D1=85=D0=BE=D1=81=D1=82=D0=BE=D0=B2=20=D0=B2=D0=BE=20=D0=B2?= =?UTF-8?q?=D1=81=D0=B5=D1=85=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Обновлен вызов скрипта: sudo scripts/manage_hosts.py вместо sudo python3 - Добавлено обновление хостов после: - make k8s manifest delete - make k8s manifest update - make k8s helm delete - Теперь хосты автоматически обновляются при любых изменениях Ingress - Применен тестовый манифест grafana-ingress --- Makefile | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 16cc8c7..e3ec67d 100644 --- a/Makefile +++ b/Makefile @@ -1054,7 +1054,7 @@ k8s: echo "🔌 Автоматическое создание port-forward..."; \ python3 scripts/portforward.py create || echo "⚠️ Не удалось создать port-forward автоматически"; \ echo "📝 Добавление Ingress хостов в /etc/hosts..."; \ - sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось добавить Ingress хосты (требуется sudo)"; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось добавить Ingress хосты (требуется sudo)"; \ echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ @@ -1065,7 +1065,7 @@ k8s: echo "🔌 Очистка port-forward..."; \ python3 scripts/portforward.py clear || echo "⚠️ Не удалось очистить port-forward"; \ echo "📝 Удаление Ingress хостов из /etc/hosts..."; \ - sudo python3 scripts/manage_hosts.py remove 2>/dev/null || echo "⚠️ Не удалось удалить Ingress хосты (требуется sudo)"; \ + sudo scripts/manage_hosts.py remove 2>/dev/null || echo "⚠️ Не удалось удалить Ingress хосты (требуется sudo)"; \ if docker ps | grep -q $$CONTAINER_NAME; then \ echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ @@ -1186,7 +1186,7 @@ k8s: docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ echo "✅ Аддон установлен"; \ echo "📝 Обновление Ingress хостов..."; \ - sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ nodes) \ echo "🖥️ Просмотр узлов кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ @@ -1240,13 +1240,17 @@ k8s: echo "📥 Применение манифеста: $$MANIFEST_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ echo "📝 Обновление Ingress хостов..."; \ - sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ delete) \ echo "🗑️ Удаление ресурсов из манифеста: $$MANIFEST_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ update) \ echo "🔄 Обновление манифеста: $$MANIFEST_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG --force";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG --force"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ *) \ echo "❌ Неизвестная команда: $$MANIFEST_CMD"; \ echo "💡 Доступные команды: apply, delete, update"; \ @@ -1280,7 +1284,7 @@ k8s: echo "📦 Установка Helm чарта: $$CHART_ARG как $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ echo "📝 Обновление Ingress хостов..."; \ - sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ delete) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ @@ -1288,7 +1292,9 @@ k8s: exit 1; \ fi; \ echo "🗑️ Удаление Helm релиза: $$RELEASE_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm uninstall $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm uninstall $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ + echo "📝 Обновление Ingress хостов..."; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ update) \ if [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза и чарт"; \ @@ -1298,7 +1304,7 @@ k8s: echo "🔄 Обновление Helm релиза: $$RELEASE_ARG"; \ docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ echo "📝 Обновление Ingress хостов..."; \ - sudo python3 scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ rollback) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ From fcf3f33e80c3fc048ea7aa227e9190573f9b20b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 10:16:24 +0300 Subject: [PATCH 31/41] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D0=B4=D0=B5=D1=82=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BE=D1=82=D1=87=D0=B5=D1=82=20=D0=BE=20=D1=81?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D0=B8=20=D0=BA=D0=BB?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создан скрипт scripts/k8s_status.py для детального отчета - Показывает: узлы, namespaces, pods, deployments, daemonsets, statefulsets, services, ingress, PVC, события, Helm релизы - Исправлена проблема с подключением kubectl (используется прямой адрес control-plane) - Команда make k8s status теперь показывает полный отчет --- Makefile | 5 +- scripts/k8s_status.py | 300 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+), 3 deletions(-) create mode 100755 scripts/k8s_status.py diff --git a/Makefile b/Makefile index e3ec67d..aa4b2f9 100644 --- a/Makefile +++ b/Makefile @@ -1117,7 +1117,7 @@ k8s: docker exec $$CONTAINER_NAME bash -c "kind get clusters | xargs -I {} kind start cluster --name {}" 2>/dev/null || true; \ echo "✅ Kind кластер запущен";; \ status) \ - echo "📊 Статус Kind кластеров:"; \ + echo "📊 Детальный отчет о состоянии кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ echo "❌ Ошибка: Укажите пресет"; \ @@ -1126,8 +1126,7 @@ k8s: fi; \ CONTAINER_NAME=k8s-controller; \ if docker ps | grep -q $$CONTAINER_NAME; then \ - docker exec $$CONTAINER_NAME bash -c "kind get clusters" 2>/dev/null || echo " Нет кластеров"; \ - docker exec $$CONTAINER_NAME bash -c "kind get clusters | while read cluster; do echo \"Кластер: \$$cluster\"; kubectl --context kind-\$$cluster get nodes 2>/dev/null || true; done" 2>/dev/null || true; \ + python3 scripts/k8s_status.py; \ else \ echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ diff --git a/scripts/k8s_status.py b/scripts/k8s_status.py new file mode 100755 index 0000000..5c76c51 --- /dev/null +++ b/scripts/k8s_status.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python3 +""" +Скрипт для детального отчета о состоянии Kubernetes кластера +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import subprocess +import json + +def get_cluster_name(): + """Получает имя кластера""" + result = subprocess.run("docker exec k8s-controller kind get clusters | head -1", shell=True, capture_output=True, text=True) + if result.returncode == 0: + return result.stdout.strip() + return None + +def run_kubectl_cmd(cmd): + """Выполняет команду kubectl внутри контейнера k8s-controller""" + cluster_name = get_cluster_name() + if cluster_name: + # Используем прямой адрес control-plane + server = f"https://{cluster_name}-control-plane:6443" + cmd_with_server = f"--server={server} --insecure-skip-tls-verify {cmd}" + else: + cmd_with_server = cmd + + full_cmd = f"docker exec k8s-controller kubectl {cmd_with_server}" + result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True) + return result.stdout + +def print_section(title): + """Выводит заголовок секции""" + print(f"\n{'='*70}") + print(f" {title}") + print(f"{'='*70}\n") + +def print_subsection(title): + """Выводит подзаголовок""" + print(f"\n{'─'*70}") + print(f" {title}") + print(f"{'─'*70}\n") + +def get_cluster_info(): + """Получает информацию о кластере""" + print_section("📊 ОБЩАЯ ИНФОРМАЦИЯ О КЛАСТЕРЕ") + + # Версия Kubernetes + version = run_kubectl_cmd("version --short") + print(version) + +def get_nodes_status(): + """Получает статус узлов""" + print_section("🖥️ УЗЛЫ КЛАСТЕРА") + + nodes = run_kubectl_cmd("get nodes -o wide") + print(nodes) + + # Детальная информация о каждом узле + node_names = run_kubectl_cmd("get nodes -o jsonpath='{.items[*].metadata.name}'") + if node_names.strip(): + for node in node_names.strip().split(): + print_subsection(f"Узел: {node}") + node_info = run_kubectl_cmd(f"describe node {node}") + print(node_info) + +def get_namespaces(): + """Получает список namespace""" + print_section("📁 NAMESPACES") + + namespaces = run_kubectl_cmd("get namespaces") + print(namespaces) + +def get_pods_by_namespace(): + """Получает поды по namespace""" + print_section("🪟 PODS") + + # Получаем список namespace + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + for ns in namespaces: + print_subsection(f"Namespace: {ns}") + pods = run_kubectl_cmd(f"get pods -n {ns} -o wide") + if pods.strip(): + print(pods) + else: + print(" (пусто)") + +def get_services(): + """Получает сервисы""" + print_section("🔗 SERVICES") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + for ns in namespaces: + print_subsection(f"Namespace: {ns}") + services = run_kubectl_cmd(f"get services -n {ns}") + if services.strip(): + print(services) + else: + print(" (пусто)") + +def get_ingress(): + """Получает Ingress ресурсы""" + print_section("🌐 INGRESS") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + ingress_found = False + for ns in namespaces: + ingress = run_kubectl_cmd(f"get ingress -n {ns} 2>/dev/null") + if ingress.strip() and "No resources found" not in ingress: + ingress_found = True + print_subsection(f"Namespace: {ns}") + print(ingress) + + if not ingress_found: + print(" Ingress ресурсы не найдены") + +def get_pvcs(): + """Получает PersistentVolumeClaims""" + print_section("💾 VOLUMES (PVC)") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + pvc_found = False + for ns in namespaces: + pvcs = run_kubectl_cmd(f"get pvc -n {ns} 2>/dev/null") + if pvcs.strip() and "No resources found" not in pvcs: + pvc_found = True + print_subsection(f"Namespace: {ns}") + print(pvcs) + + if not pvc_found: + print(" PVC не найдены") + +def get_deployments(): + """Получает Deployments""" + print_section("🚀 DEPLOYMENTS") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + for ns in namespaces: + print_subsection(f"Namespace: {ns}") + deployments = run_kubectl_cmd(f"get deployments -n {ns}") + if deployments.strip(): + print(deployments) + else: + print(" (пусто)") + +def get_daemonsets(): + """Получает DaemonSets""" + print_section("🔧 DAEMONSETS") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + for ns in namespaces: + print_subsection(f"Namespace: {ns}") + daemonsets = run_kubectl_cmd(f"get daemonsets -n {ns}") + if daemonsets.strip(): + print(daemonsets) + else: + print(" (пусто)") + +def get_statefulsets(): + """Получает StatefulSets""" + print_section("🗄️ STATEFULSETS") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + statefulsets_found = False + for ns in namespaces: + statefulsets = run_kubectl_cmd(f"get statefulsets -n {ns} 2>/dev/null") + if statefulsets.strip() and "No resources found" not in statefulsets: + statefulsets_found = True + print_subsection(f"Namespace: {ns}") + print(statefulsets) + + if not statefulsets_found: + print(" StatefulSets не найдены") + +def get_events(): + """Получает события""" + print_section("📅 СОБЫТИЯ (EVENTS)") + + namespaces_output = run_kubectl_cmd("get namespaces -o json") + try: + namespaces_data = json.loads(namespaces_output) + namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']] + except: + namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease'] + + for ns in namespaces: + print_subsection(f"Namespace: {ns}") + events = run_kubectl_cmd(f"get events -n {ns} --sort-by='.lastTimestamp' | tail -20") + if events.strip(): + print(events) + else: + print(" (пусто)") + +def get_helm_releases(): + """Получает Helm релизы""" + print_section("📦 HELM RELEASES") + + helm_output = run_kubectl_cmd("helm list --all-namespaces 2>/dev/null") + if helm_output.strip(): + print(helm_output) + else: + print(" Helm релизы не найдены") + +def get_resource_usage(): + """Получает использование ресурсов""" + print_section("📈 ИСПОЛЬЗОВАНИЕ РЕСУРСОВ") + + try: + # Проверяем наличие metrics-server + top_nodes = run_kubectl_cmd("top nodes 2>/dev/null") + if top_nodes.strip(): + print_subsection("Узлы:") + print(top_nodes) + except: + print(" metrics-server не установлен или недоступен") + + try: + top_pods = run_kubectl_cmd("top pods --all-namespaces 2>/dev/null") + if top_pods.strip(): + print_subsection("Pods (топ-20):") + # Берем только первые 20 строк + lines = top_pods.strip().split('\n') + print('\n'.join(lines[:21])) # + заголовок + except: + pass + +def main(): + """Главная функция""" + # Проверяем доступность контейнера + result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True) + if result.returncode != 0: + print("❌ Контейнер k8s-controller не запущен") + sys.exit(1) + + print("="*70) + print(" ДЕТАЛЬНЫЙ ОТЧЕТ О СОСТОЯНИИ KUBERNETES КЛАСТЕРА") + print("="*70) + + get_cluster_info() + get_nodes_status() + get_namespaces() + get_resource_usage() + get_pods_by_namespace() + get_deployments() + get_daemonsets() + get_statefulsets() + get_services() + get_ingress() + get_pvcs() + get_events() + get_helm_releases() + + print("\n" + "="*70) + print(" ОТЧЕТ ЗАВЕРШЕН") + print("="*70 + "\n") + +if __name__ == '__main__': + main() From eca556cca77e347736e5f22a18b0eb891180454c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 10:23:16 +0300 Subject: [PATCH 32/41] =?UTF-8?q?docs:=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE=20Kubernetes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создана подробная документация по скриптам (k8s-scripts.md) - Полностью переписана документация kubernetes-kind.md - Добавлено содержание с навигацией - Добавлены разделы: Helm, манифесты, Ingress, port-forward - Добавлена архитектура взаимодействия - Добавлены Best Practices и Troubleshooting - Обновлена справка в Makefile - Удалены устаревшие команды (manifest update, addon) - Добавлена ссылка на документацию по скриптам --- Makefile | 54 +- docs/k8s-scripts.md | 275 ++++++++++ docs/kubernetes-kind.md | 1125 +++++++++++++++++++++------------------ 3 files changed, 889 insertions(+), 565 deletions(-) create mode 100644 docs/k8s-scripts.md diff --git a/Makefile b/Makefile index aa4b2f9..d921a22 100644 --- a/Makefile +++ b/Makefile @@ -1160,32 +1160,6 @@ k8s: echo "❌ Не удалось получить kubeconfig"; \ rm -f $$KUBECONFIG_FILE; \ fi;; \ - addon) \ - echo "📦 Установка аддона..."; \ - PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ - MANIFEST_ARG="$(word 4, $(MAKECMDGOALS))"; \ - if [ -z "$$PRESET_ARG" ]; then \ - echo "❌ Ошибка: Укажите пресет"; \ - echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ - exit 1; \ - fi; \ - if [ -z "$$MANIFEST_ARG" ]; then \ - echo "❌ Ошибка: Укажите URL манифеста"; \ - echo "💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ - exit 1; \ - fi; \ - CONTAINER_NAME=k8s-controller; \ - if ! docker ps | grep -q $$CONTAINER_NAME; then \ - echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ - echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ - exit 1; \ - fi; \ - CLUSTER_NAME=$$(docker exec $$CONTAINER_NAME kind get clusters | head -1); \ - echo "📥 Установка аддона из $$MANIFEST_ARG..."; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ - echo "✅ Аддон установлен"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ nodes) \ echo "🖥️ Просмотр узлов кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ @@ -1245,14 +1219,9 @@ k8s: docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG"; \ echo "📝 Обновление Ingress хостов..."; \ sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ - update) \ - echo "🔄 Обновление манифеста: $$MANIFEST_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG --force"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ *) \ echo "❌ Неизвестная команда: $$MANIFEST_CMD"; \ - echo "💡 Доступные команды: apply, delete, update"; \ + echo "💡 Доступные команды: apply, delete"; \ exit 1;; \ esac;; \ helm) \ @@ -1433,17 +1402,15 @@ k8s: echo " make k8s start [cluster] - запустить остановленный кластер"; \ echo " 💡 Можно указать имя кластера или запустить все"; \ echo ""; \ - echo " make k8s status [cluster] - показать статус кластеров"; \ - echo " 💡 Можно указать имя конкретного кластера"; \ + echo " make k8s status [preset] - детальный отчет о состоянии кластера"; \ + echo " 💡 Показывает: узлы, pods, сервисы, Ingress, события, Helm релизы и т.д."; \ + echo " 💡 Требует: пресет"; \ + echo " 💡 Пример: make k8s status kubernetes"; \ echo ""; \ echo " make k8s config [cluster] - получить kubeconfig для подключения"; \ echo " 💡 Сохраняет: kubeconfig в корне проекта"; \ echo " 💡 Можно указать имя конкретного кластера"; \ echo ""; \ - echo " make k8s addon [preset] [url] - установить аддон из манифеста"; \ - echo " 💡 Требует: пресет и URL манифеста"; \ - echo " 💡 Пример: make k8s addon kubernetes https://example.com/manifest.yaml"; \ - echo ""; \ echo " make k8s nodes [preset] - показать узлы кластера"; \ echo " 💡 Требует: пресет"; \ echo " 💡 Пример: make k8s nodes kubernetes"; \ @@ -1453,7 +1420,7 @@ k8s: echo " 💡 Пример: make k8s shell kubernetes"; \ echo ""; \ echo " make k8s manifest [cmd] [preset] [url] - работа с манифестами"; \ - echo " 💡 Команды: apply, delete, update"; \ + echo " 💡 Команды: apply, delete"; \ echo " 💡 Пример: make k8s manifest apply kubernetes https://example.com/deploy.yaml"; \ echo ""; \ echo " make k8s helm [cmd] [preset] [release] [chart] - работа с Helm"; \ @@ -1475,7 +1442,7 @@ k8s: echo " make k8s config kubernetes # получить kubeconfig для кластера"; \ echo " export KUBECONFIG=kubeconfig # использовать конфиг"; \ echo " kubectl get nodes # проверить узлы"; \ - echo " make k8s addon kubernetes https://example.com/manifest.yaml # установить аддон"; \ + echo " make k8s manifest apply kubernetes https://example.com/manifest.yaml # установить манифест"; \ echo " make k8s stop kubernetes # остановить кластер"; \ echo " make k8s start kubernetes # запустить кластер"; \ echo " make k8s destroy kubernetes # удалить кластер с пресетом kubernetes";; \ @@ -1579,10 +1546,13 @@ help: @echo " make k8s destroy [preset] - удалить Kind кластер" @echo " make k8s start [preset] - запустить Kind кластер" @echo " make k8s stop [preset] - остановить Kind кластер" - @echo " make k8s status [preset] - показать статус кластера" + @echo " make k8s status [preset] - детальный отчет о состоянии кластера" @echo " make k8s nodes [preset] - показать узлы кластера" @echo " make k8s config [preset] - получить kubeconfig для подключения" - @echo " make k8s addon [preset] [url] - установить аддон из манифеста" + @echo " make k8s manifest [cmd] [preset] [url] - работа с манифестами (apply, delete)" + @echo " make k8s helm [cmd] [preset] [release] [chart] - работа с Helm" + @echo " make k8s helmrepo [cmd] [preset] [name] [url] - управление Helm репозиториями" + @echo " make k8s portforward [cmd] - управление port-forward (create, list, clear)" @echo " make k8s shell [preset] - открыть shell в контейнере k8s" @echo "" @echo "💡 ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ:" diff --git a/docs/k8s-scripts.md b/docs/k8s-scripts.md new file mode 100644 index 0000000..c57c976 --- /dev/null +++ b/docs/k8s-scripts.md @@ -0,0 +1,275 @@ +# Скрипты управления Kubernetes + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Обзор + +В проекте используются несколько Python скриптов для автоматизации работы с Kubernetes кластерами на базе Kind. Все скрипты находятся в директории `scripts/`. + +## Скрипты + +### 1. `create_k8s_cluster.py` + +**Назначение:** Создание Kind кластера, установка аддонов и создание Docker контейнеров из пресета. + +**Принцип работы:** + +1. **Парсинг пресета:** Читает YAML файл пресета (например, `molecule/presets/k8s/kubernetes.yml`) + +2. **Создание Docker сети:** + - Проверяет наличие сети (по умолчанию `labnet`) + - Создает сеть если её нет + +3. **Создание Docker контейнеров:** + - Читает секцию `hosts` из пресета + - Для каждого хоста создает Docker контейнер с настройками из `systemd_defaults` + - Использует образы из секции `images` + +4. **Создание Kind кластера:** + - Генерирует конфигурацию Kind в формате YAML + - Настраивает `extraPortMappings` для Ingress портов + - Создает кластер через команду `kind create cluster` + +5. **Установка аддонов:** + - **Ingress NGINX:** Устанавливает ingress-nginx controller через kubectl apply + - **Metrics Server:** Устанавливает metrics-server с патчем для insecure TLS + - **Istio:** Устанавливает Istio через istioctl с профилем demo + - **Kiali:** Устанавливает Kiali через Helm (использует Helm chart) + - **Prometheus Stack:** Устанавливает Prometheus + Grafana через Helm (kube-prometheus-stack) + +6. **Подключение к сети Kind:** Подключает контейнер `k8s-controller` к сети `kind` для доступа к API серверу + +**Параметры:** +```bash +python3 scripts/create_k8s_cluster.py +``` + +**Пример:** +```bash +python3 scripts/create_k8s_cluster.py molecule/presets/k8s/kubernetes.yml k8s-controller +``` + +--- + +### 2. `delete_hosts.py` + +**Назначение:** Удаление Docker контейнеров, созданных из секции `hosts` пресета. + +**Принцип работы:** + +1. **Парсинг пресета:** Читает YAML файл пресета +2. **Получение списка хостов:** Извлекает секцию `hosts` +3. **Удаление контейнеров:** Для каждого хоста выполняет `docker rm -f ` + +**Параметры:** +```bash +python3 scripts/delete_hosts.py +``` + +**Пример:** +```bash +python3 scripts/delete_hosts.py molecule/presets/k8s/kubernetes.yml +``` + +--- + +### 3. `portforward.py` + +**Назначение:** Управление port-forward для доступа к сервисам Kubernetes извне кластера. + +**Принцип работы:** + +1. **Загрузка пресета:** Читает файл `molecule/presets/k8s/kubernetes.yml` + +2. **Получение kubeconfig:** + - Копирует kubeconfig из контейнера `k8s-controller` через `docker exec` + - Сохраняет во временный файл + +3. **Модификация kubeconfig:** + - Заменяет `server: https://0.0.0.0:6443` на `server: https://localhost:6443` + - Это необходимо для доступа с локальной машины + +4. **Создание port-forward:** + - Запускает `kubectl port-forward` для каждого сервиса из `addon_ports` + - Использует формат: `kubectl port-forward -n svc/ :` + - Управляет процессами через PID + +**Команды:** +- `create` - создает port-forward для всех сервисов +- `list` - показывает список активных портов +- `clear` - останавливает все port-forward процессы +- `recreate` - очищает и заново создает port-forward +- `delete ` - удаляет конкретный port-forward + +**Пример использования:** +```bash +python3 scripts/portforward.py create +python3 scripts/portforward.py list +python3 scripts/portforward.py delete 3000 +python3 scripts/portforward.py clear +``` + +**Важно:** Скрипт должен запускаться на локальной машине, где установлены `kubectl` и Python 3. + +--- + +### 4. `manage_hosts.py` + +**Назначение:** Автоматическое управление записями в `/etc/hosts` для Ingress ресурсов. + +**Принцип работы:** + +1. **Получение списка Ingress:** + - Получает kubeconfig из контейнера `k8s-controller` + - Выполняет `kubectl get ingress --all-namespaces` + - Извлекает все хосты из Ingress правил + +2. **Работа с /etc/hosts:** + - Использует метку `#k8s` для различения автоматических и ручных записей + - Добавляет записи в формате: `127.0.0.1 #k8s` + - Удаляет только записи с меткой `#k8s` + +3. **Автоматизация:** + - Вызывается автоматически после: + - `make k8s create` (добавление) + - `make k8s destroy` (удаление) + - `make k8s manifest apply/delete` (обновление) + - `make k8s helm apply/delete/update` (обновление) + +**Команды:** +- `add` - добавляет все Ingress хосты в /etc/hosts +- `remove` - удаляет все записи с меткой #k8s +- `cleanup` - удаляет записи если кластер недоступен +- `list` - показывает текущие k8s записи + +**Пример использования:** +```bash +sudo python3 scripts/manage_hosts.py add +sudo python3 scripts/manage_hosts.py list +sudo python3 scripts/manage_hosts.py remove +``` + +**Безопасность:** +- Работает только с записями, помеченными `#k8s` +- Не трогает ручные записи пользователя +- Требует sudo для редактирования `/etc/hosts` + +--- + +### 5. `k8s_status.py` + +**Назначение:** Детальный отчет о состоянии Kubernetes кластера. + +**Принцип работы:** + +1. **Подключение к кластеру:** + - Получает имя кластера через `kind get clusters` + - Формирует адрес API сервера: `https://-control-plane:6443` + - Выполняет все kubectl команды через `docker exec k8s-controller` + +2. **Сбор информации:** + - **Общая информация:** версия Kubernetes + - **Узлы:** статус, ресурсы, описание каждого узла + - **Namespaces:** список всех namespace + - **Использование ресурсов:** метрики через metrics-server (если установлен) + - **Pods:** поды по каждому namespace + - **Deployments:** deployments по namespace + - **DaemonSets:** daemonsets по namespace + - **StatefulSets:** statefulsets по namespace + - **Services:** сервисы по namespace + - **Ingress:** ingress ресурсы + - **PVC:** PersistentVolumeClaims + - **События:** последние 20 событий по namespace + - **Helm релизы:** список установленных через Helm + +3. **Форматирование вывода:** + - Использует секции с разделителями + - Группирует информацию по namespace + - Показывает только непустые секции + +**Пример использования:** +```bash +python3 scripts/k8s_status.py +``` + +**Интеграция:** +- Автоматически вызывается командой `make k8s status [preset]` + +**Особенности:** +- Выполняет все команды внутри контейнера `k8s-controller` +- Использует прямой адрес control-plane для подключения +- Обходит проблемы с kubeconfig через `--insecure-skip-tls-verify` + +--- + +## Архитектура взаимодействия + +``` +┌─────────────────────────────────────────────────────┐ +│ Локальная машина │ +│ │ +│ Makefile → Python скрипты → Docker API │ +│ ↓ ↓ ↓ │ +│ make k8s scripts/*.py docker exec │ +└─────────────────────────────────────────────────────┘ + │ + ↓ +┌─────────────────────────────────────────────────────┐ +│ Docker контейнер │ +│ k8s-controller │ +│ ┌──────────────────────────────────────────┐ │ +│ │ kind, kubectl, helm, istioctl │ │ +│ └──────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ + │ + ↓ +┌─────────────────────────────────────────────────────┐ +│ Docker сеть: kind │ +│ ┌──────────────────────────────────────────┐ │ +│ │ Kind Kubernetes Cluster │ │ +│ │ • Control Plane (6443) │ │ +│ │ • Worker Nodes │ │ +│ │ • Services (ClusterIP) │ │ +│ │ • Ingress │ │ +│ └──────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ +``` + +## Общие принципы + +1. **Изоляция:** Все kubectl команды выполняются внутри контейнера `k8s-controller` +2. **Безопасность:** Используется `--insecure-skip-tls-verify` для обхода проблем с сертификатами +3. **Автоматизация:** Скрипты вызываются автоматически через Makefile +4. **Логгирование:** Все скрипты выводят подробную информацию о своих действиях + +## Требования + +- Python 3 (на локальной машине) +- kubectl (на локальной машине, для portforward.py) +- Docker +- Docker контейнер `k8s-controller` должен быть запущен + +## Отладка + +Если что-то не работает: + +1. **Проверьте контейнер:** + ```bash + docker ps | grep k8s-controller + ``` + +2. **Запустите скрипт вручную:** + ```bash + python3 scripts/k8s_status.py + ``` + +3. **Посмотрите логи:** + ```bash + docker logs k8s-controller + ``` + +4. **Проверьте кластер:** + ```bash + docker exec k8s-controller kubectl get nodes + ``` diff --git a/docs/kubernetes-kind.md b/docs/kubernetes-kind.md index 5ea794a..c7f576d 100644 --- a/docs/kubernetes-kind.md +++ b/docs/kubernetes-kind.md @@ -3,91 +3,150 @@ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru +## Содержание + +- [Описание](#описание) +- [Требования к системе](#требования-к-системе) +- [Возможности](#возможности) +- [Быстрый старт](#быстрый-старт) +- [Команды управления](#команды-управления) +- [Работа с Helm](#работа-с-helm) +- [Работа с манифестами](#работа-с-манифестами) +- [Управление Ingress](#управление-ingress) +- [Проброс портов (Port-forward)](#проброс-портов-port-forward) +- [Мониторинг и логи](#мониторинг-и-логи) +- [Конфигурация](#конфигурация) +- [Архитектура](#архитектура) +- [Доступ к приложениям](#доступ-к-приложениям) +- [Кроссплатформенность](#кроссплатформенность) +- [Подробная документация по скриптам](#подробная-документация-по-скриптам) +- [Best Practices](#best-practices) +- [Troubleshooting](#troubleshooting) + +--- + ## Описание Проект поддерживает автоматическое создание и управление Kubernetes кластерами на базе [Kind](https://kind.sigs.k8s.io/) для тестирования в изолированной лабораторной среде. +Kind позволяет создавать локальные Kubernetes кластеры внутри Docker контейнеров, что идеально подходит для разработки и тестирования без необходимости устанавливать полный Kubernetes. + ## Требования к системе -Для работы с Kubernetes кластерами **на локальной машине** требуется установить: +### Обязательные требования -### 1. Python 3 +1. **Docker** - для запуска Kind кластеров +2. **Python 3** - для управления port-forward +3. **kubectl** - для работы с кластером -Port-forward управляется Python скриптом. Установите Python 3: - -**macOS:** +**Установка на macOS:** ```bash -brew install python3 +brew install docker python3 kubectl ``` -**Ubuntu/Debian:** +**Установка на Ubuntu/Debian:** ```bash -sudo apt update && sudo apt install -y python3 python3-pip +sudo apt update && sudo apt install -y docker.io python3 python3-pip kubectl ``` -**CentOS/RHEL:** +**Установка на CentOS/RHEL:** ```bash -sudo yum install -y python3 python3-pip +sudo yum install -y docker python3 python3-pip kubectl +sudo systemctl start docker +sudo systemctl enable docker ``` -### 2. kubectl +### Проверка установки -`kubectl` используется для port-forward и работы с кластером. Установите kubectl: - -**macOS:** -```bash -brew install kubectl -``` - -**Linux:** -```bash -curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" -chmod +x kubectl -sudo mv kubectl /usr/local/bin/ -``` - -**Проверка установки:** ```bash +docker --version python3 --version kubectl version --client ``` +### Docker группы + +На Linux добавьте пользователя в группу docker: +```bash +sudo usermod -aG docker $USER +# Выйдите и войдите заново +``` + +--- + ## Возможности - ✅ Создание Kind кластеров с настраиваемым количеством worker-узлов - ✅ Автоматическая установка аддонов (Ingress NGINX, Metrics Server, Istio, Kiali, Prometheus Stack) -- ✅ **Автоматический port-forward** для доступа к аддонам -- ✅ Работа с Helm (установка, удаление, обновление чартов) +- ✅ **Автоматический port-forward** для доступа к сервисам +- ✅ Работа с Helm (установка, удаление, обновление, rollback) - ✅ Работа с Kubernetes манифестами +- ✅ Управление Helm репозиториями +- ✅ Автоматическое управление `/etc/hosts` для Ingress - ✅ Управление Docker контейнерами в лабораторной сети - ✅ Интеграция с Istio Service Mesh +- ✅ Детальный отчет о состоянии кластера -## Команды +--- + +## Быстрый старт + +### 1. Создание кластера + +```bash +# Создание кластера с полным набором аддонов +make k8s create kubernetes +``` + +### 2. Проверка статуса + +```bash +# Детальный отчет о кластере +make k8s status kubernetes +``` + +### 3. Доступ к аддонам + +После создания кластера автоматически создаются port-forward: +- Grafana: http://localhost:3000 +- Prometheus: http://localhost:9090 +- Kiali: http://localhost:20001 + +### 4. Удаление кластера + +```bash +make k8s destroy kubernetes +``` + +--- + +## Команды управления ### Создание кластера ```bash -# Создание минимального кластера (без аддонов) +# Минимальный кластер (без аддонов) make k8s create -# Создание кластера с полным набором аддонов +# Полный кластер с аддонами make k8s create kubernetes -# Использование пользовательского пресета +# Пользовательский пресет make k8s create my-custom-preset ``` -**Что происходит при создании:** -1. Создается Docker контейнер `k8s-controller` с Kind, kubectl, Helm, Istio CLI -2. Создаются Docker контейнеры из раздела `hosts` пресета (если есть) -3. Создается Kind кластер с настройками из пресета -4. Устанавливаются аддоны (Ingress, Metrics Server, Istio, Kiali, Prometheus) -5. **Автоматически создается port-forward** для доступа к сервисам +**Что происходит:** +1. Создается контейнер `k8s-controller` с инструментами (Kind, kubectl, Helm, Istio CLI) +2. Создаются Docker контейнеры из раздела `hosts` (если есть) +3. Создается Kind кластер +4. Устанавливаются аддоны +5. Создается автоматический port-forward +6. Добавляются записи в `/etc/hosts` для Ingress -### Управление кластером +### Управление жизненным циклом ```bash -# Удаление кластера (с автоматической очисткой port-forward) +# Удаление кластера (с очисткой port-forward и /etc/hosts) make k8s destroy [preset] # Остановка кластера (без удаления) @@ -96,615 +155,635 @@ make k8s stop [preset] # Запуск остановленного кластера make k8s start [preset] -# Проверка статуса кластера +# Детальный отчет о состоянии make k8s status [preset] -# Получение kubeconfig для подключения -make k8s config [preset] - -# Открытие shell в контейнере k8s-controller -make k8s shell [preset] - -# Просмотр узлов кластера +# Показать узлы кластера make k8s nodes [preset] ``` -### Port-Forward (Проброс портов) - -Port-forward **создается автоматически** при создании кластера и **удаляется автоматически** при его уничтожении. +### Получение kubeconfig ```bash -# Создать port-forward для всех сервисов из preset -make k8s portforward create [preset] +# Сохранить kubeconfig для подключения +make k8s config [preset] -# Просмотреть активные port-forward -make k8s portforward list - -# Удалить конкретный port-forward по порту -make k8s portforward delete [port] - -# Удалить все port-forward -make k8s portforward clear - -# Пересоздать все port-forward (очистить + создать) -make k8s portforward recreate +# Использовать конфиг +export KUBECONFIG=kubeconfig +kubectl get nodes ``` -**Порты по умолчанию:** -- **Prometheus:** 9090 -- **Grafana:** 3000 -- **Kiali:** 20001 -- **Ingress HTTP:** 8081 -- **Ingress HTTPS:** 8443 -- **Metrics Server:** 4443 - -### Установка аддонов из манифестов +### Shell доступ ```bash -# Установить аддон из URL манифеста -make k8s addon [preset] [manifest_url] - -# Пример: установить MetalLB -make k8s addon kubernetes https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml +# Открыть shell в контейнере k8s-controller +make k8s shell [preset] ``` -### Работа с манифестами +--- + +## Работа с Helm + +Helm - это менеджер пакетов для Kubernetes. + +### Управление релизами ```bash -# Применить манифест -make k8s manifest apply [preset] [manifest_url_or_path] +# Установить чарт +make k8s helm apply kubernetes nginx stable/nginx-ingress -# Удалить ресурсы из манифеста -make k8s manifest delete [preset] [manifest_url_or_path] +# Обновить релиз +make k8s helm update kubernetes nginx stable/nginx-ingress -# Обновить манифест (apply с --force) -make k8s manifest update [preset] [manifest_url_or_path] +# Откатить релиз +make k8s helm rollback kubernetes nginx -# Примеры: -make k8s manifest apply kubernetes https://example.com/app.yaml -make k8s manifest apply kubernetes ./manifests/my-app.yaml -make k8s manifest delete kubernetes ./manifests/my-app.yaml -``` +# Посмотреть статус релиза +make k8s helm status kubernetes nginx -### Работа с Helm +# Удалить релиз +make k8s helm delete kubernetes nginx -#### Установка и управление релизами - -```bash -# Установить Helm chart -make k8s helm apply [preset] [release_name] [chart_name] - -# Удалить Helm release -make k8s helm delete [preset] [release_name] - -# Обновить Helm release -make k8s helm update [preset] [release_name] [chart_name] - -# Откатить Helm release к предыдущей версии -make k8s helm rollback [preset] [release_name] - -# Список всех Helm releases -make k8s helm list [preset] - -# Статус конкретного Helm release -make k8s helm status [preset] [release_name] - -# Примеры: -make k8s helm apply kubernetes my-app bitnami/nginx -make k8s helm status kubernetes my-app +# Список всех релизов make k8s helm list kubernetes -make k8s helm rollback kubernetes my-app ``` -#### Управление Helm репозиториями +### Управление репозиториями ```bash -# Добавить Helm репозиторий -make k8s helmrepo add [preset] [repo_name] [repo_url] +# Добавить репозиторий +make k8s helmrepo add kubernetes stable https://charts.helm.sh/stable -# Список Helm репозиториев -make k8s helmrepo list [preset] +# Обновить репозиторий +make k8s helmrepo update kubernetes stable -# Удалить Helm репозиторий -make k8s helmrepo delete [preset] [repo_name] - -# Обновить Helm репозитории -make k8s helmrepo update [preset] - -# Список пакетов (charts) в репозитории -make k8s helmrepo packages [preset] [repo_name] - -# Примеры: -make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami +# Список репозиториев make k8s helmrepo list kubernetes -make k8s helmrepo packages kubernetes bitnami -make k8s helmrepo update kubernetes + +# Показать доступные чарты +make k8s helmrepo packages kubernetes stable + +# Удалить репозиторий +make k8s helmrepo delete kubernetes stable ``` -## Конфигурация +### Пример: установка MySQL -### Пресеты Kubernetes хранятся в `molecule/presets/k8s/` +```bash +# Добавить репозиторий Bitnami +make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami -#### Минимальный кластер (`k8s-minimal.yml`) +# Установить MySQL +make k8s helm apply kubernetes mysql bitnami/mysql + +# Проверить статус +make k8s helm status kubernetes mysql +``` + +--- + +## Работа с манифестами + +Применение обычных Kubernetes манифестов (без Helm). + +```bash +# Применить манифест из URL +make k8s manifest apply kubernetes https://example.com/deploy.yaml + +# Применить манифест из файла +make k8s manifest apply kubernetes /path/to/manifest.yaml + +# Удалить манифест +make k8s manifest delete kubernetes https://example.com/deploy.yaml +``` + +### Пример: создание Deployment ```yaml -kind_clusters: - - name: minimal - workers: 0 # Только control-plane узел - api_port: 6443 +# nginx-deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + replicas: 3 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:latest + ports: + - containerPort: 80 ``` -#### Полный кластер с аддонами (`kubernetes.yml`) +```bash +# Применить +make k8s manifest apply kubernetes ./nginx-deployment.yaml +``` + +--- + +## Управление Ingress + +### Автоматическое управление /etc/hosts + +При создании Ingress ресурсов, их хосты автоматически добавляются в `/etc/hosts` с меткой `#k8s`: ```yaml -kind_clusters: - - name: lab - workers: 2 - api_port: 6443 - addons: - ingress_nginx: true - metrics_server: true - istio: true - kiali: true - prometheus_stack: true - # Порты для port-forward - addon_ports: - ingress_http: 8081 - ingress_https: 8443 - prometheus: 9090 - grafana: 3000 - kiali: 20001 - metrics_server: 4443 -``` - -## Доступ к аддонам - -После создания кластера с аддонами, они доступны через **автоматический port-forward**: - -### Prometheus -```bash -# Web UI доступна на порту 9090 -http://localhost:9090 -``` - -### Grafana -```bash -# Web UI доступна на порту 3000 -http://localhost:3000 - -# Пароль администратора -kubectl get secret -n monitoring monitoring-grafana \ - -o jsonpath="{.data.admin-password}" | base64 -d - -# Логин: admin -# Пароль: (получен выше) -``` - -### Kiali -```bash -# Web UI доступна на порту 20001 -http://localhost:20001 - -# Аутентификация: anonymous (отключена по умолчанию) -``` - -### Istio Ingress -```bash -# HTTP доступен на порту 8081 -http://localhost:8081 - -# HTTPS доступен на порту 8443 -https://localhost:8443 -``` - -## Доступ к приложениям - -### Доступ через Ingress - -Если ваше приложение использует Ingress с доменным именем: - -1. **Создайте манифест с Ingress:** - -```yaml -# my-app-ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: my-app-ingress + name: grafana-ingress + namespace: monitoring spec: rules: - - host: my-app.local + - host: grafana.local http: paths: - path: / pathType: Prefix backend: service: - name: my-app-service + name: monitoring-grafana port: number: 80 ``` -2. **Примените манифест:** - -```bash -make k8s manifest apply kubernetes my-app-ingress.yaml +После применения этого манифеста в `/etc/hosts` автоматически добавится: +``` +127.0.0.1 grafana.local #k8s ``` -3. **Добавьте домен в hosts файл:** +### Доступ к приложению -**macOS/Linux:** -```bash -echo "127.0.0.1 my-app.local" | sudo tee -a /etc/hosts +После установки Ingress и портов, приложение доступно по адресу: +``` +http://grafana.local ``` -4. **Откройте приложение через Ingress порт:** +### Удаление записей + +Записи автоматически удаляются при: +- Удалении кластера (`make k8s destroy`) +- Удалении Ingress манифеста (`make k8s manifest delete`) + +--- + +## Проброс портов (Port-forward) + +Port-forward позволяет получить доступ к ClusterIP сервисам извне кластера. + +### Автоматический port-forward + +После создания кластера автоматически создаются порты: +- Ingress HTTP: `8081:80` +- Ingress HTTPS: `8443:443` +- Grafana: `3000:80` +- Prometheus: `9090:9090` +- Kiali: `20001:20001` + +### Управление вручную ```bash -# HTTP через Ingress -curl http://my-app.local:8081 -# или в браузере -open http://my-app.local:8081 +# Создать port-forward +make k8s portforward create -# HTTPS через Ingress -curl -k https://my-app.local:8443 -``` - -**Важно:** Используйте порт Ingress (8081 для HTTP, 8443 для HTTPS) вместе с доменным именем! - -### Доступ через ClusterIP (port-forward) - -Если ваше приложение имеет сервис с типом ClusterIP: - -1. **Установите приложение (например, через Helm):** - -```bash -make k8s helm apply kubernetes my-app bitnami/nginx -``` - -2. **Узнайте имя сервиса:** - -```bash -# Откройте shell в контейнере -make k8s shell kubernetes - -# Посмотрите сервисы -kubectl get svc -A - -# Выход из контейнера -exit -``` - -3. **Создайте port-forward вручную:** - -```bash -# Экспортируйте kubeconfig -export KUBECONFIG=$(pwd)/kubeconfig - -# Создайте port-forward -# Синтаксис: kubectl port-forward -n svc/ : -kubectl port-forward -n default svc/my-app-service 8080:80 -``` - -4. **Откройте приложение:** - -```bash -# Доступно на localhost:8080 -curl http://localhost:8080 -open http://localhost:8080 -``` - -**Примечание:** Port-forward запустится в фоне. Для остановки нажмите `Ctrl+C` или найдите процесс и завершите его: - -```bash -# Найти процесс port-forward -ps aux | grep "kubectl port-forward" - -# Завершить процесс (замените PID) -kill -``` - -### Автоматический port-forward для пользовательских приложений - -Если вы хотите, чтобы port-forward создавался автоматически, добавьте порт в пресет: - -```yaml -# molecule/presets/k8s/kubernetes.yml -addon_ports: - # ... существующие порты аддонов - my_app: 8080 # Ваш порт -``` - -Затем в коде `scripts/portforward.py` добавьте логику для этих портов. Однако, это требует знания имени сервиса и namespace заранее. - -**Рекомендация:** Для пользовательских приложений используйте ручной port-forward или Ingress. - -## Примеры использования - -### Создание и настройка кластера - -```bash -# 1. Создать кластер с аддонами (port-forward создастся автоматически!) -make k8s create kubernetes - -# 2. Проверить статус -make k8s status kubernetes - -# 3. Проверить port-forward +# Список активных портов make k8s portforward list -# 4. Получить kubeconfig -make k8s config kubernetes - -# 5. Использовать kubeconfig -export KUBECONFIG=kubeconfig -kubectl get nodes -kubectl get pods -A - -# 6. Открыть Grafana в браузере -open http://localhost:3000 -# Логин: admin -# Пароль: (получить командой выше) -``` - -### Работа с Helm - -```bash -# 1. Добавить репозиторий -make k8s helmrepo add kubernetes bitnami https://charts.bitnami.com/bitnami - -# 2. Посмотреть доступные charts -make k8s helmrepo packages kubernetes bitnami - -# 3. Установить nginx -make k8s helm apply kubernetes nginx bitnami/nginx - -# 4. Проверить статус -make k8s helm status kubernetes nginx - -# 5. Посмотреть все релизы -make k8s helm list kubernetes - -# 6. Откатить релиз -make k8s helm rollback kubernetes nginx - -# 7. Удалить релиз -make k8s helm delete kubernetes nginx -``` - -### Работа с манифестами - -```bash -# 1. Применить манифест из URL -make k8s manifest apply kubernetes https://k8s.io/examples/application/deployment.yaml - -# 2. Применить манифест из файла -make k8s manifest apply kubernetes ./manifests/my-app.yaml - -# 3. Обновить манифест -make k8s manifest update kubernetes ./manifests/my-app.yaml - -# 4. Удалить ресурсы -make k8s manifest delete kubernetes ./manifests/my-app.yaml -``` - -### Управление портами - -```bash -# Если нужно пересоздать port-forward -make k8s portforward recreate - -# Посмотреть активные port-forward -make k8s portforward list - -# Удалить конкретный порт (например, 3000 для Grafana) +# Удалить конкретный порт make k8s portforward delete 3000 -# Удалить все port-forward +# Очистить все порты make k8s portforward clear + +# Пересоздать порты +make k8s portforward recreate ``` -### Полное удаление кластера +### Ручной port-forward для своих сервисов ```bash -# Удалит кластер, контейнеры и все port-forward -make k8s destroy kubernetes +# Подключиться к кластеру +export KUBECONFIG=$(docker exec k8s-controller cat /root/.kube/config) + +# Создать port-forward +kubectl port-forward -n default svc/my-service 8080:80 ``` -### Работа внутри контейнера +--- + +## Мониторинг и логи + +### Детальный статус кластера ```bash -# Открыть shell в контейнере k8s-controller +make k8s status kubernetes +``` + +Показывает: +- Узлы кластера +- Namespaces +- Pods +- Services +- Ingress +- Deployments +- DaemonSets +- StatefulSets +- PVC +- События +- Helm релизы +- Использование ресурсов + +### Доступ к аддонам + +**Grafana:** +``` +URL: http://localhost:3000 +Login: admin +Password: admin +``` + +**Prometheus:** +``` +URL: http://localhost:9090 +``` + +**Kiali:** +``` +URL: http://localhost:20001 +Login: admin +Password: admin +``` + +### Логи контейнера + +```bash +# Логи k8s-controller +docker logs k8s-controller + +# Логи в реальном времени +docker logs -f k8s-controller +``` + +### Логи кластера + +```bash +# Логи конкретного pod make k8s shell kubernetes +kubectl logs -n monitoring -# Внутри контейнера: -kind get clusters -kubectl --context kind-lab get nodes -istioctl --context kind-lab proxy-status -kubectl --context kind-lab get pods -n monitoring -helm list --all-namespaces +# Все логи в namespace +kubectl logs -n monitoring --all-containers=true --tail=100 ``` +--- + +## Конфигурация + +### Пресеты + +Пресеты находятся в `molecule/presets/k8s/`: + +**kubernetes.yml** - полный набор аддонов: +- Ingress NGINX +- Metrics Server +- Istio +- Kiali +- Prometheus + Grafana + +### Структура пресета + +```yaml +k8s_cluster: + name: lab + nodes: 1 + +addons: + ingress: true + metrics_server: true + istio: true + kiali: true + prometheus_stack: true + +addon_ports: + ingress_http: 8081 + ingress_https: 8443 + prometheus: 9090 + grafana: 3000 + kiali: 20001 + metrics_server: 4443 + +hosts: + - name: test1 + image: centos8 + systemd_defaults: + container: true + +images: + centos8: + name: inecs/ansible-lab:centos8-latest +``` + +### Пользовательский пресет + +Создайте файл `molecule/presets/k8s/my-preset.yml`: + +```yaml +k8s_cluster: + name: my-cluster + nodes: 2 + +addons: + ingress: true + metrics_server: false + istio: false + kiali: false + prometheus_stack: false +``` + +Использование: +```bash +make k8s create my-preset +``` + +--- + ## Архитектура ``` -┌─────────────────────────────────────────────────────────┐ -│ Docker Network: kind │ -│ │ -│ ┌──────────────────────────────────────────────────┐ │ -│ │ Kind Cluster: "lab" │ │ -│ │ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ Control Plane│ │ Worker 1 │ │ │ -│ │ │ Port 6443 │ │ │ │ │ -│ │ └──────────────┘ └──────────────┘ │ │ -│ │ ┌──────────────┐ │ │ -│ │ │ Worker 2 │ │ │ -│ │ └──────────────┘ │ │ -│ │ │ │ -│ │ ClusterIP Services: │ │ -│ │ - Prometheus :9090 │ │ -│ │ - Grafana :3000 │ │ -│ │ - Kiali :20001 │ │ -│ │ - Ingress :80 (HTTP), :443 (HTTPS) │ │ -│ └──────────────────────────────────────────────────┘ │ -│ │ -│ ┌──────────────┐ │ -│ │ k8s-controller│ ← kubectl port-forward │ -│ │ (Kind, Helm) │ │ -│ └──────────────┘ │ -└─────────────────────────────────────────────────────────┘ - │ - │ kubectl port-forward - │ (localhost) - ▼ -┌─────────────────────────────────────────────────────────┐ -│ Локальная машина │ -│ │ -│ • localhost:9090 → Prometheus │ -│ • localhost:3000 → Grafana │ -│ • localhost:20001 → Kiali │ -│ • localhost:8081 → Ingress HTTP │ -│ • localhost:8443 → Ingress HTTPS │ -└─────────────────────────────────────────────────────────┘ +┌─────────────────────────────────────────────────────┐ +│ Локальная машина │ +│ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ Makefile → Python скрипты → Docker API │ │ +│ │ kubectl (port-forward) │ │ +│ └──────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ + │ + ↓ +┌─────────────────────────────────────────────────────┐ +│ Docker: k8s-controller │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ kind, kubectl, helm, istioctl │ │ +│ └──────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ + │ + ↓ +┌─────────────────────────────────────────────────────┐ +│ Docker сеть: kind │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ Kind Kubernetes Cluster │ │ +│ │ • Control Plane (6443) │ │ +│ │ • Worker Nodes │ │ +│ │ • Services (ClusterIP) │ │ +│ │ • Ingress Controller │ │ +│ │ • Istio Service Mesh │ │ +│ └──────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────┘ ``` -## Настройка портов аддонов +### Компоненты -Вы можете настроить порты для port-forward в пресете: +1. **k8s-controller** - контейнер с инструментами управления +2. **Kind кластер** - Kubernetes кластер в Docker контейнерах +3. **Port-forward** - локальные процессы на хосте +4. **Ingress** - маршрутизация трафика внутрь кластера -```yaml -kind_clusters: - - name: lab - workers: 2 - addons: - prometheus_stack: true - kiali: true - addon_ports: - prometheus: 9090 # Port-forward для Prometheus - grafana: 3000 # Port-forward для Grafana - kiali: 20001 # Port-forward для Kiali - ingress_http: 8081 # Port-forward для Ingress HTTP - ingress_https: 8443 # Port-forward для Ingress HTTPS - metrics_server: 4443 # Port-forward для Metrics Server +--- + +## Доступ к приложениям + +### Доступ через Ingress + +1. Создайте Ingress манифест +2. Примените через `make k8s manifest apply` +3. Запись автоматически добавится в `/etc/hosts` +4. Приложение доступно по доменному имени + +### Доступ через port-forward + +Для ClusterIP сервисов: +```bash +kubectl port-forward -n namespace svc/service-name 8080:80 ``` +Доступ: http://localhost:8080 + +### Доступ внутри кластера + +Из другого pod: +```bash +kubectl exec -it pod-name -- curl http://service-name.namespace:80 +``` + +--- + ## Кроссплатформенность -Port-forward работает одинаково на **macOS** и **Linux**: +### Поддерживаемые платформы -### macOS +- **macOS (Intel & Apple Silicon)** - полная поддержка +- **Linux (amd64 & arm64)** - полная поддержка +- **Windows** - через WSL2 -```bash -# Проверка установки -python3 --version -kubectl version --client +### Проблемы на разных платформах -# Работа с port-forward -make k8s portforward create -make k8s portforward list -``` +**Apple Silicon (M1/M2):** +- Используется ARM64 образы +- Автоматическое определение архитектуры -### Linux +**Linux:** +- Требуются права на Docker socket +- Возможны проблемы с selinux -```bash -# Проверка установки -python3 --version -kubectl version --client +--- -# Работа с port-forward (то же самое!) -make k8s portforward create -make k8s portforward list -``` +## Подробная документация по скриптам -**Важно:** Port-forward работает идентично на обеих платформах, так как использует стандартные утилиты: -- `kubectl` (кроссплатформенный) -- Python 3 (кроссплатформенный) -- `kubectl port-forward` команда (кроссплатформенная) +Для подробного описания работы всех скриптов управления Kubernetes смотрите: + +📖 [Документация по скриптам Kubernetes](k8s-scripts.md) + +Включает: +- Описание каждого скрипта +- Принцип работы +- Примеры использования +- Архитектура взаимодействия +- Отладка + +--- ## Best Practices -1. **Порты:** Port-forward создается автоматически при создании кластера - не нужно делать это вручную -2. **Локальные зависимости:** Установите Python 3 и kubectl на локальной машине для работы port-forward -3. **Минимальные ресурсы:** Для быстрого тестирования используйте `workers: 0` (только control-plane) -4. **Production-like:** Для реалистичных тестов используйте `workers: 2-3` -5. **Аддоны:** Включайте только необходимые аддоны для уменьшения времени создания -6. **Удаление:** Используйте `make k8s destroy` - он автоматически очистит port-forward -7. **Helm:** Используйте официальные Helm charts из проверенных репозиториев -8. **Изоляция:** Каждый preset может иметь свой уникальный кластер с разными настройками -9. **Ingress vs ClusterIP:** Используйте Ingress для доступа по доменным именам, ClusterIP + port-forward для прямого доступа к сервисам +### 1. Используйте пресеты + +Не создавайте кластеры вручную, используйте пресеты для консистентности. + +### 2. Очищайте после работы + +```bash +# После тестирования +make k8s destroy kubernetes +``` + +### 3. Проверяйте статус + +```bash +# Регулярно проверяйте статус +make k8s status kubernetes +``` + +### 4. Используйте Helm для сложных приложений + +Для многослойных приложений лучше использовать Helm вместо сырых манифестов. + +### 5. Логируйте изменения + +Все изменения в кластере через `make k8s` автоматически логируются. + +--- ## Troubleshooting -### Port-forward не работает - -```bash -# Проверить, что порт не занят -lsof -i :3000 - -# Пересоздать port-forward -make k8s portforward recreate - -# Проверить активные port-forward -make k8s portforward list - -# Проверить, что kubectl установлен локально -kubectl version --client -``` - ### Кластер не создается +**Проблема:** `kind create cluster` завершается с ошибкой + +**Решение:** ```bash -# Проверить логи -docker logs k8s-controller +# Проверьте Docker +docker ps -# Проверить доступное место на диске -df -h +# Перезапустите Docker +sudo systemctl restart docker -# Проверить Docker ресурсы -docker system df +# Очистите старые кластеры +kind delete cluster --all ``` -### Проблемы с аддонами +### Port-forward не работает +**Проблема:** Невозможно подключиться к порту + +**Решение:** ```bash -# Проверить статус подов -kubectl get pods -A +# Проверьте процессы +make k8s portforward list -# Проверить сервисы -kubectl get svc -A +# Пересоздайте порты +make k8s portforward recreate -# Проверить логи -kubectl logs -n monitoring -l app.kubernetes.io/name=grafana +# Проверьте, что порт свободен +lsof -i :3000 ``` -### Проблемы с Istio +### kubectl не подключается +**Проблема:** `kubectl get nodes` выдает ошибку + +**Решение:** ```bash -# Открыть shell в контейнере +# Проверьте контейнер +docker ps | grep k8s-controller + +# Зайдите в контейнер make k8s shell kubernetes -# Внутри контейнера: -istioctl proxy-status --context kind-lab -istioctl proxy-config --context kind-lab +# Проверьте кластер +kubectl get nodes + +# Если не работает, пересоздайте кластер +make k8s destroy kubernetes +make k8s create kubernetes ``` -### Python не установлен +### Аддоны не устанавливаются +**Проблема:** Grafana/Prometheus/Kiali не доступны + +**Решение:** ```bash -# macOS -brew install python3 +# Проверьте статус кластера +make k8s status kubernetes -# Ubuntu/Debian -sudo apt update && sudo apt install -y python3 python3-pip +# Проверьте pods +make k8s shell kubernetes +kubectl get pods -n monitoring +kubectl logs -n monitoring -# Проверка -python3 --version +# Пересоздайте кластер +make k8s destroy kubernetes +make k8s create kubernetes ``` -## Дополнительные ресурсы +### Ingress не работает -- [Kind Documentation](https://kind.sigs.k8s.io/docs/) -- [Istio Documentation](https://istio.io/latest/docs/) -- [Kiali Documentation](https://kiali.io/documentation/) -- [Prometheus Operator](https://prometheus-operator.dev/) -- [Helm Documentation](https://helm.sh/docs/) +**Проблема:** Доменные имена не резолвятся + +**Решение:** +```bash +# Проверьте /etc/hosts +cat /etc/hosts | grep k8s + +# Добавьте вручную +sudo scripts/manage_hosts.py add + +# Проверьте Ingress +make k8s shell kubernetes +kubectl get ingress --all-namespaces +``` + +### Helm чарты не устанавливаются + +**Проблема:** `helm install` завершается с ошибкой + +**Решение:** +```bash +# Проверьте репозитории +make k8s helmrepo list kubernetes + +# Обновите репозитории +make k8s helmrepo update kubernetes stable + +# Проверьте логи +make k8s shell kubernetes +kubectl logs -l app= +``` + +### Контейнер k8s-controller не запускается + +**Проблема:** `docker: Error response from daemon: ...` + +**Решение:** +```bash +# Проверьте образ +docker images | grep k8s + +# Пересоберите образ +make docker build-image IMAGE=k8s-amd64 +make docker build-image IMAGE=k8s-arm64 + +# Проверьте Docker +docker system prune -f +``` + +--- + +## Контакты + +- **Автор:** Сергей Антропов +- **Сайт:** https://devops.org.ru +- **GitHub:** https://github.com/your-repo + +--- + +## Лицензия + +MIT From df7473fbd9781500f2d38ebe3d955f69c75d530f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 10:58:37 +0300 Subject: [PATCH 33/41] =?UTF-8?q?fix:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB?= =?UTF-8?q?=D0=B5=D0=BC=D0=B0=20=D1=81=20=D0=BF=D1=80=D0=BE=D0=B1=D1=80?= =?UTF-8?q?=D0=BE=D1=81=D0=BE=D0=BC=20=D0=BF=D0=BE=D1=80=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20Ingress?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Исправлена логика добавления extraPortMappings в конфигурацию Kind - Порты 80 и 443 теперь правильно пробрасываются на host (8081, 8443) - Создана документация k8s-ingress-fix.md с инструкциями --- docs/k8s-ingress-fix.md | 110 ++++++++++++++++++++++++++++++++++ scripts/create_k8s_cluster.py | 32 +++++----- 2 files changed, 128 insertions(+), 14 deletions(-) create mode 100644 docs/k8s-ingress-fix.md diff --git a/docs/k8s-ingress-fix.md b/docs/k8s-ingress-fix.md new file mode 100644 index 0000000..076d2d4 --- /dev/null +++ b/docs/k8s-ingress-fix.md @@ -0,0 +1,110 @@ +# Исправление проблемы с Ingress + +## Проблема + +После создания Kubernetes кластера Ingress ресурсы не доступны по доменным именам. + +## Причина + +1. **extraPortMappings не добавлялись в конфигурацию Kind** - порты 80 и 443 не пробрасывались на host +2. **Записи в /etc/hosts не добавлялись автоматически** при применении манифестов + +## Решение + +### Вариант 1: Быстрое исправление (без пересоздания кластера) + +1. **Добавьте запись в `/etc/hosts` вручную:** + +```bash +echo "127.0.0.1 grafana.local #k8s" | sudo tee -a /etc/hosts +``` + +2. **Создайте port-forward для Ingress напрямую:** + +```bash +# Подключитесь к кластеру +export KUBECONFIG=$(docker exec k8s-controller cat /root/.kube/config) + +# Замените 0.0.0.0 на localhost в kubeconfig +export KUBECONFIG=$(echo "$KUBECONFIG" | sed 's/0\.0\.0\.0:6443/localhost:6443/g' > /tmp/kubeconfig-local.yaml && echo /tmp/kubeconfig-local.yaml) + +# Создайте port-forward для Ingress HTTP +kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8081:80 & + +# Проверьте доступность +curl -H "Host: grafana.local" http://localhost:8081 +``` + +3. **Откройте в браузере:** + +``` +http://grafana.local:8081 +``` + +### Вариант 2: Пересоздание кластера (рекомендуется) + +Исправления уже внесены в код. Просто пересоздайте кластер: + +```bash +# Удалите старый кластер +make k8s destroy kubernetes + +# Создайте новый (с исправлениями) +make k8s create kubernetes + +# Примените манифест с Ingress +make k8s manifest apply kubernetes manifests/test-grafana-ingress.yaml + +# Запись в /etc/hosts добавится автоматически +# Проверьте +cat /etc/hosts | grep k8s + +# Откройте в браузере +open http://grafana.local:8081 +``` + +## Проверка + +```bash +# 1. Проверьте Ingress +docker exec k8s-controller kubectl --server=https://lab-control-plane:6443 --insecure-skip-tls-verify get ingress --all-namespaces + +# 2. Проверьте ports control-plane +docker port lab-control-plane + +# Должны быть: +# 6443/tcp -> 0.0.0.0:6443 +# 80/tcp -> 0.0.0.0:8081 <- после исправления +# 443/tcp -> 0.0.0.0:8443 <- после исправления + +# 3. Проверьте доступность +curl -H "Host: grafana.local" http://localhost:8081 +``` + +## Что было исправлено + +1. В `scripts/create_k8s_cluster.py` исправлена логика добавления `extraPortMappings` в конфигурацию Kind +2. Порты 80 и 443 теперь правильно пробрасываются на host (8081 и 8443) +3. Манифест Ingress можно применить и автоматически добавить запись в `/etc/hosts` + +## Дополнительно + +Если Ingress всё ещё не работает: + +1. **Проверьте статус подов Ingress Controller:** + +```bash +docker exec k8s-controller kubectl --server=https://lab-control-plane:6443 --insecure-skip-tls-verify get pods -n ingress-nginx +``` + +2. **Проверьте логи Ingress Controller:** + +```bash +docker exec k8s-controller kubectl --server=https://lab-control-plane:6443 --insecure-skip-tls-verify logs -n ingress-nginx -l app.kubernetes.io/component=controller +``` + +3. **Проверьте события:** + +```bash +docker exec k8s-controller kubectl --server=https://lab-control-plane:6443 --insecure-skip-tls-verify get events -n monitoring +``` diff --git a/scripts/create_k8s_cluster.py b/scripts/create_k8s_cluster.py index 8eefbd2..993a5a9 100755 --- a/scripts/create_k8s_cluster.py +++ b/scripts/create_k8s_cluster.py @@ -126,21 +126,25 @@ def main(): # Добавляем extraPortMappings для всех портов из addon_ports addon_ports = cluster.get('addon_ports', {}) - port_mappings = [] - # Ingress порты - if addon_ports.get('ingress_http'): - port_mappings.append({ - 'containerPort': 80, - 'hostPort': addon_ports['ingress_http'], - 'protocol': 'TCP' - }) - if addon_ports.get('ingress_https'): - port_mappings.append({ - 'containerPort': 443, - 'hostPort': addon_ports['ingress_https'], - 'protocol': 'TCP' - }) + # Ingress порты для проброса на host + if addon_ports.get('ingress_http') or addon_ports.get('ingress_https'): + # Добавляем extraPortMappings к control-plane узлу + if 'extraPortMappings' not in config['nodes'][0]: + config['nodes'][0]['extraPortMappings'] = [] + + if addon_ports.get('ingress_http'): + config['nodes'][0]['extraPortMappings'].append({ + 'containerPort': 80, + 'hostPort': addon_ports['ingress_http'], + 'protocol': 'TCP' + }) + if addon_ports.get('ingress_https'): + config['nodes'][0]['extraPortMappings'].append({ + 'containerPort': 443, + 'hostPort': addon_ports['ingress_https'], + 'protocol': 'TCP' + }) # Не добавляем extraPortMappings для портов аддонов - используем port-forward From 0ffe1f1a90e9bda53a03394aa5d58586ff97efed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 11:56:14 +0300 Subject: [PATCH 34/41] =?UTF-8?q?refactor:=20=D1=83=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=20manage=5F?= =?UTF-8?q?hosts.py=20=D0=B8=20=D0=B2=D1=81=D0=B5=20=D1=83=D0=BF=D0=BE?= =?UTF-8?q?=D0=BC=D0=B8=D0=BD=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удален скрипт scripts/manage_hosts.py - Удалены все вызовы из Makefile - Удалены упоминания из документации - Обновлена документация по Ingress - теперь требуется ручное добавление в /etc/hosts - Убрано автоматическое управление /etc/hosts (не состоятельное решение) --- Makefile | 24 ++--- docs/k8s-scripts.md | 45 +--------- docs/kubernetes-kind.md | 55 ++++++------ scripts/manage_hosts.py | 190 ---------------------------------------- 4 files changed, 36 insertions(+), 278 deletions(-) delete mode 100755 scripts/manage_hosts.py diff --git a/Makefile b/Makefile index d921a22..c1fb4b4 100644 --- a/Makefile +++ b/Makefile @@ -1053,8 +1053,6 @@ k8s: echo "✅ Kind кластер создан"; \ echo "🔌 Автоматическое создание port-forward..."; \ python3 scripts/portforward.py create || echo "⚠️ Не удалось создать port-forward автоматически"; \ - echo "📝 Добавление Ingress хостов в /etc/hosts..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось добавить Ingress хосты (требуется sudo)"; \ echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ @@ -1064,8 +1062,6 @@ k8s: CONTAINER_NAME=k8s-controller; \ echo "🔌 Очистка port-forward..."; \ python3 scripts/portforward.py clear || echo "⚠️ Не удалось очистить port-forward"; \ - echo "📝 Удаление Ingress хостов из /etc/hosts..."; \ - sudo scripts/manage_hosts.py remove 2>/dev/null || echo "⚠️ Не удалось удалить Ingress хосты (требуется sudo)"; \ if docker ps | grep -q $$CONTAINER_NAME; then \ echo "🗑️ Удаление Kind кластеров..."; \ docker exec $$CONTAINER_NAME bash -c "kind delete clusters --all" 2>/dev/null || true; \ @@ -1211,14 +1207,10 @@ k8s: case "$$MANIFEST_CMD" in \ apply) \ echo "📥 Применение манифеста: $$MANIFEST_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify apply -f $$MANIFEST_ARG";; \ delete) \ echo "🗑️ Удаление ресурсов из манифеста: $$MANIFEST_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify delete -f $$MANIFEST_ARG";; \ *) \ echo "❌ Неизвестная команда: $$MANIFEST_CMD"; \ echo "💡 Доступные команды: apply, delete"; \ @@ -1250,9 +1242,7 @@ k8s: exit 1; \ fi; \ echo "📦 Установка Helm чарта: $$CHART_ARG как $$RELEASE_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade --install $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ delete) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ @@ -1260,9 +1250,7 @@ k8s: exit 1; \ fi; \ echo "🗑️ Удаление Helm релиза: $$RELEASE_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm uninstall $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm uninstall $$RELEASE_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ update) \ if [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза и чарт"; \ @@ -1270,9 +1258,7 @@ k8s: exit 1; \ fi; \ echo "🔄 Обновление Helm релиза: $$RELEASE_ARG"; \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true"; \ - echo "📝 Обновление Ingress хостов..."; \ - sudo scripts/manage_hosts.py add 2>/dev/null || echo "⚠️ Не удалось обновить Ingress хосты (требуется sudo)";; \ + docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; helm upgrade $$RELEASE_ARG $$CHART_ARG --kube-apiserver=https://\$${CLUSTER_NAME}-control-plane:6443 --kube-token=dummy --kube-context=dummy 2>&1 | grep -v '^WARNING' || true";; \ rollback) \ if [ -z "$$RELEASE_ARG" ]; then \ echo "❌ Ошибка: Укажите имя релиза"; \ diff --git a/docs/k8s-scripts.md b/docs/k8s-scripts.md index c57c976..a1f8c34 100644 --- a/docs/k8s-scripts.md +++ b/docs/k8s-scripts.md @@ -114,50 +114,7 @@ python3 scripts/portforward.py clear --- -### 4. `manage_hosts.py` - -**Назначение:** Автоматическое управление записями в `/etc/hosts` для Ingress ресурсов. - -**Принцип работы:** - -1. **Получение списка Ingress:** - - Получает kubeconfig из контейнера `k8s-controller` - - Выполняет `kubectl get ingress --all-namespaces` - - Извлекает все хосты из Ingress правил - -2. **Работа с /etc/hosts:** - - Использует метку `#k8s` для различения автоматических и ручных записей - - Добавляет записи в формате: `127.0.0.1 #k8s` - - Удаляет только записи с меткой `#k8s` - -3. **Автоматизация:** - - Вызывается автоматически после: - - `make k8s create` (добавление) - - `make k8s destroy` (удаление) - - `make k8s manifest apply/delete` (обновление) - - `make k8s helm apply/delete/update` (обновление) - -**Команды:** -- `add` - добавляет все Ingress хосты в /etc/hosts -- `remove` - удаляет все записи с меткой #k8s -- `cleanup` - удаляет записи если кластер недоступен -- `list` - показывает текущие k8s записи - -**Пример использования:** -```bash -sudo python3 scripts/manage_hosts.py add -sudo python3 scripts/manage_hosts.py list -sudo python3 scripts/manage_hosts.py remove -``` - -**Безопасность:** -- Работает только с записями, помеченными `#k8s` -- Не трогает ручные записи пользователя -- Требует sudo для редактирования `/etc/hosts` - ---- - -### 5. `k8s_status.py` +### 4. `k8s_status.py` **Назначение:** Детальный отчет о состоянии Kubernetes кластера. diff --git a/docs/kubernetes-kind.md b/docs/kubernetes-kind.md index c7f576d..de14e37 100644 --- a/docs/kubernetes-kind.md +++ b/docs/kubernetes-kind.md @@ -291,9 +291,24 @@ make k8s manifest apply kubernetes ./nginx-deployment.yaml ## Управление Ingress -### Автоматическое управление /etc/hosts +### Доступ к приложениям через Ingress -При создании Ingress ресурсов, их хосты автоматически добавляются в `/etc/hosts` с меткой `#k8s`: +Для доступа к приложениям по доменным именам нужно вручную добавить записи в `/etc/hosts`: + +```bash +# Добавить запись в /etc/hosts +echo "127.0.0.1 grafana.local #k8s" | sudo tee -a /etc/hosts + +# Очистить DNS кеш macOS +sudo killall -HUP mDNSResponder +``` + +После этого приложение будет доступно по адресу: +``` +http://grafana.local:8081 +``` + +**Пример манифеста Ingress:** ```yaml apiVersion: networking.k8s.io/v1 @@ -315,23 +330,10 @@ spec: number: 80 ``` -После применения этого манифеста в `/etc/hosts` автоматически добавится: -``` -127.0.0.1 grafana.local #k8s -``` - -### Доступ к приложению - -После установки Ingress и портов, приложение доступно по адресу: -``` -http://grafana.local -``` - -### Удаление записей - -Записи автоматически удаляются при: -- Удалении кластера (`make k8s destroy`) -- Удалении Ingress манифеста (`make k8s manifest delete`) +**Важно:** +- Записи в `/etc/hosts` нужно добавлять вручную +- Используйте порт 8081 для HTTP и 8443 для HTTPS +- Для удаления записей используйте sudo --- @@ -729,15 +731,18 @@ make k8s create kubernetes **Решение:** ```bash -# Проверьте /etc/hosts -cat /etc/hosts | grep k8s - -# Добавьте вручную -sudo scripts/manage_hosts.py add - # Проверьте Ingress make k8s shell kubernetes kubectl get ingress --all-namespaces + +# Добавьте запись в /etc/hosts вручную +echo "127.0.0.1 grafana.local #k8s" | sudo tee -a /etc/hosts + +# Очистите DNS кеш +sudo killall -HUP mDNSResponder + +# Проверьте доступность +curl http://grafana.local:8081 ``` ### Helm чарты не устанавливаются diff --git a/scripts/manage_hosts.py b/scripts/manage_hosts.py deleted file mode 100755 index bd91433..0000000 --- a/scripts/manage_hosts.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python3 -""" -Скрипт для управления записями в /etc/hosts для Ingress ресурсов -Автор: Сергей Антропов -Сайт: https://devops.org.ru -""" -import sys -import subprocess -import re -import os - -HOSTS_FILE = "/etc/hosts" -K8S_MARKER = "#k8s" - -def is_root(): - """Проверяет, запущен ли скрипт от root""" - return os.geteuid() == 0 - -def get_ingress_hosts(): - """Получает список Ingress хостов из кластера""" - # Получаем kubeconfig из контейнера k8s-controller - result = subprocess.run( - "docker exec k8s-controller kind get kubeconfig", - shell=True, capture_output=True, text=True - ) - - if result.returncode != 0: - print("⚠️ Кластер недоступен") - return [] - - # Сохраняем kubeconfig во временный файл - import tempfile - with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.yaml') as f: - f.write(result.stdout) - kubeconfig_file = f.name - - try: - # Получаем все Ingress ресурсы - result = subprocess.run( - f"kubectl --kubeconfig {kubeconfig_file} get ingress --all-namespaces -o jsonpath='{{range .items}}{{range .spec.rules}}{{.host}}{{\"\\n\"}}{{end}}{{end}}'", - shell=True, capture_output=True, text=True - ) - - hosts = [] - if result.returncode == 0: - hosts = [h.strip() for h in result.stdout.split('\n') if h.strip()] - - return hosts - finally: - # Удаляем временный файл - os.unlink(kubeconfig_file) - -def get_current_k8s_entries(): - """Получает текущие k8s записи из /etc/hosts""" - if not os.path.exists(HOSTS_FILE): - return [] - - entries = [] - with open(HOSTS_FILE, 'r') as f: - for line in f: - if K8S_MARKER in line: - # Извлекаем IP и hostname - parts = line.split() - if len(parts) >= 2: - ip = parts[0] - hostname = parts[1] - entries.append((ip, hostname)) - - return entries - -def remove_k8s_entries(): - """Удаляет все записи с меткой #k8s из /etc/hosts""" - if not os.path.exists(HOSTS_FILE): - print("⚠️ Файл /etc/hosts не существует") - return - - print("🗑️ Удаление k8s записей из /etc/hosts...") - - # Читаем файл - with open(HOSTS_FILE, 'r') as f: - lines = f.readlines() - - # Фильтруем строки с меткой #k8s - new_lines = [] - removed_count = 0 - - for line in lines: - if K8S_MARKER in line: - removed_count += 1 - print(f" ❌ Удалено: {line.strip()}") - else: - new_lines.append(line) - - # Записываем обратно - with open(HOSTS_FILE, 'w') as f: - f.writelines(new_lines) - - print(f"✅ Удалено {removed_count} записей") - -def add_ingress_entries(): - """Добавляет записи Ingress в /etc/hosts""" - if not is_root(): - print("❌ Ошибка: скрипт должен быть запущен от root") - print("💡 Используйте: sudo python3 scripts/manage_hosts.py add") - sys.exit(1) - - # Проверяем доступность кластера - result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True) - if result.returncode != 0: - print("❌ Контейнер k8s-controller не запущен") - return - - print("🔍 Поиск Ingress ресурсов в кластере...") - hosts = get_ingress_hosts() - - if not hosts: - print("ℹ️ Не найдено Ingress ресурсов в кластере") - return - - print(f"📋 Найдено {len(hosts)} Ingress хостов: {', '.join(hosts)}") - - # Получаем текущие записи - current_entries = get_current_k8s_entries() - current_hosts = [e[1] for e in current_entries] - - # Удаляем старые записи - if current_entries: - print("🗑️ Удаление старых k8s записей...") - remove_k8s_entries() - - # Добавляем новые записи - print("➕ Добавление новых записей в /etc/hosts...") - - with open(HOSTS_FILE, 'a') as f: - for host in hosts: - entry = f"127.0.0.1 {host} {K8S_MARKER}\n" - f.write(entry) - print(f" ✅ Добавлено: 127.0.0.1 {host}") - - print(f"✅ Добавлено {len(hosts)} записей в /etc/hosts") - -def cleanup_k8s_entries(): - """Очищает k8s записи если кластер недоступен""" - print("🔍 Проверка доступности кластера...") - - # Проверяем доступность контейнера - result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True) - if result.returncode == 0: - print("✅ Кластер доступен") - return - - print("⚠️ Кластер недоступен, очищаем k8s записи...") - remove_k8s_entries() - -def list_k8s_entries(): - """Выводит список текущих k8s записей""" - entries = get_current_k8s_entries() - if not entries: - print("ℹ️ Нет k8s записей в /etc/hosts") - return - - print("📋 Текущие k8s записи в /etc/hosts:") - for ip, hostname in entries: - print(f" {ip} {hostname}") - -def main(): - if len(sys.argv) < 2: - print("Usage: manage_hosts.py ") - print(" add - добавить Ingress хосты в /etc/hosts") - print(" remove - удалить все k8s записи из /etc/hosts") - print(" cleanup - удалить k8s записи если кластер недоступен") - print(" list - показать текущие k8s записи") - sys.exit(1) - - command = sys.argv[1] - - if command == "add": - add_ingress_entries() - elif command == "remove": - remove_k8s_entries() - elif command == "cleanup": - cleanup_k8s_entries() - elif command == "list": - list_k8s_entries() - else: - print(f"❌ Неизвестная команда: {command}") - sys.exit(1) - -if __name__ == '__main__': - main() From c2a84a3aee292279ff9c929d344172a5cd86493f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 11:56:57 +0300 Subject: [PATCH 35/41] =?UTF-8?q?refactor:=20=D1=83=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=B5=20=D1=81=D0=BE=D0=B7?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5=20port-forward?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Удалено автоматическое создание port-forward при создании кластера - Port-forward теперь создается только вручную через make k8s portforward create - Команда list уже доступна в help - Обновлены сообщения с подсказками --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c1fb4b4..e3ae22a 100644 --- a/Makefile +++ b/Makefile @@ -1051,8 +1051,7 @@ k8s: echo "🚀 Запуск создания кластера..."; \ docker exec $$CONTAINER_NAME bash -c "cd /workspace && python3 /workspace/scripts/create_k8s_cluster.py molecule/presets/k8s/$$PRESET.yml $$CONTAINER_NAME"; \ echo "✅ Kind кластер создан"; \ - echo "🔌 Автоматическое создание port-forward..."; \ - python3 scripts/portforward.py create || echo "⚠️ Не удалось создать port-forward автоматически"; \ + echo "💡 Для создания port-forward: make k8s portforward create"; \ echo "💡 Для подключения используйте: make k8s kubeconfig"; \ echo "💡 Для остановки используйте: make k8s stop";; \ destroy) \ From c2ab17b7b62106707c79247f3232c17db5a2294c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 12:25:26 +0300 Subject: [PATCH 36/41] =?UTF-8?q?chore:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BD=D0=B5=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7?= =?UTF-8?q?=D1=83=D0=B5=D0=BC=D1=8B=D0=B9=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF?= =?UTF-8?q?=D1=82=20portforward=5Fcontainer.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Скрипт был экспериментальным решением для запуска port-forward в контейнере - Итоговое решение: использование portforward.py напрямую на хосте - Скрипт не использовался ни в Makefile, ни в документации --- scripts/portforward_container.py | 179 ------------------------------- 1 file changed, 179 deletions(-) delete mode 100755 scripts/portforward_container.py diff --git a/scripts/portforward_container.py b/scripts/portforward_container.py deleted file mode 100755 index 2be80f7..0000000 --- a/scripts/portforward_container.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python3 -""" -Скрипт для управления контейнером k8s-portforward -Автор: Сергей Антропов -Сайт: https://devops.org.ru -""" -import sys -import yaml -import subprocess -import re -import os - -CONTAINER_NAME = "k8s-portforward" -IMAGE_NAME = "inecs/ansible-lab:k8s-portforward-latest" - -def get_ports_from_preset(): - """Получает список портов из preset""" - preset_file = "molecule/presets/k8s/kubernetes.yml" - with open(preset_file, 'r') as f: - preset = yaml.safe_load(f) - - addon_ports = preset['kind_clusters'][0].get('addon_ports', {}) - return addon_ports - -def get_port_mappings(ports): - """Генерирует список портов для -p""" - port_list = [] - - # Контейнер должен быть в той же Docker сети, что и Kind - # Пробрасываем порты как HOST_PORT:CONTAINER_PORT - if ports.get('ingress_http'): - port_list.append(f"{ports['ingress_http']}:{ports['ingress_http']}") - if ports.get('ingress_https'): - port_list.append(f"{ports['ingress_https']}:{ports['ingress_https']}") - if ports.get('prometheus'): - port_list.append(f"{ports['prometheus']}:{ports['prometheus']}") - if ports.get('grafana'): - port_list.append(f"{ports['grafana']}:{ports['grafana']}") - if ports.get('kiali'): - port_list.append(f"{ports['kiali']}:{ports['kiali']}") - if ports.get('metrics_server'): - port_list.append(f"{ports['metrics_server']}:{ports['metrics_server']}") - - return port_list - -def start_container(): - """Запускает контейнер port-forward через k8s-controller""" - print("🚀 Запуск контейнера k8s-portforward...") - - # Проверяем, что k8s-controller запущен - result = subprocess.run(["docker", "ps", "--filter", "name=k8s-controller", "--format", "{{.Names}}"], - capture_output=True, text=True) - if "k8s-controller" not in result.stdout: - print("❌ Контейнер k8s-controller не запущен") - print("💡 Запустите: make k8s create kubernetes") - return - - # Получаем порты из preset - ports = get_ports_from_preset() - port_mappings = get_port_mappings(ports) - - if not port_mappings: - print("❌ Нет портов для проброса") - return - - # Формируем команду docker run - cmd = [ - "docker", "run", - "-d", - "--name", CONTAINER_NAME, - "--rm", - "--network", "kind", # Подключаемся к сети Kind - "-v", f"{os.getcwd()}:/workspace", - ] - - # Добавляем порты через -p - for port_map in port_mappings: - cmd.extend(["-p", port_map]) - - cmd.append(IMAGE_NAME) - - print(f"📋 Команда: {' '.join(cmd)}") - - # Выполняем команду через k8s-controller - cmd_str = ' '.join(['"' + arg.replace('"', '\\"') + '"' for arg in cmd]) - result = subprocess.run(["docker", "exec", "k8s-controller", "sh", "-c", cmd_str], - capture_output=True, text=True) - - if result.returncode == 0: - print(f"✅ Контейнер {CONTAINER_NAME} запущен") - print(f"📋 Проброшены порты: {', '.join(port_mappings)}") - else: - print(f"❌ Ошибка: {result.stderr}") - return - - # Показываем логи - print("\n📋 Логи контейнера (первые 20 строк):") - subprocess.run(["docker", "logs", "--tail", "20", CONTAINER_NAME]) - -def stop_container(): - """Останавливает контейнер port-forward""" - print("🛑 Остановка контейнера k8s-portforward...") - - # Проверяем, что k8s-controller запущен - result = subprocess.run(["docker", "ps", "--filter", "name=k8s-controller", "--format", "{{.Names}}"], - capture_output=True, text=True) - if "k8s-controller" not in result.stdout: - print("⚠️ Контейнер k8s-controller не запущен, пытаемся остановить локально...") - result = subprocess.run(["docker", "stop", CONTAINER_NAME], - capture_output=True, text=True) - else: - # Останавливаем через k8s-controller - result = subprocess.run(["docker", "exec", "k8s-controller", "docker", "stop", CONTAINER_NAME], - capture_output=True, text=True) - - if result.returncode == 0: - print(f"✅ Контейнер {CONTAINER_NAME} остановлен") - else: - print(f"⚠️ {result.stderr}") - -def restart_container(): - """Перезапускает контейнер port-forward""" - print("🔄 Перезапуск контейнера k8s-portforward...") - stop_container() - start_container() - -def status_container(): - """Показывает статус контейнера""" - print("📊 Статус контейнера k8s-portforward:") - - # Проверяем, что k8s-controller запущен - result = subprocess.run(["docker", "ps", "--filter", "name=k8s-controller", "--format", "{{.Names}}"], - capture_output=True, text=True) - if "k8s-controller" not in result.stdout: - print("⚠️ Контейнер k8s-controller не запущен, проверяем локально...") - result = subprocess.run(["docker", "ps", "-a", "--filter", f"name={CONTAINER_NAME}", - "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}"], - capture_output=True, text=True) - else: - # Проверяем через k8s-controller - result = subprocess.run(["docker", "exec", "k8s-controller", "docker", "ps", "-a", - "--filter", f"name={CONTAINER_NAME}", - "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}"], - capture_output=True, text=True) - - if result.returncode == 0: - print(result.stdout) - else: - print("❌ Ошибка при получении статуса") - -def logs_container(): - """Показывает логи контейнера""" - print("📋 Логи контейнера k8s-portforward:") - subprocess.run(["docker", "logs", "-f", CONTAINER_NAME]) - -def main(): - if len(sys.argv) < 2: - print("Usage: portforward_container.py ") - sys.exit(1) - - command = sys.argv[1] - - if command == "start": - start_container() - elif command == "stop": - stop_container() - elif command == "restart": - restart_container() - elif command == "status": - status_container() - elif command == "logs": - logs_container() - else: - print(f"❌ Неизвестная команда: {command}") - print("💡 Доступные команды: start, stop, restart, status, logs") - sys.exit(1) - -if __name__ == "__main__": - main() From d8e1052e7bdd9e3193fb49b15ff72c8ca13eb97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 12:28:05 +0300 Subject: [PATCH 37/41] =?UTF-8?q?refactor:=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B8=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20AnsibleLab=20->=20?= =?UTF-8?q?DevOpsLab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Заменено название проекта во всех файлах проекта - Обновлены: * README.md * Makefile * Все файлы документации (.md) * CI/CD конфигурации (Jenkins, GitLab, GitHub Actions, Azure DevOps) * Скрипты (setup-cicd.sh, test-custom-images.sh) * Файлы конфигурации Molecule - Изменена переменная PROJECT_NAME в Makefile на devops-lab - Docker образы inecs/ansible-lab:* оставлены без изменений (чтобы не затрагивать существующие образы в Docker Hub) --- Makefile | 12 ++++++------ README.md | 8 ++++---- cicd/.gitlab-ci.yml | 2 +- cicd/azure-devops/azure-pipelines.yml | 2 +- cicd/github/workflows.yml | 4 ++-- cicd/gitlab/.gitlab-ci.yml | 2 +- cicd/jenkins/Jenkinsfile | 2 +- dockerfiles/README.md | 4 ++-- docs/cicd-setup.md | 4 ++-- docs/creating-roles.md | 4 ++-- docs/dockerfiles.md | 8 ++++---- docs/examples.md | 4 ++-- docs/getting-started.md | 6 +++--- docs/kubernetes-full-guide.md | 4 ++-- docs/linting-guide.md | 2 +- docs/molecule-guide.md | 6 +++--- docs/monitoring.md | 8 ++++---- docs/platform-support.md | 2 +- docs/vault-guide.md | 2 +- molecule/default/molecule.yml | 2 +- scripts/setup-cicd.sh | 4 ++-- scripts/test-custom-images.sh | 4 ++-- 22 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index e3ae22a..e0a6ad5 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # ============================================================================= -# AnsibleLab - Универсальная система тестирования Ansible ролей +# DevOpsLab - Универсальная система тестирования Ansible ролей # Автор: Сергей Антропов # Сайт: https://devops.org.ru # ============================================================================= @@ -20,7 +20,7 @@ WHITE := \033[0;37m RESET := \033[0m # Глобальные переменные -PROJECT_NAME ?= ansible-lab +PROJECT_NAME ?= devops-lab VERSION ?= 0.1.0 AUTHOR ?= "Сергей Антропов" SITE ?= "https://devops.org.ru" @@ -1438,7 +1438,7 @@ k8s: # ============================================================================= help: @echo "==========================================" - @echo "AnsibleLab - Универсальная система" + @echo "DevOpsLab - Универсальная система" @echo "тестирования Ansible ролей" @echo "==========================================" @echo "" @@ -1474,7 +1474,7 @@ help: @echo " make presets info - подробная информация о preset'е" @echo " make presets test - запустить тест с preset'ом" @echo "" - @echo "🖼️ СОБСТВЕННЫЕ ОБРАЗЫ (AnsibleLab):" + @echo "🖼️ СОБСТВЕННЫЕ ОБРАЗЫ (DevOpsLab):" @echo " make custom-images test [minimal|full|performance] - тест с собственными образами" @echo " make custom-images check - проверить наличие собственных образов" @echo " make custom-images build - собрать все образы для тестирования" @@ -1565,7 +1565,7 @@ help: custom-images: @case "$(word 2, $(MAKECMDGOALS))" in \ test) \ - echo "🧪 Тестирование с собственными образами AnsibleLab..."; \ + echo "🧪 Тестирование с собственными образами DevOpsLab..."; \ if [ -z "$(word 3, $(MAKECMDGOALS))" ]; then \ echo "💡 Использование: make custom-images test [minimal|full|performance]"; \ echo "💡 По умолчанию: minimal"; \ @@ -1593,7 +1593,7 @@ custom-images: echo ""; \ echo " 🔨 make custom-images build - собрать все образы для тестирования"; \ echo " 💡 Выполняет: make docker build"; \ - echo " 💡 Собирает: все образы AnsibleLab"; \ + echo " 💡 Собирает: все образы DevOpsLab"; \ echo ""; \ echo "💡 Пресеты для тестирования:"; \ echo " - custom-minimal.yml - минимальный тест (4 хоста)"; \ diff --git a/README.md b/README.md index 02fef60..979f522 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# AnsibleLab - Универсальная система тестирования Ansible ролей +# DevOpsLab - Универсальная система тестирования Ansible ролей **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -6,7 +6,7 @@ ## 📋 Описание -AnsibleLab - это универсальная система для разработки, тестирования и развертывания Ansible ролей с использованием Docker, Molecule и preset конфигураций. Система поддерживает тестирование на различных ОС через Docker контейнеры. +DevOpsLab - это универсальная система для разработки, тестирования и развертывания Ansible ролей с использованием Docker, Molecule и preset конфигураций. Система поддерживает тестирование на различных ОС через Docker контейнеры. ## ✨ Ключевые возможности @@ -22,7 +22,7 @@ AnsibleLab - это универсальная система для разра ## 📁 Структура проекта ``` -AnsibleLab/ +DevOpsLab/ ├── molecule/ # Конфигурация Molecule │ ├── default/ # Основная конфигурация │ │ ├── create.yml # Создание контейнеров @@ -84,7 +84,7 @@ AnsibleLab/ ```bash git clone -cd AnsibleLab +cd DevOpsLab ``` ### 2. Тестирование ролей diff --git a/cicd/.gitlab-ci.yml b/cicd/.gitlab-ci.yml index fc30bba..26e497c 100644 --- a/cicd/.gitlab-ci.yml +++ b/cicd/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# GitLab CI для AnsibleLab +# GitLab CI для DevOpsLab # Автор: Сергей Антропов # Сайт: https://devops.org.ru diff --git a/cicd/azure-devops/azure-pipelines.yml b/cicd/azure-devops/azure-pipelines.yml index 37ffdf6..8292da5 100644 --- a/cicd/azure-devops/azure-pipelines.yml +++ b/cicd/azure-devops/azure-pipelines.yml @@ -1,4 +1,4 @@ -# Azure DevOps Pipeline для AnsibleLab +# Azure DevOps Pipeline для DevOpsLab # Автор: Сергей Антропов # Сайт: https://devops.org.ru diff --git a/cicd/github/workflows.yml b/cicd/github/workflows.yml index ee05852..36deb68 100644 --- a/cicd/github/workflows.yml +++ b/cicd/github/workflows.yml @@ -1,8 +1,8 @@ -# GitHub Actions Workflow для AnsibleLab +# GitHub Actions Workflow для DevOpsLab # Автор: Сергей Антропов # Сайт: https://devops.org.ru -name: AnsibleLab CI/CD Pipeline +name: DevOpsLab CI/CD Pipeline on: push: diff --git a/cicd/gitlab/.gitlab-ci.yml b/cicd/gitlab/.gitlab-ci.yml index 36e80cb..a6b6842 100644 --- a/cicd/gitlab/.gitlab-ci.yml +++ b/cicd/gitlab/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# GitLab CI Pipeline для AnsibleLab +# GitLab CI Pipeline для DevOpsLab # Автор: Сергей Антропов # Сайт: https://devops.org.ru diff --git a/cicd/jenkins/Jenkinsfile b/cicd/jenkins/Jenkinsfile index 0a39be7..d6b03ef 100644 --- a/cicd/jenkins/Jenkinsfile +++ b/cicd/jenkins/Jenkinsfile @@ -1,4 +1,4 @@ -// Jenkins Pipeline для AnsibleLab +// Jenkins Pipeline для DevOpsLab // Автор: Сергей Антропов // Сайт: https://devops.org.ru diff --git a/dockerfiles/README.md b/dockerfiles/README.md index 5bcbabb..8647b0b 100644 --- a/dockerfiles/README.md +++ b/dockerfiles/README.md @@ -1,4 +1,4 @@ -# Docker образы для универсальной системы тестирования AnsibleLab +# Docker образы для универсальной системы тестирования DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -569,7 +569,7 @@ make docker reset-builder ```yaml # molecule/presets/custom-images.yml --- -#description: Preset с собственными образами AnsibleLab +#description: Preset с собственными образами DevOpsLab docker_network: labnet generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini" diff --git a/docs/cicd-setup.md b/docs/cicd-setup.md index 27d6884..9274506 100644 --- a/docs/cicd-setup.md +++ b/docs/cicd-setup.md @@ -1,4 +1,4 @@ -# Настройка CI/CD для AnsibleLab +# Настройка CI/CD для DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -847,7 +847,7 @@ pipeline { # scripts/setup-cicd.sh # Автоматическая настройка CI/CD -echo "🔧 Настройка CI/CD для AnsibleLab..." +echo "🔧 Настройка CI/CD для DevOpsLab..." # Создание директории .github/workflows mkdir -p .github/workflows diff --git a/docs/creating-roles.md b/docs/creating-roles.md index 34d6577..8475d8d 100644 --- a/docs/creating-roles.md +++ b/docs/creating-roles.md @@ -1,4 +1,4 @@ -# Создание и разработка ролей для AnsibleLab +# Создание и разработка ролей для DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -247,7 +247,7 @@ my_role_cache_dir: /var/cache/my-role galaxy_info: author: Сергей Антропов - description: Моя кастомная роль для AnsibleLab + description: Моя кастомная роль для DevOpsLab company: https://devops.org.ru license: MIT min_ansible_version: "2.9" diff --git a/docs/dockerfiles.md b/docs/dockerfiles.md index 63f6f0e..a029c62 100644 --- a/docs/dockerfiles.md +++ b/docs/dockerfiles.md @@ -1,4 +1,4 @@ -# Docker образы AnsibleLab +# Docker образы DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -6,7 +6,7 @@ ## 🐳 Обзор -AnsibleLab использует предварительно собранные Docker образы для различных операционных систем с полной поддержкой systemd. Все образы поддерживают multi-arch сборку и автоматически публикуются в Docker Hub под namespace `inecs/ansible-lab`. +DevOpsLab использует предварительно собранные Docker образы для различных операционных систем с полной поддержкой systemd. Все образы поддерживают multi-arch сборку и автоматически публикуются в Docker Hub под namespace `inecs/ansible-lab`. ## 📁 Структура dockerfiles/ @@ -406,5 +406,5 @@ make docker purge ## 🔗 Полезные ссылки - **Docker Hub**: https://hub.docker.com/r/inecs/ansible-lab -- **AnsibleLab**: https://devops.org.ru -- **Документация**: https://github.com/AnsibleLab/docs +- **DevOpsLab**: https://devops.org.ru +- **Документация**: https://github.com/DevOpsLab/docs diff --git a/docs/examples.md b/docs/examples.md index 4a85b4d..6510278 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1,4 +1,4 @@ -# Примеры использования AnsibleLab +# Примеры использования DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -304,7 +304,7 @@ make vault check ## Заключение -Эти примеры демонстрируют основные возможности AnsibleLab: +Эти примеры демонстрируют основные возможности DevOpsLab: 1. **Быстрое тестирование** с minimal preset 2. **Полное тестирование** с all-images preset diff --git a/docs/getting-started.md b/docs/getting-started.md index a1e0e2b..0d05742 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,4 +1,4 @@ -# Быстрый старт с AnsibleLab +# Быстрый старт с DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -9,8 +9,8 @@ ### 1. Клонирование репозитория ```bash -git clone ssh://git@git.antropoff.ru:222/Ansible/AnsibleLab.git -cd AnsibleLab +git clone ssh://git@git.antropoff.ru:222/Ansible/DevOpsLab.git +cd DevOpsLab ``` ### 2. Проверка структуры проекта diff --git a/docs/kubernetes-full-guide.md b/docs/kubernetes-full-guide.md index 4cc43b5..50f1607 100644 --- a/docs/kubernetes-full-guide.md +++ b/docs/kubernetes-full-guide.md @@ -17,7 +17,7 @@ ## Введение -AnsibleLab предоставляет полную поддержку создания и управления локальными Kubernetes кластерами на основе Kind (Kubernetes in Docker). Kind позволяет запускать Kubernetes кластеры внутри Docker контейнеров, что идеально подходит для разработки, тестирования и обучения. +DevOpsLab предоставляет полную поддержку создания и управления локальными Kubernetes кластерами на основе Kind (Kubernetes in Docker). Kind позволяет запускать Kubernetes кластеры внутри Docker контейнеров, что идеально подходит для разработки, тестирования и обучения. ### Основные возможности @@ -891,7 +891,7 @@ make k8s manifest apply kubernetes ./network-policy.yaml ## Заключение -AnsibleLab предоставляет полный набор инструментов для работы с Kubernetes кластерами локально. Вы можете: +DevOpsLab предоставляет полный набор инструментов для работы с Kubernetes кластерами локально. Вы можете: - Создавать и управлять кластерами - Устанавливать и настраивать приложения diff --git a/docs/linting-guide.md b/docs/linting-guide.md index 05e7f8e..1f87965 100644 --- a/docs/linting-guide.md +++ b/docs/linting-guide.md @@ -6,7 +6,7 @@ ## Обзор -Линтинг ролей - это автоматическая проверка синтаксиса, стиля и соответствия лучшим практикам Ansible. В AnsibleLab используется `ansible-lint` для обеспечения качества кода. +Линтинг ролей - это автоматическая проверка синтаксиса, стиля и соответствия лучшим практикам Ansible. В DevOpsLab используется `ansible-lint` для обеспечения качества кода. ## Команды линтинга diff --git a/docs/molecule-guide.md b/docs/molecule-guide.md index 299c3c6..855f228 100644 --- a/docs/molecule-guide.md +++ b/docs/molecule-guide.md @@ -5,7 +5,7 @@ ## 📋 Обзор -Molecule - это инструмент для тестирования Ansible ролей в изолированных окружениях. В проекте AnsibleLab используется универсальная конфигурация Molecule с поддержкой множества операционных систем и различных preset'ов для тестирования. +Molecule - это инструмент для тестирования Ansible ролей в изолированных окружениях. В проекте DevOpsLab используется универсальная конфигурация Molecule с поддержкой множества операционных систем и различных preset'ов для тестирования. ### 🔧 Fallback значения @@ -64,7 +64,7 @@ platforms: ``` - **Назначение:** Определяет доступные Docker образы для тестирования - **Поддерживаемые ОС:** Ubuntu, Debian, RHEL, CentOS, AlmaLinux, Rocky Linux, Alt Linux, Astra Linux, RedOS -- **Собственные образы:** AnsibleLab создает собственные образы для тестирования +- **Собственные образы:** DevOpsLab создает собственные образы для тестирования **Provisioner (Провижнер):** ```yaml @@ -154,7 +154,7 @@ vars: ``` - **Назначение:** Определение fallback значений для случаев когда preset файл не найден - **Функция:** Обеспечение работоспособности даже без preset файлов -- **Образы:** Собственные образы AnsibleLab для всех поддерживаемых ОС +- **Образы:** Собственные образы DevOpsLab для всех поддерживаемых ОС - **Systemd настройки:** Стандартные настройки для systemd контейнеров #### Основные задачи: diff --git a/docs/monitoring.md b/docs/monitoring.md index d7d96bc..2899e52 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -1,4 +1,4 @@ -# Мониторинг и диагностика AnsibleLab +# Мониторинг и диагностика DevOpsLab **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru @@ -15,7 +15,7 @@ docker info # Проверка запущенных контейнеров docker ps -a -# Проверка образов AnsibleLab +# Проверка образов DevOpsLab docker images | grep inecs/ansible-lab # Проверка сетей @@ -368,7 +368,7 @@ echo "✅ Диагностическая информация собрана в: ```bash # Быстрая проверка состояния echo "=== Docker Status ===" && docker info | head -5 -echo "=== AnsibleLab Images ===" && docker images | grep inecs/ansible-lab +echo "=== DevOpsLab Images ===" && docker images | grep inecs/ansible-lab echo "=== Roles ===" && ls -1 roles/ | grep -v "\.yml" echo "=== Presets ===" && ls -1 molecule/presets/*.yml 2>/dev/null | wc -l echo "=== Disk Usage ===" && df -h . | tail -1 @@ -382,7 +382,7 @@ echo "=== Disk Usage ===" && df -h . | tail -1 # Очистка контейнеров Molecule make clean-containers -# Очистка Docker образов AnsibleLab +# Очистка Docker образов DevOpsLab make docker clean # Очистка всего Docker diff --git a/docs/platform-support.md b/docs/platform-support.md index 9ce3091..34ce80a 100644 --- a/docs/platform-support.md +++ b/docs/platform-support.md @@ -6,7 +6,7 @@ ## Описание -AnsibleLab поддерживает указание конкретных платформ для хостов в пресетах. Это позволяет автоматически фильтровать хосты, которые не поддерживаются на текущей архитектуре системы. +DevOpsLab поддерживает указание конкретных платформ для хостов в пресетах. Это позволяет автоматически фильтровать хосты, которые не поддерживаются на текущей архитектуре системы. ## Обязательные требования diff --git a/docs/vault-guide.md b/docs/vault-guide.md index bfce17e..c4767f1 100644 --- a/docs/vault-guide.md +++ b/docs/vault-guide.md @@ -6,7 +6,7 @@ ## Описание -Это руководство описывает работу с зашифрованными секретами в Ansible Vault для проекта AnsibleLab. +Это руководство описывает работу с зашифрованными секретами в Ansible Vault для проекта DevOpsLab. ## Безопасность diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 10213ac..f69c93e 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -8,7 +8,7 @@ driver: platforms: # Платформы будут созданы динамически через preset файлы - # Поддержка собственных образов AnsibleLab + # Поддержка собственных образов DevOpsLab - name: placeholder image: ghcr.io/ansible-community/molecule-ubuntu-systemd:jammy pre_build_image: true diff --git a/scripts/setup-cicd.sh b/scripts/setup-cicd.sh index 3431114..7c92932 100755 --- a/scripts/setup-cicd.sh +++ b/scripts/setup-cicd.sh @@ -1,11 +1,11 @@ #!/bin/bash -# Автоматическая настройка CI/CD для AnsibleLab +# Автоматическая настройка CI/CD для DevOpsLab # Автор: Сергей Антропов # Сайт: https://devops.org.ru set -euo pipefail -echo "🔧 Настройка CI/CD для AnsibleLab..." +echo "🔧 Настройка CI/CD для DevOpsLab..." # Создание директории .github/workflows mkdir -p .github/workflows diff --git a/scripts/test-custom-images.sh b/scripts/test-custom-images.sh index 03b6360..bdb8907 100755 --- a/scripts/test-custom-images.sh +++ b/scripts/test-custom-images.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Скрипт для тестирования собственных образов AnsibleLab +# Скрипт для тестирования собственных образов DevOpsLab # Автор: Сергей Антропов # Сайт: https://devops.org.ru @@ -190,7 +190,7 @@ cleanup() { # Основная функция main() { - log "🚀 Тестирование собственных образов AnsibleLab" + log "🚀 Тестирование собственных образов DevOpsLab" echo "==========================================" # Проверки From a56de6183a8e162e18c8998ebc983f19e4d84100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 12:44:20 +0300 Subject: [PATCH 38/41] =?UTF-8?q?docs:=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20Dev?= =?UTF-8?q?OpsLab=20=D1=81=20=D1=83=D1=87=D0=B5=D1=82=D0=BE=D0=BC=20K8s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Расширено описание проекта - теперь это DevOps платформа - Добавлены ключевые компоненты: Ansible, Docker, Kubernetes - Структурированы возможности по категориям: * Ansible (автоматизация) * Docker (контейнеризация) * Kubernetes (оркестрация) - Добавлен раздел быстрого старта для K8s - Расширен раздел Kubernetes с: * Основными командами * Доступными аддонами * Путями доступа к сервисам --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 979f522..333b95b 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,36 @@ ## 📋 Описание -DevOpsLab - это универсальная система для разработки, тестирования и развертывания Ansible ролей с использованием Docker, Molecule и preset конфигураций. Система поддерживает тестирование на различных ОС через Docker контейнеры. +DevOpsLab - это универсальная DevOps платформа для разработки, тестирования и развертывания инфраструктуры. Система объединяет Ansible роли, Docker контейнеры и Kubernetes кластеры в единую среду для автоматизации и управления инфраструктурой. + +**Ключевые компоненты:** +- **Ansible** - автоматизация конфигурации и развертывания +- **Docker** - контейнеризация для изоляции и переносимости +- **Molecule** - тестирование Ansible ролей +- **Kubernetes (Kind)** - локальные K8s кластеры для разработки +- **Multi-arch поддержка** - сборка для amd64 и arm64 архитектур ## ✨ Ключевые возможности +### 🔧 Ansible - **Тестирование ролей** через Molecule с Docker - **Preset система** для быстрого выбора окружений тестирования - **Мультиплатформенное тестирование** (Ubuntu, Debian, CentOS, AlmaLinux, RHEL и другие) - **Автоматическая проверка** синтаксиса Ansible ролей - **Управление секретами** через Ansible Vault -- **Готовые Docker образы** для разных ОС -- **Kubernetes Kind кластеры** для тестирования в среде Kubernetes -- **Автоматическая установка аддонов** (Istio, Prometheus, Grafana, Kiali и другие) + +### 🐳 Docker +- **Готовые Docker образы** для разных ОС с поддержкой systemd +- **Multi-arch сборка** (amd64, arm64) +- **Автоматическая публикация** в Docker Hub +- **Контейнеры для тестирования** Ansible ролей + +### ☸️ Kubernetes +- **Kind кластеры** для локального тестирования +- **Автоматическая установка аддонов** (Istio, Prometheus, Grafana, Kiali, Ingress, Metrics Server) +- **Управление через Helm** и Kubernetes манифесты +- **Port-forward** для доступа к сервисам +- **Детальный мониторинг** состояния кластера ## 📁 Структура проекта @@ -111,6 +129,27 @@ make role lint docker make role lint ping ``` +### 4. Работа с Kubernetes + +```bash +# Создание Kind кластера с аддонами +make k8s create kubernetes + +# Статус кластера (детальный отчет) +make k8s status kubernetes + +# Создание port-forward для доступа к сервисам +make k8s portforward create + +# Установка Helm чарта +make k8s helm apply kubernetes nginx bitnami/nginx + +# Удаление кластера +make k8s destroy kubernetes +``` + +**Подробная документация:** [docs/kubernetes-kind.md](docs/kubernetes-kind.md) + ## 📚 Доступные роли ### Docker @@ -505,7 +544,51 @@ make custom-images # справка по собственным ### Kubernetes -- **[docs/kubernetes-kind.md](docs/kubernetes-kind.md)** - Документация по работе с Kind кластерами +**Полная документация:** [docs/kubernetes-kind.md](docs/kubernetes-kind.md) + +DevOpsLab предоставляет полную поддержку локальных Kubernetes кластеров на базе Kind: + +#### Основные команды + +```bash +# Создание кластера с аддонами +make k8s create kubernetes + +# Детальный статус кластера +make k8s status kubernetes + +# Управление port-forward +make k8s portforward create +make k8s portforward list +make k8s portforward clear + +# Работа с Helm +make k8s helm apply kubernetes nginx bitnami/nginx +make k8s helm list kubernetes +make k8s helm delete kubernetes nginx + +# Работа с манифестами +make k8s manifest apply kubernetes https://example.com/app.yaml + +# Удаление кластера +make k8s destroy kubernetes +``` + +#### Доступные аддоны + +- **Ingress NGINX** - маршрутизация трафика +- **Metrics Server** - сбор метрик +- **Istio** - Service Mesh +- **Prometheus Stack** - мониторинг (Prometheus + Grafana) +- **Kiali** - визуализация Service Mesh + +#### Доступ к сервисам + +- **Grafana**: http://localhost:3000 (admin/admin) +- **Prometheus**: http://localhost:9090 +- **Kiali**: http://localhost:20001 +- **Ingress HTTP**: http://localhost:8081 +- **Ingress HTTPS**: https://localhost:8443 ## 🐳 Docker образы From 85b68dc4d04d196e24bcc469cd180d648283b1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 12:48:28 +0300 Subject: [PATCH 39/41] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83?= =?UTF-8?q?=D1=80=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20?= =?UTF-8?q?=D0=B2=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 100 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 333b95b..fb43efc 100644 --- a/README.md +++ b/README.md @@ -51,22 +51,65 @@ DevOpsLab/ │ │ └── molecule.yml # Конфигурация Molecule │ └── presets/ # Preset конфигурации │ ├── default.yml # Стандартный preset -│ ├── minimal.yml # Минимальный preset │ ├── mytest.yml # Кастомный preset -│ ├── presets.yml # Основные preset'ы -│ └── examples/ # Примеры preset'ов -│ ├── all-images.yml # Все образы (16 хостов) -│ ├── multi-os.yml # Multi-OS тестирование -│ ├── performance.yml # Тест производительности -│ ├── security.yml # Тест безопасности -│ └── ... +│ ├── examples/ # Примеры preset'ов +│ │ ├── all-images.yml # Все образы (16 хостов) +│ │ ├── centos-all.yml # CentOS 7/8/9 +│ │ ├── debian-all.yml # Debian 9/10/11/12 +│ │ ├── ubuntu-all.yml # Ubuntu 20/22/24 +│ │ ├── multi-os.yml # Multi-OS тестирование +│ │ ├── performance.yml # Тест производительности +│ │ ├── security.yml # Тест безопасности +│ │ ├── minimal.yml # Минимальный preset +│ │ └── ... +│ └── k8s/ # Kubernetes preset'ы +│ ├── kubernetes.yml # Полный K8s кластер с аддонами +│ └── k8s-minimal.yml # Минимальный K8s кластер ├── roles/ # Ansible роли │ ├── docker/ # Роль установки Docker +│ │ ├── defaults/ # Переменные по умолчанию +│ │ ├── handlers/ # Обработчики +│ │ ├── meta/ # Метаданные +│ │ ├── tasks/ # Задачи +│ │ ├── templates/ # Шаблоны +│ │ ├── tests/ # Тесты +│ │ ├── vars/ # Переменные +│ │ ├── README.md +│ │ └── examples.yml │ ├── devops/ # Роль DevOps инструментов +│ │ ├── defaults/ +│ │ ├── files/ +│ │ ├── handlers/ +│ │ ├── meta/ +│ │ ├── tasks/ +│ │ ├── templates/ +│ │ ├── tests/ +│ │ ├── vars/ +│ │ ├── README.md +│ │ ├── QUICKSTART.md +│ │ ├── examples.yml +│ │ └── playbook.yml │ ├── ping/ # Роль для ping проверок +│ │ ├── defaults/ +│ │ ├── handlers/ +│ │ ├── meta/ +│ │ ├── tasks/ +│ │ ├── templates/ +│ │ ├── README.md +│ │ ├── QUICKSTART.md +│ │ └── playbook.yml │ └── deploy.yml # Playbook для развертывания ├── dockerfiles/ # Docker образы │ ├── ansible-controller/ # Ansible контроллер +│ │ ├── Dockerfile +│ │ ├── docker-compose.yml +│ │ ├── requirements.txt +│ │ └── requirements.yml +│ ├── k8s/ # Kubernetes образ (Kind, kubectl, Helm, Istio) +│ │ └── Dockerfile +│ ├── k8s-portforward/ # Port-forward контейнер (устаревший) +│ │ ├── Dockerfile +│ │ └── portforward-container.py │ ├── ubuntu20/ # Ubuntu 20.04 │ ├── ubuntu22/ # Ubuntu 22.04 │ ├── ubuntu24/ # Ubuntu 24.04 @@ -82,18 +125,51 @@ DevOpsLab/ │ ├── rhel/ # RHEL 8 │ ├── alt-linux/ # ALT Linux P9 │ ├── astra-linux/ # Astra Linux 1.7 -│ └── redos/ # RED OS 9 +│ ├── redos/ # RED OS 9 +│ └── README.md ├── cicd/ # CI/CD конфигурации │ ├── azure-devops/ # Azure DevOps +│ │ └── azure-pipelines.yml │ ├── github/ # GitHub Actions +│ │ └── workflows.yml │ ├── gitlab/ # GitLab CI +│ │ ├── config.json +│ │ ├── docker-compose.yaml +│ │ └── runner/ +│ │ └── config.toml │ └── jenkins/ # Jenkins -├── vault/ # Зашифрованные секреты -├── inventory/ # Инвентори файлы +│ └── Jenkinsfile ├── scripts/ # Вспомогательные скрипты +│ ├── create_k8s_cluster.py # Создание K8s кластера +│ ├── delete_hosts.py # Удаление хостов +│ ├── k8s_status.py # Статус K8s кластера +│ ├── portforward.py # Управление port-forward +│ ├── generate-role-docs.sh # Генерация документации +│ ├── role-manager.sh # Управление ролями +│ ├── setup-cicd.sh # Настройка CI/CD +│ ├── test-custom-images.sh # Тестирование образов +│ └── update-playbooks.sh # Обновление playbook'ов ├── docs/ # Документация +│ ├── kubernetes-kind.md # Руководство по Kubernetes +│ ├── k8s-scripts.md # Описание K8s скриптов +│ ├── kubernetes-commands.md # Команды Kubernetes +│ ├── kubernetes-full-guide.md # Полное руководство K8s +│ ├── k8s-ingress-fix.md # Исправление Ingress +│ ├── getting-started.md # Быстрый старт +│ ├── molecule-guide.md # Руководство по Molecule +│ ├── creating-roles.md # Создание ролей +│ ├── linting-guide.md # Руководство по линтингу +│ ├── platform-support.md # Поддержка платформ +│ ├── monitoring.md # Мониторинг +│ └── ... +├── manifests/ # Kubernetes манифесты +│ └── test-grafana-ingress.yaml +├── vault/ # Зашифрованные секреты +│ └── secrets.yml +├── inventory/ # Инвентори файлы +│ └── hosts.ini ├── Makefile # Основные команды -└── requirements.yml # Ansible коллекции +└── README.md # Этот файл ``` ## 🚀 Быстрый старт From 4e86b7f0c3c03599161092f0a0c696138ef19f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 12:51:30 +0300 Subject: [PATCH 40/41] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B4=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=20dockerfiles.md:=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=20k8s,=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D0=B0=20=D0=B8=20=D0=BC=D0=B0=D1=82=D1=80=D0=B8=D1=86=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B8=D0=BC=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dockerfiles.md | 77 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/docs/dockerfiles.md b/docs/dockerfiles.md index a029c62..bc6fd18 100644 --- a/docs/dockerfiles.md +++ b/docs/dockerfiles.md @@ -13,6 +13,15 @@ DevOpsLab использует предварительно собранные D ``` dockerfiles/ ├── ansible-controller/ # Ansible контроллер +│ ├── Dockerfile +│ ├── docker-compose.yml +│ ├── requirements.txt +│ └── requirements.yml +├── k8s/ # Kubernetes контроллер (Kind, kubectl, Helm, Istio) +│ └── Dockerfile +├── k8s-portforward/ # Port-forward контейнер (устаревший) +│ ├── Dockerfile +│ └── portforward-container.py ├── alt-linux/ # ALT Linux P9 ├── astra-linux/ # Astra Linux 1.7 ├── redos/ # RED OS 9 @@ -28,7 +37,8 @@ dockerfiles/ ├── debian9/ # Debian 9 Stretch ├── debian10/ # Debian 10 Buster ├── debian11/ # Debian 11 Bullseye -└── debian12/ # Debian 12 Bookworm +├── debian12/ # Debian 12 Bookworm +└── README.md # Документация по Dockerfiles ``` ## 🚀 Доступные образы @@ -44,13 +54,12 @@ Ansible контроллер с предустановленными колле #### Компоненты: - Ansible Core с последними коллекциями - Docker CLI для работы с контейнерами -- kubectl для управления Kubernetes -- Helm для управления пакетами Kubernetes -- Kind для локального Kubernetes - yq для работы с YAML - jq для работы с JSON - Molecule для тестирования ролей +**Примечание:** Kubernetes инструменты (kubectl, Helm, Kind, Istio) были перенесены в отдельный образ `k8s`. + #### Предустановленные коллекции: ```yaml collections: @@ -82,6 +91,41 @@ docker run --rm \ ansible-playbook site.yml ``` +### k8s + +**Базовый образ:** `ubuntu:22.04` +**Теги:** `inecs/ansible-lab:k8s-latest` +**Платформы:** linux/amd64, linux/arm64 + +Kubernetes контроллер с инструментами для работы с Kubernetes, Helm, Istio и Kind кластерами. + +#### Компоненты: +- **Docker CLI** (20.10.24) для работы с контейнерами +- **kubectl** (1.34.1) для управления Kubernetes +- **Helm** (latest) для управления пакетами Kubernetes +- **Kind** (0.30.0) для локальных Kubernetes кластеров +- **Istio CLI** (1.22.1) для управления Service Mesh +- Python 3 с модулем yaml для выполнения скриптов + +#### Использование: +```bash +# Создание Kind кластера +docker run -it --rm \ + --name k8s-controller \ + --network kind \ + -v /var/run/docker.sock:/var/run/docker.sock:rw \ + inecs/ansible-lab:k8s-latest \ + kind create cluster --name lab + +# Выполнение kubectl команд +docker exec k8s-controller kubectl get nodes + +# Установка Helm релиза +docker exec k8s-controller helm install prometheus prometheus-community/kube-prometheus-stack +``` + +**Примечание:** Этот образ используется автоматически при выполнении `make k8s` команд. Контейнер запускается с именем `k8s-controller` и подключен к Docker daemon хоста. + ### Ubuntu **Базовые образы:** @@ -348,18 +392,19 @@ docker run -d --privileged \ ## 📋 Матрица совместимости -| Образ | Платформы | systemd | Docker | Python 3 | -|-------|-----------|---------|--------|----------| -| ansible-controller | amd64, arm64 | ✅ | ✅ | ✅ | -| ubuntu20/22/24 | amd64, arm64 | ✅ | ✅ | ✅ | -| debian9/10/11/12 | amd64, arm64 | ✅ | ✅ | ✅ | -| centos7/8/9 | amd64, arm64 | ✅ | ✅ | ✅ | -| alma | amd64, arm64 | ✅ | ✅ | ✅ | -| rocky | amd64, arm64 | ✅ | ✅ | ✅ | -| rhel | amd64, arm64 | ✅ | ✅ | ✅ | -| alt-linux | amd64 | ✅ | ✅ | ✅ | -| astra-linux | amd64 | ✅ | ✅ | ✅ | -| redos | amd64 | ✅ | ✅ | ✅ | +| Образ | Платформы | systemd | Docker | Python 3 | Kubernetes Tools | +|-------|-----------|---------|--------|----------|------------------| +| ansible-controller | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| k8s | amd64, arm64 | ❌ | ✅ | ✅ | ✅ (kubectl, Helm, Kind, Istio) | +| ubuntu20/22/24 | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| debian9/10/11/12 | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| centos7/8/9 | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| alma | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| rocky | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| rhel | amd64, arm64 | ✅ | ✅ | ✅ | ❌ | +| alt-linux | amd64 | ✅ | ✅ | ✅ | ❌ | +| astra-linux | amd64 | ✅ | ✅ | ✅ | ❌ | +| redos | amd64 | ✅ | ✅ | ✅ | ❌ | ## 🛠️ Управление образами From 37ff18c91bc38f3c71a0109de5b070741c75f2b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Sun, 26 Oct 2025 12:54:59 +0300 Subject: [PATCH 41/41] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=B4=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=20dockerfiles/README.md:=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BE=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=B7=20k8s,=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D0=B0=20=D0=B8=20=D0=BD=D1=83=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dockerfiles/README.md | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/dockerfiles/README.md b/dockerfiles/README.md index 8647b0b..c34fcc1 100644 --- a/dockerfiles/README.md +++ b/dockerfiles/README.md @@ -15,7 +15,13 @@ dockerfiles/ ├── ansible-controller/ # Ansible контроллер с предустановленными коллекциями │ ├── Dockerfile │ ├── docker-compose.yml -│ └── requirements.txt +│ ├── requirements.txt +│ └── requirements.yml +├── k8s/ # Kubernetes контроллер (Kind, kubectl, Helm, Istio) +│ └── Dockerfile +├── k8s-portforward/ # Port-forward контейнер (устаревший) +│ ├── Dockerfile +│ └── portforward-container.py ├── alt-linux/ # ALT Linux P9 с systemd │ └── Dockerfile ├── astra-linux/ # Astra Linux 1.7 с systemd @@ -48,7 +54,7 @@ dockerfiles/ │ └── Dockerfile ├── debian12/ # Debian 12 (bookworm) с systemd │ └── Dockerfile -└── README.md # Документация +└── README.md # Этот файл ``` ## 🚀 Доступные образы @@ -62,12 +68,11 @@ dockerfiles/ #### Компоненты: - **Ansible Core** с последними коллекциями - **Docker CLI** для работы с контейнерами -- **kubectl** для управления Kubernetes -- **Helm** для управления пакетами Kubernetes -- **Kind** для локального Kubernetes - **yq** для работы с YAML - **jq** для работы с JSON +**Примечание:** Kubernetes инструменты (kubectl, Helm, Kind, Istio) были перенесены в отдельный образ `k8s`. + #### Предустановленные коллекции: ```yaml collections: @@ -109,7 +114,40 @@ docker run --rm \ ansible-playbook site.yml ``` -### 2. Ubuntu +### 2. k8s + +**Базовый образ:** `ubuntu:22.04` +**Тег:** `inecs/ansible-lab:k8s-latest` +**Описание:** Kubernetes контроллер с инструментами для работы с Kubernetes, Helm, Istio и Kind кластерами + +#### Компоненты: +- **Docker CLI** (20.10.24) для работы с контейнерами +- **kubectl** (1.34.1) для управления Kubernetes +- **Helm** (latest) для управления пакетами Kubernetes +- **Kind** (0.30.0) для локальных Kubernetes кластеров +- **Istio CLI** (1.22.1) для управления Service Mesh +- **Python 3** с модулем yaml для выполнения скриптов + +#### Использование: +```bash +# Создание Kind кластера +docker run -it --rm \ + --name k8s-controller \ + --network kind \ + -v /var/run/docker.sock:/var/run/docker.sock:rw \ + inecs/ansible-lab:k8s-latest \ + kind create cluster --name lab + +# Выполнение kubectl команд +docker exec k8s-controller kubectl get nodes + +# Установка Helm релиза +docker exec k8s-controller helm install prometheus prometheus-community/kube-prometheus-stack +``` + +**Примечание:** Этот образ используется автоматически при выполнении `make k8s` команд. Контейнер запускается с именем `k8s-controller` и подключен к Docker daemon хоста. + +### 3. Ubuntu #### Ubuntu 20.04 (focal) @@ -157,7 +195,7 @@ docker run -d --privileged \ inecs/ansible-lab:ubuntu22 ``` -### 3. Debian +### 4. Debian #### Debian 9 (stretch) @@ -216,7 +254,7 @@ docker run -d --privileged \ inecs/ansible-lab:debian12 ``` -### 4. RHEL (Red Hat Enterprise Linux) +### 5. RHEL (Red Hat Enterprise Linux) **Базовый образ:** `registry.access.redhat.com/ubi8/ubi` **Тег:** `inecs/ansible-lab:rhel-latest` @@ -240,7 +278,7 @@ docker run -d --privileged \ inecs/ansible-lab:rhel-latest ``` -### 5. CentOS +### 6. CentOS #### CentOS 7 @@ -304,7 +342,7 @@ docker run -d --privileged \ inecs/ansible-lab:centos-latest ``` -### 6. alma +### 7. alma **Базовый образ:** `almalinux:8` **Тег:** `inecs/ansible-lab:alma-latest` @@ -341,7 +379,7 @@ docker run -d --privileged \ inecs/ansible-lab:alma-latest ``` -### 7. rocky +### 8. rocky **Базовый образ:** `rockylinux:8` **Тег:** `inecs/ansible-lab:rocky-latest` @@ -378,7 +416,7 @@ docker run -d --privileged \ inecs/ansible-lab:rocky-latest ``` -### 8. alt-linux +### 9. alt-linux **Базовый образ:** `altlinux/p9` **Тег:** `inecs/ansible-lab:alt-linux-latest` @@ -415,7 +453,7 @@ docker run -d --privileged \ inecs/ansible-lab:alt-linux-latest ``` -### 9. astra-linux +### 10. astra-linux **Базовый образ:** `astralinux/astra-1.7` **Тег:** `inecs/ansible-lab:astra-linux-latest` @@ -452,7 +490,7 @@ docker run -d --privileged \ inecs/ansible-lab:astra-linux-latest ``` -### 10. redos +### 11. redos **Базовый образ:** `redos/redos:9` **Тег:** `inecs/ansible-lab:redos-latest`