feat: добавлен port-forward для кластера Kubernetes

- Создан скрипт scripts/portforward.py для управления port-forward
- Добавлены команды в Makefile:
  - make k8s portforward create - создать port-forward для всех сервисов
  - make k8s portforward list - показать активные port-forward
  - make k8s portforward clear - очистить все port-forward
  - make k8s portforward delete PORT - удалить конкретный port-forward
- Сервисы остаются с типом ClusterIP
- Port-forward автоматически создается для Prometheus, Grafana, Kiali и Metrics Server
- Порты: Prometheus 9090, Grafana 3000, Kiali 20001, Metrics Server 4443
This commit is contained in:
Сергей Антропов
2025-10-26 09:25:59 +03:00
parent d48c273e50
commit 69b547dda6
3 changed files with 240 additions and 13 deletions

200
scripts/portforward.py Executable file
View File

@@ -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 <create|list|delete|clear> [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 <port>")
sys.exit(1)
port = sys.argv[2]
delete_portforward(port)
else:
print(f"❌ Неизвестная команда: {command}")
sys.exit(1)
if __name__ == "__main__":
main()