技术指南
Linux 新连接失败:区分临时端口、conntrack 与监听队列
从新连接错误与计数增量出发,区分临时端口、conntrack 和 SYN/accept 队列耗尽,再做单变量恢复。
已有连接仍能传输、但新连接开始超时或报错时,先把故障分成三条独立路径:客户端是否还能分配本地临时端口,状态防火墙是否还能创建 conntrack 条目,服务端是否还能把新连接放进 SYN/accept 队列。三者都发生在连接建立阶段,却有不同作用域、计数器和修复方法;`TIME_WAIT` 很多、连接数很高或一次 `curl` 失败都不能单独定案。
用症状和增量把故障分流
| 观察 | 更接近哪一层 | 需要的确认 |
|---|---|---|
| 应用 `connect()` 返回 EADDRNOTAVAIL,且本地端口范围内连接持续增长 | 出站临时端口分配 | 同一网络命名空间的端口范围、保留端口、四元组分布和应用 errno |
| `nf_conntrack_count` 接近 `nf_conntrack_max`,内核同窗出现 table full | Netfilter 连接跟踪 | 计数增量、状态分布、流量来源与防火墙是否实际启用 conntrack |
| 监听存在,但 `TcpExtListenOverflows`/`ListenDrops` 在失败窗增长 | 服务端监听队列或 accept 不及时 | 目标 listener 的 Recv-Q/Send-Q、应用 backlog、CPU/阻塞与两次计数差 |
| ECONNREFUSED、ENETUNREACH、TLS 或 DNS 错误,以上计数不增长 | 不是这三种耗尽的直接证据 | 回到监听、路由、防火墙、名称解析和应用协议层 |
固定 UTC、目标和网络命名空间
umask 077
stamp=$(date --utc +%Y%m%dT%H%M%SZ)
evidence="new-connect-$stamp"
install -d -m 0700 "$evidence"
{
date --utc --iso-8601=seconds
uname -r
printf 'netns='; readlink /proc/$/ns/net
ip -brief address
ip route get 203.0.113.10
} >"$evidence/context.txt"
ss -s >"$evidence/ss-summary.txt"
ss -Htan >"$evidence/tcp-sockets.txt"网络命名空间隔离设备、协议栈、路由、防火墙、`/proc/net`、部分 `/proc/sys/net` 和端口。宿主机、容器、sidecar 或独立 netns 的计数不能混用;先从报错进程读取 `/proc/<PID>/ns/net`,再在同一命名空间执行 `ss`、`nstat` 和 sysctl 读取。
出站失败先证明临时端口分配耗尽
sysctl net.ipv4.ip_local_port_range \
net.ipv4.ip_local_reserved_ports \
net.ipv4.ip_unprivileged_port_start
ss -Htan \
| awk '$1 ~ /^(ESTAB|TIME-WAIT|SYN-SENT|FIN-WAIT-1|FIN-WAIT-2|CLOSE-WAIT)$/ {print $1, $4, $5}' \
| sort | uniq -c | sort -nr | head -n 80Linux 对未显式绑定的 IPv4 socket 会从 `ip_local_port_range` 选择可用本地端口,`ip_local_reserved_ports` 还会排除保留项。`connect(2)` 在无法分配临时端口时可返回 `EADDRNOTAVAIL`。端口是否可复用取决于本地地址、远端地址与端口、协议和 socket 选项,所以不能用 `TIME_WAIT` 总数直接减去范围宽度;应按应用、源地址和目标四元组查看分布,并保存真实 errno。
conntrack 要看 count/max 与同窗证据
for name in nf_conntrack_count nf_conntrack_max nf_conntrack_buckets; do
path="/proc/sys/net/netfilter/$name"
if test -r "$path"; then printf '%s=' "$name"; cat "$path"; else printf '%s=unavailable\n' "$name"; fi
done
command -v conntrack >/dev/null && sudo conntrack -S
command -v conntrack >/dev/null && sudo conntrack -L -o extended 2>/dev/null \
| awk '{print $1}' | sort | uniq -c | sort -nr
journalctl -k --since '-15 minutes' --no-pager \
| grep -Ei 'nf_conntrack|table full|dropping packet'内核把 `nf_conntrack_count` 定义为当前分配条目数,把 `nf_conntrack_max` 定义为允许的最大条目数。接近上限只是容量风险;要把失败归因于 conntrack,还需要失败窗口内的计数、内核丢弃信息或 conntrack 统计,以及流量来源。未加载 Netfilter conntrack、绕过状态跟踪或位于另一命名空间时,文件缺失不是零使用。
入站失败检查两级监听队列
port=443
ss -Hlnpt "sport = :$port"
ss -Htan state syn-recv "sport = :$port"
nstat -az TcpExtListenOverflows TcpExtListenDrops TcpPassiveOpens TcpAttemptFails
sysctl net.core.somaxconn net.ipv4.tcp_max_syn_backlogLinux 的 `listen()` backlog 限制已经完成握手、等待应用 `accept()` 的连接;内核会用 `somaxconn` 静默截断应用请求的更大值。未完成握手的 SYN_RECV 队列由 `tcp_max_syn_backlog` 约束。内核文档说明 accept queue 满时 `ListenOverflows` 与 `ListenDrops` 会增长,而 `ListenDrops` 也可能因内存分配失败等原因单独增加。因此只看当前 Recv-Q、只看 sysctl 或只看累计 drops 都不够。
按根因做一个最小缓解
| 根因 | 优先动作 | 扩大容量前必须证明 |
|---|---|---|
| 应用无界出站连接或重试 | 限并发、启用连接池/keep-alive、退避并停止失败扇出 | 目标端依赖允许复用,连接生命周期与超时正确 |
| 临时端口确实耗尽 | 减少 churn,分散目标或增加经规划的源地址 | 范围、保留端口、NAT 与安全策略没有冲突 |
| conntrack 被异常流量占满 | 限流或隔离来源,修复不合理超时/重试 | 条目状态和来源分布解释增长,宿主内存有明确预算 |
| 应用 accept 不及时 | 恢复 worker、解除阻塞、修复事件循环或依赖 | 应用 backlog、somaxconn 与 SYN 队列的实际瓶颈已分别确认 |
只有最小动作让对应计数停止增长且真实新连接恢复,因果链才成立。修改 `ip_local_port_range`、`nf_conntrack_max`、`somaxconn` 或 `tcp_max_syn_backlog` 都会改变容量与内存/安全边界;先保存原值和配置所有者,在 canary 或维护窗口单项变更,并明确撤销条件。
用新连接、增量和已有连接共同验收
- 已有长连接保持健康,新建 TCP/TLS/应用请求在目标用户路径连续通过,错误类型不再出现。
- 同一观察窗前后的端口分布、conntrack count/max、ListenOverflows/ListenDrops 和应用 accept 指标已保存并计算增量。
- 重试率、连接创建速率和队列深度回到基线,没有把压力转移到 NAT、代理、数据库或上游。
- 容器与宿主分别在正确网络命名空间复核;缺失的字段记录为 unavailable,不填零。
- 候选变更失败、内存压力增加或错误转移时,恢复原 sysctl/应用配置并用相同探针复测。
把容量门槛绑定到速率和作用域
监控新连接成功率与 errno、每秒 active/passive opens、重试率、端口与目标分布、conntrack count/max 比例及增量、监听 overflow/drop 增量和应用 accept 延迟。部署、流量模型、NAT、容器网络、内核或代理版本变化后重新建立基线;不要维护一组脱离命名空间、内存预算和应用 backlog 的通用 sysctl 大数。
本文命令在 2026-08-19 按 Linux 内核、Linux man-pages、iproute2 与 Netfilter 当前文档完成语义和语法审阅,没有在生产机制造端口耗尽、填满 conntrack、压满监听队列或修改 sysctl。发行版补丁、内核/iproute2 版本、IPv6、NAT、容器网络、eBPF/代理和托管防火墙会改变可见字段与故障边界;执行前先确认实际进程、网络命名空间、独立控制台和配置所有者。