Python k8s操作示例
1.安装kubernetes
pip install kubernetes
2.示例
import json import os from datetime import datetime from typing import List, Optional, Tuple import yaml from kubernetes import client, config, stream from kubernetes.client.rest import ApiException from loguru import logger from pydantic import BaseModel class PodInfo(BaseModel): """ pod信息模板 """ namespace: str # 所在命名空间 name: str # 名称 status: str # pod状态 ready: Tuple[int, int] # pod内container就绪状态,(已就绪,pod预设总数) restart_count: int = 0 # pod内container重启总数 age: int = 0 # pod创建至今的时间 ip: str = "" # pod的局域网ip node: str = "" # pod所在node名称 nominated_node: str = "" # pod调度指定的节点名称 readiness_gates: str = "" # 就绪门控 class Utils: """ 配合KubectlPy的工具 """ @staticmethod def yaml_file_check(yaml_file_path: str): """ 检查yaml文件 检查文件拓展名和文件是否存在 """ if not yaml_file_path.endswith(".yaml") and not yaml_file_path.endswith(".yml"): raise Exception("yaml文件需是yaml或yml文件") if not os.path.exists(yaml_file_path): raise Exception("yaml文件不存在") @staticmethod def get_pod_restarts(pod_status) -> int: """ 统计pod内container重启次数 """ restart_count = 0 if pod_status.container_statuses: for container in pod_status.container_statuses: restart_count += container.restart_count return restart_count @staticmethod def get_pod_age(pod_status) -> int: """ 统计pod运行时间 """ pod_start_time = pod_status.start_time if pod_start_time: # tzinfo=None表示移除时区信息 start_dt = pod_start_time.replace(tzinfo=None) now_dt = datetime.utcnow() run_time_seconds = (now_dt - start_dt).total_seconds() return int(run_time_seconds) else: return 0 @staticmethod def get_pod_readiness_gates(pod_spec, pod_status) -> str: """ 提取就绪门控 默认情况下,Kubernetes 通过检查容器的就绪探针(Readiness Probes)来判断 Pod 是否就绪。 就绪门控(Readiness Gates) 允许 Pod 定义一个自定义的就绪条件,只有满足自定义条件时 Pod 才会被视为就绪。 """ readiness_gates = [] # 获取 Pod 定义中的就绪门控条件类型 if pod_spec.readiness_gates: gate_types = [gate.condition_type for gate in pod_spec.readiness_gates] if pod_status.conditions: for condition in pod_status.conditions: if condition.type in gate_types: readiness_gates.append(f"{condition.type}: {condition.status}") readiness_gates_str = ",".join(readiness_gates) if readiness_gates else "" return readiness_gates_str @staticmethod def get_pod_ready_status(pod_status) -> Tuple[int, int]: """ 获取pod内container就绪状态 """ ready_count = 0 total_count = 0 if pod_status.container_statuses: total_count = len(pod_status.container_statuses) for container in pod_status.container_statuses: if container.ready: ready_count += 1 return ready_count, total_count def parse_one_pod_info(self, pod_object) -> PodInfo: """ 解析一个pod的信息 """ pod_metadata = pod_object.metadata pod_status = pod_object.status pod_spec = pod_object.spec item = { "namespace": pod_metadata.namespace, "name": pod_metadata.name, "status": pod_status.phase, "ready": self.get_pod_ready_status(pod_status), "restart_count": self.get_pod_restarts(pod_status), "age": self.get_pod_age(pod_status), "ip": pod_status.pod_ip if pod_status.pod_ip else "", "node": pod_spec.node_name if pod_spec.node_name else "", "nominated_node": ( pod_status.nominated_node_name if pod_status.nominated_node_name else "" ), "readiness_gates": self.get_pod_readiness_gates(pod_spec, pod_status), } return PodInfo(**item) class KubectlPy: """ apiVersion: v1 - list_namespaces: 列举集群中的命名空间名称 - list_pods: 列举指定命名空间下的Pod信息 - get_one_pod_info: 获取指定命名空间下的指定Pod信息 - exec_command_in_pod:: 在Pod中执行命令 - add_deployment_model: 添加Deployment参数模版 - show_deployment_model: 显示Deployment参数模版 - create_deployment: 创建Deployment,必须先添加Deployment参数模版,并且调用时需传入模板替换参数 - delete_deployment: 删除Deployment """ def __init__(self, config_path: Optional[str] = None): self._utils = Utils() if isinstance(config_path, str): self._utils.yaml_file_check(config_path) self.config_path = config_path # config_path不填,则从环境变量KUBECONFIG中获取,还没有,就找"~/.kube/config" config.load_kube_config(config_file=config_path) # 操作核心 API 组里的资源,Pod、Service、Namespace、ConfigMap 等. self._core_v1_client = client.CoreV1Api() # 操作apps/v1 API 组的资源, Deployment、StatefulSet、DaemonSet 等. self._apps_v1_client = client.AppsV1Api() # 底层客户端,提供基础功能,像序列化、反序列化、HTTP 请求发送等. self._api_client = client.ApiClient() logger.info("kubectl-py成功连接k8s集群") self._deployment_model: Optional[str] = None def list_namespaces(self) -> List[str]: """ 列举集群中的命名空间的名称 """ names = [] try: namespaces = self._core_v1_client.list_namespace() except ApiException as e: raise Exception( f"获取命名空间名称列表失败,状态码:{e.status},错误信息:{e.body}" ) except Exception as e: raise Exception(f"获取命名空间名称列表失败,错误信息:{e}") for ns in namespaces.items: names.append(ns.metadata.name) return names def list_pods(self, namespace: str = "default") -> List[PodInfo]: """ 列举命名空间中的pod信息 """ result = [] try: pods = self._core_v1_client.list_namespaced_pod(namespace) except ApiException as e: raise Exception( f"获取命名空间{namespace}的pod信息列表失败,状态码:{e.status},错误信息:{e.body}" ) except Exception as e: raise Exception(f"获取命名空间{namespace}的pod信息列表失败,错误信息:{e}") for p in pods.items: item = self._utils.parse_one_pod_info(p) result.append(item) return result def add_deployment_model(self, model_file_path: str): """ 通过yaml文件添加创建deployment的模板数据 yaml -> dict 需要替换的字段用"$<标识>$"表示 """ self._utils.yaml_file_check(model_file_path) with open(model_file_path, "r") as file: model_dic: dict = yaml.safe_load(file) model_json = json.dumps(model_dic, ensure_ascii=False) if not ("$<" in model_json and ">$" in model_json): raise Exception("创建deployment的模板文件中,未设置用于替换的标识") self._deployment_model = model_json def show_deployment_model(self) -> Optional[dict]: """ 展示创建deployment的模版数据(字典) """ if not self._deployment_model: return dict() return json.loads(self._deployment_model) def create_deployment(self, **model_replace_map): """ 创建deployment """ if not self._deployment_model: raise Exception("要创建deployment,请先添加deployment模板文件") if not model_replace_map: raise Exception("要创建deployment,请提供用于替换的参数") temp_model_json = self._deployment_model for k, v in model_replace_map.items(): assert k.startswith("$<") and k.endswith(">$"), "存在非法的替换标识" assert k in temp_model_json, f"deployment模板文件中,未设置{k}替换标识" temp_model_json = temp_model_json.replace(k, str(v)) assert "$<" not in temp_model_json, "存在没有完成替换的标识" model_dic = json.loads(temp_model_json) sanitized_body = self._api_client.sanitize_for_serialization(model_dic) namespace = model_dic.get("metadata", dict()).get("namespace", "default") dp_name = model_dic.get("metadata", dict()).get("name", "") try: self._apps_v1_client.create_namespaced_deployment( namespace=namespace, body=sanitized_body, ) except ApiException as e: raise Exception( f"Deployment{dp_name}创建失败,状态码:{e.status},错误信息:{e.body}" ) except Exception as e: raise Exception(f"Deployment{dp_name}创建失败,错误信息:{e}") def delete_deployment(self, name: str, namespace: str = "default"): """ 删除deployment """ delete_options = client.V1DeleteOptions() try: self._apps_v1_client.delete_namespaced_deployment( name=name, namespace=namespace, body=delete_options, ) except ApiException as e: raise Exception( f"删除Deployment{name}失败,状态码:{e.status},错误信息:{e.body}" ) except Exception as e: raise Exception(f"删除Deployment{name}失败,错误信息:{e}") def exec_command_in_pod( self, pod_name: str, command: str, container: Optional[str] = None, namespace: str = "default", shell_type: str = "bash", ): """ 到Pod里执行命令 """ if shell_type == "bash": exec_command = ["/bin/bash", "-c", command] elif shell_type == "sh": exec_command = ["/bin/sh", "-c", command] else: raise Exception(f"不支持的shell_type:{shell_type}") try: stream.stream( self._core_v1_client.connect_get_namespaced_pod_exec, pod_name, namespace, command=exec_command, container=container, stderr=True, # 捕获错误输出 stdin=False, # 不提供命令输入 stdout=True, # 捕获正确输出 tty=False, # 不分配伪终端 ) except ApiException as e: raise Exception( f'执行命令"{command}"失败,状态码:{e.status},错误信息:{e.body}' ) except Exception as e: raise Exception(f'执行命令"{command}"失败,错误信息:{e}') def get_one_pod_info(self, pod_name: str, namespace: str = "default") -> PodInfo: """ 获取单个pod信息 """ try: pod = self._core_v1_client.read_namespaced_pod( name=pod_name, namespace=namespace, ) except ApiException as e: raise Exception( f"获取Pod{pod_name}信息失败,状态码:{e.status},错误信息:{e.body}" ) except Exception as e: raise Exception(f"获取Pod{pod_name}信息失败,错误信息:{e}") return self._utils.parse_one_pod_info(pod) if __name__ == "__main__": test = KubectlPy(config_path="kubeconfig.yaml路径") print(test.list_pods())
浙公网安备 33010602011771号