- Создан скрипт 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()
|