目录结构

zhanghao@zhanghaodeMacBook-Air 136 % tree 
.
├── deployments
│   ├── CommonUtil.go
│   ├── Create.go
│   ├── DeploymentModel.go
│   ├── Detail.go
│   ├── List.go
│   └── handler.go
├── html
│   ├── deployment
│   │   ├── deployment_data.html
│   │   └── deployment_list.html
│   ├── t.css
│   ├── t.html
│   ├── table.html
│   └── test.html
├── lib
│   ├── ClientInit.go
│   ├── CommonData.go
│   └── WebUitl.go
├── main.go
├── test
│   ├── bulma
│   │   ├── t.css
│   │   ├── t.html
│   │   ├── table.html
│   │   └── test.html
│   ├── createDpl.go
│   └── getDpl.go
└── test.go

main.go

package main

import (
	"cxyzjt/136/deployments"
	"cxyzjt/136/lib"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	engine := gin.Default()

	//engine.Use(func(c *gin.Context) {
	//	defer func() {
	//		if e := recover(); e != nil {
	//			c.AbortWithStatusJSON(400, gin.H{"error": e})
	//		}
	//	}()
	//	c.Next()
	//})

	engine.LoadHTMLGlob("html/**/*")
	deployments.RegHandlers(engine)
	engine.GET("/deployments", func(c *gin.Context) {
		c.HTML(http.StatusOK, "deployment_list.html", lib.DataBuilder().
			SetTitle("deployment列表").
			SetData("DepList", deployments.ListAll("default")))
	})

	engine.GET("/deployments/:name", func(c *gin.Context) {
		c.HTML(http.StatusOK, "deployment_data.html", lib.DataBuilder().
			SetTitle("deployment详细页-"+c.Param("name")).
			SetData("DepDetail", deployments.GetDeployment("default", c.Param("name"))))
	})

	engine.Run(":8080")
}

deployments

CommonUtil.go

package deployments

import (
	"fmt"
	v1 "k8s.io/api/apps/v1"
	coreV1 "k8s.io/api/core/v1"
)

func GetImagesByPod(containers []coreV1.Container) string {
	images := containers[0].Image
	if imageLen := len(containers); imageLen > 1 {
		images += fmt.Sprintf("+其他%d个镜像", imageLen-1)
	}
	return images
}

// 根据deployment 获取对象
func GetImages(dep v1.Deployment) string {
	return GetImagesByPod(dep.Spec.Template.Spec.Containers)

}

func GetLabels(m map[string]string) string {
	labels := ""
	for k, v := range m {
		if labels != "" {
			labels += ","
		}
		labels += fmt.Sprintf("%s=%s", k, v)
	}
	return labels
}

Create.go

package deployments

import (
	"context"
	"cxyzjt/136/lib"
	"encoding/json"
	appV1 "k8s.io/api/apps/v1"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"os"
	"sigs.k8s.io/yaml"
)

func Create() {
	clientset := lib.K8sClient
	ctx := context.Background()
	createopt := metaV1.CreateOptions{}
	ngxDep := &appV1.Deployment{} // 我们要创建的deployment

	b, _ := os.ReadFile("../../yamls/nginx.yaml")
	bjson, _ := yaml.YAMLToJSON(b)
	json.Unmarshal(bjson, ngxDep)

	clientset.AppsV1().Deployments("").Create(ctx, ngxDep, createopt)
}

DeploymentModel.go

package deployments

type Pod struct {
	Name       string
	Image      string
	NodeName   string
	CreateTime string
}

type Deployment struct {
	Name       string
	Status     string
	Image      string
	Namespace  string
	Createtime string
	Replicas   [3]int32
	Pods       []*Pod
}

Detail.go

package deployments

import (
	"context"
	"cxyzjt/136/lib"
	v1 "k8s.io/api/apps/v1"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"log"
)

func GetDeployment(ns, name string) *Deployment {
	ctx := context.Background()
	getopt := metaV1.GetOptions{}

	dep, err := lib.K8sClient.AppsV1().Deployments(ns).Get(ctx, name, getopt)
	if err != nil {
		log.Fatal(err)
	}

	return &Deployment{
		Image:      GetImages(*dep),
		Name:       dep.Name,
		Namespace:  dep.Namespace,
		Createtime: dep.CreationTimestamp.Format("2006-01-02 15:03:04"),
		Pods:       GetPodsByDep(ns, dep),
		Replicas:   [3]int32{dep.Status.Replicas, dep.Status.AvailableReplicas, dep.Status.UnavailableReplicas},
	}
}

func GetPodsByDep(ns string, dep *v1.Deployment) []*Pod {
	ctx := context.Background()
	listopt := metaV1.ListOptions{
		LabelSelector: GetLabels(dep.Spec.Selector.MatchLabels),
	}
	podList, err := lib.K8sClient.CoreV1().Pods(ns).List(ctx, listopt)
	if err != nil {
		panic(err.Error())
	}
	pods := make([]*Pod, len(podList.Items))
	for i, pod := range podList.Items {
		pods[i] = &Pod{Name: pod.Name, //获取Pod名称
			Image:      GetImagesByPod(pod.Spec.Containers), //获取Pod镜像
			CreateTime: pod.CreationTimestamp.Format("2006-01-02 15:03:04"),
			NodeName:   pod.Spec.NodeName, //所属节点
		}
	}
	return pods
}

List.go

package deployments

import (
	"context"
	"cxyzjt/136/lib"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"log"
)

// 显示所有
func ListAll(namespace string) (res []*Deployment) {
	clientset := lib.K8sClient
	ctx := context.Background()
	listopt := metaV1.ListOptions{}
	deploymentList, err := clientset.AppsV1().Deployments(namespace).List(ctx, listopt)
	if err != nil {
		log.Fatal(err)
		return nil
	}

	// 遍历所有deployemnt
	for _, deployment := range deploymentList.Items {

		//for _, c := range deployment.Spec.Template.Spec.Containers {
		//	image += c.Image
		//}
		res = append(res, &Deployment{Name: deployment.Name,
			Replicas: [3]int32{deployment.Status.Replicas, deployment.Status.AvailableReplicas, deployment.Status.UnavailableReplicas},
			Image:    GetImages(deployment),
		})
	}
	return res
}

handler.go

package deployments

import (
	"context"
	"cxyzjt/136/lib"
	"github.com/gin-gonic/gin"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func RegHandlers(r *gin.Engine) {
	r.POST("/update/deployment/scale", incrRepolicas)
}

func incrRepolicas(c *gin.Context) {
	req := struct {
		Namespace  string `json:"ns" binding:"required,min=1"`
		Deployment string `json:"deployment" binding:"required,min=1"`
		Dec        bool   `json:"dec"` //是否减少一个
	}{}

	lib.CheeckError(c.ShouldBindJSON(&req))

	ctx := context.Background()
	getopt := metaV1.GetOptions{}

	scale, err := lib.K8sClient.AppsV1().Deployments(req.Namespace).GetScale(ctx, req.Deployment, getopt)
	lib.CheeckError(err)

	if req.Dec { // true 减少副本数
		scale.Spec.Replicas--
	} else {
		scale.Spec.Replicas++
	}
	_, err = lib.K8sClient.AppsV1().Deployments(req.Namespace).UpdateScale(ctx, req.Deployment, scale, metaV1.UpdateOptions{})
	lib.CheeckError(err)
	lib.Sunccess("Ok", c)
}

lib

ClientInit.go

package lib

import (
	"fmt"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"log"
)

var K8sClient *kubernetes.Clientset

func init() {
	configPath := "../etc/kube.conf"
	config, err := clientcmd.BuildConfigFromFlags("", configPath)
	if err != nil {
		fmt.Println(err)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	K8sClient = clientset
}

CommonData.go

package lib

import "fmt"

type CommonDataStruct struct {
	Title string
	Data  map[string]interface{}
}

func DataBuilder() *CommonDataStruct {
	return NewCommonDataStruct()
}

func NewCommonDataStruct() *CommonDataStruct {
	return &CommonDataStruct{Data: make(map[string]interface{})}
}

func (this *CommonDataStruct) SetTitle(title string) *CommonDataStruct {
	this.Title = title
	return this
}

func (this *CommonDataStruct) SetData(key string, value interface{}) *CommonDataStruct {
	this.Data[key] = value
	fmt.Println("########: ", value)
	return this
}

WebUitl.go

package lib

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func CheeckError(err error) {
	if err != nil {
		panic(err.Error())
	}
}

func Sunccess(msg string, c *gin.Context) {
	c.JSONP(http.StatusOK, gin.H{"messsage": msg})
}

使用list-watch机制获取deployment列表

List/Watch机制是Kubernetes中实现集群控制模块最核心的设计之一,它采用统一的异步消息处理机制,保证了消息的实时性、可靠性、顺序性和性能等,为声明式风格的API奠定了良好的基础。Informer 模块是Kubernetes中的基础组件,负责各组件与Apiserver的资源与事件同步。Kubernetes中的组件,如果要访问Kubernetes中的Object,绝大部分情况下会使用Informer中的Lister()方法,而非直接请求Kubernetes API。

原理图
client-go

test.go

package main

import (
	"cxyzjt/136/lib"
	"fmt"
	v1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/informers"
	"sync"
)

type DeploymentMap struct {
	data sync.Map
}

type DepHandler struct {
}

func (this *DepHandler) OnAdd(obj interface{}) {
	fmt.Println(obj.(*v1.Deployment).Name)
}

func (this *DepHandler) OnUpdate(oldObj, newObj interface{}) {
	if dep, ok := newObj.(*v1.Deployment); ok {
		fmt.Println(dep.Name)
	}
}

func (this *DepHandler) OnDelete(obj interface{}) {

}

func main() {
	fact := informers.NewSharedInformerFactory(lib.K8sClient, 0)
	depInformer := fact.Apps().V1().Deployments()
	depInformer.Informer().AddEventHandler(&DepHandler{})
	fact.Start(wait.NeverStop)
	select {}
}

//func main() {
//	_, c := cache.NewInformer(
//		cache.NewListWatchFromClient(lib.K8sClient.AppsV1().RESTClient(),
//			"deployments", "default", fields.Everything()),
//		&v1.Deployment{},
//		0,
//		&DepHandler{},
//	)
//	c.Run(wait.NeverStop)
//	select {}
//}

更新中…

Q.E.D.