diff --git a/Makefile b/Makefile index c450ef5..d841973 100644 --- a/Makefile +++ b/Makefile @@ -19,30 +19,30 @@ CYAN := \033[0;36m WHITE := \033[0;37m RESET := \033[0m -# Глобальные переменные +# Глобальные переменные (Podman) PROJECT_NAME ?= devops-lab 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 EDITOR ?= nano +# Сокет Podman (rootful: /run/podman/podman.sock) +PODMAN_SOCKET ?= /run/podman/podman.sock -# Переменные для Docker Hub +# Переменные для registry образов DOCKER_REGISTRY ?= inecs/ansible-lab DOCKER_VERSION ?= latest DOCKER_IMAGES := ansible-controller alt9 alt10 astra-linux redos rhel centos7 centos8 centos9 alma rocky ubuntu20 ubuntu22 ubuntu24 debian9 debian10 debian11 debian12 -# Multi-arch поддержка +# Multi-arch поддержка (podman build --platform) DOCKER_PLATFORMS ?= linux/amd64,linux/arm64 -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 k8s 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 buildall buildall-image # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С РОЛЯМИ @@ -54,11 +54,11 @@ role: ROLE_NAME="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$ROLE_NAME" ]; then \ echo "🔍 Проверка синтаксиса всех ролей ..."; \ - docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace -e ANSIBLE_FORCE_COLOR=1 $(DOCKER_IMAGE) bash -c "ansible-lint roles/ --config-file .ansible-lint || true"; \ + podman run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace -e ANSIBLE_FORCE_COLOR=1 $(DOCKER_IMAGE) bash -c "ansible-lint roles/ --config-file .ansible-lint || true"; \ else \ echo "🔍 Проверка синтаксиса роли: $$ROLE_NAME"; \ if [ -d "roles/$$ROLE_NAME" ]; then \ - docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace -e ANSIBLE_FORCE_COLOR=1 $(DOCKER_IMAGE) bash -c "ansible-lint roles/$$ROLE_NAME/ --config-file .ansible-lint || true"; \ + podman run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace -e ANSIBLE_FORCE_COLOR=1 $(DOCKER_IMAGE) bash -c "ansible-lint roles/$$ROLE_NAME/ --config-file .ansible-lint || true"; \ else \ echo "❌ Роль '$$ROLE_NAME' не найдена в roles/"; \ echo "📋 Доступные роли:"; \ @@ -85,11 +85,12 @@ role: fi; \ echo ""; \ $(MAKE) decrypt-all; \ - echo "🔧 Запуск ansible-controller контейнера..."; \ - docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ - -v /var/run/docker.sock:/var/run/docker.sock \ + echo "🔧 Запуск ansible-controller контейнера (Podman)..."; \ + podman run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ + -v $(PODMAN_SOCKET):/run/podman/podman.sock \ -u root \ -e ANSIBLE_FORCE_COLOR=1 \ + -e CONTAINER_HOST=unix:///run/podman/podman.sock \ -e MOLECULE_PRESET=$$PRESET \ -e MOLECULE_EPHEMERAL_DIRECTORY=/tmp/molecule_workspace \ -e MOLECULE_VAULT_ENABLED=$${MOLECULE_VAULT_ENABLED:-false} \ @@ -149,10 +150,10 @@ role: echo "📋 Проверяемая роль: $$ROLE_NAME"; \ echo "📋 Используется inventory: inventory/hosts.ini"; \ echo "📄 Содержимое inventory:"; \ - docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) cat inventory/hosts.ini || cat inventory/hosts.ini; \ + podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) cat inventory/hosts.ini || cat inventory/hosts.ini; \ echo ""; \ echo "🔍 Запуск dry-run проверки роли $$ROLE_NAME..."; \ - docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ + podman run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v ~/.ssh:/root/.ssh:ro \ -e ANSIBLE_FORCE_COLOR=1 \ $(DOCKER_IMAGE) \ @@ -171,10 +172,10 @@ role: $(MAKE) decrypt-all; \ echo "📋 Используется inventory: inventory/hosts.ini"; \ echo "📄 Содержимое inventory:"; \ - docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) cat inventory/hosts.ini || cat inventory/hosts.ini; \ + podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) cat inventory/hosts.ini || cat inventory/hosts.ini; \ echo ""; \ echo "🚀 Запуск развертывания (в контейнере)..."; \ - docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ + podman run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v ~/.ssh:/root/.ssh:ro \ -e ANSIBLE_FORCE_COLOR=1 \ $(DOCKER_IMAGE) \ @@ -182,7 +183,7 @@ role: echo ""; \ read -p "Продолжить развертывание? (y/N): " confirm; \ if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ - docker run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ + podman run --rm --name $(CONTAINER_NAME) -v "$(PWD):/workspace" -w /workspace \ -v ~/.ssh:/root/.ssh:ro \ -e ANSIBLE_FORCE_COLOR=1 \ $(DOCKER_IMAGE) \ @@ -340,10 +341,10 @@ vault: FILE="roles/$$ROLE/vars/main.yml"; \ if [ ! -f "$$FILE" ]; then echo "❌ Файл $$FILE не найден"; exit 1; fi; \ $(MAKE) decrypt-role ROLE=$$ROLE; \ - docker run --rm -it -v "$(PWD):/workspace" -w /workspace \ + podman run --rm -it -v "$(PWD):/workspace" -w /workspace \ -e EDITOR=$(EDITOR) \ $(DOCKER_IMAGE) \ - bash -c 'exec ${EDITOR:-nano} "$1"' _ "$$FILE"; \ + bash -c 'exec $${EDITOR:-nano} "$1"' _ "$$FILE"; \ $(MAKE) encrypt-role ROLE=$$ROLE;; \ show) \ echo "🔐 Просмотр vars/main.yml выбранной роли..."; \ @@ -352,7 +353,7 @@ vault: read -p "Введите имя роли: " ROLE; \ FILE="roles/$$ROLE/vars/main.yml"; \ if [ ! -f "$$FILE" ]; then echo "❌ Файл $$FILE не найден"; exit 1; fi; \ - docker run --rm -v "$(PWD):/workspace" -w /workspace \ + podman run --rm -v "$(PWD):/workspace" -w /workspace \ $(DOCKER_IMAGE) bash -c 'f="$1"; \ if grep -q "ANSIBLE_VAULT" "$${f}" 2>/dev/null; then \ ansible-vault view --vault-password-file vault/.vault "$${f}"; \ @@ -453,7 +454,7 @@ git: echo "🔐 Шифрование vault/*.yml..."; \ for f in $$VAULT_FILES; do \ if ! grep -q "ANSIBLE_VAULT" "$$f" 2>/dev/null; then \ - docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) \ + podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) \ ansible-vault encrypt --encrypt-vault-id default --vault-password-file vault/.vault "$$f" || true; \ fi; \ done; \ @@ -497,76 +498,67 @@ docker: echo "📋 Images: $(DOCKER_IMAGES)"; \ echo ""; \ echo "💡 Для работы с Docker Hub выполните:"; \ - echo " docker login - авторизация в Docker Hub"; \ + echo " podman login - авторизация в registry"; \ echo " make docker build - сборка образов"; \ - echo " make docker push - отправка в Docker Hub";; \ + echo " make docker push - отправка в registry";; \ build) \ - echo "🐳 Сборка Docker образов (multi-arch)..."; \ + echo "🐳 Сборка образов (Podman, multi-arch)..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ - echo "📋 Builder: $(DOCKER_BUILDX_BUILDER)"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "📋 Version: $(DOCKER_VERSION)"; \ echo "📋 Images: $(DOCKER_IMAGES)"; \ echo "⚠️ ВНИМАНИЕ: RED OS и Astra Linux собираются только для AMD64"; \ echo ""; \ - $(MAKE) docker setup-builder; \ for image in $(DOCKER_IMAGES); do \ echo "🔨 Сборка $(DOCKER_REGISTRY)/$$image:$(DOCKER_VERSION)"; \ $(MAKE) docker-build-image IMAGE=$$image; \ done; \ echo "✅ Образы собраны";; \ rebuild) \ - echo "🔄 Полная пересборка Docker образов (multi-arch)..."; \ + echo "🔄 Полная пересборка образов (Podman, multi-arch)..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ - echo "📋 Builder: $(DOCKER_BUILDX_BUILDER)"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ - echo "📋 Version: $(DOCKER_VERSION)"; \ echo "📋 Images: $(DOCKER_IMAGES)"; \ - echo "⚠️ ВНИМАНИЕ: RED OS и Astra Linux собираются только для AMD64"; \ - echo "🧹 Очистка кеша и старых образов..."; \ + echo "🧹 Очистка старых образов..."; \ echo ""; \ $(MAKE) docker clean; \ - $(MAKE) docker clean-builder; \ - $(MAKE) docker setup-builder; \ for image in $(DOCKER_IMAGES); do \ echo "🔨 Пересборка $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ $(MAKE) docker-build-image IMAGE=$$image; \ done; \ echo "✅ Образы пересобраны с нуля";; \ push) \ - echo "📤 Отправка Docker образов в Docker Hub..."; \ + echo "📤 Отправка образов в registry (Podman)..."; \ for image in $(DOCKER_IMAGES); do \ echo "📤 Отправка $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ - docker push $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION); \ + podman push $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION); \ done; \ - echo "✅ Образы отправлены в Docker Hub";; \ + echo "✅ Образы отправлены в registry";; \ pull) \ - echo "📥 Загрузка Docker образов из Docker Hub..."; \ + echo "📥 Загрузка образов из registry (Podman)..."; \ echo "⚠️ ВНИМАНИЕ: astra-linux и redos собираются только для AMD64"; \ for image in $(DOCKER_IMAGES); do \ echo "📥 Загрузка $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ if [ "$$image" = "astra-linux" ] || [ "$$image" = "redos" ]; then \ - docker pull --platform linux/amd64 $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) || echo "⚠️ Образ $$image не найден в Docker Hub"; \ + podman pull --platform linux/amd64 $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) || echo "⚠️ Образ $$image не найден"; \ else \ - docker pull $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) || echo "⚠️ Образ $$image не найден в Docker Hub"; \ + podman pull $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) || echo "⚠️ Образ $$image не найден"; \ fi; \ done; \ echo "✅ Загрузка завершена";; \ clean) \ - echo "🧹 Очистка Docker образов и builds..."; \ + echo "🧹 Очистка образов (Podman)..."; \ for image in $(DOCKER_IMAGES); do \ echo "🗑️ Удаление $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ - docker rmi $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) 2>/dev/null || true; \ + podman rmi $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION) 2>/dev/null || true; \ done; \ - echo "🗑️ Удаление кеша builds для наших образов..."; \ - docker buildx prune --filter type=exec.cachemount --filter type=source.local --filter type=source.git.checkout --force 2>/dev/null || true; \ - echo "✅ Образы и кеш builds очищены";; \ + echo "✅ Образы очищены";; \ info) \ - echo "📊 Информация об образах..."; \ + echo "📊 Информация об образах (Podman)..."; \ for image in $(DOCKER_IMAGES); do \ - if docker images | grep -q "$(DOCKER_REGISTRY):$$image"; then \ + if podman images | grep -q "$(DOCKER_REGISTRY):$$image"; then \ echo "📦 $(DOCKER_REGISTRY):$$image-$(DOCKER_VERSION)"; \ - docker images | grep "$(DOCKER_REGISTRY):$$image" | head -1; \ + podman images | grep "$(DOCKER_REGISTRY):$$image" | head -1; \ fi; \ done;; \ update) \ @@ -576,24 +568,20 @@ docker: $(MAKE) docker push; \ echo "✅ Все образы обновлены";; \ purge) \ - echo "🧹 Полная очистка Docker..."; \ - echo "⚠️ ВНИМАНИЕ: Это удалит ВСЕ Docker данные!"; \ + echo "🧹 Полная очистка Podman..."; \ + echo "⚠️ ВНИМАНИЕ: Это удалит ВСЕ данные Podman!"; \ echo ""; \ read -p "Продолжить? (y/N): " confirm; \ if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \ echo "🛑 Остановка всех контейнеров..."; \ - docker stop $$(docker ps -aq) 2>/dev/null || true; \ + podman stop $$(podman ps -aq) 2>/dev/null || true; \ echo "🗑️ Удаление всех контейнеров..."; \ - docker rm $$(docker ps -aq) 2>/dev/null || true; \ + podman rm -f $$(podman ps -aq) 2>/dev/null || true; \ echo "🗑️ Удаление всех образов..."; \ - docker rmi $$(docker images -aq) 2>/dev/null || true; \ - echo "🗑️ Удаление всех томов..."; \ - docker volume rm $$(docker volume ls -q) 2>/dev/null || true; \ - echo "🗑️ Удаление всех сетей..."; \ - docker network rm $$(docker network ls -q) 2>/dev/null || true; \ + podman rmi -f $$(podman images -aq) 2>/dev/null || true; \ echo "🧹 Очистка системы..."; \ - docker system prune -af --volumes; \ - echo "✅ Docker полностью очищен"; \ + podman system prune -af --volumes; \ + echo "✅ Podman полностью очищен"; \ else \ echo "❌ Очистка отменена"; \ fi;; \ @@ -619,10 +607,8 @@ docker: fi; \ echo "🔨 Сборка отдельного образа: $(IMAGE)"; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ - echo "📋 Builder: $(DOCKER_BUILDX_BUILDER)"; \ echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo ""; \ - $(MAKE) docker setup-builder; \ $(MAKE) docker-build-image IMAGE=$(IMAGE); \ echo "✅ Образ $(IMAGE) собран";; \ build-astra-arm64) \ @@ -632,15 +618,14 @@ docker: echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "⚠️ ВНИМАНИЕ: Используется совместимый образ на базе Debian"; \ echo ""; \ - $(MAKE) docker setup-builder; \ cd dockerfiles/astra-linux && \ - docker buildx build \ - --platform linux/amd64,linux/arm64 \ + podman build --platform linux/amd64,linux/arm64 \ --tag $(DOCKER_REGISTRY):astra-linux-arm64-latest \ --tag $(DOCKER_REGISTRY):astra-linux-latest \ --file Dockerfile.arm64 \ - --push \ .; \ + podman push $(DOCKER_REGISTRY):astra-linux-arm64-latest; \ + podman push $(DOCKER_REGISTRY):astra-linux-latest; \ echo "✅ Astra Linux для ARM64 собран и отправлен";; \ build-redos-arm64) \ echo "🔨 Сборка RedOS для ARM64 (совместимый образ)..."; \ @@ -649,39 +634,28 @@ docker: echo "📋 Registry: $(DOCKER_REGISTRY)"; \ echo "⚠️ ВНИМАНИЕ: Используется совместимый образ на базе CentOS Stream 9"; \ echo ""; \ - $(MAKE) docker setup-builder; \ cd dockerfiles/redos && \ - docker buildx build \ - --platform linux/amd64,linux/arm64 \ + podman build --platform linux/amd64,linux/arm64 \ --tag $(DOCKER_REGISTRY):redos-arm64-latest \ --tag $(DOCKER_REGISTRY):redos-latest \ --file Dockerfile.arm64 \ - --push \ .; \ + podman push $(DOCKER_REGISTRY):redos-arm64-latest; \ + podman push $(DOCKER_REGISTRY):redos-latest; \ echo "✅ RedOS для ARM64 собран и отправлен";; \ setup-builder) \ - echo "🔧 Настройка multi-arch builder в контейнере..."; \ - if $(MAKE) docker-check-builder >/dev/null 2>&1; then \ - echo "✅ Builder $(DOCKER_BUILDX_BUILDER) уже существует и готов"; \ - docker buildx use $(DOCKER_BUILDX_BUILDER); \ - else \ - echo "📦 Создание builder $(DOCKER_BUILDX_BUILDER)..."; \ - $(MAKE) docker-create-builder; \ - fi; \ - echo "🔍 Финальная проверка builder..."; \ - $(MAKE) docker-check-builder;; \ + echo "ℹ️ Podman: multi-arch через podman build --platform (builder не требуется)";; \ diagnose) \ - echo "🔍 Диагностика buildx проблем..."; \ - $(MAKE) docker-diagnose-buildx;; \ + echo "🔍 Диагностика Podman..."; \ + podman version;; \ reset-builder) \ - echo "🔄 Сброс buildx builder..."; \ - $(MAKE) docker-reset-builder;; \ + echo "ℹ️ Podman: builder не используется";; \ *) \ - echo "🎯 Доступные команды:"; \ + echo "🎯 Доступные команды (Podman):"; \ echo ""; \ - echo " 🔧 make docker prepare - подготовка к работе с Docker Hub"; \ + echo " 🔧 make docker prepare - подготовка к работе с registry"; \ echo " 💡 Показывает: registry, version, список образов"; \ - echo " 💡 Рекомендует: docker login перед работой"; \ + echo " 💡 Рекомендует: podman login перед работой"; \ echo ""; \ echo " 🐳 make docker build - собрать все Docker образы (multi-arch)"; \ echo " 💡 Собирает: ansible-controller, alt9, alt10, astra-linux, redos"; \ @@ -717,34 +691,20 @@ docker: echo " 💡 Сохраняет: другие builds в системе"; \ echo " 💡 Безопасно: игнорирует ошибки"; \ echo ""; \ - echo " 🧹 make docker clean-builder - очистка multi-arch builder"; \ - echo " 💡 Удаляет: builder контейнер и buildkit контейнеры"; \ - echo " 💡 Полезно: при проблемах со сборкой";; \ + echo " 🧹 make docker clean-builder - заглушка (Podman не использует builder)"; \ setup-builder) \ - echo "🔧 Настройка multi-arch builder в контейнере..."; \ - if $(MAKE) docker-check-builder >/dev/null 2>&1; then \ - echo "✅ Builder $(DOCKER_BUILDX_BUILDER) уже существует и готов"; \ - docker buildx use $(DOCKER_BUILDX_BUILDER); \ - else \ - echo "📦 Создание builder $(DOCKER_BUILDX_BUILDER)..."; \ - $(MAKE) docker-create-builder; \ - fi; \ - echo "🔍 Финальная проверка builder..."; \ - $(MAKE) docker-check-builder;; \ + echo "ℹ️ Podman: builder не требуется";; \ diagnose) \ - echo "🔍 Диагностика buildx проблем..."; \ - $(MAKE) docker-diagnose-buildx;; \ + podman version;; \ reset-builder) \ - echo "🔄 Сброс buildx builder..."; \ - $(MAKE) docker-reset-builder;; \ + echo "ℹ️ Podman: builder не используется";; \ *) \ - echo "🎯 Доступные команды:"; \ + echo "🎯 Доступные команды (Podman):"; \ echo ""; \ - echo " 🔧 make docker prepare - подготовка к работе с Docker Hub"; \ - echo " 💡 Показывает: registry, version, список образов"; \ - echo " 💡 Рекомендует: docker login перед работой"; \ + echo " 🔧 make docker prepare - подготовка к работе с registry"; \ + echo " 💡 Рекомендует: podman login перед работой"; \ echo ""; \ - echo " 🐳 make docker build - собрать все Docker образы (multi-arch)"; \ + echo " 🐳 make docker build - собрать все образы (Podman, multi-arch)"; \ echo " 💡 Собирает: ansible-controller, alt9, alt10, astra-linux, redos"; \ echo " 💡 Собирает: rhel, centos, alma, rocky, ubuntu, debian"; \ echo " 💡 Платформы: $(DOCKER_PLATFORMS)"; \ @@ -815,90 +775,22 @@ docker: # Надежная проверка существования buildx builder без использования buildx ls # Использует docker buildx inspect вместо buildx ls для избежания зависаний +# Podman: buildx не используется, заглушки для совместимости .PHONY: docker-check-builder docker-check-builder: - @echo "🔍 Проверка buildx builder $(DOCKER_BUILDX_BUILDER)..." - @if docker buildx inspect $(DOCKER_BUILDX_BUILDER) >/dev/null 2>&1; then \ - echo "✅ Builder $(DOCKER_BUILDX_BUILDER) существует и готов"; \ - exit 0; \ - else \ - echo "❌ Builder $(DOCKER_BUILDX_BUILDER) не найден"; \ - exit 1; \ - fi + @echo "ℹ️ Podman: builder не используется"; exit 0 -# Безопасное создание buildx builder с предварительной очисткой .PHONY: docker-create-builder docker-create-builder: - @echo "🔧 Создание buildx builder $(DOCKER_BUILDX_BUILDER)..." - @echo "📦 Предварительная загрузка образа buildkit..." - @docker pull moby/buildkit:buildx-stable-1 || echo "⚠️ Не удалось загрузить moby/buildkit:buildx-stable-1, будет использован авто-пулл" - @echo "🗑️ Удаление существующего builder (если есть)..." - @docker buildx rm -f $(DOCKER_BUILDX_BUILDER) 2>/dev/null || true - @echo "📦 Создание нового builder..." - @docker buildx create \ - --name $(DOCKER_BUILDX_BUILDER) \ - --driver docker-container \ - --driver-opt image=moby/buildkit:buildx-stable-1 \ - --use || exit 1 - @echo "⏳ Ожидание запуска buildkit контейнера..." - @sleep 3 - @echo "🔍 Проверка готовности builder..." - @docker buildx inspect --builder $(DOCKER_BUILDX_BUILDER) --bootstrap >/dev/null || exit 1 - @echo "✅ Builder $(DOCKER_BUILDX_BUILDER) создан и готов к работе" + @echo "ℹ️ Podman: builder не требуется" -# Диагностика проблем с buildx .PHONY: docker-diagnose-buildx docker-diagnose-buildx: - @echo "🔍 ДИАГНОСТИКА BUILDX ПРОБЛЕМ" - @echo "==========================================" - @echo "" - @echo "📊 1. Версии Docker и Buildx:" - @docker version --format "Docker: {{.Server.Version}}" 2>/dev/null || echo "❌ Docker недоступен" - @docker buildx version 2>/dev/null || echo "❌ Buildx недоступен" - @echo "" - @echo "📋 2. Docker контексты (поиск мертвых tcp://):" - @docker context ls 2>/dev/null || echo "❌ Не удалось получить список контекстов" - @echo "" - @echo "🔍 3. Проверка builder $(DOCKER_BUILDX_BUILDER):" - @if docker buildx inspect $(DOCKER_BUILDX_BUILDER) >/dev/null 2>&1; then \ - echo "✅ Builder $(DOCKER_BUILDX_BUILDER) существует"; \ - docker buildx inspect $(DOCKER_BUILDX_BUILDER) --bootstrap >/dev/null 2>&1 && echo "✅ Builder готов" || echo "❌ Builder не готов"; \ - else \ - echo "❌ Builder $(DOCKER_BUILDX_BUILDER) не найден"; \ - fi - @echo "" - @echo "🐳 4. Buildkit контейнеры:" - @docker ps -a --filter "name=buildx_buildkit" --format "table {{.Names}}\t{{.Status}}\t{{.CreatedAt}}" 2>/dev/null || echo "❌ Не удалось получить список контейнеров" - @echo "" - @echo "🌐 5. Проверка доступа к registry:" - @echo "📥 Тест загрузки moby/buildkit:buildx-stable-1..." - @timeout 30 docker pull moby/buildkit:buildx-stable-1 >/dev/null 2>&1 && echo "✅ Доступ к registry OK" || echo "❌ Проблемы с доступом к registry" - @echo "" - @echo "🔧 6. Docker socket доступ:" - @if [ -S /var/run/docker.sock ]; then \ - echo "✅ Docker socket доступен: /var/run/docker.sock"; \ - ls -la /var/run/docker.sock; \ - else \ - echo "❌ Docker socket недоступен: /var/run/docker.sock"; \ - fi - @echo "" - @echo "💡 РЕКОМЕНДАЦИИ:" - @echo " - Если buildx ls зависает: удалите мертвые контексты (docker context rm )" - @echo " - Если pull зависает: настройте прокси или используйте mirror registry" - @echo " - Если builder не создается: проверьте права доступа к Docker socket" - @echo " - Для полной очистки: make docker clean-builder && make docker-create-builder" + @echo "🔍 Podman:"; podman version -# Быстрая очистка и пересоздание builder .PHONY: docker-reset-builder docker-reset-builder: - @echo "🔄 Сброс buildx builder..." - @echo "🗑️ Удаление builder $(DOCKER_BUILDX_BUILDER)..." - @docker buildx rm -f $(DOCKER_BUILDX_BUILDER) 2>/dev/null || true - @echo "🧹 Очистка buildkit контейнеров..." - @docker ps -a --filter "name=buildx_buildkit" --format "{{.Names}}" | xargs -r docker rm -f 2>/dev/null || true - @echo "📦 Создание нового builder..." - @$(MAKE) docker-create-builder - @echo "✅ Builder сброшен и готов к работе" + @echo "ℹ️ Podman: builder не используется" # Извлечение тега из базового образа @@ -911,98 +803,98 @@ docker-get-base-tag: alt9) \ BASE_IMAGE="alt:p9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ alt10) \ BASE_IMAGE="alt:p10"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ astra-linux) \ BASE_IMAGE="registry.astralinux.ru/library/astra/ubi17:1.7.6.uu2"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ redos) \ BASE_IMAGE="registry.red-soft.ru/ubi7/ubi:latest"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG="latest";; \ astra-linux-arm64) \ BASE_IMAGE="debian:bookworm-slim"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG="latest";; \ redos-arm64) \ BASE_IMAGE="quay.io/centos/centos:stream9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ TAG="latest";; \ rhel) \ BASE_IMAGE="registry.access.redhat.com/ubi8/ubi"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ centos7) \ BASE_IMAGE="centos:7"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ centos8) \ BASE_IMAGE="quay.io/centos/centos:8"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ centos9) \ BASE_IMAGE="quay.io/centos/centos:stream9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ alma) \ BASE_IMAGE="almalinux:8"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ rocky) \ BASE_IMAGE="rockylinux:8"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ubuntu20) \ BASE_IMAGE="ubuntu:20.04"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ubuntu22) \ BASE_IMAGE="ubuntu:22.04"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ ubuntu24) \ BASE_IMAGE="ubuntu:24.04"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian9) \ BASE_IMAGE="debian:9"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian10) \ BASE_IMAGE="debian:10"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian11) \ BASE_IMAGE="debian:11"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ debian12) \ BASE_IMAGE="debian:bookworm"; \ echo "📦 Загрузка базового образа $$BASE_IMAGE..." >&2; \ - docker pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ - TAG=$$(docker inspect --format='{{.RepoTags}}' $$BASE_IMAGE 2>/dev/null | tr -d '[]' | cut -d',' -f1 | cut -d':' -f2 | tr -d ' ' || echo "latest");; \ + podman pull $$BASE_IMAGE >/dev/null 2>&1 || echo "⚠️ Не удалось загрузить $$BASE_IMAGE" >&2; \ + TAG=$$(podman 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) \ @@ -1055,35 +947,69 @@ docker-build-image: echo ""; \ if [ "$(IMAGE)" = "astra-linux-arm64" ]; then \ cd dockerfiles/astra-linux && \ - docker buildx build \ - --platform $$PLATFORMS \ - --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG \ - --tag $(DOCKER_REGISTRY):$(IMAGE)-latest \ - --file Dockerfile.arm64 \ - --push \ - .; \ + podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG --tag $(DOCKER_REGISTRY):$(IMAGE)-latest --file Dockerfile.arm64 .; \ + podman push $(DOCKER_REGISTRY):$(IMAGE)-$$TAG; \ + podman push $(DOCKER_REGISTRY):$(IMAGE)-latest; \ elif [ "$(IMAGE)" = "redos-arm64" ]; then \ cd dockerfiles/redos && \ - docker buildx build \ - --platform $$PLATFORMS \ - --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG \ - --tag $(DOCKER_REGISTRY):$(IMAGE)-latest \ - --file Dockerfile.arm64 \ - --push \ - .; \ + podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG --tag $(DOCKER_REGISTRY):$(IMAGE)-latest --file Dockerfile.arm64 .; \ + podman push $(DOCKER_REGISTRY):$(IMAGE)-$$TAG; \ + podman push $(DOCKER_REGISTRY):$(IMAGE)-latest; \ else \ cd dockerfiles/$(IMAGE) && \ - docker buildx build \ - --platform $$PLATFORMS \ + podman build --platform $$PLATFORMS \ --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG \ --tag $(DOCKER_REGISTRY):$(IMAGE)-latest \ - --push \ .; \ + podman push $(DOCKER_REGISTRY):$(IMAGE)-$$TAG; \ + podman push $(DOCKER_REGISTRY):$(IMAGE)-latest; \ fi; \ echo ""; \ echo "✅ УСПЕШНО: $(DOCKER_REGISTRY):$(IMAGE)-$$TAG собран и отправлен"; \ echo "==========================================" +# ============================================================================= +# BUILDALL — сборка всех образов только локально (без push в registry) +# Для использования в presets Podman без доступа к registry +# ============================================================================= +.PHONY: buildall buildall-image +buildall: + @echo "🔨 Сборка всех образов локально (Podman, без push в registry)..."; \ + echo "📋 Образы: $(DOCKER_IMAGES)"; \ + echo ""; \ + for image in $(DOCKER_IMAGES); do \ + $(MAKE) buildall-image IMAGE=$$image; \ + done; \ + echo ""; \ + echo "✅ Все образы собраны локально. Для тестов: make role test [preset]" + +# Сборка одного образа локально (без push) +buildall-image: + @if [ -z "$(IMAGE)" ]; then \ + echo "❌ Укажите IMAGE=имя (например: make buildall-image IMAGE=ubuntu22)"; \ + echo "💡 Образы: $(DOCKER_IMAGES)"; \ + exit 1; \ + fi; \ + if [ ! -d "dockerfiles/$(IMAGE)" ]; then \ + echo "⚠️ Пропуск $(IMAGE): нет dockerfiles/$(IMAGE)"; \ + exit 0; \ + fi; \ + TAG=$$($(MAKE) docker-get-base-tag IMAGE=$(IMAGE)); \ + PLATFORMS="$(DOCKER_PLATFORMS)"; \ + if [ "$(IMAGE)" = "redos" ] || [ "$(IMAGE)" = "astra-linux" ]; then PLATFORMS="linux/amd64"; fi; \ + echo "🔨 Локальная сборка $(DOCKER_REGISTRY):$(IMAGE)-latest (без push)..."; \ + if [ "$(IMAGE)" = "astra-linux-arm64" ]; then \ + cd dockerfiles/astra-linux && podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-latest --file Dockerfile.arm64 .; \ + elif [ "$(IMAGE)" = "redos-arm64" ]; then \ + cd dockerfiles/redos && podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-latest --file Dockerfile.arm64 .; \ + elif [ "$(IMAGE)" = "astra-linux" ]; then \ + cd dockerfiles/astra-linux && podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-latest .; \ + elif [ "$(IMAGE)" = "redos" ]; then \ + cd dockerfiles/redos && podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-latest .; \ + else \ + cd dockerfiles/$(IMAGE) && podman build --platform $$PLATFORMS --tag $(DOCKER_REGISTRY):$(IMAGE)-$$TAG --tag $(DOCKER_REGISTRY):$(IMAGE)-latest .; \ + fi; \ + echo "✅ $(DOCKER_REGISTRY):$(IMAGE)-latest готов локально" # ============================================================================= # КОМАНДЫ ДЛЯ РАБОТЫ С ANSIBLE-CONTROLLER @@ -1091,38 +1017,34 @@ docker-build-image: controller: @case "$(word 2, $(MAKECMDGOALS))" in \ build) \ - echo "🔨 Сборка ansible-controller (multi-arch)..."; \ + echo "🔨 Сборка ansible-controller (Podman, multi-arch)..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ - $(MAKE) docker setup-builder; \ cd dockerfiles/ansible-controller && \ - docker buildx build \ - --platform $(DOCKER_PLATFORMS) \ + podman build --platform $(DOCKER_PLATFORMS) \ --tag $(DOCKER_REGISTRY):ansible-controller-$(DOCKER_VERSION) \ --tag $(DOCKER_REGISTRY):ansible-controller-latest \ - --push \ .; \ + podman push $(DOCKER_REGISTRY):ansible-controller-$(DOCKER_VERSION); \ + podman push $(DOCKER_REGISTRY):ansible-controller-latest; \ echo "✅ ansible-controller собран и отправлен";; \ rebuild) \ echo "🔄 Пересборка ansible-controller с исправлениями..."; \ echo "📋 Платформы: $(DOCKER_PLATFORMS)"; \ - echo "🔧 Исправления: добавлен passlib для хеширования паролей"; \ - $(MAKE) docker setup-builder; \ cd dockerfiles/ansible-controller && \ - docker buildx build \ - --platform $(DOCKER_PLATFORMS) \ + podman build --no-cache --platform $(DOCKER_PLATFORMS) \ --tag $(DOCKER_REGISTRY):ansible-controller-$(DOCKER_VERSION) \ --tag $(DOCKER_REGISTRY):ansible-controller-latest \ - --push \ - --no-cache \ .; \ - echo "✅ ansible-controller пересобран с исправлениями";; \ + podman push $(DOCKER_REGISTRY):ansible-controller-$(DOCKER_VERSION); \ + podman push $(DOCKER_REGISTRY):ansible-controller-latest; \ + echo "✅ ansible-controller пересобран";; \ run) \ - echo "🚀 Запуск ansible-controller..."; \ - cd dockerfiles/ansible-controller && docker-compose up -d; \ + echo "🚀 Запуск ansible-controller (Podman)..."; \ + cd dockerfiles/ansible-controller && podman compose up -d; \ echo "✅ ansible-controller запущен";; \ stop) \ echo "🛑 Остановка ansible-controller..."; \ - cd dockerfiles/ansible-controller && docker-compose down; \ + cd dockerfiles/ansible-controller && podman compose down; \ echo "✅ ansible-controller остановлен";; \ *) \ echo "🎯 Доступные команды:"; \ @@ -1148,355 +1070,102 @@ controller: esac # ============================================================================= -# КОМАНДЫ ДЛЯ РАБОТЫ С KUBERNETES KIND +# КОМАНДЫ ДЛЯ РАБОТЫ С KUBERNETES (Minikube + Podman) # ============================================================================= k8s: @case "$(word 2, $(MAKECMDGOALS))" in \ create) \ - echo "☸️ Создание Kind кластера..."; \ + echo "☸️ Создание Minikube кластера (драйвер: podman)..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ if [ -z "$$PRESET_ARG" ]; then \ PRESET=k8s-minimal; \ - echo "📋 Используется preset по умолчанию: $$PRESET (минимальный без аддонов)"; \ + 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-controller; \ - 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 "💡 Для создания port-forward: make k8s portforward create"; \ - echo "💡 Для подключения используйте: make k8s kubeconfig"; \ - echo "💡 Для остановки используйте: make k8s stop";; \ + python3 scripts/create_minikube_cluster.py molecule/presets/k8s/$$PRESET.yml; \ + echo "✅ Minikube кластер создан"; \ + echo "💡 kubectl config use-context minikube"; \ + echo "💡 make k8s status";; \ destroy) \ - echo "🗑️ Удаление Kind кластера и контейнеров..."; \ + echo "🗑️ Удаление Minikube кластера..."; \ PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ - PRESET=$${PRESET_ARG:-k8s-minimal}; \ - 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; \ - else \ - echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ - fi; \ - docker rm -f $$CONTAINER_NAME 2>/dev/null || true; \ - echo "🗑️ Удаление контейнеров из пресета..."; \ - if [ -f "molecule/presets/k8s/$$PRESET.yml" ]; then \ - 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; \ + PROFILE=$${PRESET_ARG:-minikube}; \ + minikube delete -p $$PROFILE 2>/dev/null || true; \ echo "✅ Удаление завершено";; \ 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-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 кластер остановлен"; \ - else \ - echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ - fi; \ - echo "💡 Кластер остановлен, но не удален"; \ - echo "💡 Для перезапуска: make k8s start $$PRESET_ARG"; \ - echo "💡 Для полного удаления: make k8s destroy $$PRESET_ARG";; \ + PROFILE=$${PRESET_ARG:-minikube}; \ + echo "🛑 Остановка Minikube (профиль: $$PROFILE)..."; \ + minikube stop -p $$PROFILE 2>/dev/null || true; \ + echo "💡 Для запуска: make k8s start $$PROFILE";; \ 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-controller; \ - 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 кластер запущен";; \ + PROFILE=$${PRESET_ARG:-minikube}; \ + echo "🚀 Запуск Minikube (профиль: $$PROFILE)..."; \ + minikube start --driver=podman -p $$PROFILE; \ + echo "✅ Minikube запущен";; \ status) \ - echo "📊 Детальный отчет о состоянии кластера..."; \ - PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ - if [ -z "$$PRESET_ARG" ]; then \ - echo "❌ Ошибка: Укажите пресет"; \ - echo "💡 Пример: make k8s status kubernetes"; \ - exit 1; \ - fi; \ - CONTAINER_NAME=k8s-controller; \ - if docker ps | grep -q $$CONTAINER_NAME; then \ - python3 scripts/k8s_status.py; \ - else \ - echo "⚠️ Контейнер $$CONTAINER_NAME не запущен"; \ - echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ - fi;; \ + echo "📊 Отчет о состоянии кластера (Minikube)..."; \ + python3 scripts/k8s_status.py;; \ 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-controller; \ - 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;; \ + echo "📋 Minikube обновляет ~/.kube/config автоматически."; \ + echo "💡 Использование: kubectl get nodes"; \ + echo " или: eval \$$(minikube docker-env -p minikube 2>/dev/null) для Podman"; \ + kubectl config view --minify 2>/dev/null || true;; \ nodes) \ - echo "🖥️ Просмотр узлов кластера..."; \ - PRESET_ARG="$(word 3, $(MAKECMDGOALS))"; \ - if [ -z "$$PRESET_ARG" ]; then \ - echo "❌ Ошибка: Укажите пресет"; \ - echo "💡 Пример: make k8s nodes kubernetes"; \ - 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); \ - docker exec $$CONTAINER_NAME bash -c "CLUSTER_NAME=$$CLUSTER_NAME; kubectl --server=https://\$${CLUSTER_NAME}-control-plane:6443 --insecure-skip-tls-verify get nodes";; \ + echo "🖥️ Узлы кластера:"; \ + kubectl get nodes -o wide;; \ 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-controller; \ - 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 "💡 Minikube работает на хосте. Используйте kubectl и minikube напрямую."; \ + echo " minikube ssh -p minikube # вход в ноду"; \ + exec $$SHELL;; \ 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"; \ + MANIFEST_ARG="$(word 4, $(MAKECMDGOALS))"; \ + if [ -z "$$MANIFEST_CMD" ] || [ -z "$$MANIFEST_ARG" ]; then \ + echo "❌ Укажите команду и путь: make k8s manifest apply 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); \ 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";; \ - *) \ - echo "❌ Неизвестная команда: $$MANIFEST_CMD"; \ - echo "💡 Доступные команды: apply, delete"; \ - exit 1;; \ + apply) kubectl apply -f $$MANIFEST_ARG;; \ + delete) kubectl delete -f $$MANIFEST_ARG;; \ + *) echo "💡 Команды: apply, delete"; 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; \ + RELEASE_ARG="$(word 4, $(MAKECMDGOALS))"; \ + CHART_ARG="$(word 5, $(MAKECMDGOALS))"; \ + if [ -z "$$HELM_CMD" ]; then \ + helm list -A; \ + exit 0; \ 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); \ 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;; \ + apply) [ -z "$$RELEASE_ARG" ] || [ -z "$$CHART_ARG" ] && { echo "💡 make k8s helm apply RELEASE CHART"; exit 1; }; helm upgrade --install $$RELEASE_ARG $$CHART_ARG;; \ + delete) [ -z "$$RELEASE_ARG" ] && { echo "💡 make k8s helm delete RELEASE"; exit 1; }; helm uninstall $$RELEASE_ARG;; \ + list) helm list -A;; \ + *) echo "💡 Команды: apply, delete, list"; 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-controller; \ - if ! docker ps | grep -q $$CONTAINER_NAME; then \ - echo "❌ Контейнер $$CONTAINER_NAME не запущен"; \ - echo "💡 Запустите: make k8s create $$PRESET_ARG"; \ - exit 1; \ - fi; \ + NAME_ARG="$(word 4, $(MAKECMDGOALS))"; \ + URL_ARG="$(word 5, $(MAKECMDGOALS))"; \ 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;; \ + add) [ -z "$$NAME_ARG" ] || [ -z "$$URL_ARG" ] && { echo "💡 make k8s helmrepo add NAME URL"; exit 1; }; helm repo add $$NAME_ARG $$URL_ARG; helm repo update;; \ + list) helm repo list;; \ + update) helm repo update;; \ + *) echo "💡 Команды: add, list, update"; 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;; \ - recreate) \ - echo "🔄 Пересоздание port-forward..."; \ - python3 scripts/portforward.py recreate;; \ - 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, recreate, delete"; \ - exit 1;; \ - esac;; \ + if [ -z "$$PORTFWD_CMD" ]; then echo "💡 make k8s portforward create|list|clear"; exit 1; fi; \ + python3 scripts/portforward.py $$PORTFWD_CMD;; \ *) \ echo "☸️ Доступные команды:"; \ echo ""; \ @@ -1602,10 +1271,12 @@ help: @echo " make presets list - показать все доступные preset'ы" @echo " make presets info - подробная информация о preset'е" @echo "" - @echo "🐳 DOCKER ОБРАЗЫ (Multi-Arch):" - @echo " make docker prepare - подготовка к работе с Docker Hub" - @echo " make docker build - собрать все Docker образы (amd64 + arm64)" - @echo " make docker build-image IMAGE=<имя> - собрать отдельный образ" + @echo "🐳 ОБРАЗЫ (Podman, локально без registry):" + @echo " make buildall - собрать ВСЕ образы локально (без push), для presets" + @echo " make buildall-image IMAGE=<имя> - собрать один образ локально (напр. IMAGE=ubuntu22)" + @echo " make docker prepare - подготовка к работе с registry" + @echo " make docker build - собрать все образы и отправить в registry" + @echo " make docker build-image IMAGE=<имя> - собрать отдельный образ и push" @echo " make docker rebuild - полная пересборка с очисткой кеша" @echo " make docker push - отправить образы в Docker Hub" @echo " make docker pull - загрузить образы из Docker Hub" @@ -1703,7 +1374,7 @@ setup-cicd: .PHONY: encrypt decrypt rekey-all encrypt-all: @echo "🔐 Шифрование всех roles/*/vars/main.yml (только незашифрованных) ..." - @docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ + @podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ VAULT=vault/.vault; \ for f in roles/*/vars/main.yml; do [ -f "$$f" ] || continue; \ if ! grep -q "ANSIBLE_VAULT" "$$f" 2>/dev/null; then \ @@ -1716,7 +1387,7 @@ encrypt-all: decrypt-all: @echo "🔓 Расшифровка всех roles/*/vars/main.yml (только зашифрованных) ..." - @docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ + @podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ VAULT=vault/.vault; \ for f in roles/*/vars/main.yml; do [ -f "$$f" ] || continue; \ if grep -q "ANSIBLE_VAULT" "$$f" 2>/dev/null; then \ @@ -1730,7 +1401,7 @@ decrypt-all: rekey-all: @echo "🔑 Смена пароля для всех roles/*/vars/main.yml ..." @echo "🔍 Проверка статуса шифрования файлов..." - @UNENCRYPTED_FILES=$$(docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ + @UNENCRYPTED_FILES=$$(podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ for f in roles/*/vars/main.yml; do [ -f "$$f" ] || continue; \ if ! grep -q "ANSIBLE_VAULT" "$$f" 2>/dev/null; then \ echo "$$f"; \ @@ -1750,7 +1421,7 @@ rekey-all: echo ""; \ echo "$$NEW_PASSWORD" > vault/.vault.new; \ chmod 600 vault/.vault.new; \ - docker run --rm -it -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ + podman run --rm -it -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ VAULT=vault/.vault; \ NEW_VAULT=vault/.vault.new; \ for f in roles/*/vars/main.yml; do [ -f "$$f" ] || continue; \ @@ -1783,7 +1454,7 @@ encrypt-role: echo "ℹ️ Уже зашифровано: $$FILE"; \ exit 0; \ fi; \ - docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ + podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ VAULT=vault/.vault; \ ansible-vault encrypt --encrypt-vault-id default --vault-password-file "$$VAULT" "$$1" || true' _ "$$FILE"; \ echo "✅ Зашифровано: $$FILE" @@ -1808,7 +1479,7 @@ decrypt-role: echo "ℹ️ Уже расшифровано: $$FILE"; \ exit 0; \ fi; \ - docker run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ + podman run --rm -v "$(PWD):/workspace" -w /workspace $(DOCKER_IMAGE) bash -c '\ VAULT=vault/.vault; \ ansible-vault decrypt --vault-password-file "$$VAULT" "$$1" || true' _ "$$FILE"; \ echo "✅ Расшифровано: $$FILE" @@ -1838,7 +1509,7 @@ rekey-role: echo "ℹ️ Файл $$FILE не зашифрован, сначала зашифруйте его"; \ exit 1; \ fi; \ - docker run --rm -it -v "$(PWD):/workspace" -w /workspace \ + podman run --rm -it -v "$(PWD):/workspace" -w /workspace \ -e EDITOR=$(EDITOR) \ $(DOCKER_IMAGE) \ ansible-vault rekey --vault-password-file vault/.vault "$$FILE"; \ @@ -1847,15 +1518,15 @@ rekey-role: # Очистка контейнеров Molecule .PHONY: clean-containers clean-containers: - @echo "🧹 Очистка контейнеров Molecule..." + @echo "🧹 Очистка контейнеров Molecule (Podman)..." @echo "📋 Поиск контейнеров проекта..." - @docker ps -a --filter "ancestor=inecs/ansible-lab" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" || true + @podman ps -a --filter "ancestor=inecs/ansible-lab" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" || true @echo "" @echo "🗑️ Удаление контейнеров..." - @docker ps -a --filter "ancestor=inecs/ansible-lab" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true - @docker ps -a --filter "network=labnet" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true + @podman ps -a --filter "ancestor=inecs/ansible-lab" --format "{{.ID}}" | xargs -r podman rm -f 2>/dev/null || true + @podman ps -a --filter "network=labnet" --format "{{.ID}}" | xargs -r podman rm -f 2>/dev/null || true @echo "🧹 Очистка сетей..." - @docker network rm labnet 2>/dev/null || true + @podman network rm labnet 2>/dev/null || true @echo "✅ Очистка завершена" # Пустые цели для совместимости diff --git a/README.md b/README.md index 8423493..6e05eee 100644 --- a/README.md +++ b/README.md @@ -634,6 +634,7 @@ make custom-images # справка по собственным ### Основная документация - **[docs/getting-started.md](docs/getting-started.md)** - Быстрый старт +- **[docs/quickstart-for-dummies.md](docs/quickstart-for-dummies.md)** - Пошаговое руководство для новичков (роли, плейбук, линт, тесты, пресеты, инвентори, деплой) - **[docs/molecule-guide.md](docs/molecule-guide.md)** - Руководство по Molecule - **[docs/creating-roles.md](docs/creating-roles.md)** - Создание ролей - **[docs/devops-role.md](docs/devops-role.md)** - Универсальная роль devops для настройки пользователей и SSH diff --git a/dockerfiles/ansible-controller/Dockerfile b/dockerfiles/ansible-controller/Dockerfile index 2af0d70..66d3d7c 100644 --- a/dockerfiles/ansible-controller/Dockerfile +++ b/dockerfiles/ansible-controller/Dockerfile @@ -35,8 +35,12 @@ RUN apt-get install -y \ RUN wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_arm64 \ && chmod +x /usr/local/bin/yq -# Устанавливаем Docker CLI -RUN apt-get install -y docker.io docker-compose +# Устанавливаем Podman (вместо Docker). Для Ubuntu 22.04 — из universe +RUN apt-get install -y software-properties-common \ + && add-apt-repository -y universe \ + && apt-get update \ + && apt-get install -y podman \ + && apt-get clean # Устанавливаем kubectl RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \ @@ -46,10 +50,10 @@ RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/s # Устанавливаем Helm RUN curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash -# Устанавливаем Kind -RUN curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 \ - && chmod +x ./kind \ - && mv ./kind /usr/local/bin/ +# Устанавливаем Minikube (вместо Kind, для использования с драйвером podman) +RUN curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \ + && chmod +x minikube-linux-amd64 \ + && mv minikube-linux-amd64 /usr/local/bin/minikube ## Устанавливаем Istio CLI #RUN curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.22.1 sh - \ @@ -79,18 +83,17 @@ RUN chown -R ansible:ansible /ansible # Переключаемся на пользователя ansible USER ansible -# Устанавливаем дополнительные роли -RUN ansible-galaxy install geerlingguy.docker \ - && ansible-galaxy install geerlingguy.kubernetes +# Устанавливаем дополнительные роли (коллекция containers.podman в requirements.yml) +RUN ansible-galaxy install geerlingguy.kubernetes -# Устанавливаем molecule как root -RUN pip3 install ansible ansible-core ansible-lint molecule molecule-docker passlib +# Устанавливаем molecule как root (delegated driver для Podman) +RUN pip3 install ansible ansible-core ansible-lint molecule passlib # Проверяем, что molecule установлен RUN which molecule || echo "molecule not found" -# Настройки для работы с Docker -ENV DOCKER_HOST=unix:///var/run/docker.sock +# Настройки для работы с Podman (сокет монтируется с хоста) +ENV CONTAINER_HOST=unix:///run/podman/podman.sock ENV ANSIBLE_FORCE_COLOR=1 ENV ANSIBLE_STDOUT_CALLBACK=yaml ENV ANSIBLE_CALLBACKS_ENABLED=profile_tasks diff --git a/dockerfiles/ansible-controller/docker-compose.yml b/dockerfiles/ansible-controller/docker-compose.yml index 11b3d16..6eb7d1a 100644 --- a/dockerfiles/ansible-controller/docker-compose.yml +++ b/dockerfiles/ansible-controller/docker-compose.yml @@ -1,3 +1,6 @@ +# Ansible Controller для Podman (сокет Podman) +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru version: "3.9" services: @@ -7,10 +10,10 @@ services: privileged: true command: sleep infinity environment: - DOCKER_HOST: unix:///var/run/docker.sock + CONTAINER_HOST: unix:///run/podman/podman.sock ANSIBLE_VAULT_PASSWORD_FILE: /ansible/vault/.vault volumes: - - /var/run/docker.sock:/var/run/docker.sock + - /run/podman/podman.sock:/run/podman/podman.sock - .:/ansible working_dir: /ansible networks: diff --git a/dockerfiles/ansible-controller/requirements.yml b/dockerfiles/ansible-controller/requirements.yml index 037030c..587e4d6 100644 --- a/dockerfiles/ansible-controller/requirements.yml +++ b/dockerfiles/ansible-controller/requirements.yml @@ -1,8 +1,10 @@ --- -# Ansible Collections for Molecule Universal +# Ansible Collections for Molecule Universal (Podman) +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru collections: - - name: community.docker - version: ">=3.0.0" + - name: containers.podman + version: ">=1.10.0" - name: community.general version: ">=7.0.0" - name: ansible.posix diff --git a/docs/podman.md b/docs/podman.md new file mode 100644 index 0000000..d894844 --- /dev/null +++ b/docs/podman.md @@ -0,0 +1,86 @@ +# Работа с Podman (ветка podman) + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +В ветке `podman` проект переведён на использование **только Podman** (без Docker и Docker Compose). + +## Требования + +- **Podman** установлен и запущен (rootful: сокет `/run/podman/podman.sock`) +- **podman compose** (встроен в Podman 4.1+ или пакет `podman-compose`) +- Для K8s: **Minikube** и **kubectl** на хосте + +## Основные изменения + +### Molecule + +- Драйвер: `delegated` (создание/удаление контейнеров в `create.yml` / `destroy.yml` через Ansible). +- Используется коллекция **containers.podman** (`podman_network`, `podman_container`). +- Сокет: `/run/podman/podman.sock` (DOoD-узлы заменены на POoD с монтированием сокета Podman). +- Инвентарь: `ansible_connection=containers.podman.podman`. + +### Makefile + +- Все вызовы `docker` заменены на `podman`. +- Сборка образов: `podman build --platform ...` (без buildx). +- Compose: `podman compose` (вместо `docker-compose`). +- Сокет в целях контроллера: `PODMAN_SOCKET ?= /run/podman/podman.sock`, монтируется в контейнер ansible-controller. + +### Ansible-controller + +- Образ собирается и запускается через Podman. +- В образе установлен **Podman** (вместо Docker). +- При запуске монтируется сокет хоста: `-v $(PODMAN_SOCKET):/run/podman/podman.sock`. +- Переменная окружения: `CONTAINER_HOST=unix:///run/podman/podman.sock`. + +### Kubernetes + +- Вместо **Kind** используется **Minikube** с драйвером `podman`. +- Кластер создаётся на хосте: `minikube start --driver=podman`. +- Скрипт: `scripts/create_minikube_cluster.py` (читает пресет из `molecule/presets/k8s/*.yml`). +- В пресетах K8s: `minikube_profile`, `minikube_addons`, `minikube_cpus`, `minikube_memory` (вместо `kind_clusters`). + +## Локальные образы (без доступа к registry) + +Во всех пресетах при использовании Podman **используются только локальные образы**. Выгрузка из registry отключена. + +1. Соберите все образы локально один раз: + ```bash + make buildall + ``` +2. Либо один образ для нужного пресета: + ```bash + make buildall-image IMAGE=ubuntu22 + make buildall-image IMAGE=debian11 + ``` +3. После этого Molecule при `create` проверяет наличие образов локально и не выполняет `podman pull`. Если какого-то образа нет — выведет сообщение с указанием выполнить `make buildall`. + +## Быстрый старт + +```bash +# 1. Собрать локальные образы (без registry) +make buildall + +# 2. Сеть labnet для compose (если нужен controller) +podman network create labnet 2>/dev/null || true + +# 3. Запуск ansible-controller +make controller run + +# 4. Тест ролей (контейнеры создаются через Podman из локальных образов) +make role test minimal + +# Minikube (на хосте) +make k8s create k8s-minimal +make k8s status +kubectl get nodes +``` + +## Переменные + +- **PODMAN_SOCKET** — путь к сокету Podman на хосте (по умолчанию `/run/podman/podman.sock` для rootful). + +## CI/CD + +Секция CI/CD (**cicd/**) в этой ветке **не менялась** — рассчитана на окружения с Docker. Для Podman в пайплайнах нужно отдельно устанавливать Podman и вызывать `podman` / `podman compose` в job’ах. diff --git a/docs/quickstart-for-dummies.md b/docs/quickstart-for-dummies.md new file mode 100644 index 0000000..df17ac7 --- /dev/null +++ b/docs/quickstart-for-dummies.md @@ -0,0 +1,332 @@ +# DevOpsLab: пошаговое руководство для новичков + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +Это руководство описывает по шагам: создание роли, объединение ролей в плейбук, линт, тестирование на контейнерах (подах) с разным количеством и разными ОС, а также формирование инвентори для деплоя на реальные серверы. Подходит и для **Docker**, и для **Podman** (отличия указаны в конце). + +--- + +## Что нужно установить + +- **Make** (обычно уже есть в Linux/macOS). +- **Podman** (ветка podman) или **Docker** (ветка main) — для контейнеров. +- **Ansible** не обязателен на хосте: тесты и деплой можно запускать через контейнер из Makefile. + +Клонируйте репозиторий и перейдите в каталог проекта. Все команды ниже выполняются из корня проекта. + +--- + +## Шаг 1. Создать новую роль + +Роль — это набор задач (tasks), шаблонов и переменных для одной цели (например, установка nginx или настройка пользователей). + +**Команда:** + +```bash +make role create +``` + +Скрипт спросит имя роли (латиницей, например `nginx` или `myapp`). Будет создана структура: + +- `roles/<имя_роли>/tasks/main.yml` — основные задачи +- `roles/<имя_роли>/handlers/main.yml` +- `roles/<имя_роли>/defaults/main.yml` — переменные по умолчанию +- `roles/<имя_роли>/meta/main.yml` +- и др. + +**Что сделать после создания:** + +1. Открыть `roles/<имя_роли>/tasks/main.yml` и добавить свои задачи (модули `package`, `copy`, `template`, `service` и т.д.). +2. При необходимости задать переменные в `roles/<имя_роли>/defaults/main.yml`. + +Роль автоматически попадёт в общий плейбук развёртывания после следующего шага (обновления плейбуков). + +--- + +## Шаг 2. Объединить роли в один плейбук + +Все роли, которые должны выполняться на серверах, собираются в один плейбук: **`roles/deploy.yml`**. + +**Вариант А — обновить плейбук автоматически (все роли из `roles/`):** + +```bash +make update-playbooks +``` + +Скрипт перезапишет `roles/deploy.yml`: для каждой роли в каталоге `roles/` будет добавлен свой play (hosts: all, одна роль в play). + +**Вариант Б — править вручную:** + +Откройте `roles/deploy.yml`. Каждая роль описывается своим блоком: + +```yaml +- name: Установка роли repo + hosts: all + become: true + roles: + - repo + +- name: Установка роли devops + hosts: all + become: true + roles: + - devops +``` + +- Чтобы **включить** роль — добавьте такой блок или раскомментируйте существующий. +- Чтобы **выключить** роль — закомментируйте блок (перед каждой строкой поставьте `#`). +- Порядок блоков задаёт порядок применения ролей. + +Итог: в `roles/deploy.yml` перечислены все роли, которые будут и тестироваться в контейнерах, и деплоиться на реальные серверы (см. шаги 4 и 6). + +--- + +## Шаг 3. Запуск линт-проверок + +Линт проверяет синтаксис и типичные ошибки в ролях (и в плейбуках, которые их вызывают). + +**Проверить все роли:** + +```bash +make role lint +``` + +**Проверить одну роль:** + +```bash +make role lint <имя_роли> +# Пример: +make role lint repo +make role lint devops +``` + +При ошибках будут указаны файл и строка. Исправьте замечания и запустите линт снова. Без ошибок линт завершится без падения. + +--- + +## Шаг 4. Тестирование ролей на контейнерах (подах) + +Тесты поднимают контейнеры с разными ОС, применяют к ним плейбук (в т.ч. ваши роли) и проверяют результат. Контейнеры в этом шаге — это и есть «поды» (тестовые хосты). + +### 4.1. Подготовка образов (чтобы не качать из registry) + +**Если используете Podman (ветка podman):** + +Образы должны быть собраны **локально**. Один раз выполните: + +```bash +make buildall +``` + +Собираются все образы (Ubuntu, Debian, CentOS и т.д.). Долго, но делается один раз. Для одного образа, например для минимального теста: + +```bash +make buildall-image IMAGE=ubuntu22 +make buildall-image IMAGE=debian12 +``` + +**Если используете Docker (ветка main):** + +```bash +make docker build +``` + +(при необходимости сначала `make docker setup-builder`). + +### 4.2. Запуск тестов + +**С пресетом по умолчанию (обычно 2 хоста):** + +```bash +make role test +``` + +**С конкретным пресетом:** + +```bash +make role test minimal +make role test default +make role test all-images +``` + +Что происходит: создаются контейнеры по выбранному пресету, к ним применяется плейбук (converge), затем они удаляются. Результат виден в выводе команды. + +--- + +## Шаг 5. Менять количество подов и ОС (пресеты) + +Количество «подов» (контейнеров) и их ОС задаются **пресетами** — файлами в `molecule/presets/` и `molecule/presets/examples/`. + +### 5.1. Посмотреть доступные пресеты + +```bash +make presets list +``` + +Будет выведен список пресетов и краткое описание (в т.ч. количество хостов). + +### 5.2. Что задаёт пресет + +В пресете задаётся: + +- **hosts** — список хостов (контейнеров): имя, семейство ОС (`family`), группы. +- **images** — соответствие `family` и образа (например `ubuntu22: "inecs/ansible-lab:ubuntu22-latest"`). +- Общие настройки (сеть, systemd и т.д.). + +От количества элементов в **hosts** и от выбранных **family** зависит, сколько подов поднимется и какие ОС будут использоваться. + +### 5.3. Примеры пресетов по количеству и ОС + +| Пресет | Описание | Кол-во хостов | +|-------------|------------------------------------|----------------| +| `minimal` | Один хост (быстрый тест) | 1 | +| `default` | Два хоста (например Ubuntu + Debian) | 2 | +| `all-images`| Максимум образов/ОС | много | + +Запуск: + +```bash +make role test minimal +make role test default +make role test all-images +``` + +### 5.4. Создать свой пресет (свои поды и ОС) + +1. Скопируйте существующий пресет, например: + + ```bash + cp molecule/presets/default.yml molecule/presets/my-preset.yml + ``` + +2. Откройте `molecule/presets/my-preset.yml`. + +3. В секции **hosts** укажите свои хосты. Формат одного хоста: + + ```yaml + hosts: + - name: web1 + family: ubuntu22 + groups: [web, test] + - name: db1 + family: centos9 + groups: [db, test] + - name: lb1 + family: debian12 + groups: [lb, test] + ``` + + - **name** — уникальное имя контейнера (пода). + - **family** — ключ из секции **images** (ubuntu20, ubuntu22, ubuntu24, debian9–debian12, centos7–centos9, alma, rocky, rhel, redos, astra, alt9, alt10 и т.д.). + - **groups** — группы для инвентори (можно использовать в плейбуках через `hosts: web` или `hosts: db`). + +4. Сохраните файл. Запуск теста с вашим пресетом: + + ```bash + make role test my-preset + ``` + +Количество подов = количество элементов в списке **hosts**. Разные ОС = разные **family** при наличии соответствующих образов в **images** (и собранных локально при Podman через `make buildall`). + +--- + +## Шаг 6. Инвентори и деплой на реальные серверы + +Когда роли и плейбук готовы и протестированы в контейнерах, их можно применять к реальным серверам. Для этого нужен **инвентори** и команды деплоя. + +### 6.1. Где лежит инвентори + +Файл инвентори по умолчанию: + +- **`inventory/hosts.ini`** + +Именно его используют команды `make role deploy` и `make role dryrun` (см. ниже). + +### 6.2. Как сформировать инвентори + +Откройте `inventory/hosts.ini`. Формат — обычный ini-инвентори Ansible. + +**Пример минимального инвентори:** + +```ini +# Группа веб-серверов +[web_servers] +web1 ansible_host=192.168.1.10 ansible_user=deploy +web2 ansible_host=192.168.1.11 ansible_user=deploy + +# Группа БД +[db_servers] +db1 ansible_host=192.168.1.20 ansible_user=deploy + +# Общие переменные для всех хостов +[all:vars] +ansible_ssh_private_key_file=~/.ssh/id_rsa +ansible_ssh_common_args='-o StrictHostKeyChecking=no' +``` + +- **Группы** — `[web_servers]`, `[db_servers]`. Имена можно менять и добавлять свои. +- **Хосты** — каждая строка: имя хоста + переменные. Обязательно укажите **ansible_host** (IP или hostname) и **ansible_user** (пользователь для SSH). +- **all:vars** — переменные для всех хостов (ключ SSH, опции подключения и т.д.). + +Чтобы применять плейбук только к части серверов, в плейбуке можно использовать группы (например `hosts: web_servers`) или ограничивать запуск через `--limit` (см. ниже). + +### 6.3. Проверка без изменений (dry-run) + +Перед реальным деплоем полезно проверить, что плейбук выполнится без ошибок и что изменения именно те, что нужны: + +```bash +make role dryrun <имя_роли> +# Пример: +make role dryrun repo +``` + +Команда запускает плейбук в режиме **check** (без реальных изменений) для указанной роли. Убедитесь, что в `inventory/hosts.ini` указаны ваши серверы и что с них есть SSH-доступ. + +### 6.4. Реальный деплой плейбука на серверы + +Когда инвентори заполнен и dry-run устраивает: + +```bash +make role deploy +``` + +Будет выполнен плейбук **roles/deploy.yml** на всех хостах из **inventory/hosts.ini** (с подтверждением перед применением). При необходимости можно запускать Ansible вручную, например: + +- Только на одной группе: + ```bash + ansible-playbook -i inventory/hosts.ini roles/deploy.yml --limit web_servers + ``` +- Только проверка (аналог dry-run для всего плейбука): + ```bash + ansible-playbook -i inventory/hosts.ini roles/deploy.yml --check --diff + ``` + +(Если Ansible запускается через контейнер в Makefile, пути и вызов могут отличаться; логика та же: тот же инвентори и тот же плейбук.) + +### 6.5. Кратко по шагам деплоя + +1. Заполнить **inventory/hosts.ini** (группы, хосты, ansible_host, ansible_user, ключ SSH). +2. Проверить доступ: `ansible -i inventory/hosts.ini all -m ping` (если Ansible установлен локально). +3. Запустить **make role dryrun <роль>** для проверки. +4. Запустить **make role deploy** для применения плейбука к реальным серверам. + +--- + +## Универсальность: Docker и Podman + +Один и тот же сценарий (роли, плейбук, пресеты, инвентори) работает и с Docker, и с Podman. Отличия только в подготовке образов и в том, как вызываются контейнеры внутри Makefile. + +| Действие | Podman (ветка podman) | Docker (ветка main) | +|-----------------------|---------------------------|----------------------------| +| Сборка образов | `make buildall` | `make docker build` | +| Один образ | `make buildall-image IMAGE=ubuntu22` | `make docker build-image IMAGE=ubuntu22` | +| Запуск тестов | `make role test [preset]` | то же | +| Линт | `make role lint` | то же | +| Деплой / инвентори | `make role deploy`, `inventory/hosts.ini` | то же | + +- **Пресеты, роли, deploy.yml, inventory** — одни и те же. +- **Линт, тесты, деплой** — одни и те же команды `make role ...`. +- Разница только в том, как и откуда берутся образы (локальная сборка через `buildall` у Podman или сборка/пулл через `docker build` у Docker). + +K8s в этом руководстве не рассматривается; см. отдельную документацию по Kubernetes при необходимости. diff --git a/molecule/default/create.yml b/molecule/default/create.yml index 98ceb15..5080f51 100644 --- a/molecule/default/create.yml +++ b/molecule/default/create.yml @@ -10,8 +10,9 @@ # Проверяем сначала в папке 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 файл не найден + # Fallback значения если preset файл не найден (Podman использует ту же сеть) docker_network: labnet + podman_network: "{{ docker_network }}" generated_inventory: "{{ molecule_ephemeral_directory }}/inventory/hosts.ini" images: alt9: "inecs/ansible-lab:alt9-latest" @@ -89,20 +90,20 @@ hosts: "{{ filtered_hosts | default(hosts) }}" # ============================================================================= - # СЕТЕВОЕ ПОДКЛЮЧЕНИЕ + # СЕТЕВОЕ ПОДКЛЮЧЕНИЕ (Podman) # ============================================================================= - name: Network setup debug: msg: | ================================================================================ - НАСТРОЙКА СЕТИ + НАСТРОЙКА СЕТИ (Podman) ================================================================================ - Network: {{ docker_network }} + Network: {{ podman_network | default(docker_network) }} ================================================================================ - name: Ensure network exists - community.docker.docker_network: - name: "{{ docker_network }}" + containers.podman.podman_network: + name: "{{ podman_network | default(docker_network) }}" state: present # ============================================================================= @@ -117,39 +118,39 @@ Count: {{ hosts | selectattr('type','undefined') | list | length }} ================================================================================ - - name: Pull systemd images with correct platform - command: "docker pull --platform {{ ansible_architecture }} {{ images[item.family] }}" + # Только локальные образы (registry запрещён). Сборка: make buildall + - name: Проверка наличия локальных образов (Podman) + command: "podman image exists {{ 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 + register: image_check + failed_when: false + changed_when: false - - 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: Остановка при отсутствии локальных образов + fail: + msg: | + Локальные образы не найдены (доступ к registry запрещён). + Выполните: make buildall + Отсутствуют образы: {{ image_check.results | default([]) | selectattr('rc', 'ne', 0) | map(attribute='item') | map(attribute='family') | list | join(', ') }} + when: image_check.results is defined and (image_check.results | selectattr('rc', 'ne', 0) | list | length > 0) - name: Start systemd nodes - community.docker.docker_container: + containers.podman.podman_container: name: "{{ item.name }}" image: "{{ images[item.family] }}" - networks: - - name: "{{ docker_network }}" + network: "{{ podman_network | default(docker_network) }}" privileged: "{{ systemd_defaults.privileged }}" command: "{{ '/bin/bash -c \"while true; do sleep 30; done\"' if item.family in ['alt10', 'alt9'] else systemd_defaults.command }}" - volumes: "{{ systemd_defaults.volumes | default([]) + (item.volumes | default([])) + ['/Users/inecs/PycharmProjects/DevOpsLab/vault:/workspace/vault:ro', '/Users/inecs/PycharmProjects/DevOpsLab/files:/workspace/files:ro', '/Users/inecs/PycharmProjects/DevOpsLab/roles:/workspace/roles:ro'] }}" + volume: "{{ systemd_defaults.volumes | default([]) + (item.volumes | default([])) + ['/workspace/vault:/workspace/vault:ro', '/workspace/files:/workspace/files:ro', '/workspace/roles:/workspace/roles:ro'] }}" tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}" - capabilities: "{{ systemd_defaults.capabilities | default([]) }}" - published_ports: "{{ item.publish | default([]) }}" + cap_add: "{{ systemd_defaults.capabilities | default([]) }}" + ports: "{{ item.publish | default([]) }}" env: "{{ item.env | default({}) }}" - # Специальные настройки для Astra Linux и RedOS - security_opts: "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}" - platform: "{{ item.docker_platform | default(item.platform) | default(omit) }}" + security_opt: "{{ ['seccomp=unconfined', 'apparmor=unconfined'] if item.family in ['astra', 'redos'] else [] }}" state: started - restart_policy: unless-stopped + 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 @@ -160,75 +161,44 @@ seconds: 10 when: hosts | length > 0 - # Проверка готовности контейнеров + # Проверка готовности контейнеров (Podman) - name: Wait for containers to be running - community.docker.docker_container_info: - name: "{{ item.name }}" + command: "podman inspect --format '{{ '{{' }}.State.Running{{ '}}' }}' {{ item.name }}" register: container_info loop: "{{ hosts | selectattr('type','undefined') | list }}" loop_control: { label: "{{ item.name }}" } when: item.family is defined and images[item.family] is defined retries: 10 delay: 5 - until: container_info.container.State.Running | default(false) + until: container_info.stdout == 'true' # ============================================================================= - # DIND NODES - Создание контейнеров Docker-in-Docker + # POoD NODES - Создание контейнеров Podman-out-of-Podman (сокет Podman) # ============================================================================= - - name: DinD nodes setup + - name: POoD nodes setup debug: msg: | ================================================================================ - DIND NODES - Создание контейнеров Docker-in-Docker - ================================================================================ - Count: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - ================================================================================ - - - 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 - Создание контейнеров Docker-out-of-Docker - # ============================================================================= - - name: DOoD nodes setup - debug: - msg: | - ================================================================================ - DOOD NODES - Создание контейнеров Docker-out-of-Docker + POoD NODES - Контейнеры с монтированием сокета Podman ================================================================================ Count: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} ================================================================================ - - name: Start DOoD nodes (systemd + docker.sock mount) - community.docker.docker_container: + - name: Start POoD nodes (systemd + podman.sock mount) + containers.podman.podman_container: name: "{{ item.name }}" image: "{{ images[item.family] }}" - networks: - - name: "{{ docker_network }}" + network: "{{ podman_network | default(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([])) + ['/Users/inecs/PycharmProjects/DevOpsLab/vault:/workspace/vault:ro', '/Users/inecs/PycharmProjects/DevOpsLab/files:/workspace/files:ro', '/Users/inecs/PycharmProjects/DevOpsLab/roles:/workspace/roles:ro'] }}" + volume: "{{ (systemd_defaults.volumes | default([])) + ['/run/podman/podman.sock:/run/podman/podman.sock'] + (item.volumes | default([])) + ['/workspace/vault:/workspace/vault:ro', '/workspace/files:/workspace/files:ro', '/workspace/roles:/workspace/roles:ro'] }}" tmpfs: "{{ systemd_defaults.tmpfs | default([]) }}" - capabilities: "{{ systemd_defaults.capabilities | default([]) }}" - published_ports: "{{ item.publish | default([]) }}" - env: "{{ item.env | default({}) }}" - platform: "{{ item.docker_platform | default(item.platform) | default(omit) }}" + cap_add: "{{ systemd_defaults.capabilities | default([]) }}" + ports: "{{ item.publish | default([]) }}" + env: "{{ (item.env | default({})) | combine({'CONTAINER_HOST': 'unix:///run/podman/podman.sock'}) }}" state: started - restart_policy: unless-stopped + 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 @@ -264,7 +234,7 @@ set_fact: inv_content: | [all:vars] - ansible_connection=community.docker.docker + ansible_connection=containers.podman.podman ansible_remote_tmp=/tmp/.ansible-tmp {% for group, members in (groups_map | dictsort) %} diff --git a/molecule/default/destroy.yml b/molecule/default/destroy.yml index 5d01050..1834e86 100644 --- a/molecule/default/destroy.yml +++ b/molecule/default/destroy.yml @@ -56,71 +56,37 @@ Count: {{ hosts | length }} containers ================================================================================ - - name: Stop and remove containers - community.docker.docker_container: + - name: Stop and remove containers (Podman) + containers.podman.podman_container: name: "{{ item.name }}" state: absent - force_kill: true - cleanup: true + force_delete: true loop: "{{ hosts }}" loop_control: { label: "{{ item.name }}" } ignore_errors: true - name: Force remove any remaining containers shell: | - docker ps -a --filter "name={{ item.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f + podman ps -a --filter "name={{ item.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true loop: "{{ hosts }}" loop_control: { label: "{{ item.name }}" } ignore_errors: true - - name: Remove DinD volumes - community.docker.docker_volume: - name: "{{ item.name }}-docker" - state: absent - loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}" - loop_control: { label: "{{ item.name }}" } - ignore_errors: true - - - name: Remove custom volumes - community.docker.docker_volume: - name: "{{ item.volumes | default([]) | select('match', '^[^:]+$') | list }}" - state: absent - loop: "{{ hosts }}" - loop_control: { label: "{{ item.name }}" } - ignore_errors: true - when: item.volumes is defined - - # ============================================================================= - # ОЧИСТКА СЕТИ - Удаление Docker сети - # ============================================================================= - - name: Network cleanup - debug: - msg: | - ================================================================================ - ОЧИСТКА СЕТИ - Удаление Docker сети - ================================================================================ - Network: {{ docker_network }} - ================================================================================ - - name: Remove network - community.docker.docker_network: - name: "{{ docker_network }}" + containers.podman.podman_network: + name: "{{ podman_network | default(docker_network) }}" state: absent ignore_errors: true - name: Force cleanup all project containers shell: | - # Удаляем все контейнеры из загруженного пресета {% for host in hosts %} - docker ps -a --filter "name={{ host.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f 2>/dev/null || true + podman ps -a --filter "name={{ host.name }}" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true {% endfor %} - # Удаляем все контейнеры с образами ansible-lab - docker ps -a --filter "ancestor=inecs/ansible-lab" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f 2>/dev/null || true - # Удаляем все контейнеры с сетью labnet - docker ps -a --filter "network=labnet" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r docker rm -f 2>/dev/null || true + podman ps -a --filter "ancestor=inecs/ansible-lab" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true + podman ps -a --filter "network=labnet" --format "{{ '{{' }}.ID{{ '}}' }}" | xargs -r podman rm -f 2>/dev/null || true ignore_errors: true vars: - # Используем переменную hosts из загруженного пресета hosts: "{{ hosts }}" - name: Display cleanup summary @@ -131,8 +97,8 @@ ================================================================================ Containers: {{ hosts | length }} Volumes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list | length }} - Network: {{ docker_network }} - Clusters: {{ kind_clusters | default([]) | length }} + Network: {{ podman_network | default(docker_network) }} + Clusters: {{ minikube_profiles | default([]) | length }} ================================================================================ - name: Display filtered hosts diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 600f9a9..8b2475d 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -3,8 +3,11 @@ # Автор: Сергей Антропов # Сайт: https://devops.org.ru +# Используем delegated: создание/удаление контейнеров в create.yml и destroy.yml через Podman +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru driver: - name: docker + name: delegated platforms: # Платформы будут созданы динамически через preset файлы diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index 29da1bd..dde7b8c 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -46,10 +46,8 @@ Count: {{ hosts | selectattr('type','undefined') | list | length }} ================================================================================ - - name: Check systemd nodes status - community.docker.docker_container_exec: - container: "{{ item.name }}" - command: systemctl is-system-running + - name: Check systemd nodes status (Podman exec) + command: "podman exec {{ item.name }} systemctl is-system-running" loop: "{{ hosts | selectattr('type','undefined') | list }}" loop_control: { label: "{{ item.name }}" } register: systemd_status @@ -61,43 +59,23 @@ loop: "{{ systemd_status.results | default([]) }}" when: systemd_status is defined - # Проверка DinD узлов - - name: Check DinD nodes docker daemon - community.docker.docker_container_exec: - container: "{{ item.name }}" - command: docker version --format '{{.Server.Version}}' - loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dind') | list }}" - loop_control: { label: "{{ item.name }}" } - register: dind_status - ignore_errors: true - - - name: Display DinD nodes status - debug: - msg: "DinD node {{ item.0.name }}: Docker {{ item.1.stdout | default('not running') }}" - loop: "{{ dind_status.results | default([]) }}" - when: dind_status is defined - - # Проверка DOoD узлов - - name: Check DOoD nodes docker access - community.docker.docker_container_exec: - container: "{{ item.name }}" - command: docker ps --format '{{.Names}}' + # Проверка POoD узлов (Podman-out-of-Podman) + - name: Check POoD nodes podman access + command: "podman exec {{ item.name }} podman ps --format '{{'{{' }}.Names{{ '}}' }}'" loop: "{{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list }}" loop_control: { label: "{{ item.name }}" } register: dood_status ignore_errors: true - - name: Display DOoD nodes status + - name: Display POoD nodes status debug: - msg: "DOoD node {{ item.0.name }}: Can access {{ item.1.stdout_lines | length | default(0) }} containers" + msg: "POoD node {{ item.0.name }}: Can access {{ item.1.stdout_lines | length | default(0) }} containers" loop: "{{ dood_status.results | default([]) }}" when: dood_status is defined # Проверка сетевого подключения - name: Test network connectivity between nodes - community.docker.docker_container_exec: - container: "{{ item.0.name }}" - command: ping -c 1 {{ item.1.name }} + command: "podman exec {{ item.0.name }} ping -c 1 {{ item.1.name }}" loop: "{{ hosts | subelements(hosts, 'name') }}" loop_control: { label: "{{ item.0.name }} -> {{ item.1.name }}" } when: item.0.name != item.1.name @@ -112,9 +90,7 @@ # Проверка портов - name: Check published ports - community.docker.docker_container_exec: - container: "{{ item.name }}" - command: netstat -tlnp + command: "podman exec {{ item.name }} netstat -tlnp 2>/dev/null || podman exec {{ item.name }} ss -tlnp" loop: "{{ hosts | selectattr('publish','defined') | list }}" loop_control: { label: "{{ item.name }}" } register: port_status @@ -139,11 +115,10 @@ - name: Display verification summary debug: msg: | - ✅ Verification Summary: + ✅ Verification Summary (Podman): - Total hosts: {{ hosts | length }} - 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 }} + - POoD nodes: {{ hosts | selectattr('type','defined') | selectattr('type','equalto','dood') | list | length }} - Groups: {{ groups_map.keys() | list | join(', ') }} - Network: {{ docker_network }} diff --git a/molecule/presets/k8s/k8s-minimal.yml b/molecule/presets/k8s/k8s-minimal.yml index 5408b26..245eb53 100644 --- a/molecule/presets/k8s/k8s-minimal.yml +++ b/molecule/presets/k8s/k8s-minimal.yml @@ -33,10 +33,10 @@ systemd_defaults: tmpfs: ["/run", "/run/lock"] capabilities: ["SYS_ADMIN"] -# Минимальный Kind кластер без аддонов -kind_clusters: - - name: minimal - workers: 0 # Только control-plane - api_port: 6443 +# Minikube (драйвер podman) — минимальный кластер без аддонов +minikube_profile: minikube +minikube_cpus: "2" +minikube_memory: "4096" +minikube_addons: [] hosts: [] diff --git a/scripts/create_minikube_cluster.py b/scripts/create_minikube_cluster.py new file mode 100644 index 0000000..579517f --- /dev/null +++ b/scripts/create_minikube_cluster.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +""" +Скрипт для создания Minikube кластера с драйвером Podman. +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess + + +def run_cmd(cmd, check=True): + """Выполнить команду на хосте.""" + print(f"[run] {cmd}") + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if check and result.returncode != 0: + print(f"[error] {result.stderr}") + sys.exit(1) + if result.stdout: + print(result.stdout) + return result + + +def main(): + if len(sys.argv) < 2: + print("Usage: create_minikube_cluster.py ") + print(" Создаёт Minikube кластер с драйвером podman и опционально включает аддоны из пресета.") + sys.exit(1) + + preset_file = sys.argv[1] + print(f"📋 Читаю пресет: {preset_file}") + + with open(preset_file, "r", encoding="utf-8") as f: + preset = yaml.safe_load(f) or {} + + profile = preset.get("minikube_profile", "minikube") + addons = preset.get("minikube_addons", []) + cpus = preset.get("minikube_cpus", "2") + memory = preset.get("minikube_memory", "4096") + + print(f"\n☸️ Создание Minikube кластера (драйвер: podman)") + print(f" Профиль: {profile}") + print(f" CPU: {cpus}, Memory: {memory}") + + # Проверяем, запущен ли уже кластер + result = subprocess.run( + f"minikube profile list 2>/dev/null | grep -E '^{profile}' | grep 'Running'", + shell=True, + capture_output=True, + text=True, + ) + if result.returncode == 0 and result.stdout.strip(): + print(f"⚠️ Кластер с профилем '{profile}' уже запущен.") + print(" Для пересоздания выполните: minikube delete -p " + profile) + else: + run_cmd( + f"minikube start --driver=podman --profile={profile} --cpus={cpus} --memory={memory}" + ) + print(f"✅ Minikube кластер '{profile}' создан и запущен.") + + # Включаем аддоны из пресета + if addons: + print(f"\n📦 Включение аддонов: {', '.join(addons)}") + for addon in addons: + run_cmd(f"minikube addons enable {addon} -p {profile}", check=False) + + print("\n🎉 Готово. Использование:") + print(f" kubectl config use-context {profile}") + print(" minikube kubectl -- get nodes") + print(" minikube dashboard -p " + profile) + + +if __name__ == "__main__": + main() diff --git a/scripts/delete_hosts.py b/scripts/delete_hosts.py index 865dc1a..9f20228 100644 --- a/scripts/delete_hosts.py +++ b/scripts/delete_hosts.py @@ -29,11 +29,11 @@ def main(): host_name = host['name'] # Проверяем существование контейнера - result = subprocess.run(f"docker ps -a --format '{{{{.Names}}}}' | grep -x {host_name}", + result = subprocess.run(f"podman 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) + subprocess.run(f"podman rm -f {host_name}", shell=True, capture_output=True, text=True) print(f"✅ Контейнер '{host_name}' удален") else: print(f"⚠️ Контейнер '{host_name}' не найден") diff --git a/scripts/k8s_status.py b/scripts/k8s_status.py index 5c76c51..1ec776f 100755 --- a/scripts/k8s_status.py +++ b/scripts/k8s_status.py @@ -9,23 +9,18 @@ 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) + """Получает имя текущего контекста (Minikube).""" + result = subprocess.run( + "kubectl config current-context 2>/dev/null", 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}" + """Выполняет команду kubectl на хосте (контекст Minikube).""" + full_cmd = f"kubectl {cmd}" result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True) return result.stdout diff --git a/scripts/portforward.py b/scripts/portforward.py index bf2d993..5113d32 100755 --- a/scripts/portforward.py +++ b/scripts/portforward.py @@ -12,11 +12,13 @@ import signal import time def get_cluster_name(): - """Получаем имя кластера из preset файла""" + """Получаем имя профиля Minikube из 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'] + if not os.path.exists(preset_file): + return "minikube" + with open(preset_file, "r", encoding="utf-8") as f: + preset = yaml.safe_load(f) or {} + return preset.get("minikube_profile", "minikube") def run_cmd(cmd): """Выполняет команду и возвращает результат""" @@ -80,38 +82,24 @@ def clear_portforwards(): pass def create_portforwards(): - """Создает port-forward для всех сервисов из preset на локальном компьютере""" - # Загружаем preset + """Создает port-forward для всех сервисов из preset на локальном компьютере (Minikube).""" preset_file = "molecule/presets/k8s/kubernetes.yml" - with open(preset_file, 'r') as f: - preset = yaml.safe_load(f) + preset = {} + if os.path.exists(preset_file): + with open(preset_file, "r", encoding="utf-8") as f: + preset = yaml.safe_load(f) or {} + # Поддержка minikube_profile и addon_ports (на верхнем уровне или в kind_clusters[0]) + cluster_name = preset.get("minikube_profile", "minikube") + addon_ports = preset.get("addon_ports") or (preset.get("kind_clusters") or [{}])[0].get("addon_ports", {}) - 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}") + # Minikube обновляет ~/.kube/config — используем его + kubeconfig_file = os.environ.get("KUBECONFIG", os.path.expanduser("~/.kube/config")) + if not os.path.exists(kubeconfig_file): + print(f"❌ Kubeconfig не найден: {kubeconfig_file}. Запустите: make k8s create") 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 подготовлен") + print(f"🔌 Создание port-forward для Minikube (профиль: {cluster_name})") + print(f"📋 Kubeconfig: {kubeconfig_file}") # Ingress HTTP (80) if addon_ports.get('ingress_http'):