podman: переход на Podman, Minikube, локальные образы и док для новичков

- Molecule: драйвер delegated, коллекция containers.podman, create/destroy/verify на Podman
- Makefile: все вызовы docker заменены на podman, сокет /run/podman/podman.sock
- Сборка образов: podman build (без buildx), buildall/buildall-image — только локально без push
- Ansible-controller: Podman в образе, docker-compose на podman compose, сокет Podman
- K8s: Kind заменён на Minikube (драйвер podman), скрипты и Makefile обновлены
- Пресеты: проверка локальных образов, без podman pull (registry запрещён)
- Документация: docs/podman.md, docs/quickstart-for-dummies.md (роли, плейбук, линт, тесты, пресеты, инвентори)
- README: ссылка на quickstart-for-dummies

Made-with: Cursor
This commit is contained in:
Sergey Antropoff
2026-03-11 19:59:47 +03:00
parent 23e1a6037b
commit 05881e8d74
16 changed files with 859 additions and 790 deletions

View File

@@ -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 <preset_file>")
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()

View File

@@ -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}' не найден")

View File

@@ -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

View File

@@ -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'):