节假日高峰自动生成预案
2025-09-10 15:39 luoguoling 阅读(4) 评论(0) 收藏 举报提前准备好文本
cat deployments.txt
cc-uat api-gateway 一级服务
aa-uat xx-api-gateway 大网关
aa-uat yy-api-gateway 大网关
代码实现(通过读取上面的信息,获取上面服务的资源,使用率,根据最终用户确认节假日升级数量,统计出资源使用量,扩容和缩容的脚本)
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/xuri/excelize/v2"
)
// DeploymentInfo 存储Deployment信息
type DeploymentInfo struct {
Namespace string
Deployment string
ResourcePool string // 新增资源池字段
Replicas int
RequestCPU string
CPUUsage string
CPUUsagePct string
RequestMemory string
MemoryUsage string
MemoryUsagePct string
}
// ResourceUsage 存储资源使用情况
type ResourceUsage struct {
CPU int
Memory int
}
func main() {
fmt.Println("开始生成Kubernetes部署资源Excel报告...")
fmt.Println("==========================================")
// 读取deployments.txt文件
deployments, err := readDeploymentsFile("deployments.txt")
if err != nil {
fmt.Printf("错误: 读取deployments.txt文件失败: %v\n", err)
os.Exit(1)
}
if len(deployments) == 0 {
fmt.Println("错误: 没有找到有效的Deployment配置")
os.Exit(1)
}
// 创建Excel文件
f := excelize.NewFile()
defer f.Close()
// 设置工作表名称
f.SetSheetName("Sheet1", "部署资源报告")
// 设置表头样式
headerStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Bold: true, Size: 12, Color: "#FFFFFF"},
Fill: excelize.Fill{Type: "pattern", Color: []string{"#4F81BD"}, Pattern: 1},
Alignment: &excelize.Alignment{
Horizontal: "center",
Vertical: "center",
},
Border: []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
{Type: "top", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
},
})
// 设置数据样式
dataStyle, _ := f.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{
Vertical: "center",
},
Border: []excelize.Border{
{Type: "left", Color: "#D9D9D9", Style: 1},
{Type: "right", Color: "#D9D9D9", Style: 1},
{Type: "top", Color: "#D9D9D9", Style: 1},
{Type: "bottom", Color: "#D9D9D9", Style: 1},
},
})
// 设置公式单元格样式(浅灰色背景)
formulaStyle, _ := f.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{
Vertical: "center",
},
Border: []excelize.Border{
{Type: "left", Color: "#D9D9D9", Style: 1},
{Type: "right", Color: "#D9D9D9", Style: 1},
{Type: "top", Color: "#D9D9D9", Style: 1},
{Type: "bottom", Color: "#D9D9D9", Style: 1},
},
Fill: excelize.Fill{Type: "pattern", Color: []string{"#F0F0F0"}, Pattern: 1},
})
// 设置命令单元格样式(浅绿色背景)
commandStyle, _ := f.NewStyle(&excelize.Style{
Alignment: &excelize.Alignment{
Vertical: "center",
},
Border: []excelize.Border{
{Type: "left", Color: "#D9D9D9", Style: 1},
{Type: "right", Color: "#D9D9D9", Style: 1},
{Type: "top", Color: "#D9D9D9", Style: 1},
{Type: "bottom", Color: "#D9D9D9", Style: 1},
},
Fill: excelize.Fill{Type: "pattern", Color: []string{"#E8F5E8"}, Pattern: 1},
})
// 设置使用率样式(颜色根据百分比变化)
highUsageStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Color: "#FF0000"},
Alignment: &excelize.Alignment{Vertical: "center"},
})
mediumUsageStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Color: "#FF9900"},
Alignment: &excelize.Alignment{Vertical: "center"},
})
lowUsageStyle, _ := f.NewStyle(&excelize.Style{
Font: &excelize.Font{Color: "#00B050"},
Alignment: &excelize.Alignment{Vertical: "center"},
})
// 设置表头 - 增加资源池列,调整其他列位置
headers := []string{
"命名空间", "服务", "资源池", "副本数", // 新增资源池列
"Request_CPU(m)", "CPU_Usage(m)", "cpu使用率(%)",
"Request_Memory(Mi)", "Memory_Usage(Mi)", "内存使用率(%)",
"节假日扩容数量", "扩容CPU资源(m)", "扩容内存资源(Mi)",
"缩容命令", "扩容命令",
}
for col, header := range headers {
cell, _ := excelize.CoordinatesToCellName(col+1, 1)
f.SetCellValue("部署资源报告", cell, header)
f.SetCellStyle("部署资源报告", cell, cell, headerStyle)
}
// 设置列宽(调整列位置)
f.SetColWidth("部署资源报告", "A", "A", 15) // 命名空间
f.SetColWidth("部署资源报告", "B", "B", 20) // 服务
f.SetColWidth("部署资源报告", "C", "C", 15) // 资源池(新增)
f.SetColWidth("部署资源报告", "D", "D", 10) // 副本数
f.SetColWidth("部署资源报告", "E", "E", 15) // Request_CPU(m)
f.SetColWidth("部署资源报告", "F", "F", 15) // CPU_Usage(m)
f.SetColWidth("部署资源报告", "G", "G", 15) // cpu使用率(%)
f.SetColWidth("部署资源报告", "H", "H", 18) // Request_Memory(Mi)
f.SetColWidth("部署资源报告", "I", "I", 18) // Memory_Usage(Mi)
f.SetColWidth("部署资源报告", "J", "J", 15) // 内存使用率(%)
f.SetColWidth("部署资源报告", "K", "K", 18) // 节假日扩容数量
f.SetColWidth("部署资源报告", "L", "L", 18) // 扩容CPU资源(m)
f.SetColWidth("部署资源报告", "M", "M", 18) // 扩容内存资源(Mi)
f.SetColWidth("部署资源报告", "N", "N", 50) // 缩容命令
f.SetColWidth("部署资源报告", "O", "O", 50) // 扩容命令
row := 2
successCount := 0
// 处理每个Deployment
for _, dep := range deployments {
if dep.Namespace == "" || dep.Deployment == "" {
continue
}
fmt.Printf("处理: %s/%s\n", dep.Namespace, dep.Deployment)
info, err := processDeployment(dep.Namespace, dep.Deployment, dep.ResourcePool)
if err != nil {
fmt.Printf(" 错误: %v\n", err)
continue
}
// 去除单位的CPU和内存值
requestCPUWithoutUnit := removeCPUUnit(info.RequestCPU)
cpuUsageWithoutUnit := removeCPUUnit(info.CPUUsage)
requestMemoryWithoutUnit := removeMemoryUnit(info.RequestMemory)
memoryUsageWithoutUnit := removeMemoryUnit(info.MemoryUsage)
// 写入基础数据(数值不带单位)
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", row), info.Namespace)
f.SetCellValue("部署资源报告", fmt.Sprintf("B%d", row), info.Deployment)
f.SetCellValue("部署资源报告", fmt.Sprintf("C%d", row), info.ResourcePool) // 资源池
f.SetCellValue("部署资源报告", fmt.Sprintf("D%d", row), info.Replicas)
f.SetCellValue("部署资源报告", fmt.Sprintf("E%d", row), requestCPUWithoutUnit)
f.SetCellValue("部署资源报告", fmt.Sprintf("F%d", row), cpuUsageWithoutUnit)
f.SetCellValue("部署资源报告", fmt.Sprintf("G%d", row), info.CPUUsagePct)
f.SetCellValue("部署资源报告", fmt.Sprintf("H%d", row), requestMemoryWithoutUnit)
f.SetCellValue("部署资源报告", fmt.Sprintf("I%d", row), memoryUsageWithoutUnit)
f.SetCellValue("部署资源报告", fmt.Sprintf("J%d", row), info.MemoryUsagePct)
// 设置节假日扩容数量默认值(等于当前副本数)
f.SetCellValue("部署资源报告", fmt.Sprintf("K%d", row), info.Replicas)
// 设置扩容CPU资源公式(数值计算,不带单位)- 调整列引用
cpuFormula := fmt.Sprintf(`=IF(K%d>D%d, (K%d-D%d)*E%d, "无需扩容")`, row, row, row, row, row)
f.SetCellFormula("部署资源报告", fmt.Sprintf("L%d", row), cpuFormula)
// 设置扩容内存资源公式(数值计算,不带单位)- 调整列引用
memoryFormula := fmt.Sprintf(`=IF(K%d>D%d, (K%d-D%d)*H%d, "无需扩容")`, row, row, row, row, row)
f.SetCellFormula("部署资源报告", fmt.Sprintf("M%d", row), memoryFormula)
// 设置缩容命令公式 - 调整列引用
scaleDownFormula := fmt.Sprintf(`="kubectl scale deployment/"&B%d&" --replicas="&D%d&" -n "&A%d`, row, row, row)
f.SetCellFormula("部署资源报告", fmt.Sprintf("N%d", row), scaleDownFormula)
// 设置扩容命令公式 - 调整列引用
scaleUpFormula := fmt.Sprintf(`="kubectl scale deployment/"&B%d&" --replicas="&K%d&" -n "&A%d`, row, row, row)
f.SetCellFormula("部署资源报告", fmt.Sprintf("O%d", row), scaleUpFormula)
// 设置数据行样式(调整列数)
for col := 1; col <= 15; col++ {
cell, _ := excelize.CoordinatesToCellName(col, row)
if col >= 12 && col <= 13 { // L列和M列(扩容资源列)使用公式样式
f.SetCellStyle("部署资源报告", cell, cell, formulaStyle)
} else if col >= 14 && col <= 15 { // N列和O列(命令列)使用命令样式
f.SetCellStyle("部署资源报告", cell, cell, commandStyle)
} else {
f.SetCellStyle("部署资源报告", cell, cell, dataStyle)
}
}
// 设置使用率颜色 - 调整列位置
if info.CPUUsagePct != "N/A" {
cpuPct := parsePercentage(info.CPUUsagePct)
cpuStyle := getUsageStyle(cpuPct, highUsageStyle, mediumUsageStyle, lowUsageStyle)
f.SetCellStyle("部署资源报告", fmt.Sprintf("G%d", row), fmt.Sprintf("G%d", row), cpuStyle)
}
if info.MemoryUsagePct != "N/A" {
memoryPct := parsePercentage(info.MemoryUsagePct)
memoryStyle := getUsageStyle(memoryPct, highUsageStyle, mediumUsageStyle, lowUsageStyle)
f.SetCellStyle("部署资源报告", fmt.Sprintf("J%d", row), fmt.Sprintf("J%d", row), memoryStyle)
}
fmt.Printf(" 副本数: %d\n", info.Replicas)
fmt.Printf(" 资源池: %s\n", info.ResourcePool)
fmt.Printf(" CPU请求: %s(m), 使用: %s(m), 使用率: %s\n", requestCPUWithoutUnit, cpuUsageWithoutUnit, info.CPUUsagePct)
fmt.Printf(" 内存请求: %s(Mi), 使用: %s(Mi), 使用率: %s\n", requestMemoryWithoutUnit, memoryUsageWithoutUnit, info.MemoryUsagePct)
fmt.Printf(" 节假日扩容数量(默认): %d\n", info.Replicas)
fmt.Printf(" 扩容CPU资源公式: %s\n", cpuFormula)
fmt.Printf(" 扩容内存资源公式: %s\n", memoryFormula)
fmt.Printf(" 缩容命令: kubectl scale deployment/%s --replicas=%d -n %s\n", info.Deployment, info.Replicas, info.Namespace)
fmt.Printf(" 扩容命令: kubectl scale deployment/%s --replicas=%d -n %s\n", info.Deployment, info.Replicas, info.Namespace)
fmt.Println()
row++
successCount++
}
// 添加筛选器 - 调整列范围
f.AutoFilter("部署资源报告", "A1:O1", nil)
// 添加使用说明 - 调整行位置
noteRow := row + 2
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow), "使用说明:")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+1), "1. K列「节假日扩容数量」已默认设置为当前副本数")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+2), "2. 请人工修改K列的数值为实际的节假日扩容数量")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+3), "3. 修改K列后,L列和M列会自动计算扩容所需资源")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+4), "4. N列和O列会自动生成kubectl命令")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+5), "5. 如果节假日扩容数量 ≤ 当前副本数,显示'无需扩容'")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+6), "6. 单位说明:")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+7), " - CPU相关列: 单位均为毫核(m)")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+8), " - 内存相关列: 单位均为MiB(Mi)")
f.SetCellValue("部署资源报告", fmt.Sprintf("A%d", noteRow+9), "7. 在Excel中按 F9 键可以手动重算所有公式")
// 启用Excel的自动计算
f.SetSheetProps("部署资源报告", &excelize.SheetPropsOptions{
EnableFormatConditionsCalculation: &[]bool{true}[0],
})
// 保存文件
filename := fmt.Sprintf("deployment_report_%s.xlsx", time.Now().Format("20060102_150405"))
if err := f.SaveAs(filename); err != nil {
fmt.Printf("错误: 保存Excel文件失败: %v\n", err)
os.Exit(1)
}
fmt.Println("==========================================")
fmt.Printf("Excel报告已生成: %s\n", filename)
fmt.Printf("成功处理 %d 个Deployment\n", successCount)
fmt.Println("请打开Excel文件,修改K列「节假日扩容数量」")
fmt.Println("L列和M列会自动计算扩容所需的CPU和内存资源")
fmt.Println("N列和O列会自动生成kubectl扩容/缩容命令")
fmt.Println("注意: 如果公式没有自动计算,请按F9键手动重算")
}
// Deployment 表示一个Deployment的命名空间、名称和资源池
type Deployment struct {
Namespace string
Deployment string
ResourcePool string
}
// readDeploymentsFile 读取deployments.txt文件(支持三列格式)
func readDeploymentsFile(filename string) ([]Deployment, error) {
content, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
var deployments []Deployment
lines := strings.Split(string(content), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.Fields(line)
if len(parts) >= 2 {
deployment := Deployment{
Namespace: parts[0],
Deployment: parts[1],
}
// 如果有第三列(资源池),则读取
if len(parts) >= 3 {
deployment.ResourcePool = parts[2]
}
deployments = append(deployments, deployment)
}
}
return deployments, nil
}
// processDeployment 处理单个Deployment
func processDeployment(namespace, deployment, resourcePool string) (DeploymentInfo, error) {
info := DeploymentInfo{
Namespace: namespace,
Deployment: deployment,
ResourcePool: resourcePool,
}
// 获取Deployment信息
deploymentJSON, err := getDeploymentInfo(namespace, deployment)
if err != nil {
return info, fmt.Errorf("获取Deployment信息失败: %v", err)
}
// 解析副本数
var dep struct {
Spec struct {
Replicas int `json:"replicas"`
Template struct {
Spec struct {
Containers []struct {
Resources struct {
Requests map[string]string `json:"requests"`
} `json:"resources"`
} `json:"containers"`
} `json:"spec"`
} `json:"template"`
} `json:"spec"`
}
if err := json.Unmarshal(deploymentJSON, &dep); err != nil {
return info, fmt.Errorf("解析Deployment JSON失败: %v", err)
}
info.Replicas = dep.Spec.Replicas
// 获取请求资源
if len(dep.Spec.Template.Spec.Containers) > 0 {
requests := dep.Spec.Template.Spec.Containers[0].Resources.Requests
info.RequestCPU = requests["cpu"]
info.RequestMemory = requests["memory"]
// 处理空值
if info.RequestCPU == "" {
info.RequestCPU = "N/A"
}
if info.RequestMemory == "" {
info.RequestMemory = "N/A"
}
} else {
info.RequestCPU = "N/A"
info.RequestMemory = "N/A"
}
// 获取资源使用情况
usage, err := getResourceUsage(namespace, deployment)
if err != nil {
fmt.Printf(" 警告: 获取资源使用情况失败: %v\n", err)
usage = &ResourceUsage{CPU: 0, Memory: 0}
}
info.CPUUsage = fmt.Sprintf("%dm", usage.CPU)
info.MemoryUsage = fmt.Sprintf("%dMi", usage.Memory)
// 计算使用率百分比
info.CPUUsagePct = calculateUsagePercentage(info.RequestCPU, usage.CPU, "cpu")
info.MemoryUsagePct = calculateUsagePercentage(info.RequestMemory, usage.Memory, "memory")
return info, nil
}
// 去除CPU单位(转换为毫核数值)
func removeCPUUnit(value string) string {
if value == "N/A" || value == "" {
return "N/A"
}
if strings.HasSuffix(value, "m") {
return strings.TrimSuffix(value, "m")
}
// 如果是整数(如 "1" 表示 1核 = 1000m)
if num, err := strconv.Atoi(value); err == nil {
return fmt.Sprintf("%d", num*1000)
}
// 如果是浮点数(如 "0.5" 表示 0.5核 = 500m)
if num, err := strconv.ParseFloat(value, 64); err == nil {
return fmt.Sprintf("%.0f", num*1000)
}
return value
}
// 去除内存单位(转换为MiB数值)
func removeMemoryUnit(value string) string {
if value == "N/A" || value == "" {
return "N/A"
}
if strings.HasSuffix(value, "Mi") {
return strings.TrimSuffix(value, "Mi")
}
if strings.HasSuffix(value, "Gi") {
if num, err := strconv.Atoi(strings.TrimSuffix(value, "Gi")); err == nil {
return fmt.Sprintf("%d", num*1024)
}
}
if strings.HasSuffix(value, "Ki") {
if num, err := strconv.Atoi(strings.TrimSuffix(value, "Ki")); err == nil {
return fmt.Sprintf("%d", num/1024)
}
}
// 如果没有单位,假设已经是MiB
return value
}
// getDeploymentInfo 获取Deployment的JSON信息
func getDeploymentInfo(namespace, deployment string) ([]byte, error) {
cmd := exec.Command("kubectl", "get", "deployment", "-n", namespace, deployment, "-o", "json")
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("kubectl命令执行失败: %v, 输出: %s", err, string(output))
}
return output, nil
}
// getResourceUsage 获取资源使用情况
func getResourceUsage(namespace, deployment string) (*ResourceUsage, error) {
cmd := exec.Command("kubectl", "top", "pods", "-n", namespace)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("获取资源使用情况失败: %v", err)
}
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, deployment) {
parts := strings.Fields(line)
if len(parts) >= 3 {
cpu := extractNumber(parts[1])
memory := extractNumber(parts[2])
return &ResourceUsage{CPU: cpu, Memory: memory}, nil
}
}
}
return &ResourceUsage{CPU: 0, Memory: 0}, nil
}
// extractNumber 从字符串中提取数字
func extractNumber(s string) int {
var numberStr strings.Builder
for _, char := range s {
if char >= '0' && char <= '9' {
numberStr.WriteRune(char)
}
}
if numberStr.Len() > 0 {
result, _ := strconv.Atoi(numberStr.String())
return result
}
return 0
}
// calculateUsagePercentage 计算使用率百分比
func calculateUsagePercentage(request string, usage int, resourceType string) string {
if request == "N/A" || request == "" || usage == 0 {
return "N/A"
}
requestNum := convertResourceToNumber(request, resourceType)
if requestNum == 0 {
return "N/A"
}
percentage := float64(usage) * 100 / float64(requestNum)
return fmt.Sprintf("%.2f%%", percentage)
}
// convertResourceToNumber 转换资源值为数字
func convertResourceToNumber(value string, resourceType string) int {
if value == "N/A" || value == "" {
return 0
}
if resourceType == "cpu" {
if strings.HasSuffix(value, "m") {
num, _ := strconv.Atoi(strings.TrimSuffix(value, "m"))
return num
} else if num, err := strconv.ParseFloat(value, 64); err == nil {
return int(num * 1000)
}
} else { // memory
if strings.HasSuffix(value, "Mi") {
num, _ := strconv.Atoi(strings.TrimSuffix(value, "Mi"))
return num
} else if strings.HasSuffix(value, "Gi") {
num, _ := strconv.ParseFloat(strings.TrimSuffix(value, "Gi"), 64)
return int(num * 1024)
} else if strings.HasSuffix(value, "Ki") {
num, _ := strconv.Atoi(strings.TrimSuffix(value, "Ki"))
return num / 1024
} else if num, err := strconv.Atoi(value); err == nil {
return num
}
}
return 0
}
// parsePercentage 解析百分比字符串
func parsePercentage(pctStr string) float64 {
if pctStr == "N/A" {
return 0
}
pctStr = strings.TrimSuffix(pctStr, "%")
result, _ := strconv.ParseFloat(pctStr, 64)
return result
}
// getUsageStyle 根据使用率获取样式
func getUsageStyle(percentage float64, highStyle, mediumStyle, lowStyle int) int {
if percentage > 90 {
return highStyle
} else if percentage > 70 {
return mediumStyle
} else {
return lowStyle
}
}

浙公网安备 33010602011771号