目录结构
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。
原理图
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 {}
//}
更新中…