#!/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()