怎样用脚本自动摘除故障后端?

wen 实用脚本 2

本文目录导读:

怎样用脚本自动摘除故障后端?

  1. 核心逻辑
  2. 场景一:使用 Nginx + OpenResty(或 Lua)动态摘除
  3. 场景二:使用 HAProxy(通过 Socket 动态摘除)
  4. 场景三:云服务商负载均衡(如阿里云SLB、腾讯云CLB)
  5. 场景四:Kubernetes(Service + Endpoint)
  6. 场景五:自研网关 / 配置中心(如 Consul、Etcd)
  7. 重要注意事项(防坑指南)

要用脚本自动摘除故障后端,通常需要结合健康检查负载均衡器/网关的API,具体实现方式取决于你的技术栈(如 Nginx、HAProxy、Kubernetes、云服务商CLB、自研网关等)。

以下是针对不同场景的通用思路和脚本示例(以 ShellPython 为主):

核心逻辑

  1. 健康检查:对后端服务进行 HTTP/TCP 探测。
  2. 判断故障:连续失败N次(例如3次)则判为故障。
  3. 自动摘除:调用负载均衡的 API 或修改配置文件,将该后端从上游列表移除。
  4. 通知/记录:发送告警或写入日志。

使用 Nginx + OpenResty(或 Lua)动态摘除

Nginx 原生不支持运行时 API 修改 upstream,但可以通过 OpenRestynginx-upsync 模块配合 Consul 实现动态摘除。

方案:写一个 Shell 脚本定时探测,并调用 Consul API 注销节点

#!/bin/bash
# 脚本:health_check_remove.sh
# 作用:检测后端服务健康,不健康则从 Consul(或直接修改 upstream)摘除
UPSTREAM_NAME="backend_servers"
CHECK_URL="http://192.168.1.10:8080/health"
SERVICE_ID="backend-1"
# 检测函数
check_health() {
    local url=$1
    # curl 探测,只获取 HTTP 状态码(-o /dev/null -s -w "%{http_code}")
    status=$(curl -o /dev/null -s -w "%{http_code}" --connect-timeout 3 --max-time 5 "$url")
    if [ "$status" -eq 200 ]; then
        return 0
    else
        return 1
    fi
}
# 摘除函数(以 Consul 为例)
deregister_consul() {
    local service_id=$1
    # 调用 Consul 的注销 API
    curl -X PUT "http://localhost:8500/v1/agent/service/deregister/$service_id"
    echo "[$(date)] 摘除 $service_id 从 Consul"
}
# 主逻辑
if ! check_health "$CHECK_URL"; then
    deregister_consul "$SERVICE_ID"
    # 可在此发送告警(如钉钉、邮件)
else
    echo "[$(date)] 服务正常"
fi

配合定时任务 (crontab):

*/1 * * * * /path/to/health_check_remove.sh >> /var/log/remove_backend.log 2>&1

使用 HAProxy(通过 Socket 动态摘除)

HAProxy 支持通过 Unix SocketStats Socket 在运行时动态启用/禁用服务器。

#!/bin/bash
# 脚本:haproxy_remove.sh
# 依赖命令:socat (需要安装)
HAPROXY_SOCK="/var/run/haproxy/admin.sock"
BACKEND_NAME="mybackend"
SERVER_NAME="web1"
CHECK_URL="http://192.168.1.11:80/health"
check_health() {
    local url=$1
    # 探测 3 次,连续失败才判为故障(避免误判)
    for i in {1..3}; do
        status=$(curl -o /dev/null -s -w "%{http_code}" --connect-timeout 2 "$url")
        if [ "$status" == "200" ]; then
            return 0 # 立即返回健康
        fi
        sleep 1
    done
    return 1 # 3次都失败,判为故障
}
if ! check_health "$CHECK_URL"; then
    # 通过 socat 发送 HAProxy 命令,禁用该服务器
    echo "disable server $BACKEND_NAME/$SERVER_NAME" | socat stdio "$HAPROXY_SOCK"
    echo "[$(date)] HAProxy 摘除 $BACKEND_NAME/$SERVER_NAME"
    # 可选:发送告警
else
    # 如果之前被禁用了,可以自动恢复(也可以另写脚本恢复)
    # echo "enable server $BACKEND_NAME/$SERVER_NAME" | socat stdio "$HAPROXY_SOCK"
    echo "[$(date)] 服务正常"
fi

云服务商负载均衡(如阿里云SLB、腾讯云CLB)

云厂商一般提供 SDKCLI 来操作后端服务器,示例使用阿里云 CLI(需配置 AccessKey)。

前提: 安装 aliyun CLI 并配置凭证。

#!/bin/bash
# 脚本:alicloud_remove_backend.sh
REGION="cn-hangzhou"
SLB_ID="lb-xxxxx"
BACKEND_SERVER_IP="192.168.1.20"
BACKEND_PORT=80
CHECK_URL="http://192.168.1.20:80/health"
check_health() {
    local url=$1
    status=$(curl -o /dev/null -s -w "%{http_code}" --connect-timeout 3 "$url")
    if [ "$status" == "200" ]; then
        return 0
    fi
    return 1
}
if ! check_health "$CHECK_URL"; then
    # 调用阿里云 CLI 摘除后端服务器
    aliyun slb RemoveBackendServers \
        --RegionId $REGION \
        --LoadBalancerId $SLB_ID \
        --BackendServers "[{\"ServerId\":\"$BACKEND_SERVER_IP\",\"Port\":$BACKEND_PORT}]"
    # 也可以使用 Python SDK 更灵活
    echo "[$(date)] 从 SLB 摘除 $BACKEND_SERVER_IP:$BACKEND_PORT"
else
    echo "[$(date)] 健康检查通过"
fi

Kubernetes(Service + Endpoint)

如果你使用 K8s,故障后端通常由 PodlivenessProbereadinessProbe 自动处理,Pod 不健康,K8s 会自动将其从 Service 的 Endpoint 摘除。

但如果需要主动摘除一个节点(例如机器故障),可以用脚本:

#!/bin/bash
# 脚本:k8s_remove_pod.sh
# 通过 patch 方式将 Pod 标记为不可调度或者删除
NAMESPACE="default"
POD_NAME="my-app-xxxxx"
# 摘除方式1:直接删除 Pod(K8s 会重新创建,但新 Pod 可能仍在故障节点)
# kubectl delete pod $POD_NAME -n $NAMESPACE
# 摘除方式2:给节点打污点,驱逐所有 Pod(更彻底)
# kubectl taint nodes node1 key=value:NoSchedule
# 摘除方式3:通过修改 Service 的 Endpoint(不建议手动操作,应依靠 readiness probe)
# kubectl patch svc my-service -p '{"spec":{"externalTrafficPolicy":"Local"}}'
echo "[$(date)] 执行 K8s 摘除逻辑"

最佳实践: 在 K8s 中,不要用外部脚本来摘除 Pod(除非是临时维护),应该依赖于 Kubelet 的探活,如果探活失败,Pod 会被自动踢出 Service。


自研网关 / 配置中心(如 Consul、Etcd)

如果你的网关从 ConsulEtcd 读取服务列表,可以写一个 Watch 脚本。

Python 脚本示例(使用 Consul 库):

import requests
import consul
def check_and_remove():
    c = consul.Consul()
    service_name = "my-backend"
    check_url = "http://192.168.1.10:8080/health"
    try:
        r = requests.get(check_url, timeout=3)
        if r.status_code != 200:
            # 从 Consul 注销
            c.agent.service.deregister(f"{service_name}-10")
            print(f"[{datetime.now()}] 摘除 {service_name}-10")
    except:
        # 网络异常也视为故障
        c.agent.service.deregister(f"{service_name}-10")
        print(f"[{datetime.now()}] 异常,摘除 {service_name}-10")
if __name__ == "__main__":
    import time
    while True:
        check_and_remove()
        time.sleep(5)

重要注意事项(防坑指南)

  1. 避免误判:不要一次失败就摘除,建议连续失败N次(如3次,间隔2秒)再执行摘除。
  2. 防止脑裂:如果脚本本身所在节点网络异常,可能会误判所有后端为故障,考虑多节点探测只摘除探测失败的特定IP
  3. 幂等性:摘除操作应能重复执行不影响结果(例如再次摘除已摘除的节点不报错)。
  4. 恢复机制:故障恢复后,需要自动重新加入,通常可以另写一个脚本,间隔一段时间对已摘除节点进行恢复探测,或手动运维。
  5. 日志和告警:每次摘除动作一定要记录日志,并发送到监控系统(如Prometheus、告警群)。
环境 推荐摘除方式 关键工具/API
Nginx (静态配置) 手动或通过 Consul 动态更新 nginx -s reload + Consul
Nginx + OpenResty 直接调用 Lua API balancer.set_peer_down(true)
HAProxy 通过 Socket 命令 disable server (socat)
Kubernetes 依赖 readiness probe 不推荐外部脚本直接操作 Pod
云 SLB/CLB 云厂商 CLI/SDK aliyun slb RemoveBackendServers
Consul / Etcd 调用 API 注销服务 consul agent.service.deregister

根据你的实际负载均衡组件(Nginx、HAProxy、K8s、云LB)选择对应方案,如果需要具体某一种的详细实现(如带重试、告警、统计),可以继续追问。

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