前言
在研發之旅的不斷深入中,愈發感到在接到需求後,畫出實現的邏輯圖是十分必要的。本篇將試圖通過對容器平臺工作負載的擴縮容/啓動/停止/重啓需求來驗證前面一句。
需求分析
本需求可分爲兩大類:
1、擴縮容/啓動/停止 本質是對Pod的副本數的變更操作
2、重啓 本質是對 ReplicaSet的變更操作,不涉及更改Pod副本數
對於第一類還可以細分爲兩類:
1、擴縮容 需要計算該工作負載的請求計算型資源(CPU&MEMORY)的總和,集羣的剩餘計算型資源(CPU&MEMORY),得出可擴容的最大數,然後再進行擴容。應該需要清晰的時,即使無法得出可擴容的最大數,依然不影響進行擴容操作
2、停止/啓動 也就是將副本數縮小爲0和恢復至期望副本數
邏輯圖
根據上述需求分析,可得出一下邏輯圖:
上圖其實不具備過多可道之處,1、生成k8s client 2、查詢集羣節點是否允許調度 3、查工作負載的請求資源 4、查節點的剩餘資源(依賴metrics-server) 5、分別計算出cpu可調度數和memory可調度數,取最小數 6、擴縮容
上圖需要主要重啓時應做到優雅,無中斷
核心代碼
調度:
// 獲取可調度節點
dbNodes, rows := w.nodeDao.FindList(map[string]interface{}{"cluster_name": w.clusterManager.ClusterName, "schedulable": 1})
if rows == 0 {
return nil, errors.New("err: There are currently no schedulable nodes in the cluster")
}
// 獲取工作負載的請求資源
WorkloadRep , err := w.cli.FindOne(namespace,name)
if err != nil {
return nil,err
}
var useMemValue,useCPUValue,useCPU,useMemory ,totalCPU,totalMem float64
var schedulerNumber int
for _, container := range WorkloadRep.Containers {
useMemValue += tool.CpuAndMem(container.Resources.Requests.Memory().String(), string(container.Resources.Requests.Memory().Format), string(container.Resources.Requests.Memory().Value()))
useCPUValue += tool.CpuAndMem(container.Resources.Requests.Cpu().String(), string(container.Resources.Requests.Cpu().Format), string(container.Resources.Requests.Cpu().Value()))
}
// 獲取集羣可調度節點剩餘資源
for _, dbNode := range dbNodes{
totalCPU += dbNode.TotalCPU
totalMem += dbNode.TotalMemory
metricsNode, err := w.nodeKubernetes.MetricsNode(w.clusterManager.Metrics,dbNode.NodeName)
if err == nil {
useCPU = tool.CPUAndMemStringToFloat64(metricsNode.Usage.Cpu().String())
useMemory = tool.CPUAndMemStringToFloat64(metricsNode.Usage.Memory().String())
} else {
continue
}
}
if useCPU == 0 || useMemory == 0 {
return nil, errors.New("err: metrics-server error")
}
// 計算可調度數
schedulerNumberCpu := (totalCPU * 1000- useCPU ) / useCPUValue
schedulerNumberMem := (totalMem * 1024 - useMemory ) / useMemValue
if schedulerNumberCpu > schedulerNumberMem {
schedulerNumber = int(schedulerNumberMem)
} else {
schedulerNumber = int(schedulerNumberCpu)
}
return schedulerNumber, err
重啓
// 生成k8s client
clusterManager, err := buildKubernetesClient(c)
if err != nil {
ErrorResponse(c, ecode.KubernetesBuildClientFail, "err :buildKubernetesClient failed")
return
}
namespace := c.Params.ByName("ns")
kind := c.Params.ByName("kind")
kind = tool.FirstUpper(kind)
name := c.Params.ByName("name")
// 更改annotations 重啓時間
patchdata := map[string]interface{}{
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"restartTime": time.Now().Format(time.Stamp),
},
},
},
},
}
body , _ := json.Marshal(patchdata)
if data, err := service.NewWorkloadService(clusterManager, kind).Patch(namespace, name,body); err != nil {
ErrorResponse(c, ecode.PatchLabelFail, err.Error())
} else {
SuccessResponse(c, data)
}