【k8s多集群管理平台开发实践】五、client-go实现k8s的hpa水平扩缩容的列表显示、创建hpa,读取yaml配置功能
通过client-go实现hpa【Horizontal Pod Autoscalert】水平扩缩容的列表、创建hap、读取yaml配置文件。通过hap可以实现根据cpu和内存设置的阀值实现deploy,statefulset的扩容和缩容。该部分代码主要有控制器部分代码、模型部分代码、前端html【采用layui框架、layuimini模板】、路由配置。yaml配置部分采用微软的monaco-edi
文章目录
简介
本章节主要讲解通过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"></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="" ο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
}
七.效果图
更多推荐
所有评论(0)