简介

本章节主要讲解通过client-go实现hpa【Horizontal Pod Autoscalert】水平扩缩容的列表、创建hap、读取yaml配置文件。通过hap可以实现根据cpu和内存设置的阀值实现deploy,statefulset的扩容和缩容。该部分代码主要有控制器部分代码、模型部分代码、前端html【采用layui框架、layuimini模板】、路由配置。yaml配置部分采用微软的monaco-editor编辑器来显示。

一.k8s的hpa列表

1.1.controllers控制器部分代码

控制器部分代码通过传递集群id,命名空间,hpa的名称来进行列表过滤,如果传递值是空时,就默认加载全部数据。

func (this *HpaController) List() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	hpaName := this.GetString("hpaName")
	targetName := this.GetString("targetName")
	List, err := m.HpaList(clusterId, namespace, hpaName, targetName)
	msg := "success"
	code := 0
	count := len(List)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &List}
	//this.Data["json"] = &datas
	this.ServeJSON()
}
1.2.models模型部分代码

先实现一个结构体来显示hpa中的信息,并不需要将全部进行显示。然后读取hpa列表将信息组合到结构体,并追加到定义的var bbb = make([]Hpa, 0) 结构体列表中。

type Hpa struct {
	HpaName    string `json:"hpaName"`
	NameSpace  string `json:"nameSpace"`
	TargetKind string `json:"targetKind"`
	TargetName string `json:"targetName"`
	//DeployName      string `json:"deployName"`
	MinReplicas     int32  `json:"minReplicas"`     //最大副本
	MaxReplicas     int32  `json:"maxReplicas"`     //最小副本
	CurrentReplicas int32  `json:"currentReplicas"` //当前副本
	Metrics         string `json:"metrics"`         //目标使用率
	CreateTime      string `json:"createTime"`
}

//AutoscalingV2beta2 适合 1.26.0以下版本
//AutoscalingV2适合1.26.0以上的版本

func HpaList(kubeconfig, nameSpace, hpaName, targetName string) ([]Hpa, error) {
	clientset := common.ClientSet(kubeconfig)
	if nameSpace == "" {
		//namespace = corev1.NamespaceDefault
		nameSpace = corev1.NamespaceAll
	}

	//设置ListOptions
	var listOptions = metav1.ListOptions{}

	//hpas, err := clientset.AutoscalingV1().HorizontalPodAutoscalers(nameSpace).List(context.TODO(), metav1.ListOptions{}) //v1版本无vv.Spec.Metrics
	//hpas, err := clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).List(context.TODO(), metav1.ListOptions{}) //低版本的会提示:list haps error, err:the server could not find the requested resource
	
	//查询api返回list
	hpas, err := clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list haps error, err:%v\n", err)
	}

	var bbb = make([]Hpa, 0)
	for _, vv := range hpas.Items {

		//搜索
		if hpaName != "" {
			if !strings.Contains(vv.Name, hpaName) {
				continue
			}
		}

		//根据对象名称来查询
		if targetName != "" {
			if vv.Spec.ScaleTargetRef.Name != targetName {
				continue
			}
		}

		var metricsStr string
		for _, v1 := range vv.Spec.Metrics {
			metricsStr += fmt.Sprintf("%s:%d%%,", v1.Resource.Name, *v1.Resource.Target.AverageUtilization)
		}
		if len(metricsStr) > 0 {
			metricsStr = metricsStr[0 : len(metricsStr)-1]
		}
		Items := &Hpa{
			HpaName:         vv.Name,
			NameSpace:       vv.Namespace,
			TargetKind:      vv.Spec.ScaleTargetRef.Kind,
			TargetName:      vv.Spec.ScaleTargetRef.Name,
			MinReplicas:     *vv.Spec.MinReplicas,
			MaxReplicas:     vv.Spec.MaxReplicas,
			CurrentReplicas: vv.Status.CurrentReplicas,
			Metrics:         metricsStr,
			CreateTime:      vv.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

二.创建hpa

2.1.controllers控制器部分代码

通过传递的集群id,和前端post的表单传递到HpaCreate函数进行处理,前端传递过来的内容:this.Ctx.Input.RequestBody

func (this *HpaController) Create() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	err := m.HpaCreate(clusterId, this.Ctx.Input.RequestBody)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] hpa Create Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}
2.2.models模型部分代码

HpaCreate函数通过gjson解析传递过来的json,提取hap的各项值,赋值到定义的autoscalingv2.HorizontalPodAutoscaler结构体中。并调用clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Create函数创建。

func HpaCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	hpaName := gp.Get("hpaName").String()
	nameSpace := gp.Get("nameSpace").String()

	var labelsMap = make(map[string]string)
	labelsMap["app"] = hpaName
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").Str] = vv.Get("value").Str
	}

	hpaKind := gp.Get("hpaKind").String()
	objName := gp.Get("objName").String()
	cpuUsage := gp.Get("cpuUsage").Int()
	memUsage := gp.Get("memUsage").Int()
	maxPods := gp.Get("maxPods").Int()
	minPods := gp.Get("minPods").Int()
	if minPods < 1 {
		minPods = 1
	}
	if maxPods < 1 {
		maxPods = 2
	}
	if cpuUsage < 1 {
		cpuUsage = 80
	}

    //定义一个hpa,然后将解析的值赋值进去
	hpa := &autoscalingv2.HorizontalPodAutoscaler{
		ObjectMeta: metav1.ObjectMeta{
			Name:      hpaName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
			ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
				Kind:       hpaKind,
				Name:       objName,
				APIVersion: "apps/v1",
			},
			MinReplicas: int32Ptr(int32(minPods)),
			MaxReplicas: int32(maxPods),
			//TargetCPUUtilizationPercentage: int32Ptr(int32(cpuUsage)), v1版本
			Metrics: []autoscalingv2.MetricSpec{
				{
					Resource: &autoscalingv2.ResourceMetricSource{
						Name: "memory",
						Target: autoscalingv2.MetricTarget{
							Type:               autoscalingv2.UtilizationMetricType,
							AverageUtilization: int32Ptr(int32(memUsage)),
						},
					},
					Type: "Resource",
				},
				{
					Resource: &autoscalingv2.ResourceMetricSource{
						Name: "cpu",
						Target: autoscalingv2.MetricTarget{
							Type:               autoscalingv2.UtilizationMetricType,
							AverageUtilization: int32Ptr(int32(cpuUsage)),
						},
					},
					Type: "Resource",
				},
			},
		},
	}
	//创建
	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Create(context.TODO(), hpa, metav1.CreateOptions{})
	return err
}

三.读取hpa的yaml配置

3.1.controllers控制器部分代码

通过传递的集群id、命名空间、hpa名称调用模型中的GetHpaYaml函数来读取配置

func (this *HpaController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	hpaName := this.GetString("hpaName")

	yamlStr, _ := m.GetHpaYaml(clusterId, namespace, hpaName)
	this.Ctx.WriteString(yamlStr)
}
3.2.models模型部分代码实现

通过AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Get读取hpa,并进行UnstructuredConverter,最后在调用yaml.Marshal函数解析成yaml配置的bytes,通过string转成字符串返回

func GetHpaYaml(kubeconfig, nameSpace, hpaName string) (string, error) {

	hpa, err := common.ClientSet(kubeconfig).AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Get(context.TODO(), hpaName, metav1.GetOptions{})
	deploymentUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(hpa)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(deploymentUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}

四.路由设置

4.1.路由设置

将此代码添加到routers/route.go中

	beego.Router("/hpa/v1/List", &controllers.HpaController{}, "*:List")
	beego.Router("/hpa/v1/Yaml", &controllers.HpaController{}, "*:Yaml")
	beego.Router("/hpa/v1/Create", &controllers.HpaController{}, "*:Create")
	beego.Router("/hpa/v1/Del", &controllers.HpaController{}, "*:Del")

五.前端部分代码

5.1.hpaList.html

5.1 显示hpa的表格,主要通过table.render函数实现

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>hpa列表</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="/lib/layui-v2.6.3/css/layui.css" media="all">
    <link rel="stylesheet" href="/css/public.css" media="all">
    <script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
    <script src="/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
    <script src="/js/xkube.js?v=1" charset="utf-8"></script>
    <script src="/js/lay-config.js?v=1.0.4" charset="utf-8"></script>
<style type="text/css">
  .layui-table-cell {
    height: auto;
    line-height: 22px !important;
    text-overflow: inherit;
    overflow: visible;
    white-space: normal;
  }
  .layui-table-cell .layui-table-tool-panel li {
    word-break: break-word;
  }
</style>
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">
        <script type="text/html" id="toolbarDemo">
            <div class="layui-btn-container">
                <button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="create"><i class="layui-icon">&#xe61f;</i>创建HPA</button>
            </div>
        </script>

        <table class="layui-table" id="currentTableId" lay-filter="currentTableFilter"></table>

        <script type="text/html" id="currentTableBar">
            <a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="viewYaml">查看yaml</a>
            <a class="layui-btn layui-btn-normal layui-btn-xs layui-btn-danger" lay-event="delete">删除</a>
        </script>
    </div>
</div>
</body>
<script type="text/html" id="tagTpl">
    {{# if (d.metrics != "") { }}
            {{# layui.each(d.metrics.split(','), function(index, item){ }}
                {{# if(index == 0) { }}
                        <span>{{ item }}</span>
                {{# }else{ }}
                        <br><span>{{ item }}</span>
                {{# } }}  
            {{# });  }}
    {{# }else{  }}
            <span></span>
    {{# } }}
</script>	

<script>
var clusterId = getQueryString("clusterId");
if (clusterId == null) {
	clusterId = getCookie("clusterId")
}
    layui.use(['form', 'table','miniTab'], function () {
        var $ = layui.jquery,
            form = layui.form,
            table = layui.table;
            miniTab = layui.miniTab,
            miniTab.listen();

        table.render({
            elem: '#currentTableId',
            url: '/hpa/v1/List?clusterId='+clusterId,
            toolbar: '#toolbarDemo',
            defaultToolbar: ['filter', 'exports', 'print', {
                title: '提示',
                layEvent: 'LAYTABLE_TIPS',
                icon: 'layui-icon-tips'
            }],
            parseData: function(res) { //实现加载全部数据后再分页
            	if(this.page.curr) {
            		result=res.data.slice(this.limit*(this.page.curr-1),this.limit*this.page.curr);
            	}else{
            	  result=res.data.slice(0,this.limit);
              }
              return {
              	"code": res.code,
              	"msg":'',
              	"count":res.count,
              	"data":result
              };
            },
            cols: [[
                {field: 'hpaName',title: '名称', sort: true},
                {field: 'nameSpace',title: '命名空间', sort: true},
                {field: 'targetKind', title: '类型', sort: true},
                {field: 'targetName', title: '对象名称', sort: true},
                {field: 'minReplicas',title: '最小副本', sort: true},
                {field: 'maxReplicas',title: '最大副本', sort: true},
                {field: 'currentReplicas', title: '当前副本', sort: true},
                {field: 'metrics', title: '触发阀值', sort: true,templet: '#tagTpl'},
                {field: 'createTime',title: '创建时间'},
                {title: '操作', minWidth:180,  toolbar: '#currentTableBar', align: "center"}
            ]],
            //size:'lg',
            limits: [25, 50, 100],
            limit: 25,
            page: true
        });
        /**
         * toolbar监听事件
         */
        table.on('toolbar(currentTableFilter)', function (obj) {
            if (obj.event === 'create') {  // 监听添加操作
                var index = layer.open({
                    title: '创建',
                    type: 2,
                    shade: 0.2,
                    maxmin:true,
                    shadeClose: true,
                    area: ['60%', '90%'],
                    content: '/page/xkube/hpaCreate.html?v=1',
                    //end: function(){
                    //	window.parent.location.reload();//关闭open打开的页面时,刷新父页面
                    //}
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
            } else if (obj.event === 'yamlCreate') {  // 监听删除操作
                var index = layer.open({
                    title: '创建',
                    type: 2,
                    shade: 0.2,
                    maxmin:true,
                    shadeClose: true,
                    area: ['60%', '90%'],
                    content: '/page/xkube/create_by_yaml.html',
                    //end: function(){
                    //	window.parent.location.reload();//关闭open打开的页面时,刷新父页面
                    //}
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
            } 
        });
        table.on('tool(currentTableFilter)', function (obj) {
            var data = obj.data;
            if (obj.event === 'viewYaml') {
                var index = layer.open({
                    title: 'yaml',
                    type: 2,
                    shade: 0.2,
                    maxmin:true,
                    shadeClose: true,
                    area: ['50%', '92%'],
                    content: '/page/xkube/hpaYaml.html?clusterId='+clusterId+'&nameSpace='+data.nameSpace+'&hpaName='+data.hpaName,
                });
                $(window).on("resize", function () {
                    layer.full(index);
                });
                return false;
            } else if (obj.event === 'delete') {
                layer.confirm('确定删除'+data.hpaName+'?', {icon: 3, title:'删除提示',yes: function(index){
                     var index2 = layer.load(0, {shade: false});
                     layer.msg('此处需运行1-2s左右');
                			 $.ajax({
                			    url: '/hpa/v1/Del?clusterId='+clusterId+'&nameSpace='+data.nameSpace+'&hpaName='+data.hpaName,
                			    type: "get",
                			    //data: data,
                			    //dataType: "json",
                			    success: function (resp) {
                            layer.close(index2);
                						//console.log(resp);
                						 if(resp.code == 0){
                							 layer.msg('删除成功', {icon: 1});
                							 window.location.reload();
                							 //obj.del();
                						 }else{
                							 layer.msg(resp.msg,{icon:2});
                						 }
                				  }
                		   });		  	  
                },
            		  cancel: function(index, layero){ 
            			    layer.close(index);
                      layer.close(index2);
            			    console.log("不操作");
            		  } 
        		    });                
            }
        });
    });
</script>
</html>
5.2.hpaCreate.html

5.2 创建一个表单,将输入的值转成json格式,并提交

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>创建</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="/lib/layui-v2.6.3/css/layui.css" media="all">
    <link rel="stylesheet" href="/css/public.css" media="all">
    <script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
    <script src="/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
    <script src="/js/xkube.js?v=1" charset="utf-8"></script>
    <style>
        body {
            background-color: #ffffff;
        }
    </style>
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">
        <form class="layui-form layui-form-pane" action="">

            <div class="layui-form-item">
                <div class="layui-inline">
                    <label class="layui-form-label">当前集群</label>
                    <div class="layui-input-inline">
                        <select name="clusterId" lay-filter="cluster_Id" lay-search="" id="cluster_Id">
                          <option value="">请选择集群</option>
                        </select>
                    </div>
                </div>
            </div>

            <div class="layui-form-item">
                <label class="layui-form-label required">HPA名称</label>
                <div class="layui-input-inline">
                    <input type="text" name="hpaName" placeholder="不能为空" value="" class="layui-input">
                </div>
                <label class="layui-form-label">命名空间</label>
                <div class="layui-input-inline">
                  <select name="nameSpace" lay-filter="name_Space" lay-search="" id="name_Space">
  		            </select>
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">HPA类型</label>
                <div class="layui-input-inline">
                  <select name="hpaKind" lay-filter="hpaKind" lay-search="" id="hpaKind">
              			<option value="Deployment" selected="">Deployment</option>
              			<option value="StatefulSet">StatefulSet</option>
  		            </select>
                </div>
                <label class="layui-form-label">对象名称</label>
                <div class="layui-input-inline">
                  <input type="text" name="objName" placeholder="关联deploy或sts" value="" class="layui-input">
                </div>
            </div>

            <div class="layui-form-item">
                <label class="layui-form-label">最大</label>
                <div class="layui-input-inline">
                  <input type="text" name="maxPods" placeholder="最大容器数量" value="" class="layui-input">
                </div>
                <label class="layui-form-label">最小</label>
                <div class="layui-input-inline">
                  <input type="text" name="minPods" placeholder="最小容器数量" value="" class="layui-input">
                </div>
            </div>

            <div class="layui-form-item">
                <label class="layui-form-label">CPU</label>
                <div class="layui-input-inline">
                  <input type="text" name="cpuUsage" placeholder="CPU使用率%" value="" class="layui-input">
                </div>
                <label class="layui-form-label">内存</label>
                <div class="layui-input-inline">
                  <input type="text" name="memUsage" placeholder="内存使用率%" value="" class="layui-input">
                </div>
            </div>

            <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">
                <legend>标签与注解</legend>
            </fieldset>
            <div class="labelsTpl">
              <div class="layui-form-item">
                  <label class="layui-form-label">标签</label>
                  <div class="layui-input-inline" style="width:150px">  
                      <input type="text" id="labels_key0" name="labels_key[]" placeholder="key" value="" class="layui-input">
                  </div>
                  <div class="layui-input-inline" style="width:150px">  
                      <input type="text" id="labels_value0" name="labels_value[]" placeholder="value" value="" class="layui-input">
                  </div>
                  <div class="layui-input-inline">  
                      <button class="layui-btn layui-btn-normal" id="newaddbtn"><i class="layui-icon layui-icon-add-circle"></i></button>
                  </div>                
              </div> 
            </div>

  <br>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">确认保存</button>
                </div>
            </div>
        </form>
    </div>
</div>
</body>
<script>

    //标签删除
    var TplIndex = 0;
    function delTpl(id) {
      TplIndex--;
      $("#tpl-"+id).remove();
    }

    layui.use(['form'], function () {
        var form = layui.form,
            layer = layui.layer,
            $ = layui.$;

        //labes add
        $('#newaddbtn').on("click",function(){
          TplIndex++;
          var addTpl =
              '<div class="layui-form-item" id="tpl-'+TplIndex+'">' +
                  '<label class="layui-form-label">标签</label>' +
                  '<div class="layui-input-inline" style="width:150px">' +
        	              '<input type="text" name="labels_key[]" id="labels_key'+TplIndex+'" placeholder="key" value="" class="layui-input">' +
                  '</div>' +    
                  '<div class="layui-input-inline" style="width:150px">' +  
                      '<input type="text" name="labels_value[]" id="labels_value'+TplIndex+'" placeholder="value" value="" class="layui-input">' +
                  '</div>' +
                  '<div class="layui-input-inline">' +  
                      '<input class="layui-btn layui-btn-normal layui-bg-orange layui-icon" style="width:60px" type="button" id="newDelbtn" value="&#xe616;" οnclick="delTpl('+TplIndex+');" />' +
                  '</div>' +                
              '</div>'
            $('.labelsTpl').append(addTpl);   
          form.render(); 
          return false;   
        });

        //监听提交
        form.on('submit(saveBtn)', function (data) {
                data.field.hpaName = data.field.hpaName.replace(/^\s*|\s*$/g,""); //替换空格
                //lables 处理
                var labelsArry = [];
                for (var i=0;i<=TplIndex;i++) {
                  //delete data.field.lables_key[i];                  
                  //delete data.field.labels_value[i];
                  var kk = document.getElementById("labels_key"+i).value;
                  var vv = document.getElementById("labels_value"+i).value; 
                  if ( kk != "" && vv != "") {
                    labelsArry.push(
                      {
                        key:kk,
                        value:vv,
                      }
                    )
                  }
                }
                if (labelsArry.length > 0) {
                  data.field.lables = labelsArry;
                }

					      console.log(data.field);
			          layer.confirm('确定添加?', {icon: 3, title:'提示',yes: function(index){
                     var index2 = layer.load(0, {shade: false});
                     layer.msg('稍等片刻');
                     $.ajax({
                       url: "/hpa/v1/Create",
                       type: "post",
                       data: JSON.stringify(data.field),
                       dataType: "json",
                       success: function (resp) {
                                layer.close(index2);
                                 if(resp.code == 0){
                                    layer.msg('添加成功', {icon: 1});
									                  //window.location.reload();
                                 }else{
                                    layer.msg(resp.msg,{icon:2});
                                 }
                        }
                      });		  	  
                  },
                  cancel: function(index, layero){ 
                    layer.close(index);
                    layer.close(index2);
		                console.log("不操作");
                  } 
                });
              return false;
        });

    });
</script>
</body>
</html>
5.3.hpaYaml.html

5.3 将读取的yaml配置通过monaco-editor编辑器进行显示

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>yaml编辑</title>
    <meta name="renderer" content="webkit">
    <link rel="stylesheet" href="/lib/layui-v2.6.3/css/layui.css" media="all">
    <link rel="stylesheet" href="/css/public.css" media="all">
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
		<link
			rel="stylesheet"
			data-name="vs/editor/editor.main"
			href="/monaco-editor/min/vs/editor/editor.main.css"
		/>
    <script src="/js/xkube.js?v=1" charset="utf-8"></script>
    <script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">
        <div id="container" style="width: 100%; height:460px; border: 1px solid grey"></div>
        <br>
        <fieldset class="table-search-fieldset">
            <legend></legend>
            <button class="layui-btn layui-btn-normal layui-btn-sm" id="DownLoadBtn">下载</button>
        </fieldset>   
    </div>
</div>
<script src="/lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script>
	var require = { paths: { vs: '/monaco-editor/min/vs' } };
</script>
<script src="/monaco-editor/min/vs/loader.js"></script>
<script src="/monaco-editor/min/vs/editor/editor.main.nls.js"></script>
<script src="/monaco-editor/min/vs/editor/editor.main.js"></script>
<script>
    layui.use(['form', 'table','code'], function () {
        var $ = layui.jquery;
        var form = layui.form,
            layer = layui.layer;
        
    		var editor = monaco.editor.create(document.getElementById('container'), {
    			value: '',
    			language: 'yaml',
    			//automaticLayout: true,
    			minimap: {enabled: false},
    			wordWrap: 'on',
    			theme: 'vs-dark'
    		});

    		$.ajax({
    		    url: "/hpa/v1/Yaml"+location.search,
    		    type: "GET",
    		    success: function (resp) {
    			  //codeyaml = resp;
    			  editor.setValue(resp);
    			}
    		});	
			//monaco.editor.setTheme('vs-dark');
			//var code = `function x() {', '\tconsole.log("Hello world!");', '}`;			
			//console.log(editor.getValue());
        $('#DownLoadBtn').on("click",function(){
              var deployName = getQueryString("deployName");
              ExportRaw(deployName+'.yaml',editor.getValue());
        });

    });
</script>
</body>
</html>

六.完整代码

6.1.hpa.go

6.1 控制器中hpa.go的完整代码

package controllers

import (
	//"encoding/json"
	"log"
	m "myk8s/models"

	beego "github.com/beego/beego/v2/server/web"
)

type HpaController struct {
	beego.Controller
}

func (this *HpaController) List() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	hpaName := this.GetString("hpaName")
	targetName := this.GetString("targetName")
	List, err := m.HpaList(clusterId, namespace, hpaName, targetName)
	msg := "success"
	code := 0
	count := len(List)
	if err != nil {
		log.Println(err)
		msg = err.Error()
		code = -1
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg, "count": count, "data": &List}
	//this.Data["json"] = &datas
	this.ServeJSON()
}

func (this *HpaController) Yaml() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	hpaName := this.GetString("hpaName")

	yamlStr, _ := m.GetHpaYaml(clusterId, namespace, hpaName)
	this.Ctx.WriteString(yamlStr)
}

func (this *HpaController) Create() {
	clusterId := this.GetString("clusterId")
	code := 0
	msg := "success"
	err := m.HpaCreate(clusterId, this.Ctx.Input.RequestBody)
	if err != nil {
		code = -1
		msg = err.Error()
		log.Printf("[ERROR] hpa Create Fail:%s\n", err)
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

func (this *HpaController) Del() {
	clusterId := this.GetString("clusterId")
	namespace := this.GetString("nameSpace")
	hpaName := this.GetString("hpaName")
	err := m.HpaDelete(clusterId, namespace, hpaName)
	code := 0
	msg := "success"
	if err != nil {
		log.Println(err)
		code = -1
		msg = err.Error()
	}
	this.Data["json"] = &map[string]interface{}{"code": code, "msg": msg}
	this.ServeJSON()
}

6.2.hpaModel.go

6.2 模型中hpaModel.go的完整代码

// storageclass_model.go
package models

import (
	"context"
	"fmt"
	"strings"

	//"time"
	"log"
	"myk8s/common"

	"github.com/tidwall/gjson"

	//v1 "k8s.io/api/core/v1"
	//autoscalingv1 "k8s.io/api/autoscaling/v1"
	autoscalingv2 "k8s.io/api/autoscaling/v2"
	//autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	//"k8s.io/apimachinery/pkg/types"
	//"k8s.io/client-go/kubernetes"
	//"k8s.io/client-go/tools/clientcmd"
	//"k8s.io/client-go/util/retry"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/yaml"
)

type Hpa struct {
	HpaName    string `json:"hpaName"`
	NameSpace  string `json:"nameSpace"`
	TargetKind string `json:"targetKind"`
	TargetName string `json:"targetName"`
	//DeployName      string `json:"deployName"`
	MinReplicas     int32  `json:"minReplicas"`     //最大副本
	MaxReplicas     int32  `json:"maxReplicas"`     //最小副本
	CurrentReplicas int32  `json:"currentReplicas"` //当前副本
	Metrics         string `json:"metrics"`         //目标使用率
	CreateTime      string `json:"createTime"`
}

//AutoscalingV2beta2 适合zx-pcauto的版本 1.20.11-aliyun.1
//AutoscalingV2适合yt-pcauto的版本 1.28.1

func HpaList(kubeconfig, nameSpace, hpaName, targetName string) ([]Hpa, error) {
	clientset := common.ClientSet(kubeconfig)
	if nameSpace == "" {
		//namespace = corev1.NamespaceDefault
		nameSpace = corev1.NamespaceAll
	}

	//设置ListOptions
	var listOptions = metav1.ListOptions{}
	// if hpaName != "" {
	// 	listOptions = metav1.ListOptions{
	// 		FieldSelector: fmt.Sprintf("metadata.name=%s", hpaName),
	// 	}
	// }

	//hpas, err := clientset.AutoscalingV1().HorizontalPodAutoscalers(nameSpace).List(context.TODO(), metav1.ListOptions{}) //无vv.Spec.Metrics
	//hpas, err := clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).List(context.TODO(), metav1.ListOptions{}) //zx-pcauto会提示:list haps error, err:the server could not find the requested resource
	hpas, err := clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).List(context.TODO(), listOptions)
	if err != nil {
		log.Printf("list haps error, err:%v\n", err)
	}

	var bbb = make([]Hpa, 0)
	for _, vv := range hpas.Items {

		//搜索
		if hpaName != "" {
			if !strings.Contains(vv.Name, hpaName) {
				continue
			}
		}

		//根据对象名称来查询
		if targetName != "" {
			if vv.Spec.ScaleTargetRef.Name != targetName {
				continue
			}
		}

		var metricsStr string
		for _, v1 := range vv.Spec.Metrics {
			metricsStr += fmt.Sprintf("%s:%d%%,", v1.Resource.Name, *v1.Resource.Target.AverageUtilization)
		}
		if len(metricsStr) > 0 {
			metricsStr = metricsStr[0 : len(metricsStr)-1]
		}
		Items := &Hpa{
			HpaName:         vv.Name,
			NameSpace:       vv.Namespace,
			TargetKind:      vv.Spec.ScaleTargetRef.Kind,
			TargetName:      vv.Spec.ScaleTargetRef.Name,
			MinReplicas:     *vv.Spec.MinReplicas,
			MaxReplicas:     vv.Spec.MaxReplicas,
			CurrentReplicas: vv.Status.CurrentReplicas,
			Metrics:         metricsStr,
			CreateTime:      vv.CreationTimestamp.Format("2006-01-02 15:04:05"),
		}
		bbb = append(bbb, *Items)
	}
	return bbb, err
}

func GetHpaYaml(kubeconfig, nameSpace, hpaName string) (string, error) {

	hpa, err := common.ClientSet(kubeconfig).AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Get(context.TODO(), hpaName, metav1.GetOptions{})
	deploymentUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(hpa)
	if err != nil {
		return "", err
	}
	yamlBytes, err := yaml.Marshal(deploymentUnstructured)
	if err != nil {
		return "", err
	}
	return string(yamlBytes), nil
}

func HpaCreate(kubeconfig string, bodys []byte) error {
	gp := gjson.ParseBytes(bodys)
	clusterId := gp.Get("clusterId").String()
	if kubeconfig == "" {
		kubeconfig = clusterId
	}
	hpaName := gp.Get("hpaName").String()
	nameSpace := gp.Get("nameSpace").String()

	var labelsMap = make(map[string]string)
	labelsMap["app"] = hpaName
	for _, vv := range gp.Get("lables").Array() {
		labelsMap[vv.Get("key").Str] = vv.Get("value").Str
	}

	hpaKind := gp.Get("hpaKind").String()
	objName := gp.Get("objName").String()
	cpuUsage := gp.Get("cpuUsage").Int()
	memUsage := gp.Get("memUsage").Int()
	maxPods := gp.Get("maxPods").Int()
	minPods := gp.Get("minPods").Int()
	if minPods < 1 {
		minPods = 1
	}
	if maxPods < 1 {
		maxPods = 2
	}
	if cpuUsage < 1 {
		cpuUsage = 80
	}

	hpa := &autoscalingv2.HorizontalPodAutoscaler{
		ObjectMeta: metav1.ObjectMeta{
			Name:      hpaName,
			Namespace: nameSpace,
			Labels:    labelsMap,
		},
		Spec: autoscalingv2.HorizontalPodAutoscalerSpec{
			ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{
				Kind:       hpaKind,
				Name:       objName,
				APIVersion: "apps/v1",
			},
			MinReplicas: int32Ptr(int32(minPods)),
			MaxReplicas: int32(maxPods),
			//TargetCPUUtilizationPercentage: int32Ptr(int32(cpuUsage)), v1版本
			Metrics: []autoscalingv2.MetricSpec{
				{
					Resource: &autoscalingv2.ResourceMetricSource{
						Name: "memory",
						Target: autoscalingv2.MetricTarget{
							Type:               autoscalingv2.UtilizationMetricType,
							AverageUtilization: int32Ptr(int32(memUsage)),
						},
					},
					Type: "Resource",
				},
				{
					Resource: &autoscalingv2.ResourceMetricSource{
						Name: "cpu",
						Target: autoscalingv2.MetricTarget{
							Type:               autoscalingv2.UtilizationMetricType,
							AverageUtilization: int32Ptr(int32(cpuUsage)),
						},
					},
					Type: "Resource",
				},
			},
		},
	}
	clientset := common.ClientSet(kubeconfig)
	_, err := clientset.AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Create(context.TODO(), hpa, metav1.CreateOptions{})
	return err
}

func HpaDelete(kubeconfig, nameSpace, hpaName string) error {
	err := common.ClientSet(kubeconfig).AutoscalingV2().HorizontalPodAutoscalers(nameSpace).Delete(context.TODO(), hpaName, metav1.DeleteOptions{})
	return err
}

七.效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐