当前位置: 首页 > news >正文

高效运维新选择:Shell 脚本自动化生成可视化 Linux 系统健康报告

1. 为什么需要自动化系统健康报告?

每次登录服务器手动检查系统状态的日子该结束了。想象一下,你管理着几十台Linux服务器,每天要重复执行相同的命令查看CPU、内存、磁盘使用情况,这种重复劳动不仅效率低下,还容易遗漏关键指标。我管理服务器集群时就深有体会,直到开始使用Shell脚本自动化生成可视化报告,工作效率提升了至少3倍。

自动化健康报告的核心价值在于把零散的系统信息整合成结构化的可视化文档。传统方式需要依次执行top、free -h、df -h等命令,而脚本可以一次性收集所有关键指标,生成包含以下核心信息的HTML报告:系统基本信息(OS版本、内核版本、运行时间)、硬件配置(CPU型号、内存大小、磁盘空间)、实时性能指标(CPU/内存/磁盘使用率、负载情况)、安全配置(SSH设置、防火墙状态)以及网络信息(IP地址、活跃网卡)。

对于运维团队来说,这种报告最实用的场景是日常巡检和故障排查。上周我们有个服务突然响应变慢,通过对比前后两天的自动化报告,立刻发现是磁盘空间不足导致的,而这个问题在传统检查方式下很容易被忽略。报告还特别适合向上级汇报系统状态,毕竟漂亮的HTML表格比黑底白字的命令行输出直观多了。

2. 基础脚本框架搭建

我们先从最基础的脚本框架开始。创建一个名为system_report.sh的文件,用chmod +x赋予执行权限。这个脚本的核心逻辑分为三部分:收集系统信息、生成HTML报告、输出状态标记。下面是最简框架:

#!/bin/bash REPORT_FILE="system_report_$(date +%Y%m%d%H%M).html" generate_html_header() { cat > $REPORT_FILE <<EOF <html> <head> <title>Linux系统健康报告</title> <style> body { font-family: Arial, sans-serif; } .ok { background: #d4edda; } .warning { background: #fff3cd; } .critical { background: #f8d7da; } </style> </head> <body> <h1>$(hostname) 系统状态报告</h1> <p>生成时间: $(date)</p> EOF } generate_html_footer() { cat >> $REPORT_FILE <<EOF </body> </html> EOF } # 主执行流程 generate_html_header # 这里添加信息收集函数 generate_html_footer echo "报告已生成: $REPORT_FILE"

这个框架已经包含了关键元素:动态生成带时间戳的报告文件名、基本的HTML结构和CSS样式。样式表中定义了三种状态颜色:绿色表示正常(ok)、黄色表示警告(warning)、红色表示严重(critical)。实际使用时,我们会用这些颜色高亮显示异常指标。

建议将脚本放在/usr/local/bin目录下,这样任何位置都可以直接执行。我习惯为每类信息创建单独的函数,比如get_cpu_info()、get_memory_usage()等,这样后期维护更方便。曾经有个项目因为所有逻辑都写在一起,修改时差点引发灾难,这个教训让我深刻理解了模块化的重要性。

3. 核心信息采集实现

现在我们来填充具体的系统信息采集功能。首先是CPU和内存信息,这是判断系统负载的关键指标:

get_cpu_info() { local cpu_cores=$(grep -c 'processor' /proc/cpuinfo) local cpu_model=$(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | sed 's/^ //') local load_avg=$(uptime | awk -F 'load average:' '{print $2}') cat >> $REPORT_FILE <<EOF <h2>CPU信息</h2> <table> <tr><th>参数</th><th>值</th><th>状态</th></tr> <tr><td>型号</td><td>$cpu_model</td><td class="ok">正常</td></tr> <tr><td>核心数</td><td>$cpu_cores</td><td class="ok">正常</td></tr> <tr><td>15分钟负载</td><td>$load_avg</td> EOF # 负载状态判断 local load_status=$(echo $load_avg | awk -v cores=$cpu_cores '{ if ($1 > cores*2) print "critical"; else if ($1 > cores) print "warning"; else print "ok"; }') echo "<td class=\"$load_status\">$load_status</td></tr></table>" >> $REPORT_FILE }

内存和磁盘信息的采集也很重要,特别是交换空间(swap)的使用情况:

get_memory_info() { local mem_total=$(free -h | awk '/Mem/{print $2}') local mem_used=$(free -h | awk '/Mem/{print $3}') local mem_percent=$(free | awk '/Mem/{printf("%.1f"), $3/$2*100}') local swap_total=$(free -h | awk '/Swap/{print $2}') local swap_used=$(free -h | awk '/Swap/{print $3}') cat >> $REPORT_FILE <<EOF <h2>内存信息</h2> <table> <tr><th>类型</th><th>总量</th><th>已用</th><th>使用率</th><th>状态</th></tr> <tr><td>物理内存</td><td>$mem_total</td><td>$mem_used</td> <td>$mem_percent%</td> EOF # 内存状态判断 local mem_status="ok" [ ${mem_percent%.*} -gt 90 ] && mem_status="critical" [ ${mem_percent%.*} -gt 70 ] && mem_status="warning" echo "<td class=\"$mem_status\">$mem_status</td></tr>" >> $REPORT_FILE echo "<tr><td>交换空间</td><td>$swap_total</td><td>$swap_used</td>" >> $REPORT_FILE if [ "$swap_used" != "0B" ]; then echo "<td colspan=\"2\" class=\"warning\">警告: 使用了交换空间</td></tr>" >> $REPORT_FILE else echo "<td colspan=\"2\" class=\"ok\">正常</td></tr>" >> $REPORT_FILE fi echo "</table>" >> $REPORT_FILE }

磁盘检查需要特别注意根分区(/)的使用情况,这是最常见的问题点:

get_disk_info() { cat >> $REPORT_FILE <<EOF <h2>磁盘信息</h2> <table> <tr><th>挂载点</th><th>文件系统</th><th>总大小</th><th>已用</th><th>使用率</th><th>状态</th></tr> EOF df -hT | awk 'NR>1 {print $7,$1,$3,$4,$5,$6}' | while read mount fs size used percent avail; do percent_num=${percent%\%} status="ok" [ $percent_num -gt 90 ] && status="critical" [ $percent_num -gt 80 ] && status="warning" echo "<tr><td>$mount</td><td>$fs</td><td>$size</td><td>$used</td>" >> $REPORT_FILE echo "<td>$percent</td><td class=\"$status\">$status</td></tr>" >> $REPORT_FILE done echo "</table>" >> $REPORT_FILE }

4. 高级功能扩展

基础信息采集完成后,我们可以添加更多实用功能。安全配置检查是运维最关心的部分之一:

check_security() { cat >> $REPORT_FILE <<EOF <h2>安全配置</h2> <table> <tr><th>检查项</th><th>当前设置</th><th>推荐值</th><th>状态</th></tr> EOF # SSH Root登录检查 local root_login=$(grep "^PermitRootLogin" /etc/ssh/sshd_config | awk '{print $2}') [ -z "$root_login" ] && root_login="yes" # 默认值 if [ "$root_login" == "yes" ]; then echo "<tr><td>SSH Root登录</td><td>允许</td><td>禁止</td><td class=\"critical\">危险</td></tr>" >> $REPORT_FILE else echo "<tr><td>SSH Root登录</td><td>禁止</td><td>禁止</td><td class=\"ok\">安全</td></tr>" >> $REPORT_FILE fi # 密码认证检查 local pass_auth=$(grep "^PasswordAuthentication" /etc/ssh/sshd_config | awk '{print $2}') [ -z "$pass_auth" ] && pass_auth="yes" # 默认值 if [ "$pass_auth" == "yes" ]; then echo "<tr><td>SSH密码认证</td><td>启用</td><td>禁用</td><td class=\"warning\">警告</td></tr>" >> $REPORT_FILE else echo "<tr><td>SSH密码认证</td><td>禁用</td><td>禁用</td><td class=\"ok\">安全</td></tr>" >> $REPORT_FILE fi # 防火墙状态检查 if command -v ufw >/dev/null; then local firewall_status=$(ufw status | grep "Status: active") elif command -v firewall-cmd >/dev/null; then local firewall_status=$(firewall-cmd --state 2>&1 | grep "running") fi if [ -n "$firewall_status" ]; then echo "<tr><td>防火墙状态</td><td>运行中</td><td>运行中</td><td class=\"ok\">安全</td></tr>" >> $REPORT_FILE else echo "<tr><td>防火墙状态</td><td>未运行</td><td>运行中</td><td class=\"critical\">危险</td></tr>" >> $REPORT_FILE fi echo "</table>" >> $REPORT_FILE }

网络连接和监听端口信息对于排查异常非常有用:

get_network_info() { cat >> $REPORT_FILE <<EOF <h2>网络信息</h2> <h3>IP地址</h3> <table> <tr><th>接口</th><th>IP地址</th><th>MAC地址</th></tr> EOF ip -o addr show | awk '$3 == "inet" || $3 == "inet6"' | while read iface family addr rest; do mac=$(ip link show $iface | awk '/link\/ether/{print $2}') echo "<tr><td>$iface</td><td>$addr</td><td>$mac</td></tr>" >> $REPORT_FILE done echo "</table>" >> $REPORT_FILE # 监听端口 cat >> $REPORT_FILE <<EOF <h3>监听端口</h3> <table> <tr><th>协议</th><th>端口</th><th>服务</th><th>PID/程序</th></tr> EOF ss -tulnp | awk 'NR>1 {print $1,$5,$7}' | while read proto port service; do port_num=$(echo $port | awk -F: '{print $NF}') service_name=$(grep "$port_num/" /etc/services | head -1 | awk '{print $1}') [ -z "$service_name" ] && service_name="unknown" echo "<tr><td>$proto</td><td>$port_num</td><td>$service_name</td><td>${service##*:}</td></tr>" >> $REPORT_FILE done echo "</table>" >> $REPORT_FILE }

5. 定时任务与报告分发

让脚本定期自动运行才能真正解放双手。使用crontab设置每天凌晨2点运行:

# 编辑当前用户的crontab crontab -e # 添加以下内容(假设脚本路径为/usr/local/bin/system_report.sh) 0 2 * * * /usr/local/bin/system_report.sh

生成的报告可以通过多种方式分发。最简单的办法是用Python内置HTTP服务器临时共享:

# 在报告所在目录执行(Python 3) python3 -m http.server 8000 # 然后就可以通过浏览器访问 http://服务器IP:8000/system_report_20230801_0200.html

对于团队协作,我推荐将报告发送到Slack或邮件列表。以下是邮件发送示例:

send_email() { local report_file=$1 local recipient="team@example.com" local subject="$(hostname) 系统健康报告 $(date +%Y-%m-%d)" mutt -s "$subject" -a "$report_file" -- "$recipient" <<EOF 您好,附件是今日系统健康报告,请查收。 此邮件为自动发送,请勿直接回复。 EOF } # 在主函数最后调用 send_email "$REPORT_FILE"

记得先安装mutt邮件客户端(yum/apt install mutt)并配置好SMTP。如果公司有内部IM工具,也可以调用它们的Webhook API发送通知。我在实际项目中集成了飞书机器人,每次报告生成后自动推送到运维群组。

6. 可视化增强技巧

基础的表格已经能清晰展示信息,但我们可以做得更专业。以下是几个提升报告视觉效果的方法:

  1. 添加状态图标:在CSS中添加字体图标库(如Font Awesome),用直观的图标代替文字状态
<style> @import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"); .ok-icon::before { content: "\f058"; color: #28a745; } .warning-icon::before { content: "\f06a"; color: #ffc107; } .critical-icon::before { content: "\f057"; color: #dc3545; } </style>
  1. 添加趋势图表:虽然纯HTML无法实现动态图表,但可以用ASCII艺术图展示简单趋势
# 在报告中添加CPU使用率趋势 echo "<pre>" >> $REPORT_FILE top -bn1 | head -5 | tail -3 | awk '{printf "CPU%d: %3d%% [", NR-1, $9; for(i=0;i<$9/2;i++)printf "="; printf "]\n"}' >> $REPORT_FILE echo "</pre>" >> $REPORT_FILE
  1. 响应式设计:添加媒体查询让报告在手机上也易读
<style> @media (max-width: 600px) { table, th, td { display: block; } th { position: absolute; top: -9999px; } tr { border: 1px solid #ddd; margin-bottom: 10px; } td { border: none; position: relative; padding-left: 50%; } td:before { position: absolute; left: 6px; content: attr(data-label); } } </style>
  1. 添加历史对比:在报告中显示与昨日数据的差异
# 假设昨天的报告还在 yesterday_report=$(find . -name "system_report_$(date -d yesterday +%Y%m%d)*.html" | head -1) if [ -f "$yesterday_report" ]; then # 提取昨天的内存使用率 yesterday_mem=$(grep "内存使用率" "$yesterday_report" | awk -F'[<>]' '{print $5}' | tr -d '%') today_mem=$(free | awk '/Mem/{printf "%.1f", $3/$2*100}') diff_mem=$(echo "$today_mem - $yesterday_mem" | bc) echo "<p>内存使用率变化: " >> $REPORT_FILE if [ $(echo "$diff_mem > 0" | bc) -eq 1 ]; then echo "<span style='color:red'>+${diff_mem}%</span> (比昨天高)" >> $REPORT_FILE else echo "<span style='color:green'>${diff_mem}%</span> (比昨天低)" >> $REPORT_FILE fi echo "</p>" >> $REPORT_FILE fi

7. 实战经验与避坑指南

在实际部署过程中,我总结了几个关键注意事项:

权限问题:脚本需要读取/proc、/sys等系统信息,建议以root权限运行。如果使用普通用户,需要配置sudo权限:

# 在/etc/sudoers.d/下添加文件 User_Alias MONITOR_USERS = user1, user2 Cmnd_Alias MONITOR_CMDS = /usr/bin/df, /usr/bin/free, /usr/bin/uptime MONITOR_USERS ALL=(root) NOPASSWD: MONITOR_CMDS

性能影响:避免在脚本中使用高开销命令。曾经有个脚本每5分钟运行一次vmstat 10 5,导致系统负载明显升高。应该选择轻量级命令,或者降低采集频率。

错误处理:关键命令要检查执行结果。例如:

if ! df -h >/dev/null 2>&1; then echo "<tr><td colspan='3' class='critical'>错误: 无法获取磁盘信息</td></tr>" >> $REPORT_FILE return 1 fi

日志记录:添加简单的日志功能,记录脚本运行情况:

LOG_FILE="/var/log/system_report.log" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE } log "开始生成系统报告" # ...脚本主体... log "报告生成完成: $REPORT_FILE"

安全性:报告可能包含敏感信息,要做好访问控制:

  1. 设置正确的文件权限:chmod 640 $REPORT_FILE
  2. 使用HTTPS访问报告
  3. 添加基础认证到HTTP服务器
  4. 定期清理历史报告(find /path/to/reports -type f -mtime +7 -delete)

跨平台兼容:不同Linux发行版的命令输出可能有差异。例如,获取IP地址信息时:

# 兼容多种方式获取IP if command -v ip >/dev/null; then ip_addr=$(ip addr show | grep 'inet ' | awk '{print $2}') elif command -v ifconfig >/dev/null; then ip_addr=$(ifconfig | grep 'inet ' | awk '{print $2}') else ip_addr="未知" fi

遇到最棘手的问题是在Alpine Linux上运行时,发现它使用musl libc而不是glibc,很多命令参数都不一样。解决方案是为特殊环境添加条件判断:

if [ -f /etc/alpine-release ]; then # Alpine Linux特有处理 mem_total=$(free -m | awk '/Mem/{print $2}') else # 标准Linux处理 mem_total=$(free -m | awk '/Mem/{print $2}') fi
http://www.cnnetsun.cn/news/1371699.html

相关文章:

  • AI建站工具怎么选?从零到一搭建网站的完整流程指南
  • 突破音乐格式枷锁:4大维度重构NCM文件的自由转换技术
  • Python 实战2:新浪新闻静态 + 动态数据采集与清洗全流程
  • Element Plus 2.2.27 的单选框 Radio 组件,选中一个选项后,全部选项都变为选中状态
  • U8g2自定义中文字库实战:从零构建Arduino OLED专属字体
  • YOLOv5后处理全流程拆解:从6万个候选框到最终结果的‘过滤漏斗’
  • Appwrite:开源后端即服务神器,快速构建Web和移动应用
  • nodejs+vue基于springboot的安全生产培训管理系统
  • 【异常】OpenClaw LLM请求超时故障全链路排查与解决方案 LLM request timed out. 网络连接超时了
  • Ubuntu 22.04换国内源保姆级教程(附清华/阿里云/中科大源对比)
  • 股票预测避坑指南:为什么你的Transformer模型跑不过LSTM?(含数据集+超参配置)
  • C++游戏毕设从零起步:新手避坑指南与最小可运行架构实践
  • SpringBoot微服务性能调优实战:SkyWalking链路追踪深度集成指南
  • 【优选算法篇】快速排序模型——从数组划分到快速选择
  • 【数据结构与算法】 二叉树做题
  • YOLOv11模型调参指南:如何让交通灯检测准确率提升15%(附训练曲线分析)
  • 知识图谱在教育领域的5个创新应用:从个性化推荐到自适应学习(含Django实现案例)
  • 3.5%稳增!全球电子引信2032年锚定12.41亿美元
  • 2026 年 8 款安卓数据擦除软件和应用对比
  • ESP32玩转SSD1306 OLED:Adafruit_GFX与u8g2库实战对比(附避坑指南)
  • 3大向量索引终极指南:如何在Milvus中选择最适合你的AI应用方案
  • 如何为Steam打造专属交互体验:SFP工具的深度探索
  • TDengine连接池配置实战:HikariCP与Java应用的高效集成指南
  • 三边封制袋机程序(采用松下PLC及威纶通触摸屏控制,前后双伺服送料,高效温控模块常州汇邦
  • 告别重复劳动:用快马AI自动化你的Python数据分析周报任务
  • Mirage Flow 科学计算应用:与MATLAB协同进行数据分析
  • 【Frida Android】实战篇:Java层Hook进阶——拦截与篡改普通方法参数
  • 基于快马平台提升java八股文复习与知识整理效率
  • 回归商业本质,全面升级“三横一纵”出海战略!
  • springboot项目对接质检管理系统