Akvicor
Akvicor
发布于 2026-05-17 / 13 阅读
0
0

Nginx 上游健康检查插件

项目仓库: https://github.com/Akvicor/nginx-healthcheck-module

Nginx 自带的 upstream 失败处理主要依赖请求过程中的被动失败触发,例如连接失败、超时或上游返回错误后再调整 peer 状态。这种机制过于简单,当一个后端已经不可用时,流量可能仍然需要先打到它,Nginx 才能感知失败,而且就算后端一直不可用,一段时间后nginx也会自动将有问题的上游恢复,此时又会有用户访问到有问题的上游。

nginx-healthcheck-module 插件就是为了解决无法主动探测上游的问题,插件会在请求流量之外,主动周期性检查 upstream peer,并把不健康的 peer 标记为异常,并从负载均衡选择中跳过。

nginx-healthcheck-module 插件由 (Akvicor) 维护,修改自 yaoweibin/nginx_upstream_check_module,重点适配 Nginx 1.26+,并聚焦 TCP/UDP 健康检查。

当前维护版本支持

  • HTTP upstream 健康检查

  • Stream upstream 健康检查

  • TCP 检查

  • UDP 检查

  • HTML、CSV、JSON、Prometheus 状态输出

  • 上游检查延迟统计

  • TCP 检查连接复用

不再支持原项目里的 HTTP/FastCGI/MySQL/AJP/SSL hello 等七层检查类型

编译安装

首先你需要下载nginx的源码, 你可以从 GitHubNginx官网 上下载源码

项目仓库中有一个 build.sh 脚本, 里面有从nginx官方源码编译的方式, 你可以参照脚本内的方式编译出nginx的二进制可执行文件, 直接替换掉系统已安装的nginx的二进制可执行文件, 如果不知道当前系统的nginx命令文件的位置, 可以使用 which nginx 命令查询

当然也可以使用下面这个方式, 通过git库直接编译安装(下面这个configure中的参数并不全, 如果想要全部的默认参数看build.sh脚本)

git clone https://github.com/nginx/nginx.git
git clone https://github.com/Akvicor/nginx-healthcheck-module.git

cd nginx
git checkout release-1.26.3
git apply ../nginx-healthcheck-module/nginx_healthcheck_for_nginx_1.26+.patch

./auto/configure --with-stream --add-module=../nginx-healthcheck-module
make
make install

补丁的作用是把健康检查结果接入 Nginx upstream peer 选择逻辑中。也就是说,模块不是只提供一个状态页面,而是真的会影响 round robin、hash、consistent hash、least_conn 等 upstream 负载均衡路径,让 down 状态的 peer 不再被选中。

完整配置示例

worker_processes 1;
error_log logs/error.log info;

events {
    worker_connections 256;
}

http {
    check_shm_size 2m;

    server {
        listen 8081;

        location /health {
            healthcheck_status;
        }

        location /status {
            healthcheck_status json;
        }

        location /metrics {
            healthcheck_status prometheus;
        }

        location / {
            proxy_pass http://tcp-http-backends;
        }
    }

    upstream tcp-http-backends {
        server 127.0.0.1:8082;
        server 127.0.0.2:8082;

        check interval=3000 rise=2 fall=5 timeout=5000 default_down=true type=tcp;
    }
}

stream {
    check_shm_size 2m;

    upstream tcp-cluster {
        server 127.0.0.1:22;
        server 192.168.0.2:22;

        check interval=3000 rise=2 fall=5 timeout=5000 default_down=true type=tcp reuse=on;
        check_keepalive_requests 10;
    }

    server {
        listen 522;
        proxy_pass tcp-cluster;
    }

    upstream udp-cluster {
        server 127.0.0.1:53;
        server 8.8.8.8:53;

        check interval=3000 rise=2 fall=5 timeout=5000 default_down=true type=udp;
    }

    server {
        listen 53 udp;
        proxy_pass udp-cluster;
    }
}

最关键的是 upstream 里的 check 指令

check interval=3000 rise=2 fall=5 timeout=5000 default_down=true type=tcp;

这表示每 3000ms 检查一次,单次检查超时为 5000ms。连续 2 次成功后标记为 up,连续 5 次失败后标记为 down。default_down=true 表示 Nginx 启动后 peer 默认先处于 down,等主动检查成功后再进入可用状态。

TCP 和 UDP 检查语义

TCP 检查会尝试连接 upstream peer。连接成功并且 socket 状态符合预期时,peer 被认为健康;连接失败、超时或 socket 错误会推动 fall 计数增长。

UDP 检查的语义和 TCP 不一样。UDP 没有连接建立过程,模块会发送一段默认 payload,通过接收路径检测 ICMP 错误。如果超时但没有收到 ICMP 错误,当前实现会按成功处理。这一点很重要:UDP 检查更适合发现明确的端口不可达等错误,不等价于完整的应用层协议检查。

TCP复用

默认情况下,TCP 检查连接不会复用。每次检查结束后,连接都会关闭。每一轮检查都会重新和上游建立 TCP 连接。

如果显式配置 reuse=on,模块会复用健康检查连接:

check interval=3000 rise=2 fall=5 timeout=1000 default_down=true type=tcp reuse=on;
check_keepalive_requests 10; # 非必须,省略这行插件会使用默认值10

check_keepalive_requests 控制单条复用连接最多执行多少次检查,默认值是 10。达到次数后,连接会关闭并在后续检查中重新建立。

开启 reuse 后,需要理解一个行为差异:

  • 第一次没有可复用连接时,模块会真正向上游发起 TCP connect。

  • 后续复用同一条连接时,模块不再每轮新建 TCP 连接,而是在保存的 socket 上执行 recv(..., MSG_PEEK)

  • 如果 socket 仍可读,或者返回 EAGAIN,模块认为这条内核维护的连接仍然健康。

  • 如果 peer 关闭连接、socket 出错、检查超时或达到复用次数,连接会被关闭,后续再重新建立。

因此reuse=on 更适合希望降低健康检查连接开销的场景(例如降低流量消耗)。它验证的是既有 TCP 连接是否仍然正常,而不是每个检查周期都验证“新建连接是否成功”。如果你希望每一轮都强制执行一次新的 TCP connect,就保持默认的 reuse=off

当开启 TCP reuse 且 worker 数量大于 1 时,peer 会按 peer_index % worker_processes 分配给特定 worker 检查。这样可以把健康检查连接数量控制在接近 peer 数,而不是 peer 数 * worker_processes

状态接口和延迟统计

模块提供 healthcheck_status 指令暴露状态接口:

location /status {
    healthcheck_status;
}

location /json {
    healthcheck_status json;
}

location /metrics {
    healthcheck_status prometheus;
}

也可以通过 query 参数切换格式:

/status?format=html
/status?format=csv
/status?format=json
/status?format=prometheus
/status?format=json&status=down
/status?format=json&status=up

JSON 输出会按 httpstream 分类展示 peer 状态,典型字段包括:

{
  "index": 0,
  "upstream": "web_backends",
  "name": "127.0.0.1:8081",
  "status": "up",
  "rise": 2,
  "fall": 0,
  "type": "tcp",
  "port": 0,
  "last_delay_ms": 1,
  "avg_delay_ms": 1,
  "min_delay_ms": 1,
  "max_delay_ms": 2
}

这里的延迟字段来自健康检查本身

  • last_delay_ms:最近一次成功检查耗时。

  • avg_delay_ms:成功检查平均耗时。

  • min_delay_ms:成功检查最小耗时。

  • max_delay_ms:成功检查最大耗时。

这些字段对于排查后端抖动很有用。健康状态只告诉你 up 或 down,延迟统计可以进一步暴露“还能连上,但连接变慢”的问题。

Prometheus 输出则更适合接入监控系统,当前包含总数、up/down 数量、generation,以及每个 peer 的 rise、fall、active 指标。

配置建议

如果你是第一次接入,可以从保守配置开始:

check interval=3000 rise=2 fall=5 timeout=1000 default_down=true type=tcp;
  • default_down=true 更适合生产环境,避免 Nginx 启动后把尚未检查成功的后端直接纳入流量。

  • rise 不要设得太低,否则短暂成功可能导致不稳定后端过快回到流量路径。

  • fall 不要设得太低,否则偶发网络抖动可能导致后端频繁上下线。

  • timeout 应该小于业务可接受的失败等待时间。

  • 后端数量很多时,调大 check_shm_size

  • 如果健康检查连接数压力明显,或者健康检查消耗流量过多,再考虑 type=tcp reuse=on

  • UDP 检查不要当作应用层协议健康检查,它只表达当前模块实现下的 UDP/socket 可达性判断。

已经编译好的nginx的debian源

增加的模块

  • nginx_upstream_check_module

  • ngx_http_geoip2_module

增加GPG密钥

curl -fsSL https://cdn.ksyaki.com/debian/nginx-team/public-key.asc \
  | gpg --dearmor --batch --yes -o /usr/share/keyrings/akvicor.gpg

debian 13 源(Trixie)

tee /etc/apt/preferences.d/99-akvicor-nginx.pref >/dev/null <<'EOF'
Package: nginx nginx-* libnginx-mod-*
Pin: origin cdn.ksyaki.com
Pin-Priority: 1001
EOF

echo "deb [signed-by=/usr/share/keyrings/akvicor.gpg] https://cdn.ksyaki.com/debian/nginx-team trixie main" \
  | tee /etc/apt/sources.list.d/akvicor-nginx.list

apt update
apt install nginx

debian 12 源(Bookworm)

tee /etc/apt/preferences.d/99-akvicor-nginx.pref >/dev/null <<'EOF'
Package: nginx nginx-* libnginx-mod-*
Pin: origin cdn.ksyaki.com
Pin-Priority: 1001
EOF

echo "deb [signed-by=/usr/share/keyrings/akvicor.gpg] https://cdn.ksyaki.com/debian/nginx-team bookworm main" \
  | tee /etc/apt/sources.list.d/akvicor-nginx.list

apt update
apt install nginx


评论