后端: - 新增 buildPodMetricsMap() 公共辅助函数,聚合多容器 CPU/Memory - ListPods / listPodsWithNS / ListPodsAllNS 三个 handler 全部注入 - PodSummary 增加 cpu_usage_m / mem_usage_mi 字段 前端: - KubernetesWorkloads Pod 表格增加 CPU/内存列 - NamespaceResourcePage Pod 表格增加 CPU/内存列 验证: - 17 个 Pod 全部返回真实 metrics (argocd/demo-jar) - curl /kubernetes-clusters/1/pods → cpu_usage_m='2m' mem_usage_mi='61Mi' 文档: - ROADMAP M1→done, M2→done, 验收标准全部勾选 - TDD_PLAN M2 通过标准补充 - PROJECT_EVALUATION P0 缺失项移除
632 lines
26 KiB
Go
632 lines
26 KiB
Go
package main
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"ezops/internal/models"
|
||
|
||
"ezops/pkg/toolinstaller"
|
||
|
||
"go.uber.org/zap"
|
||
"golang.org/x/crypto/bcrypt"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// seedData 初始化种子数据(admin用户 + LDAP配置 + 审批模板 + 权限角色)
|
||
func seedData(db *gorm.DB) {
|
||
// 1. 创建默认 admin 用户(仅首次)
|
||
var userCount int64
|
||
db.Model(&models.User{}).Count(&userCount)
|
||
if userCount == 0 {
|
||
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
|
||
admin := models.User{
|
||
Username: "admin",
|
||
Password: string(hashedPassword),
|
||
Email: "admin@ezops.local",
|
||
RealName: "管理员",
|
||
Status: 1,
|
||
IsAdmin: true,
|
||
AuthSource: "local",
|
||
}
|
||
if err := db.Create(&admin).Error; err != nil {
|
||
zap.L().Warn("failed to seed admin user", zap.Error(err))
|
||
} else {
|
||
zap.L().Info("seeded default admin user (admin/123456)")
|
||
}
|
||
}
|
||
|
||
// 2. 创建默认 LDAP 配置
|
||
var ldapCount int64
|
||
db.Model(&models.LDAPConfig{}).Count(&ldapCount)
|
||
if ldapCount == 0 {
|
||
port := 389
|
||
useLDAPS := false
|
||
searchFilter := "(objectClass=inetOrgPerson)"
|
||
userMapping := `{"username":"uid","email":"mail","display_name":"cn"}`
|
||
ldapCfg := models.LDAPConfig{
|
||
LDAPHost: "8.217.205.92",
|
||
LDAPPort: &port,
|
||
UseLDAPS: &useLDAPS,
|
||
AdminDN: "cn=admin,dc=opsx,dc=vip",
|
||
AdminPassword: "LQl2GX7EEg2N",
|
||
BaseDN: "dc=opsx,dc=vip",
|
||
SearchFilter: &searchFilter,
|
||
UserMapping: &userMapping,
|
||
}
|
||
if err := db.Create(&ldapCfg).Error; err != nil {
|
||
zap.L().Warn("failed to seed LDAP config", zap.Error(err))
|
||
} else {
|
||
zap.L().Info("seeded default LDAP config (8.217.205.92:389)")
|
||
}
|
||
}
|
||
|
||
// 3. 创建默认审批模板(资源申请)
|
||
var tplCount int64
|
||
db.Model(&models.ApprovalTemplate{}).Count(&tplCount)
|
||
if tplCount == 0 {
|
||
templates := []models.ApprovalTemplate{
|
||
{
|
||
Name: "云服务器申请",
|
||
Code: "server_apply",
|
||
Description: "申请云服务器资源(ECS/EC2),包含规格、数量、用途等信息",
|
||
Type: "server_apply",
|
||
FormSchema: `{
|
||
"fields": [
|
||
{"key": "provider", "label": "云厂商", "type": "select", "options": ["阿里云","腾讯云","华为云","AWS"], "required": true},
|
||
{"key": "region", "label": "地域", "type": "text", "placeholder": "如 cn-hangzhou", "required": true},
|
||
{"key": "spec", "label": "规格", "type": "select", "options": ["2C4G","4C8G","8C16G","16C32G","32C64G"], "required": true},
|
||
{"key": "disk", "label": "系统盘(GB)", "type": "number", "default": 40, "required": true},
|
||
{"key": "count", "label": "数量(台)", "type": "number", "default": 1, "required": true},
|
||
{"key": "duration", "label": "时长", "type": "select", "options": ["1个月","3个月","6个月","1年"], "required": true},
|
||
{"key": "purpose", "label": "用途说明", "type": "textarea", "required": true}
|
||
]
|
||
}`,
|
||
FlowConfig: `[{"name":"直接主管","approver_role":"team_lead"},{"name":"运维负责人","approver_role":"ops_manager"}]`,
|
||
Enabled: true,
|
||
},
|
||
{
|
||
Name: "应用部署申请",
|
||
Code: "app_deploy",
|
||
Description: "申请新应用部署,包含 Git 地址、端口、Nacos 等配置信息",
|
||
Type: "app_deploy",
|
||
FormSchema: `{
|
||
"fields": [
|
||
{"key": "app_name", "label": "应用名称", "type": "text", "placeholder": "如 user-service", "required": true},
|
||
{"key": "git_url", "label": "Git 地址", "type": "text", "placeholder": "如 https://github.com/org/repo.git", "required": true},
|
||
{"key": "branch", "label": "分支", "type": "text", "default": "main", "required": true},
|
||
{"key": "language", "label": "开发语言", "type": "select", "options": ["Java","Go","Python","Node.js","C++"], "required": true},
|
||
{"key": "http_port", "label": "HTTP 端口", "type": "number", "placeholder": "如 8080", "required": false},
|
||
{"key": "grpc_port", "label": "gRPC 端口", "type": "number", "placeholder": "如 9090", "required": false},
|
||
{"key": "nacos_addr", "label": "Nacos 地址", "type": "text", "placeholder": "如 nacos.opsx.vip:8848", "required": true},
|
||
{"key": "nacos_namespace", "label": "Nacos 命名空间", "type": "text", "placeholder": "如 production", "required": false},
|
||
{"key": "env", "label": "部署环境", "type": "select", "options": ["dev","staging","prod"], "required": true},
|
||
{"key": "replicas", "label": "副本数", "type": "number", "default": 2, "required": true},
|
||
{"key": "remark", "label": "备注", "type": "textarea", "required": false}
|
||
]
|
||
}`,
|
||
FlowConfig: `[{"name":"技术负责人","approver_role":"tech_lead"},{"name":"运维负责人","approver_role":"ops_manager"}]`,
|
||
Enabled: true,
|
||
},
|
||
}
|
||
if err := db.Create(&templates).Error; err != nil {
|
||
zap.L().Warn("failed to seed approval templates", zap.Error(err))
|
||
} else {
|
||
zap.L().Info("seeded default approval templates (server_apply + app_deploy)")
|
||
}
|
||
}
|
||
|
||
// 4. 创建默认权限和角色权限映射
|
||
var permCount int64
|
||
db.Model(&models.Permission{}).Count(&permCount)
|
||
if permCount == 0 {
|
||
perms := []models.Permission{
|
||
// 资产管理
|
||
{Name: "服务器管理", Code: "server:view", Type: "menu", Sort: 1, Status: 1},
|
||
{Name: "服务器创建", Code: "server:create", Type: "button", Sort: 2, Status: 1},
|
||
{Name: "服务器编辑", Code: "server:update", Type: "button", Sort: 3, Status: 1},
|
||
{Name: "服务器删除", Code: "server:delete", Type: "button", Sort: 4, Status: 1},
|
||
{Name: "WebShell终端", Code: "server:webshell", Type: "button", Sort: 5, Status: 1},
|
||
// 云资源
|
||
{Name: "云账号管理", Code: "cloud:account", Type: "menu", Sort: 10, Status: 1},
|
||
{Name: "域名管理", Code: "cloud:domain", Type: "menu", Sort: 11, Status: 1},
|
||
{Name: "云账单", Code: "cloud:billing", Type: "menu", Sort: 12, Status: 1},
|
||
// Kubernetes
|
||
{Name: "集群管理", Code: "k8s:cluster", Type: "menu", Sort: 20, Status: 1},
|
||
{Name: "节点管理", Code: "k8s:nodes", Type: "menu", Sort: 21, Status: 1},
|
||
{Name: "工作负载", Code: "k8s:workloads", Type: "menu", Sort: 22, Status: 1},
|
||
{Name: "架构拓扑", Code: "k8s:topology", Type: "menu", Sort: 23, Status: 1},
|
||
// 应用发布
|
||
{Name: "流水线", Code: "deploy:pipeline", Type: "menu", Sort: 30, Status: 1},
|
||
{Name: "应用发布", Code: "deploy:release", Type: "menu", Sort: 31, Status: 1},
|
||
// 监控告警
|
||
{Name: "告警中心", Code: "monitor:alert", Type: "menu", Sort: 40, Status: 1},
|
||
{Name: "告警规则", Code: "monitor:rule", Type: "button", Sort: 41, Status: 1},
|
||
{Name: "Agent监控", Code: "agent:monitor", Type: "menu", Sort: 42, Status: 1},
|
||
{Name: "Agent告警", Code: "agent:alert", Type: "menu", Sort: 43, Status: 1},
|
||
// 自动化运维
|
||
{Name: "定时任务", Code: "auto:cron", Type: "menu", Sort: 50, Status: 1},
|
||
{Name: "脚本管理", Code: "auto:script", Type: "menu", Sort: 51, Status: 1},
|
||
{Name: "Ansible", Code: "auto:ansible", Type: "menu", Sort: 52, Status: 1},
|
||
// AIOps
|
||
{Name: "智能助手", Code: "aiops:assistant", Type: "menu", Sort: 60, Status: 1},
|
||
{Name: "告警分析", Code: "aiops:analysis", Type: "menu", Sort: 61, Status: 1},
|
||
{Name: "容量预测", Code: "aiops:forecast", Type: "menu", Sort: 62, Status: 1},
|
||
// 资源申请
|
||
{Name: "资源申请", Code: "approval:apply", Type: "menu", Sort: 70, Status: 1},
|
||
{Name: "审批管理", Code: "approval:manage", Type: "button", Sort: 71, Status: 1},
|
||
// 系统管理
|
||
{Name: "用户管理", Code: "system:user", Type: "menu", Sort: 80, Status: 1},
|
||
{Name: "角色权限", Code: "system:role", Type: "menu", Sort: 81, Status: 1},
|
||
{Name: "操作审计", Code: "system:audit", Type: "menu", Sort: 82, Status: 1},
|
||
{Name: "LDAP配置", Code: "system:ldap", Type: "menu", Sort: 83, Status: 1},
|
||
// 仪表盘
|
||
{Name: "仪表盘", Code: "dashboard:view", Type: "menu", Sort: 90, Status: 1},
|
||
}
|
||
db.Create(&perms)
|
||
zap.L().Info("seeded default permissions")
|
||
}
|
||
|
||
// 5. 创建默认角色(如果不存在)
|
||
var roleCount int64
|
||
db.Model(&models.Role{}).Count(&roleCount)
|
||
if roleCount == 0 {
|
||
roles := []models.Role{
|
||
{Name: "管理员", Code: "admin", Description: "系统管理员,拥有全部权限", Status: 1},
|
||
{Name: "运维工程师", Code: "ops", Description: "日常运维管理", Status: 1},
|
||
{Name: "开发工程师", Code: "developer", Description: "开发人员,只读权限", Status: 1},
|
||
{Name: "只读用户", Code: "viewer", Description: "仅查看权限", Status: 1},
|
||
}
|
||
db.Create(&roles)
|
||
zap.L().Info("seeded default roles")
|
||
|
||
// 为各角色分配权限
|
||
var allPerms []models.Permission
|
||
db.Find(&allPerms)
|
||
for _, role := range roles {
|
||
var permIds []uint
|
||
switch role.Code {
|
||
case "admin":
|
||
for _, p := range allPerms {
|
||
permIds = append(permIds, p.ID)
|
||
}
|
||
case "ops":
|
||
for _, p := range allPerms {
|
||
if !strings.HasPrefix(p.Code, "system:") {
|
||
permIds = append(permIds, p.ID)
|
||
}
|
||
}
|
||
case "developer":
|
||
devPerms := map[string]bool{
|
||
"dashboard:view": true, "server:view": true, "cloud:account": true,
|
||
"cloud:domain": true, "cloud:billing": true,
|
||
"k8s:cluster": true, "k8s:nodes": true, "k8s:workloads": true, "k8s:topology": true,
|
||
"monitor:alert": true, "deploy:pipeline": true, "deploy:release": true,
|
||
"approval:apply": true, "aiops:assistant": true, "aiops:analysis": true, "aiops:forecast": true,
|
||
}
|
||
for _, p := range allPerms {
|
||
if devPerms[p.Code] {
|
||
permIds = append(permIds, p.ID)
|
||
}
|
||
}
|
||
case "viewer":
|
||
for _, p := range allPerms {
|
||
if p.Code == "dashboard:view" || p.Code == "server:view" || p.Code == "monitor:alert" {
|
||
permIds = append(permIds, p.ID)
|
||
}
|
||
}
|
||
}
|
||
if len(permIds) > 0 {
|
||
var rolePerms []models.Permission
|
||
db.Where("id IN ?", permIds).Find(&rolePerms)
|
||
db.Model(&role).Association("Permissions").Append(rolePerms)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 6. 创建默认 Ansible Playbook 模板(15 个高频运维场景)// Logan.Li
|
||
var pbCount int64
|
||
db.Model(&models.AnsiblePlaybook{}).Count(&pbCount)
|
||
if pbCount == 0 {
|
||
playbooks := []models.AnsiblePlaybook{
|
||
{Name: "系统信息收集", Description: "收集主机基本信息:OS、内核、CPU、内存、磁盘、网络接口", Category: "maintenance", Tags: `["系统信息","诊断","基础"]`, Content: `---
|
||
- name: 系统信息收集
|
||
hosts: all
|
||
gather_facts: yes
|
||
tasks:
|
||
- name: 系统基本信息
|
||
debug:
|
||
msg:
|
||
- "主机: {{ ansible_hostname }}"
|
||
- "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
|
||
- "内核: {{ ansible_kernel }} ({{ ansible_architecture }})"
|
||
- "CPU: {{ ansible_processor_vcpus }} 核"
|
||
- "内存: {{ (ansible_memtotal_mb / 1024) | round(1) }} GB"
|
||
- name: 磁盘使用率
|
||
shell: df -h / | tail -1 | awk '{print $5}'
|
||
register: disk_pct
|
||
- name: 系统负载
|
||
shell: uptime
|
||
register: load
|
||
- name: 运行摘要
|
||
debug:
|
||
msg: "磁盘: {{ disk_pct.stdout }} | 负载: {{ load.stdout.split('load average:')[1].strip() if 'load average:' in load.stdout else 'N/A' }}"`, CreatedBy: "system"},
|
||
{Name: "系统安全更新", Description: "安装系统安全补丁(自动识别 apt/yum/dnf)", Category: "security", Tags: `["安全","更新","补丁"]`, Content: `---
|
||
- name: 系统安全更新
|
||
hosts: all
|
||
become: yes
|
||
tasks:
|
||
- name: 检测包管理器
|
||
shell: which apt yum dnf 2>/dev/null | head -1 | xargs basename
|
||
register: pkg_mgr
|
||
- name: 更新软件包 (APT)
|
||
apt: update_cache=yes upgrade=safe
|
||
when: pkg_mgr.stdout == "apt"
|
||
- name: 更新软件包 (YUM/DNF)
|
||
shell: "{{ pkg_mgr.stdout }} update -y --security"
|
||
when: pkg_mgr.stdout in ["yum", "dnf"]
|
||
- name: 检查重启需求
|
||
shell: test -f /var/run/reboot-required && echo "需要重启" || echo "无需重启"
|
||
register: reboot
|
||
- name: 更新完成
|
||
debug: msg="更新完成,{{ reboot.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "磁盘空间清理", Description: "清理日志、临时文件、Docker 缓存、包管理器缓存", Category: "maintenance", Tags: `["磁盘","清理","维护"]`, Content: `---
|
||
- name: 磁盘空间清理
|
||
hosts: all
|
||
become: yes
|
||
tasks:
|
||
- name: 清理前磁盘使用
|
||
shell: df -h / | awk 'NR==2{print "使用率:",$5,"可用:",$4}'
|
||
register: before
|
||
- name: 清理30天前日志
|
||
shell: find /var/log -type f \( -name "*.log" -o -name "*.gz" \) -mtime +30 -delete 2>/dev/null; echo "done"
|
||
- name: 清理临时文件
|
||
shell: find /tmp /var/tmp -type f -mtime +7 -delete 2>/dev/null; echo "done"
|
||
- name: 清理 journald (保留3天)
|
||
shell: journalctl --vacuum-time=3d 2>/dev/null || true
|
||
- name: 清理包管理器缓存
|
||
shell: (apt-get clean 2>/dev/null || yum clean all 2>/dev/null || dnf clean all 2>/dev/null); echo "done"
|
||
- name: 清理 Docker (如有)
|
||
shell: docker system prune -af 2>/dev/null || true
|
||
- name: 清理后磁盘使用
|
||
shell: df -h / | awk 'NR==2{print "使用率:",$5,"可用:",$4}'
|
||
register: after
|
||
- name: 清理摘要
|
||
debug: msg="清理前 - {{ before.stdout }} | 清理后 - {{ after.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "服务状态巡检", Description: "检查关键服务状态,自动启动已停止的服务", Category: "maintenance", Tags: `["服务","巡检","重启"]`, Content: `---
|
||
- name: 服务状态巡检
|
||
hosts: all
|
||
become: yes
|
||
vars:
|
||
services: [nginx, docker, sshd, cron, rsyslog]
|
||
tasks:
|
||
- name: 收集服务状态
|
||
service_facts:
|
||
- name: 检查并启动服务
|
||
service:
|
||
name: "{{ item }}"
|
||
state: started
|
||
loop: "{{ services }}"
|
||
when: item in ansible_facts.services
|
||
- name: 显示服务状态
|
||
debug:
|
||
msg: "{{ item }}: {{ ansible_facts.services[item].state if item in ansible_facts.services else '未安装' }}"
|
||
loop: "{{ services }}"
|
||
- name: 检查关键端口
|
||
shell: ss -tlnp | awk '{print $4}' | grep -E ':22$|:80$|:443$'
|
||
register: ports
|
||
- name: 端口监听
|
||
debug: msg="监听端口: {{ ports.stdout_lines }}"`, CreatedBy: "system"},
|
||
{Name: "用户与SSH管理", Description: "创建运维用户、配置SSH密钥、禁用root密码登录", Category: "security", Tags: `["用户","SSH","安全"]`, Content: `---
|
||
- name: 用户与SSH管理
|
||
hosts: all
|
||
become: yes
|
||
vars:
|
||
ops_user: devops
|
||
ops_ssh_key: ""
|
||
tasks:
|
||
- name: 创建运维用户
|
||
user: name={{ ops_user }} shell=/bin/bash groups=sudo append=yes state=present
|
||
- name: 配置SSH密钥
|
||
authorized_key: user={{ ops_user }} key="{{ ops_ssh_key }}" state=present
|
||
when: ops_ssh_key != ""
|
||
- name: sudo免密
|
||
copy: content="{{ ops_user }} ALL=(ALL) NOPASSWD:ALL" dest=/etc/sudoers.d/{{ ops_user }} validate="visudo -cf %s"
|
||
- name: 禁用root密码登录
|
||
lineinfile: path=/etc/ssh/sshd_config regexp="^PermitRootLogin" line="PermitRootLogin prohibit-password"
|
||
notify: reload sshd
|
||
- name: 可登录用户列表
|
||
shell: awk -F: '$$7 !~ /(false|nologin)/ {print $$1}' /etc/passwd
|
||
register: users
|
||
- name: 用户摘要
|
||
debug: msg="可登录用户: {{ users.stdout_lines }}"
|
||
handlers:
|
||
- name: reload sshd
|
||
service: name=sshd state=reloaded`, CreatedBy: "system"},
|
||
{Name: "Nginx配置检查", Description: "检查Nginx语法、显示虚拟主机、执行重载", Category: "configuration", Tags: `["Nginx","配置","Web"]`, Content: `---
|
||
- name: Nginx配置管理
|
||
hosts: all
|
||
become: yes
|
||
tasks:
|
||
- name: Nginx状态
|
||
shell: nginx -v 2>&1 || echo "未安装"
|
||
register: nginx_ver
|
||
- name: 配置语法检查
|
||
shell: nginx -t 2>&1
|
||
register: nginx_test
|
||
when: "'nginx' in nginx_ver.stdout"
|
||
- name: 列出站点
|
||
shell: ls /etc/nginx/sites-enabled/ 2>/dev/null || ls /etc/nginx/conf.d/ 2>/dev/null
|
||
register: sites
|
||
- name: 重载Nginx
|
||
service: name=nginx state=reloaded
|
||
when: nginx_test.stdout is defined and 'successful' in nginx_test.stdout
|
||
- name: 检查监听端口
|
||
shell: ss -tlnp | grep nginx
|
||
register: nginx_ports
|
||
- name: 配置摘要
|
||
debug:
|
||
msg:
|
||
- "版本: {{ nginx_ver.stdout }}"
|
||
- "站点: {{ sites.stdout_lines }}"
|
||
- "端口: {{ nginx_ports.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "SSL证书检查", Description: "检查本地证书和HTTPS证书到期时间", Category: "security", Tags: `["SSL","证书","HTTPS"]`, Content: `---
|
||
- name: SSL证书到期检查
|
||
hosts: all
|
||
vars:
|
||
domains: []
|
||
tasks:
|
||
- name: 本地证书检查
|
||
shell: |
|
||
for f in /etc/nginx/ssl/*.crt /etc/letsencrypt/live/*/fullchain.pem 2>/dev/null; do
|
||
[ -f "$$f" ] && echo "$$f: $$(openssl x509 -enddate -noout -in "$$f" | cut -d= -f2)"
|
||
done
|
||
register: local_certs
|
||
- name: 远程HTTPS检查
|
||
shell: |
|
||
for d in {% for d in target_hosts.split(',') %}{{ d }} {% endfor %}; do
|
||
expiry=$$(echo | openssl s_client -servername "$$d" -connect "$$d:443" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
|
||
[ -n "$$expiry" ] && echo "$$d: $$expiry"
|
||
done
|
||
register: remote_certs
|
||
when: target_hosts is defined
|
||
- name: 证书到期列表
|
||
debug:
|
||
msg:
|
||
- "本地: {{ local_certs.stdout_lines }}"
|
||
- "远程: {{ remote_certs.stdout_lines | default([]) }}"`, CreatedBy: "system"},
|
||
{Name: "配置文件备份", Description: "备份关键配置到压缩包", Category: "backup", Tags: `["备份","配置"]`, Content: `---
|
||
- name: 配置备份
|
||
hosts: all
|
||
become: yes
|
||
vars:
|
||
backup_dir: "/tmp/config-backup-{{ ansible_date_time.date }}"
|
||
tasks:
|
||
- name: 创建备份目录
|
||
file: path={{ backup_dir }} state=directory mode=0700
|
||
- name: 备份关键配置
|
||
archive:
|
||
path: "{{ item }}"
|
||
dest: "{{ backup_dir }}/{{ item | basename | replace('/', '_') }}.tar.gz"
|
||
loop: [/etc/nginx, /etc/ssh/sshd_config, /etc/fstab, /etc/hosts]
|
||
ignore_errors: yes
|
||
- name: 备份crontab
|
||
shell: crontab -l > {{ backup_dir }}/crontab.txt 2>/dev/null || true
|
||
- name: 备份汇总
|
||
shell: du -sh {{ backup_dir }}
|
||
register: size
|
||
- name: 备份完成
|
||
debug: msg="备份: {{ backup_dir }} ({{ size.stdout.split()[0] }})"`, CreatedBy: "system"},
|
||
{Name: "进程健康诊断", Description: "CPU/内存TOP10、僵尸进程、进程总数", Category: "monitoring", Tags: `["进程","性能","诊断"]`, Content: `---
|
||
- name: 进程健康诊断
|
||
hosts: all
|
||
tasks:
|
||
- name: CPU TOP10
|
||
shell: ps aux --sort=-%cpu | head -11
|
||
register: cpu
|
||
- name: 内存 TOP10
|
||
shell: ps aux --sort=-%mem | head -11
|
||
register: mem
|
||
- name: 僵尸进程
|
||
shell: ps aux | awk '$$8=="Z"' || echo "无僵尸进程"
|
||
register: zombies
|
||
- name: 进程总数
|
||
shell: ps aux | wc -l
|
||
register: total
|
||
- name: 诊断摘要
|
||
debug:
|
||
msg:
|
||
- "总进程: {{ total.stdout }} | 僵尸: {{ zombies.stdout_lines | length }}"
|
||
- "=== CPU TOP10 ==="
|
||
- "{{ cpu.stdout }}"
|
||
- "=== 内存 TOP10 ==="
|
||
- "{{ mem.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "Docker环境清理", Description: "清理未使用容器、镜像、构建缓存和网络", Category: "maintenance", Tags: `["Docker","清理","容器"]`, Content: `---
|
||
- name: Docker环境清理
|
||
hosts: all
|
||
become: yes
|
||
tasks:
|
||
- name: 检查Docker
|
||
shell: docker --version 2>/dev/null || echo "NONE"
|
||
register: dk
|
||
- name: Docker资源概览
|
||
shell: |
|
||
echo "容器:" && docker ps -a --format "table {{.Names}} {{.Status}}" 2>/dev/null
|
||
echo "镜像:" && docker images --format "table {{.Repository}} {{.Size}}" 2>/dev/null
|
||
register: status
|
||
when: dk.stdout != "NONE"
|
||
- name: 清理停止容器
|
||
shell: docker container prune -f
|
||
when: dk.stdout != "NONE"
|
||
- name: 清理未用镜像
|
||
shell: docker image prune -a -f
|
||
when: dk.stdout != "NONE"
|
||
- name: 清理构建缓存
|
||
shell: docker builder prune -f
|
||
when: dk.stdout != "NONE"
|
||
- name: 清理未用网络/卷
|
||
shell: docker system prune -f --volumes
|
||
when: dk.stdout != "NONE"
|
||
- name: 清理完成
|
||
debug: msg="Docker清理完成"`, CreatedBy: "system"},
|
||
{Name: "防火墙规则管理", Description: "查看/配置防火墙规则(ufw/firewalld/iptables)", Category: "security", Tags: `["防火墙","安全","网络"]`, Content: `---
|
||
- name: 防火墙规则管理
|
||
hosts: all
|
||
become: yes
|
||
vars:
|
||
open_ports: [22, 80, 443]
|
||
tasks:
|
||
- name: 防火墙类型
|
||
shell: |
|
||
ufw status 2>/dev/null | grep -q "active" && echo ufw && exit
|
||
firewall-cmd --state 2>/dev/null | grep -q "running" && echo firewalld && exit
|
||
echo iptables
|
||
register: fw
|
||
- name: 当前规则
|
||
shell: |
|
||
case {{ fw.stdout }} in
|
||
ufw) ufw status verbose ;;
|
||
firewalld) firewall-cmd --list-all ;;
|
||
*) iptables -L -n ;;
|
||
esac
|
||
register: rules
|
||
- name: 开放端口(UFW)
|
||
ufw: rule=allow port={{ item }} proto=tcp
|
||
loop: "{{ open_ports }}"
|
||
when: fw.stdout == "ufw"
|
||
- name: 开放端口(firewalld)
|
||
firewalld: port={{ item }}/tcp permanent=yes state=enabled immediate=yes
|
||
loop: "{{ open_ports }}"
|
||
when: fw.stdout == "firewalld"
|
||
- name: 防火墙规则
|
||
debug: msg="{{ rules.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "时间同步检查", Description: "检查NTP/Chrony同步状态和时间一致性", Category: "configuration", Tags: `["时间","NTP","同步"]`, Content: `---
|
||
- name: 时间同步检查
|
||
hosts: all
|
||
become: yes
|
||
tasks:
|
||
- name: 当前时间
|
||
shell: date '+%Y-%m-%d %H:%M:%S %Z'
|
||
register: now
|
||
- name: 同步服务
|
||
shell: |
|
||
systemctl is-active chronyd 2>/dev/null && echo "chronyd" ||
|
||
systemctl is-active ntpd 2>/dev/null && echo "ntpd" ||
|
||
systemctl is-active systemd-timesyncd 2>/dev/null && echo "timesyncd" ||
|
||
echo "未配置"
|
||
register: svc
|
||
- name: 同步状态
|
||
shell: |
|
||
case {{ svc.stdout }} in
|
||
chronyd) chronyc tracking 2>/dev/null | grep -E "Stratum|System time" ;;
|
||
ntpd) ntpq -p 2>/dev/null | tail -5 ;;
|
||
timesyncd) timedatectl show-timesync 2>/dev/null ;;
|
||
*) echo "未同步" ;;
|
||
esac
|
||
register: sync
|
||
- name: 时间摘要
|
||
debug:
|
||
msg: "时间: {{ now.stdout }} | 服务: {{ svc.stdout }} | {{ sync.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "日志轮转配置", Description: "安装logrotate并配置应用日志轮转策略", Category: "configuration", Tags: `["日志","轮转","logrotate"]`, Content: `---
|
||
- name: 日志轮转配置
|
||
hosts: all
|
||
become: yes
|
||
vars:
|
||
log_path: /var/log/app
|
||
keep_days: 30
|
||
tasks:
|
||
- name: 安装logrotate
|
||
package: name=logrotate state=present
|
||
- name: 应用日志轮转
|
||
copy:
|
||
dest: /etc/logrotate.d/app-logs
|
||
content: |
|
||
{{ log_path }}/*.log {
|
||
daily
|
||
rotate {{ keep_days }}
|
||
missingok
|
||
notifempty
|
||
compress
|
||
delaycompress
|
||
sharedscripts
|
||
postrotate
|
||
systemctl reload app 2>/dev/null || true
|
||
endscript
|
||
}
|
||
- name: 测试配置
|
||
shell: logrotate -d /etc/logrotate.conf 2>&1 | head -10
|
||
register: test
|
||
- name: 配置完成
|
||
debug: msg="Logrotate已配置 | {{ test.stdout }}"`, CreatedBy: "system"},
|
||
{Name: "内核参数调优", Description: "优化网络/内存/文件句柄等内核参数", Category: "configuration", Tags: `["内核","优化","性能"]`, Content: `---
|
||
- name: 内核参数调优
|
||
hosts: all
|
||
become: yes
|
||
tasks:
|
||
- name: 网络参数优化
|
||
sysctl:
|
||
name: "{{ item.name }}"
|
||
value: "{{ item.value }}"
|
||
state: present
|
||
reload: yes
|
||
loop:
|
||
- { name: net.core.somaxconn, value: '65535' }
|
||
- { name: net.ipv4.tcp_tw_reuse, value: '1' }
|
||
- { name: net.ipv4.tcp_fin_timeout, value: '30' }
|
||
- name: 内存参数
|
||
sysctl: name=vm.swappiness value=10 state=present reload=yes
|
||
- name: 文件句柄
|
||
sysctl: name=fs.file-max value=655350 state=present reload=yes
|
||
- name: limits.conf
|
||
pam_limits:
|
||
domain: '*'
|
||
limit_type: "{{ item.t }}"
|
||
limit_item: "{{ item.i }}"
|
||
value: "{{ item.v }}"
|
||
loop:
|
||
- { t: soft, i: nofile, v: '65535' }
|
||
- { t: hard, i: nofile, v: '65535' }
|
||
- name: 调优完成
|
||
debug: msg="内核参数已优化"`, CreatedBy: "system"},
|
||
{Name: "网络连通性诊断", Description: "DNS解析、Ping延迟、端口检测、路由追踪", Category: "monitoring", Tags: `["网络","诊断","连通性"]`, Content: `---
|
||
- name: 网络诊断
|
||
hosts: all
|
||
vars:
|
||
targets: [8.8.8.8, 1.1.1.1]
|
||
domains: [baidu.com]
|
||
tasks:
|
||
- name: DNS解析
|
||
shell: |
|
||
for d in {% for d in domains %}{{ d }} {% endfor %}; do
|
||
echo "$$d: $$(nslookup $$d 2>/dev/null | awk '/^Address/{print $$2}' | tail -1 || echo 'FAIL')"
|
||
done
|
||
register: dns
|
||
- name: Ping延迟
|
||
shell: ping -c 2 -W 2 {{ item }} 2>&1 | tail -1
|
||
loop: "{{ targets }}"
|
||
register: ping
|
||
- name: 端口检测
|
||
shell: timeout 2 bash -c "echo >/dev/tcp/{{ item }}/53" 2>/dev/null && echo "{{ item }}:53 OK" || echo "{{ item }}:53 FAIL"
|
||
loop: "{{ targets }}"
|
||
register: port
|
||
- name: 诊断结果
|
||
debug:
|
||
msg:
|
||
- "=== DNS ==="
|
||
- "{{ dns.stdout_lines }}"
|
||
- "=== Ping ==="
|
||
- "{{ ping.results | map(attribute='stdout') }}"
|
||
- "=== 端口 ==="
|
||
- "{{ port.results | map(attribute='stdout') }}"`, CreatedBy: "system"},
|
||
}
|
||
if err := db.Create(&playbooks).Error; err != nil {
|
||
zap.L().Warn("failed to seed playbook templates", zap.Error(err))
|
||
} else {
|
||
zap.L().Info("seeded 15 Ansible playbook templates (deprecated — replaced by remote exec)")
|
||
}
|
||
}
|
||
|
||
// 7. 种子工具配置(kubectl/maven/node/ansible/git/docker)// Logan.Li
|
||
ti := toolinstaller.New(db)
|
||
ti.SeedTools()
|
||
}
|