Apache HTTP Server 完全指南 / 监控与状态
监控与状态
有效的监控是保障 Apache 服务器稳定运行的关键。本章介绍 mod_status、服务器监控工具和 Prometheus 集成。
1. mod_status
1.1 启用 mod_status
# 启用模块
sudo a2enmod status
sudo systemctl reload apache2
1.2 基本配置
# /etc/apache2/mods-available/status.conf
<IfModule mod_status.c>
# 启用扩展状态信息
ExtendedStatus On
# 状态页面
<Location "/server-status">
SetHandler server-status
# 访问控制
Require ip 127.0.0.1
Require ip ::1
Require ip 192.168.1.0/24
</Location>
# 自动刷新页面
<Location "/server-status-auto">
SetHandler server-status
Require ip 127.0.0.1
</Location>
</IfModule>
1.3 状态页面输出
访问 http://localhost/server-status:
Apache Server Status for localhost (via 127.0.0.1)
Server Version: Apache/2.4.58 (Ubuntu)
Server MPM: event
Server Built: 2024-01-10T09:30:00
Current Time: Saturday, 10-May-2026 12:00:00 CST
Restart Time: Saturday, 10-May-2026 06:00:00 CST
Parent Server Config. Generation: 1
Parent Server MPM Generation: 0
Server uptime: 6 hours 0 minutes 0 seconds
Server load: 0.50 0.30 0.20
Total accesses: 100000 - Total Traffic: 2.5 GB
CPU Usage: u5.2 s2.1 cu0 cs0 - .0337% CPU load
4.63 requests/sec - 118.5 kB/second
100 requests currently being processed
500 idle workers
Scoreboard Key:
"_" Waiting for Connection, "S" Starting up, "R" Reading Request
"W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup
"C" Closing connection, "L" Logging, "G" Gracefully finishing
"I" Idle cleanup of worker, "." Open slot with no current process
1.4 JSON 格式输出
# Apache 2.4.35+ 支持 JSON
<Location "/server-status">
SetHandler server-status
Require ip 127.0.0.1
</Location>
# 访问 http://localhost/server-status?auto (机器可读格式)
# 访问 http://localhost/server-status?notable (不含表格)
1.5 自动化监控脚本
#!/bin/bash
# apache-monitor.sh
STATUS_URL="http://localhost/server-status?auto"
# 获取状态信息
STATUS=$(curl -s $STATUS_URL)
# 解析指标
TOTAL_ACCESS=$(echo "$STATUS" | grep "Total Accesses" | awk '{print $3}')
TOTAL_TRAFFIC=$(echo "$STATUS" | grep "Total kBytes" | awk '{print $3}')
CPU_LOAD=$(echo "$STATUS" | grep "CPULoad" | awk '{print $2}')
UPTIME=$(echo "$STATUS" | grep "Uptime" | awk '{print $2}')
REQUESTS_SEC=$(echo "$STATUS" | grep "ReqPerSec" | awk '{print $2}')
BUSY_WORKERS=$(echo "$STATUS" | grep "BusyWorkers" | awk '{print $2}')
IDLE_WORKERS=$(echo "$STATUS" | grep "IdleWorkers" | awk '{print $2}')
echo "=== Apache 状态 $(date) ==="
echo "总访问数: $TOTAL_ACCESS"
echo "总流量: $TOTAL_TRAFFIC KB"
echo "CPU 负载: $CPU_LOAD"
echo "运行时间: $UPTIME 秒"
echo "每秒请求: $REQUESTS_SEC"
echo "繁忙工作线程: $BUSY_WORKERS"
echo "空闲工作线程: $IDLE_WORKERS"
# 告警检查
if (( $(echo "$CPU_LOAD > 80" | bc -l) )); then
echo "警告:CPU 负载过高!" | mail -s "Apache CPU 告警" admin@example.com
fi
if [ "$BUSY_WORKERS" -gt 350 ]; then
echo "警告:繁忙工作线程过多!" | mail -s "Apache 并发告警" admin@example.com
fi
2. 服务器监控
2.1 系统资源监控
# 安装监控工具
sudo apt install htop iotop sysstat
# 监控 CPU
top -p $(pgrep -d',' apache2)
# 监控内存
ps aux | grep apache2 | awk '{sum+=$6} END {print "Apache 内存使用: " sum/1024 " MB"}'
# 监控磁盘 I/O
iotop -p $(pgrep -d',' apache2)
# 监控网络连接
ss -s
ss -tlnp | grep apache2
netstat -an | grep :80 | wc -l
# 监控文件描述符
ls /proc/$(pgrep -o apache2)/fd | wc -l
cat /proc/sys/fs/file-nr
2.2 监控脚本
#!/bin/bash
# apache-health-check.sh
LOG_FILE="/var/log/apache2-health.log"
ALERT_EMAIL="admin@example.com"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> $LOG_FILE
}
# 检查 Apache 进程
if ! pgrep -x apache2 > /dev/null; then
log "ERROR: Apache 未运行"
echo "Apache 已停止!" | mail -s "Apache 宕机告警" $ALERT_EMAIL
systemctl restart apache2
exit 1
fi
# 检查端口响应
if ! curl -s -o /dev/null -w "%{http_code}" http://localhost/ | grep -q "200\|301\|302"; then
log "ERROR: Apache 无法响应 HTTP 请求"
echo "Apache HTTP 无响应!" | mail -s "Apache 响应告警" $ALERT_EMAIL
fi
# 检查错误日志
ERROR_COUNT=$(tail -100 /var/log/apache2/error.log | grep -c "\[error\]\|\[crit\]")
if [ "$ERROR_COUNT" -gt 50 ]; then
log "WARN: 最近错误数过多: $ERROR_COUNT"
fi
# 检查磁盘空间
DISK_USAGE=$(df /var/log | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then
log "WARN: 日志分区磁盘使用率过高: ${DISK_USAGE}%"
echo "日志分区磁盘使用率: ${DISK_USAGE}%" | mail -s "磁盘空间告警" $ALERT_EMAIL
fi
# 检查并发连接数
CONNECTIONS=$(ss -tlnp | grep ":80 " | wc -l)
if [ "$CONNECTIONS" -gt 500 ]; then
log "WARN: 并发连接数过高: $CONNECTIONS"
fi
log "OK: Apache 健康检查通过"
2.3 日志实时监控
# 实时监控访问日志
tail -f /var/log/apache2/access.log | \
awk '{print $1, $9, $7, $NF}' | \
column -t
# 实时统计状态码
tail -f /var/log/apache2/access.log | \
awk '{print $9}' | \
uniq -c
# 实时统计 IP
watch -n 1 "tail -1000 /var/log/apache2/access.log | awk '{print \$1}' | sort | uniq -c | sort -rn | head -10"
# 实时统计 URL
watch -n 1 "tail -1000 /var/log/apache2/access.log | awk '{print \$7}' | sort | uniq -c | sort -rn | head -10"
# 实时监控错误日志
tail -f /var/log/apache2/error.log | grep -E "\[error\]|\[crit\]|\[alert\]"
3. Prometheus 集成
3.1 安装 Apache Exporter
# 下载 apache_exporter
wget https://github.com/Lusitaniae/apache_exporter/releases/download/v0.11.0/apache_exporter-0.11.0.linux-amd64.tar.gz
tar -xzf apache_exporter-0.11.0.linux-amd64.tar.gz
sudo mv apache_exporter-0.11.0.linux-amd64/apache_exporter /usr/local/bin/
# 创建 systemd 服务
sudo tee /etc/systemd/system/apache_exporter.service > /dev/null <<EOF
[Unit]
Description=Apache Exporter
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/apache_exporter --scrape_uri=http://localhost/server-status?auto
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl start apache_exporter
sudo systemctl enable apache_exporter
3.2 Prometheus 配置
# /etc/prometheus/prometheus.yml
scrape_configs:
- job_name: 'apache'
scrape_interval: 15s
static_configs:
- targets: ['localhost:9117']
labels:
instance: 'web-server-1'
# 多个 Apache 实例
# - targets: ['server1:9117', 'server2:9117']
3.3 Grafana 仪表板
{
"dashboard": {
"title": "Apache 监控",
"panels": [
{
"title": "每秒请求数",
"type": "graph",
"targets": [
{
"expr": "rate(apache_accesses_total[5m])",
"legendFormat": "Requests/sec"
}
]
},
{
"title": "繁忙工作线程",
"type": "gauge",
"targets": [
{
"expr": "apache_busy_workers",
"legendFormat": "Busy Workers"
}
]
},
{
"title": "CPU 使用率",
"type": "graph",
"targets": [
{
"expr": "apache_cpu_load",
"legendFormat": "CPU Load"
}
]
},
{
"title": "流量",
"type": "graph",
"targets": [
{
"expr": "rate(apache_sent_kilobytes_total[5m])",
"legendFormat": "KB/sec"
}
]
}
]
}
}
3.4 Prometheus 查询示例
# 每秒请求数
rate(apache_accesses_total[5m])
# 繁忙工作线程百分比
apache_busy_workers / (apache_busy_workers + apache_idle_workers) * 100
# CPU 负载
apache_cpu_load
# 每秒流量 (MB)
rate(apache_sent_kilobytes_total[5m]) / 1024
# 5xx 错误率
rate(apache_scoreboard{state="error"}[5m])
# 运行时间
apache_uptime_seconds
# 总访问数增长
increase(apache_accesses_total[1h])
4. Nagios/Icinga 监控
4.1 check_apache 插件
# 安装 check_apache
sudo apt install monitoring-plugins
# 使用 check_http
/usr/lib/nagios/plugins/check_http -H localhost -p 80 -u /server-status -e 200
# 检查 SSL
/usr/lib/nagios/plugins/check_http -H example.com -p 443 -S -u / -e 200
# 自定义检查脚本
#!/bin/bash
# check_apache_status.sh
STATUS_URL="http://localhost/server-status?auto"
STATUS=$(curl -s $STATUS_URL)
if [ $? -ne 0 ]; then
echo "CRITICAL: 无法获取 Apache 状态"
exit 2
fi
BUSY=$(echo "$STATUS" | grep "BusyWorkers" | awk '{print $2}')
IDLE=$(echo "$STATUS" | grep "IdleWorkers" | awk '{print $2}')
TOTAL=$((BUSY + IDLE))
if [ "$TOTAL" -eq 0 ]; then
echo "CRITICAL: Apache 无工作线程"
exit 2
fi
USAGE=$((BUSY * 100 / TOTAL))
if [ "$USAGE" -gt 90 ]; then
echo "CRITICAL: 工作线程使用率 ${USAGE}% (繁忙: $BUSY, 空闲: $IDLE)"
exit 2
elif [ "$USAGE" -gt 70 ]; then
echo "WARNING: 工作线程使用率 ${USAGE}% (繁忙: $BUSY, 空闲: $IDLE)"
exit 1
else
echo "OK: 工作线程使用率 ${USAGE}% (繁忙: $BUSY, 空闲: $IDLE)"
exit 0
fi
4.2 Nagios 配置
# /etc/nagios4/conf.d/apache.cfg
define service {
use generic-service
host_name web-server
service_description Apache HTTP
check_command check_http!-p 80 -u / -e 200
}
define service {
use generic-service
host_name web-server
service_description Apache HTTPS
check_command check_http!-p 443 -S -u / -e 200
}
define service {
use generic-service
host_name web-server
service_description Apache Status
check_command check_http!-p 80 -u /server-status -e 200 -s "Server Status"
}
5. ELK Stack 集成
5.1 Filebeat 配置
# /etc/filebeat/filebeat.yml
filebeat.inputs:
- type: filestream
enabled: true
paths:
- /var/log/apache2/access.log
parsers:
- apache:
types:
access: log
fields:
service: apache
type: access
fields_under_root: true
- type: filestream
enabled: true
paths:
- /var/log/apache2/error.log
parsers:
- multiline:
pattern: '^\['
negate: true
match: after
fields:
service: apache
type: error
fields_under_root: true
output.elasticsearch:
hosts: ["localhost:9200"]
index: "apache-%{+yyyy.MM.dd}"
setup.template:
name: apache
pattern: apache-*
5.2 Logstash 配置
# /etc/logstash/conf.d/apache.conf
input {
beats {
port => 5044
}
}
filter {
if [type] == "access" {
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
}
date {
match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
}
geoip {
source => "clientip"
}
useragent {
source => "agent"
target => "user_agent"
}
}
if [type] == "error" {
grok {
match => { "message" => "\[%{WORD:log_level}\] \[client %{IPORHOST:clientip}\] %{GREEDYDATA:error_message}" }
}
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "apache-%{type}-%{+YYYY.MM.dd}"
}
}
6. 告警配置
6.1 告警规则
# Prometheus 告警规则
groups:
- name: apache_alerts
rules:
- alert: ApacheDown
expr: up{job="apache"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Apache 服务停止"
- alert: ApacheHighLoad
expr: apache_cpu_load > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Apache CPU 负载过高"
- alert: ApacheHighBusyWorkers
expr: apache_busy_workers / (apache_busy_workers + apache_idle_workers) > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Apache 工作线程使用率过高"
- alert: ApacheHighErrorRate
expr: rate(apache_accesses_total{code="5xx"}[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "Apache 5xx 错误率过高"
6.2 邮件告警脚本
#!/bin/bash
# apache-alert.sh
SUBJECT="$1"
MESSAGE="$2"
ALERT_EMAIL="admin@example.com"
cat <<EOF | mail -s "$SUBJECT" $ALERT_EMAIL
Apache 告警通知
时间: $(date)
主机: $(hostname)
告警: $SUBJECT
详情:
$MESSAGE
请及时处理!
EOF
7. 性能监控仪表板
7.1 自定义 HTML 仪表板
#!/bin/bash
# generate-dashboard.sh
STATUS=$(curl -s http://localhost/server-status?auto)
cat <<EOF
<!DOCTYPE html>
<html>
<head>
<title>Apache 监控仪表板</title>
<meta http-equiv="refresh" content="30">
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.metric { display: inline-block; margin: 10px; padding: 15px; background: #f0f0f0; border-radius: 5px; }
.metric-value { font-size: 24px; font-weight: bold; }
.metric-label { font-size: 12px; color: #666; }
</style>
</head>
<body>
<h1>Apache 监控仪表板</h1>
<div class="metrics">
<div class="metric">
<div class="metric-value">$(echo "$STATUS" | grep "Total Accesses" | awk '{print $3}')</div>
<div class="metric-label">总访问数</div>
</div>
<div class="metric">
<div class="metric-value">$(echo "$STATUS" | grep "BusyWorkers" | awk '{print $2}')</div>
<div class="metric-label">繁忙线程</div>
</div>
<div class="metric">
<div class="metric-value">$(echo "$STATUS" | grep "IdleWorkers" | awk '{print $2}')</div>
<div class="metric-label">空闲线程</div>
</div>
<div class="metric">
<div class="metric-value">$(echo "$STATUS" | grep "ReqPerSec" | awk '{print $2}')</div>
<div class="metric-label">每秒请求</div>
</div>
<div class="metric">
<div class="metric-value">$(echo "$STATUS" | grep "CPULoad" | awk '{print $2}')%</div>
<div class="metric-label">CPU 负载</div>
</div>
</div>
<p>更新时间: $(date)</p>
</body>
</html>
EOF
8. 业务场景
8.1 生产环境监控栈
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3000:3000"
depends_on:
- prometheus
apache-exporter:
image: lusotycoon/apache-exporter
ports:
- "9117:9117"
command: --scrape_uri=http://host.docker.internal/server-status?auto
8.2 日志分析自动化
#!/bin/bash
# daily-report.sh
LOG="/var/log/apache2/access.log"
DATE=$(date -d "yesterday" +%d/%b/%Y)
REPORT="/var/log/apache2/reports/report-$(date -d "yesterday" +%Y%m%d).txt"
mkdir -p /var/log/apache2/reports
{
echo "=== Apache 日报 $(date -d "yesterday" +%Y-%m-%d) ==="
echo ""
echo "总请求数: $(grep "$DATE" $LOG | wc -l)"
echo "独立 IP 数: $(grep "$DATE" $LOG | awk '{print $1}' | sort -u | wc -l)"
echo ""
echo "状态码分布:"
grep "$DATE" $LOG | awk '{print $9}' | sort | uniq -c | sort -rn
echo ""
echo "Top 10 URL:"
grep "$DATE" $LOG | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "Top 10 IP:"
grep "$DATE" $LOG | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "带宽使用: $(grep "$DATE" $LOG | awk '{sum+=$10} END {print sum/1024/1024 " MB"}')"
} > $REPORT
mail -s "Apache 日报 $(date -d "yesterday" +%Y-%m-%d)" admin@example.com < $REPORT
9. 注意事项
- 安全访问:mod_status 页面必须限制访问
- 性能影响:ExtendedStatus 会略微影响性能
- 监控频率:合理设置采集频率,避免过度监控
- 告警阈值:根据实际业务设置合理的告警阈值
- 日志保留:监控日志也需要定期清理
10. 扩展阅读
11. 总结
有效的 Apache 监控包括:
- mod_status:内置状态监控
- 系统监控:CPU、内存、磁盘、网络
- Prometheus:时序数据采集和告警
- 日志分析:ELK Stack 或自定义脚本
- 告警通知:及时发现和处理问题
建立完善的监控体系是保障服务稳定的关键。