diff --git a/Makefile b/Makefile index 2efbc30..1694773 100644 --- a/Makefile +++ b/Makefile @@ -1358,6 +1358,37 @@ k8s: echo "💡 Доступные команды: add, list, delete, update, packages"; \ exit 1;; \ esac;; \ + portforward) \ + PORTFWD_CMD="$(word 3, $(MAKECMDGOALS))"; \ + PORT_ARG="$(word 4, $(MAKECMDGOALS))"; \ + if [ -z "$$PORTFWD_CMD" ]; then \ + echo "❌ Ошибка: Укажите команду"; \ + echo "💡 Пример: make k8s portforward create"; \ + exit 1; \ + fi; \ + case "$$PORTFWD_CMD" in \ + create) \ + echo "🔌 Создание port-forward..."; \ + python3 scripts/portforward.py create;; \ + list) \ + echo "📋 Список активных port-forward..."; \ + python3 scripts/portforward.py list;; \ + clear) \ + echo "🗑️ Очистка всех port-forward..."; \ + python3 scripts/portforward.py clear;; \ + delete) \ + if [ -z "$$PORT_ARG" ]; then \ + echo "❌ Ошибка: Укажите порт"; \ + echo "💡 Пример: make k8s portforward delete 3000"; \ + exit 1; \ + fi; \ + echo "🗑️ Удаление port-forward на порту $$PORT_ARG..."; \ + python3 scripts/portforward.py delete $$PORT_ARG;; \ + *) \ + echo "❌ Неизвестная команда: $$PORTFWD_CMD"; \ + echo "💡 Доступные команды: create, list, clear, delete"; \ + exit 1;; \ + esac;; \ *) \ echo "☸️ Доступные команды:"; \ echo ""; \ diff --git a/molecule/presets/k8s/kubernetes.yml b/molecule/presets/k8s/kubernetes.yml index 0c22283..0284f48 100644 --- a/molecule/presets/k8s/kubernetes.yml +++ b/molecule/presets/k8s/kubernetes.yml @@ -45,26 +45,22 @@ kind_clusters: kiali: true prometheus_stack: true # Порты для доступа к аддонам извне - # Документация: https://devops.org.ru - # Ingress HTTP: http://localhost:80 - # Ingress HTTPS: https://localhost:443 + # Ingress HTTP: http://localhost:8081 + # Ingress HTTPS: https://localhost:8443 # Prometheus: http://localhost:9090 # Grafana: http://localhost:3000 (admin/admin) # Kiali: http://localhost:20001 # Metrics Server: http://localhost:4443 addon_ports: - ingress_http: 80 - ingress_https: 443 + ingress_http: 8081 + ingress_https: 8443 prometheus: 9090 grafana: 3000 kiali: 20001 metrics_server: 4443 -hosts: - # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) - - name: u1 - family: ubuntu22 - groups: [test, web] - - name: u2 - family: debian12 - groups: [test, web] +hosts: [] +# # Стандартный набор - 2 хоста для базового тестирования (стабильные ОС) +# - name: u1 +# family: ubuntu22 +# groups: [test, web] diff --git a/scripts/portforward.py b/scripts/portforward.py new file mode 100755 index 0000000..6eb046d --- /dev/null +++ b/scripts/portforward.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +Скрипт для управления port-forward для Kubernetes сервисов +Автор: Сергей Антропов +Сайт: https://devops.org.ru +""" +import sys +import yaml +import subprocess +import os +import signal +import time + +def get_cluster_name(): + """Получаем имя кластера из preset файла""" + preset_file = "molecule/presets/k8s/kubernetes.yml" + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + return preset['kind_clusters'][0]['name'] + +def run_cmd(cmd): + """Выполняет команду и возвращает результат""" + print(f"[run] {cmd}") + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode != 0: + print(f"[error] {result.stderr}") + else: + print(result.stdout) + return result + +def get_portforward_pids(): + """Получает PID процессов port-forward""" + result = subprocess.run("ps aux | grep 'kubectl.*port-forward' | grep -v grep", shell=True, capture_output=True, text=True) + pids = [] + for line in result.stdout.split('\n'): + if line.strip(): + pids.append(int(line.split()[1])) + return pids + +def list_portforwards(): + """Показывает список всех активных port-forward""" + pids = get_portforward_pids() + if not pids: + print("❌ Нет активных port-forward") + return + + print("📋 Активные port-forward:") + result = subprocess.run("ps aux | grep 'kubectl.*port-forward' | grep -v grep", shell=True, capture_output=True, text=True) + for line in result.stdout.split('\n'): + if line.strip(): + print(f" {line}") + +def clear_portforwards(): + """Завершает все процессы port-forward""" + pids = get_portforward_pids() + if not pids: + print("❌ Нет активных port-forward") + return + + print(f"🗑️ Завершение {len(pids)} процессов port-forward...") + for pid in pids: + try: + os.kill(pid, signal.SIGTERM) + print(f"✅ Процесс {pid} завершен") + except ProcessLookupError: + print(f"⚠️ Процесс {pid} уже не существует") + + # Ждем завершения процессов + time.sleep(2) + + # Принудительно убиваем оставшиеся + remaining_pids = get_portforward_pids() + if remaining_pids: + print("⚠️ Принудительное завершение оставшихся процессов...") + for pid in remaining_pids: + try: + os.kill(pid, signal.SIGKILL) + print(f"✅ Процесс {pid} принудительно завершен") + except ProcessLookupError: + pass + +def create_portforwards(): + """Создает port-forward для всех сервисов из preset""" + # Загружаем preset + preset_file = "molecule/presets/k8s/kubernetes.yml" + with open(preset_file, 'r') as f: + preset = yaml.safe_load(f) + + cluster_name = preset['kind_clusters'][0]['name'] + addon_ports = preset['kind_clusters'][0].get('addon_ports', {}) + + print(f"🔌 Создание port-forward для кластера: {cluster_name}") + + # Prometheus + if addon_ports.get('prometheus'): + port = addon_ports['prometheus'] + print(f" - Prometheus: localhost:{port} -> monitoring/monitoring-kube-prometheus-prometheus:9090") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "monitoring", + "svc/monitoring-kube-prometheus-prometheus", + f"{port}:9090" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Grafana + if addon_ports.get('grafana'): + port = addon_ports['grafana'] + print(f" - Grafana: localhost:{port} -> monitoring/monitoring-grafana:80") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "monitoring", + "svc/monitoring-grafana", + f"{port}:80" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Kiali + if addon_ports.get('kiali'): + port = addon_ports['kiali'] + print(f" - Kiali: localhost:{port} -> istio-system/kiali:20001") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "istio-system", + "svc/kiali", + f"{port}:20001" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Metrics Server + if addon_ports.get('metrics_server'): + port = addon_ports['metrics_server'] + print(f" - Metrics Server: localhost:{port} -> kube-system/metrics-server:4443") + subprocess.Popen([ + "kubectl", + "--server=https://{}-control-plane:6443".format(cluster_name), + "--insecure-skip-tls-verify", + "port-forward", + "-n", "kube-system", + "svc/metrics-server", + f"{port}:4443" + ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + time.sleep(2) + print("✅ Port-forward создан") + list_portforwards() + +def delete_portforward(port): + """Удаляет port-forward для конкретного порта""" + pids = get_portforward_pids() + if not pids: + print(f"❌ Нет активных port-forward на порту {port}") + return + + # Находим процесс с нужным портом + result = subprocess.run(f"ps aux | grep 'kubectl.*port-forward.*:{port}' | grep -v grep", shell=True, capture_output=True, text=True) + if not result.stdout.strip(): + print(f"❌ Не найден port-forward на порту {port}") + return + + # Извлекаем PID + pid = int(result.stdout.split()[1]) + print(f"🗑️ Завершение port-forward на порту {port} (PID: {pid})...") + try: + os.kill(pid, signal.SIGTERM) + print(f"✅ Port-forward на порту {port} завершен") + except ProcessLookupError: + print(f"⚠️ Процесс {pid} уже не существует") + +def main(): + if len(sys.argv) < 2: + print("Usage: portforward.py [port]") + sys.exit(1) + + command = sys.argv[1] + + if command == "create": + create_portforwards() + elif command == "list": + list_portforwards() + elif command == "clear": + clear_portforwards() + elif command == "delete": + if len(sys.argv) < 3: + print("Usage: portforward.py delete ") + sys.exit(1) + port = sys.argv[2] + delete_portforward(port) + else: + print(f"❌ Неизвестная команда: {command}") + sys.exit(1) + +if __name__ == "__main__": + main()