19.1 使用k8s的sdk编写一个项目获取pod和node信息

本节重点介绍 :

  • 引入k8s sdk获取k8s 的node和pod信息
    • 定义相关metrics
    • 初始化k8s-client
    • 使用k8s-client get node
    • 使用k8s-client get pod
    • 打点

k8s中关注四大块指标总结

  • 之前在k8s中关注4块指标有过总结
指标类型 采集源 应用举例 发现类型 grafana截图
容器基础资源指标 kubelet 内置cadvisor metrics接口 查看容器cpu、mem利用率等 k8s_sd node级别直接访问node_ip 容器基础资源
k8s对象资源指标 kube-stats-metrics (简称ksm) 具体可以看 看pod状态如pod waiting状态的原因 数个数如:查看node pod按namespace分布情况 通过coredns访问域名 k8s对象资源指标
k8s服务组件指标 服务组件 metrics接口 查看apiserver 、scheduler、etc、coredns请求延迟等 k8s_sd endpoint级别 k8s服务组件指标
部署在pod中业务埋点指标 pod 的metrics接口 依据业务指标场景 k8s_sd pod级别,访问pod ip的metricspath

使用golang引入sdk编写一个项目跑在k8s中

需求分析

  • 编写一个go的项目,引用k8s的sdk 获取节点信息,获取pod信息
  • 将获取到的信息通过prometheus sdk打点打出来
  • 编写dockerfile 将该项目打成镜像
  • 编写k8s 的yaml运行改项目
  • prometheus采集该项目的pod指标

新建项目 ink8s-pod-metrics

  • go 1.16以上,初始化项目
shell 复制代码
go mod init ink8s-pod-metrics

编写go代码

1. 定义相关metrics

go 复制代码
const (
	namespace = "ink8s_pod_metrics"
	getNode   = "get_node"
	getPod    = "get_pod"
)

var (
    // 将每个node的信息打印出来
	k8sNodeDetail = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Name: prometheus.BuildFQName(namespace, getNode, "detail"),
		Help: "k8s node detail each",
	}, []string{"ip", "hostname", "containerRuntimeVersion", "kubeletVersion"})

    // 计算获取节点的耗时
	getNodeDuration = prometheus.NewGauge(prometheus.GaugeOpts{
		Name: prometheus.BuildFQName(namespace, getNode, "last_duration_seconds"),
		Help: "get node last duration seconds",
	})
     // 将每个控制平面的pod信息打印出来
	k8sPodDetail = prometheus.NewGaugeVec(prometheus.GaugeOpts{
		Name: prometheus.BuildFQName(namespace, getPod, "control_plane_pod_detail"),
		Help: "k8s pod detail of control plane",
	}, []string{"ip", "pod_name", "component"})
    // 计算获取pod的耗时
	getPodDuration = prometheus.NewGauge(prometheus.GaugeOpts{
		Name: prometheus.BuildFQName(namespace, getPod, "last_duration_seconds"),
		Help: "get pod last duration seconds",
	})
)
  • metrics讲解
    • k8sNodeDetail 将每个node的信息打印出来
    • getNodeDuration 计算获取节点的耗时
    • k8sPodDetail 将每个控制平面的pod信息打印出来
    • getPodDuration 计算获取pod的耗时
  • prometheus.BuildFQName(namespace, getNode, "detail") 代表使用共同前缀,namespace + subsystem

2. 注册metrics

go 复制代码
func newMetrics() {
	prometheus.DefaultRegisterer.MustRegister(k8sNodeDetail)
	prometheus.DefaultRegisterer.MustRegister(k8sPodDetail)
	prometheus.DefaultRegisterer.MustRegister(getNodeDuration)
	prometheus.DefaultRegisterer.MustRegister(getPodDuration)
}

3. 初始化k8s-client

go 复制代码
func getK8sClient() *kubernetes.Clientset {
	// creates the in-cluster config
	config, err := rest.InClusterConfig()
	if err != nil {
		logger.Errorf("[create_k8s_InClusterConfig_err][err:%v]", err)
		return nil
	}
	// creates the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		logger.Errorf("[create_the_clientset_error][err:%v]", err)
		return nil
	}
	return clientset
}

4. 使用k8s-client get node

  • clientset.CoreV1().Nodes().List代表 get node
  • 遍历nodes
    • 获取ip地址 p.Status.Addresses 中的类型为 apiv1.NodeInternalIP 就是内网ip
    • containerRuntimeVersion和kubeletVersion信息在 p.Status.NodeInfo中
  • 在结尾的时候打印个日志,记录下节点数和耗时,并把耗时打个metrics上报
  • 完整代码如下
go 复制代码
func doGetNode() {
	start := time.Now()

	clientset := getK8sClient()
	if clientset == nil {
		return
	}

	nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		logger.Errorf("list_kube-system_pod_error:%v", err)
		return
	}

	if len(nodes.Items) == 0 {
		return
	}
	for _, p := range nodes.Items {
		var ip string
		addr := p.Status.Addresses
		if len(addr) == 0 {
			continue
		}

		for _, a := range addr {
			if a.Type == apiv1.NodeInternalIP {
				ip = a.Address
			}
		}

		k8sNodeDetail.With(prometheus.Labels{
			"ip":                      ip,
			"hostname":                p.Name,
			"containerRuntimeVersion": p.Status.NodeInfo.ContainerRuntimeVersion,
			"kubeletVersion":          p.Status.NodeInfo.KubeletVersion,
		}).Set(1)
	}
	timeTook := time.Since(start).Seconds()
	getNodeDuration.Set(timeTook)
	logger.Infof("server_node_ips_result][num_node:%v][time_took_seconds:%v]", len(nodes.Items), timeTook)

}

5. 使用k8s-client get pod

  • clientset.CoreV1().Pods("kube-system").List 代表获取kube-system namespace下面的pods

  • 遍历pods

    • 控制平面中的pod 都会有 tie=control-plane的标签
    • 打点即可
  • 完整代码如下

go 复制代码
func doGetPod() {
	start := time.Now()
	clientset := getK8sClient()
	if clientset == nil {
		return
	}

	pods, err := clientset.CoreV1().Pods("kube-system").List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		logger.Errorf("list_kube-system_pod_error:%v", err)
		return
	}

	if len(pods.Items) == 0 {
		return
	}
	for _, p := range pods.Items {
		logger.Infof("[pod.label:%v]", p.Labels)
		if p.Labels["tier"] == "control-plane" {
			ip := p.Status.PodIP
			component := p.Labels["component"]
			k8sPodDetail.With(prometheus.Labels{
				"ip":        ip,
				"pod_name":  p.Name,
				"component": component,
			}).Set(1)

		}
	}

	timeTook := time.Since(start).Seconds()
	getPodDuration.Set(timeTook)
	logger.Infof("server_pod_ips_result][num_pod:%v][time_took_seconds:%v]", len(pods.Items), timeTook)

}

6. 编写运行的ticker函数

  • 每隔10秒就执行一下getnode 和getpod上报数据
  • 外部的ctx被cancel会导致for退出
go 复制代码
func getK8sObjTicker(ctx context.Context) {
	ticker := time.NewTicker(time.Second * 10)
	logger.Infof("GetK8sObjTicker start....")

	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			go doGetNode()
			go doGetPod()
		case <-ctx.Done():
			return
		}
	}

}

7. 编写main函数

  • newMetrics 注册metrics
  • go getK8sObjTicker(ctx) 开启获取 k8s对象的协程
  • http.Handle("/metrics", promhttp.Handler()) 开启prometheus metric path
go 复制代码
func main() {
	// 注册metrics
	newMetrics()
	ctx := context.Background()
	// 开启获取 k8s对象的协程
	go getK8sObjTicker(ctx)
	// 开启prometheus metric path
	http.Handle("/metrics", promhttp.Handler())
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		logger.Errorf("failed to start prometheus metrics web :%v", err)
	}
}

本节重点总结 :

  • 引入k8s sdk获取k8s 的node和pod信息
    • 定义相关metrics
    • 初始化k8s-client
    • 使用k8s-client get node
    • 使用k8s-client get pod
    • 打点
相关推荐
为什么这亚子1 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
ZHOU西口3 小时前
微服务实战系列之玩转Docker(十八)
分布式·docker·云原生·架构·数据安全·etcd·rbac
牛角上的男孩4 小时前
Istio Gateway发布服务
云原生·gateway·istio
JuiceFS5 小时前
好未来:多云环境下基于 JuiceFS 建设低运维模型仓库
运维·云原生
景天科技苑6 小时前
【云原生开发】K8S多集群资源管理平台架构设计
云原生·容器·kubernetes·k8s·云原生开发·k8s管理系统
wclass-zhengge7 小时前
K8S篇(基本介绍)
云原生·容器·kubernetes
颜淡慕潇7 小时前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
川石课堂软件测试9 小时前
性能测试|docker容器下搭建JMeter+Grafana+Influxdb监控可视化平台
运维·javascript·深度学习·jmeter·docker·容器·grafana
昌sit!15 小时前
K8S node节点没有相应的pod镜像运行故障处理办法
云原生·容器·kubernetes
A ?Charis18 小时前
Gitlab-runner running on Kubernetes - hostAliases
容器·kubernetes·gitlab