gin模板展示k8s命名空间的资源
- 这里学习如何在前端单页面,调用后端接口展示k8s的资源
技术栈
- 后端 -> go -> gin -> gin模板
- 前端 -> gin模板 -> html + js
- k8s -> k8s-go-client ,基本资源(deployment等)
环境
- go 1.19
- k8s 1.23
- go module
- github.com/gin-gonic/gin v1.6.3
- k8s.io/client-go v0.20.2
搭建环境
- 安装 k8s 、go 开发环境(此处省略)
- ide打开后创建项目(我的项目名叫gin_k8s_deploy),然后安装go module
go get github.com/gin-gonic/gin@v1.6.3
go get k8s.io/client-go@v0.20.2
- 复制k8s的kubeconfig(/root/.kube/config)到项目
- 注:假如是公网的环境,需特别处理。假如是内网(虚拟机等)可以直连的情况则无需处理
- 注:假如是公网的环境,需特别处理。假如是内网(虚拟机等)可以直连的情况则无需处理
- 以下是公网k8s的kubeconfig的处理,将里面的内网ip换成公网ip(因为你无法通过内网ip直连),并且ca认证这行删除
后端
- 初始化k8s的客户端
- client/K8sClient.go
package clientimport ("k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd""log"
)func InitK8sClient() *kubernetes.Clientset {config, err := clientcmd.BuildConfigFromFlags("", "config")if err != nil {log.Fatal(err)}// config.Insecure 假如k8s是内网可以走https则不需要设置config.Insecure = trueclientset, err := kubernetes.NewForConfig(config)if err != nil {log.Fatal(err)}return clientset
}
- main.go 添加 k8sClient (可运行测试一下,能返回对象即成功)
package mainimport ("fmt""gin_k8s_deploy/client"
)func main() {k8sClient := client.InitK8sClient()fmt.Println(k8sClient)
}
使用gin作为web后端
- main.go
package mainimport ("fmt""gin_k8s_deploy/client""github.com/gin-gonic/gin"
)func main() {k8sClient := client.InitK8sClient()fmt.Println(k8sClient)// gin实例r := gin.New()// 加载html模板r.LoadHTMLGlob("templates/*")// 后端返回页面r.GET("/", func(c *gin.Context) {c.HTML(200, "index.html", gin.H{"data": "success"})})// 运行r.Run(":8080")
}
- templates/index.html (此处渲染gin的 “data”)
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title>
</head>
<body>
{{ .data }}
</body>
</html>
- 运行main.go,并测试访问首页 http://localhost:8080
编写gin请求k8s的接口
- 以下是gin请求k8s namespace api的接口
- main.go
// k8s 命名空间 接口r.GET("/ns", func(c *gin.Context) {// namespace 在 k8s 的核心(core)api组ns, err := k8sClient.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})if err != nil {log.Fatal(err)}nsRet := make([]string, 0)for _, nsItem := range ns.Items {nsRet = append(nsRet, nsItem.Name)}c.JSON(200, gin.H{"ns_list": nsRet})})
- 请求测试 (运行如报错请 go mod tidy )
- gin请求k8s deployment api的接口
// k8s deployment 接口r.GET("/:ns/deployment/list", func(c *gin.Context) {ns := c.Param("ns")// deployment在k8s的 appsv1 api组deployment, err := k8sClient.AppsV1().Deployments(ns).List(context.Background(), metav1.ListOptions{})if err != nil {log.Fatal(err)}deploymentRet := make([]string, 0)for _, item := range deployment.Items {deploymentRet = append(deploymentRet, item.Name)}c.JSON(200, gin.H{"deployment_list": deploymentRet})})
- 请求测试
前端
- 前端使用Promise与后端交互
- index.html
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title><script>// 调用后端 /ns 接口,返回ns的数组function getK8sNsList() {return fetch("/ns", {method: 'GET',headers: {'Content-Type': 'application/json',}}).then(response => {return response.json()}).then(data => {return data.ns_list})}// 使用dom将ns数组的值保存到select框中的optionfunction displayK8sNsList(ns_list) {ns_list.forEach(ns => {var ele = document.getElementById("selectNs")var option = document.createElement("option")option.name = nsoption.value = nsoption.text = nsele.add(option)})}// 获取后端 /ns 返回的第一个命名空间function getFirstK8sNsList() {return fetch("/ns", {method: 'GET',headers: {'Content-Type': 'application/json',}}).then(response => {return response.json()}).then(data => {return data.ns_list[0]})}// 调用后盾 /deployment/:ns/list 接口,返回deployment数组function getK8sDeployments(ns) {return fetch("/deployment/" + ns + "/list", {method: 'GET',headers: {'Content-Type': 'application/json',}}).then(response => {return response.json()}).then(data => {return data.deployment_list})}// function displayK8sDeploymentList(deployment_list) {var ele = document.getElementById("deployment_list")deployment_list.forEach(deployment => {var li = document.createElement("li")li.textContent= deploymentele.appendChild(li)})}// select框选中其他值时进行请求function onNamespaceChange() {var selectedNs = document.getElementById("selectNs").value;var deploymentList = document.getElementById("deployment_list");while (deploymentList.firstChild) {deploymentList.removeChild(deploymentList.firstChild);}getK8sDeployments(selectedNs).then(deployment_list => {displayK8sDeploymentList(deployment_list)})}</script>
</head>
<body>
<div class="header"><h1>K8s面板</h1><p style="display: inline-block">请选择命名空间: </p><select name="selectNs" id="selectNs" onchange="onNamespaceChange()"></select>
</div>
<div class="content"><p>deployments</p><ul id="deployment_list"></ul>
</div>
<script>// select框中填充ns数组getK8sNsList().then(ns_list => {displayK8sNsList(ns_list)})// 首先打印第一个命名空间的deploymentsgetFirstK8sNsList().then(ns => {getK8sDeployments(ns).then(deployment_list => {displayK8sDeploymentList(deployment_list)})})
</script>
</body>
</html>
效果