- Создан скрипт scripts/k8s_status.py для детального отчета - Показывает: узлы, namespaces, pods, deployments, daemonsets, statefulsets, services, ingress, PVC, события, Helm релизы - Исправлена проблема с подключением kubectl (используется прямой адрес control-plane) - Команда make k8s status теперь показывает полный отчет
		
			
				
	
	
		
			301 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | ||
| """
 | ||
| Скрипт для детального отчета о состоянии Kubernetes кластера
 | ||
| Автор: Сергей Антропов
 | ||
| Сайт: https://devops.org.ru
 | ||
| """
 | ||
| import sys
 | ||
| import subprocess
 | ||
| import json
 | ||
| 
 | ||
| def get_cluster_name():
 | ||
|     """Получает имя кластера"""
 | ||
|     result = subprocess.run("docker exec k8s-controller kind get clusters | head -1", shell=True, capture_output=True, text=True)
 | ||
|     if result.returncode == 0:
 | ||
|         return result.stdout.strip()
 | ||
|     return None
 | ||
| 
 | ||
| def run_kubectl_cmd(cmd):
 | ||
|     """Выполняет команду kubectl внутри контейнера k8s-controller"""
 | ||
|     cluster_name = get_cluster_name()
 | ||
|     if cluster_name:
 | ||
|         # Используем прямой адрес control-plane
 | ||
|         server = f"https://{cluster_name}-control-plane:6443"
 | ||
|         cmd_with_server = f"--server={server} --insecure-skip-tls-verify {cmd}"
 | ||
|     else:
 | ||
|         cmd_with_server = cmd
 | ||
|     
 | ||
|     full_cmd = f"docker exec k8s-controller kubectl {cmd_with_server}"
 | ||
|     result = subprocess.run(full_cmd, shell=True, capture_output=True, text=True)
 | ||
|     return result.stdout
 | ||
| 
 | ||
| def print_section(title):
 | ||
|     """Выводит заголовок секции"""
 | ||
|     print(f"\n{'='*70}")
 | ||
|     print(f"  {title}")
 | ||
|     print(f"{'='*70}\n")
 | ||
| 
 | ||
| def print_subsection(title):
 | ||
|     """Выводит подзаголовок"""
 | ||
|     print(f"\n{'─'*70}")
 | ||
|     print(f"  {title}")
 | ||
|     print(f"{'─'*70}\n")
 | ||
| 
 | ||
| def get_cluster_info():
 | ||
|     """Получает информацию о кластере"""
 | ||
|     print_section("📊 ОБЩАЯ ИНФОРМАЦИЯ О КЛАСТЕРЕ")
 | ||
|     
 | ||
|     # Версия Kubernetes
 | ||
|     version = run_kubectl_cmd("version --short")
 | ||
|     print(version)
 | ||
| 
 | ||
| def get_nodes_status():
 | ||
|     """Получает статус узлов"""
 | ||
|     print_section("🖥️  УЗЛЫ КЛАСТЕРА")
 | ||
|     
 | ||
|     nodes = run_kubectl_cmd("get nodes -o wide")
 | ||
|     print(nodes)
 | ||
|     
 | ||
|     # Детальная информация о каждом узле
 | ||
|     node_names = run_kubectl_cmd("get nodes -o jsonpath='{.items[*].metadata.name}'")
 | ||
|     if node_names.strip():
 | ||
|         for node in node_names.strip().split():
 | ||
|             print_subsection(f"Узел: {node}")
 | ||
|             node_info = run_kubectl_cmd(f"describe node {node}")
 | ||
|             print(node_info)
 | ||
| 
 | ||
| def get_namespaces():
 | ||
|     """Получает список namespace"""
 | ||
|     print_section("📁 NAMESPACES")
 | ||
|     
 | ||
|     namespaces = run_kubectl_cmd("get namespaces")
 | ||
|     print(namespaces)
 | ||
| 
 | ||
| def get_pods_by_namespace():
 | ||
|     """Получает поды по namespace"""
 | ||
|     print_section("🪟 PODS")
 | ||
|     
 | ||
|     # Получаем список namespace
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     for ns in namespaces:
 | ||
|         print_subsection(f"Namespace: {ns}")
 | ||
|         pods = run_kubectl_cmd(f"get pods -n {ns} -o wide")
 | ||
|         if pods.strip():
 | ||
|             print(pods)
 | ||
|         else:
 | ||
|             print("  (пусто)")
 | ||
| 
 | ||
| def get_services():
 | ||
|     """Получает сервисы"""
 | ||
|     print_section("🔗 SERVICES")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     for ns in namespaces:
 | ||
|         print_subsection(f"Namespace: {ns}")
 | ||
|         services = run_kubectl_cmd(f"get services -n {ns}")
 | ||
|         if services.strip():
 | ||
|             print(services)
 | ||
|         else:
 | ||
|             print("  (пусто)")
 | ||
| 
 | ||
| def get_ingress():
 | ||
|     """Получает Ingress ресурсы"""
 | ||
|     print_section("🌐 INGRESS")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     ingress_found = False
 | ||
|     for ns in namespaces:
 | ||
|         ingress = run_kubectl_cmd(f"get ingress -n {ns} 2>/dev/null")
 | ||
|         if ingress.strip() and "No resources found" not in ingress:
 | ||
|             ingress_found = True
 | ||
|             print_subsection(f"Namespace: {ns}")
 | ||
|             print(ingress)
 | ||
|     
 | ||
|     if not ingress_found:
 | ||
|         print("  Ingress ресурсы не найдены")
 | ||
| 
 | ||
| def get_pvcs():
 | ||
|     """Получает PersistentVolumeClaims"""
 | ||
|     print_section("💾 VOLUMES (PVC)")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     pvc_found = False
 | ||
|     for ns in namespaces:
 | ||
|         pvcs = run_kubectl_cmd(f"get pvc -n {ns} 2>/dev/null")
 | ||
|         if pvcs.strip() and "No resources found" not in pvcs:
 | ||
|             pvc_found = True
 | ||
|             print_subsection(f"Namespace: {ns}")
 | ||
|             print(pvcs)
 | ||
|     
 | ||
|     if not pvc_found:
 | ||
|         print("  PVC не найдены")
 | ||
| 
 | ||
| def get_deployments():
 | ||
|     """Получает Deployments"""
 | ||
|     print_section("🚀 DEPLOYMENTS")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     for ns in namespaces:
 | ||
|         print_subsection(f"Namespace: {ns}")
 | ||
|         deployments = run_kubectl_cmd(f"get deployments -n {ns}")
 | ||
|         if deployments.strip():
 | ||
|             print(deployments)
 | ||
|         else:
 | ||
|             print("  (пусто)")
 | ||
| 
 | ||
| def get_daemonsets():
 | ||
|     """Получает DaemonSets"""
 | ||
|     print_section("🔧 DAEMONSETS")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     for ns in namespaces:
 | ||
|         print_subsection(f"Namespace: {ns}")
 | ||
|         daemonsets = run_kubectl_cmd(f"get daemonsets -n {ns}")
 | ||
|         if daemonsets.strip():
 | ||
|             print(daemonsets)
 | ||
|         else:
 | ||
|             print("  (пусто)")
 | ||
| 
 | ||
| def get_statefulsets():
 | ||
|     """Получает StatefulSets"""
 | ||
|     print_section("🗄️  STATEFULSETS")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     statefulsets_found = False
 | ||
|     for ns in namespaces:
 | ||
|         statefulsets = run_kubectl_cmd(f"get statefulsets -n {ns} 2>/dev/null")
 | ||
|         if statefulsets.strip() and "No resources found" not in statefulsets:
 | ||
|             statefulsets_found = True
 | ||
|             print_subsection(f"Namespace: {ns}")
 | ||
|             print(statefulsets)
 | ||
|     
 | ||
|     if not statefulsets_found:
 | ||
|         print("  StatefulSets не найдены")
 | ||
| 
 | ||
| def get_events():
 | ||
|     """Получает события"""
 | ||
|     print_section("📅 СОБЫТИЯ (EVENTS)")
 | ||
|     
 | ||
|     namespaces_output = run_kubectl_cmd("get namespaces -o json")
 | ||
|     try:
 | ||
|         namespaces_data = json.loads(namespaces_output)
 | ||
|         namespaces = [ns['metadata']['name'] for ns in namespaces_data['items']]
 | ||
|     except:
 | ||
|         namespaces = ['default', 'kube-system', 'kube-public', 'kube-node-lease']
 | ||
|     
 | ||
|     for ns in namespaces:
 | ||
|         print_subsection(f"Namespace: {ns}")
 | ||
|         events = run_kubectl_cmd(f"get events -n {ns} --sort-by='.lastTimestamp' | tail -20")
 | ||
|         if events.strip():
 | ||
|             print(events)
 | ||
|         else:
 | ||
|             print("  (пусто)")
 | ||
| 
 | ||
| def get_helm_releases():
 | ||
|     """Получает Helm релизы"""
 | ||
|     print_section("📦 HELM RELEASES")
 | ||
|     
 | ||
|     helm_output = run_kubectl_cmd("helm list --all-namespaces 2>/dev/null")
 | ||
|     if helm_output.strip():
 | ||
|         print(helm_output)
 | ||
|     else:
 | ||
|         print("  Helm релизы не найдены")
 | ||
| 
 | ||
| def get_resource_usage():
 | ||
|     """Получает использование ресурсов"""
 | ||
|     print_section("📈 ИСПОЛЬЗОВАНИЕ РЕСУРСОВ")
 | ||
|     
 | ||
|     try:
 | ||
|         # Проверяем наличие metrics-server
 | ||
|         top_nodes = run_kubectl_cmd("top nodes 2>/dev/null")
 | ||
|         if top_nodes.strip():
 | ||
|             print_subsection("Узлы:")
 | ||
|             print(top_nodes)
 | ||
|     except:
 | ||
|         print("  metrics-server не установлен или недоступен")
 | ||
|     
 | ||
|     try:
 | ||
|         top_pods = run_kubectl_cmd("top pods --all-namespaces 2>/dev/null")
 | ||
|         if top_pods.strip():
 | ||
|             print_subsection("Pods (топ-20):")
 | ||
|             # Берем только первые 20 строк
 | ||
|             lines = top_pods.strip().split('\n')
 | ||
|             print('\n'.join(lines[:21]))  # + заголовок
 | ||
|     except:
 | ||
|         pass
 | ||
| 
 | ||
| def main():
 | ||
|     """Главная функция"""
 | ||
|     # Проверяем доступность контейнера
 | ||
|     result = subprocess.run("docker ps | grep k8s-controller", shell=True, capture_output=True, text=True)
 | ||
|     if result.returncode != 0:
 | ||
|         print("❌ Контейнер k8s-controller не запущен")
 | ||
|         sys.exit(1)
 | ||
|     
 | ||
|     print("="*70)
 | ||
|     print("  ДЕТАЛЬНЫЙ ОТЧЕТ О СОСТОЯНИИ KUBERNETES КЛАСТЕРА")
 | ||
|     print("="*70)
 | ||
|     
 | ||
|     get_cluster_info()
 | ||
|     get_nodes_status()
 | ||
|     get_namespaces()
 | ||
|     get_resource_usage()
 | ||
|     get_pods_by_namespace()
 | ||
|     get_deployments()
 | ||
|     get_daemonsets()
 | ||
|     get_statefulsets()
 | ||
|     get_services()
 | ||
|     get_ingress()
 | ||
|     get_pvcs()
 | ||
|     get_events()
 | ||
|     get_helm_releases()
 | ||
|     
 | ||
|     print("\n" + "="*70)
 | ||
|     print("  ОТЧЕТ ЗАВЕРШЕН")
 | ||
|     print("="*70 + "\n")
 | ||
| 
 | ||
| if __name__ == '__main__':
 | ||
|     main()
 |