如何编写检测服务健康状态脚本

wen 实用脚本 29

从入门到生产级实践指南

📖 目录导读

  1. 为什么需要健康检测脚本? —— 故障自愈的第一步
  2. 健康检测核心设计原则 —— 别让检测本身成为故障
  3. 三行代码入门版 —— 最简HTTP检测脚本
  4. 生产级脚本必备要素 —— 超时/重试/告警
  5. 实战:检测MySQL数据库健康 —— 不止是ping
  6. 常见问题FAQ —— 你踩过的坑我们都填平了

❓ 问答引入

问题:当服务器宕机时,你是等用户投诉才知道,还是脚本提前30秒报警?
答案:97%的运维事故本可以通过简单的健康检测脚本避免,本文将手把手教你写出能用于生产环境的健康检测脚本,无论你是刚学Python的运维新人,还是需要监控微服务的架构师。

如何编写检测服务健康状态脚本


为什么需要健康检测脚本?

微服务架构下,一个节点故障可能引发“雪崩效应”,健康检测脚本的核心价值在于:

  • 自动化故障发现:取代人工每分钟刷新监控页面
  • 自愈前置条件:为K8s的livenessProbe或自愈系统提供决策依据
  • 性能基线记录:通过连续检测数据发现潜在退化(如响应时间逐渐变长)

💡 关键认知:检测脚本本身必须比被检测服务更可靠,否则你会收到“监控挂了”的假警报。


健康检测核心设计原则

原则 说明 违反后果
轻量级 脚本启动时间<50ms, 内存占用<10MB 检测进程成为新的性能瓶颈
幂等性 多次执行结果一致,不产生副作用 脚本可能写入脏数据
可观测 执行结果需输出结构化日志 故障复盘时无据可查
容错性 网络抖动时自动重试,但需防“惊群效应” 雪崩式告警淹没值班群

三行代码入门版(HTTP健康检测)

import requests
import sys
try:
    resp = requests.get("https://yourdomain.com/health", timeout=5)
    sys.exit(0 if resp.status_code == 200 else 1)
except Exception:
    sys.exit(1)

这个脚本有什么问题?

  • 没有重试:一次网络抖动就误报
  • 没有详细输出:失败无任何上下文
  • 硬编码URL:无法复用

改进版(带重试和日志):

import requests, sys, time, logging
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
def check_health(url, retries=3):
    session = requests.Session()
    retry = Retry(total=retries, backoff_factor=0.5, status_forcelist=[500, 502, 503])
    adapter = HTTPAdapter(max_retries=retry)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    try:
        resp = session.get(url, timeout=10)
        if resp.status_code == 200:
            logging.info(f"Health check passed: {url}")
            return True
        else:
            logging.error(f"Health check failed: {resp.status_code} | {resp.text[:200]}")
            return False
    except Exception as e:
        logging.error(f"Health check exception: {str(e)}")
        return False
if __name__ == "__main__":
    result = check_health("https://yourdomain.com/health")
    sys.exit(0 if result else 1)

生产级脚本必备要素

超时控制

# 总超时 = 连接超时 + 读取超时
timeout = (3.0, 5.0)  # (connect_timeout, read_timeout)

自定义健康检查逻辑

有时候HTTP 200不代表服务正常,比如返回一个{"status": "degraded"}

def parse_health_response(json_data):
    required_fields = ["status", "version", "uptime_seconds"]
    if all(field in json_data for field in required_fields):
        return json_data["status"] == "healthy"
    return False

多种检测协议支持

服务类型 检测方式 示例命令
HTTP API GET /health requests.get()
TCP端口 端口可达性 socket.connect()
数据库 执行简单查询 SELECT 1
进程 检查PID文件 os.path.exists()

实战:检测MySQL数据库健康

需求

  • 每秒新建连接数是否正常
  • 复制延迟是否超过阈值
  • 关键表是否能正常查询

脚本片段

#!/bin/bash
MYSQL_USER="monitor"
MYSQL_PASS="yourpassword"
MYSQL_HOST="127.0.0.1"
# 1. 基础连接检测
mysqladmin -u$MYSQL_USER -p$MYSQL_PASS -h$MYSQL_HOST ping > /dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "CRITICAL: MySQL is not responding to ping"
    exit 2
fi
# 2. 复制延迟检查 (假设是Slave)
SLAVE_STATUS=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -e "SHOW SLAVE STATUS\G" 2>/dev/null)
SECONDS_BEHIND=$(echo "$SLAVE_STATUS" | grep "Seconds_Behind_Master" | awk '{print $2}')
if [ "$SECONDS_BEHIND" -gt 60 ]; then
    echo "WARNING: Replication lag is $SECONDS_BEHIND seconds"
    exit 1
fi
# 3. 业务健康检查
QUERY_RESULT=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -e "SELECT count(*) FROM orders WHERE status='pending' LIMIT 1" 2>/dev/null)
echo "OK: MySQL healthy, pending orders: $QUERY_RESULT"
exit 0

注意:生产环境不要硬编码密码,应使用~/.my.cnf或密钥管理服务。


常见问题FAQ

Q1: 检测脚本应该用什么语言写?

A: 首选Python,其次是Shell(简单场景)和Go(高并发场景),不推荐使用Perl或Ruby,维护成本高。

Q2: 健康检测频率多少合适?

A:

  • 对时间敏感的服务(如支付): 每10秒一次
  • 普通Web服务: 每30秒一次
  • 后台批处理: 每5分钟一次
    原则:检测频率不能超过服务处理一次检测请求的耗时。

Q3: 检测脚本应该放在哪里执行?

A:

  • 分布式监控:部署在独立的监控节点(如Prometheus的probe节点)
  • 本地监控:作为cron job运行在被监控机器上(需注意自保问题)

Q4: 为什么我的脚本偶尔会挂起?

A: 最常见原因是DNS解析阻塞防火墙丢包,解决方案:

  1. 使用IP直连代替域名
  2. 设置严格的socket超时
  3. select.poll()实现非阻塞IO

Q5: 检测结果如何告警?

A: 推荐与监控系统集成:

# 集成Prometheus PushGateway
from prometheus_client import CollectorRegistry, Gauge, push_to_gateway
registry = CollectorRegistry()
g = Gauge('service_health', '1=healthy, 0=unhealthy', ['service_name'], registry=registry)
g.labels(service_name='api_gateway').set(1 if result else 0)
push_to_gateway('pushgateway.example.com:9091', job='health_check', registry=registry)

编写健康检测脚本的黄金三步

  1. 先理清检测目标:是检测“服务活着”还是“服务正常工作”?后者需要业务层探针。
  2. 从最小代码开始:先用最简单的方式运行起来,再逐步加入重试、超时、日志。
  3. 加入可观测性:每个检测结果必须包含时间戳、状态码、响应时间和错误详情。

最后送一句运维老兵的忠告

“不要信任任何一个你无法解释其失败原因的健康检测脚本。” —— 你的监控系统应该比你更了解系统的弱点。


结合了Google SRE手册、Prometheus官方文档及多家一线互联网公司运维实践,经过重组优化以确保符合当前搜索引擎对原创深度内容的质量要求。*

抱歉,评论功能暂时关闭!