Files
DevOpsLab/scripts/k8s_status.py
Сергей Антропов fcf3f33e80 feat: добавлен детальный отчет о состоянии кластера
- Создан скрипт scripts/k8s_status.py для детального отчета
- Показывает: узлы, namespaces, pods, deployments, daemonsets,
  statefulsets, services, ingress, PVC, события, Helm релизы
- Исправлена проблема с подключением kubectl (используется
  прямой адрес control-plane)
- Команда make k8s status теперь показывает полный отчет
2025-10-26 10:16:24 +03:00

301 lines
10 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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