
概述
干了十年运维,我发现一个有意思的现象:很多同行在排查网络问题时,喜欢凭经验乱试。ping不通就重启网卡,连接超时就加timeout,服务挂了就重启容器。这种"重启大法"有时候确实能解决问题,但更多时候只是在碰运气。
OSI七层模型这东西,大学课本里都讲过,但说实话,当年我也觉得这玩意儿太理论了,实际工作中用不上。直到有一次,线上出了个诡异的问题:用户反馈访问慢,但服务端日志看着一切正常,监控指标也没啥异常。折腾了大半夜,最后发现是机房交换机的一个端口出了问题,偶发丢包。
从那以后我就明白了,网络问题的排查必须要有体系化的思维。OSI模型不是什么高深的理论,它就是一套分层排查的方法论。你把网络通信拆成七层,从下往上一层层排查,问题自然就能定位出来。
为什么运维必须掌握OSI模型
现在云原生、微服务、Service Mesh这些东西已经成了标配。网络环境比以前复杂了不知道多少倍。以前一台服务器部署一个应用,网络问题相对简单。现在呢,一个请求可能要经过Ingress、Service、Sidecar、Pod,中间还有各种网络策略、服务网格。
复杂归复杂,但网络通信的本质没变。不管你用的是Kubernetes还是传统架构,数据包该怎么封装还是怎么封装,TCP三次握手该怎么做还是怎么做。掌握了OSI模型的底层逻辑,再复杂的网络问题你也能有条不紊地排查。
OSI模型与TCP/IP模型的关系
实际工作中,我们更常用的是TCP/IP四层模型,它把OSI的七层简化成了四层:
OSI七层模型 TCP/IP四层模型 实际对应协议
─────────────────────────────────────────────────────
应用层 (7) ─┐
表示层 (6) ─┼─ 应用层 HTTP、DNS、gRPC
会话层 (5) ─┘
传输层 (4) ─── 传输层 TCP、UDP、QUIC
网络层 (3) ─── 网络层 IP、ICMP
数据链路层(2) ─┐
物理层 (1) ─┘ 网络接口层 以太网、WiFi
但我建议大家还是要理解OSI的七层划分。因为在实际排查问题时,七层模型的划分更细致,能帮你更精准地定位问题所在。比如TLS握手失败,按TCP/IP模型都算应用层的事儿,但按OSI模型来看,这其实是表示层(加密)和会话层(会话建立)的问题。
OSI七层模型详解:运维视角
下面我从运维实战的角度,详细讲讲每一层都在干什么,以及对应的故障排查思路。
第一层:物理层(Physical Layer)
物理层是最底层,负责的是比特流的传输,说白了就是0和1怎么通过物理介质传输出去。
涉及的内容:
网线(双绞线、光纤) 网卡和网口 交换机端口 电气信号、光信号 传输速率、双工模式
运维需要关注的点:
网线是否插好,有没有松动 网口指示灯状态 网卡是否识别,驱动是否正常 链路速率协商是否正确(比如千兆网卡跑成了百兆) 双工模式是否匹配(全双工vs半双工)
物理层的问题往往表现得很诡异。我见过一个案例,某台服务器偶发性地出现网络抖动,丢包率大概在1%左右。查了半天,最后发现是网线水晶头氧化了,接触不良。换了根新网线就好了。
检查物理层状态的命令:
# 查看网卡状态
ip link show
# 查看网卡详细信息(包括速率、双工模式)
ethtool eth0
# 输出示例
Settings for eth0:
Supported ports: [ TP ]
Supported link modes: 10baseT/Half 10baseT/Full
100baseT/Half 100baseT/Full
1000baseT/Full
Speed: 1000Mb/s
Duplex: Full
Link detected: yes
# 查看网卡统计信息(错误包、丢包等)
ethtool -S eth0
# 查看网卡驱动信息
ethtool -i eth0
# 检查网线连接状态
cat /sys/class/net/eth0/carrier
# 1表示连接,0表示断开
# 查看网卡环回测试(部分网卡支持)
ethtool -t eth0 online
物理层故障的典型表现:
网口指示灯不亮或闪烁异常 链路速率异常(千兆变百兆) 大量CRC错误或帧错误 间歇性断网
第二层:数据链路层(Data Link Layer)
数据链路层负责在相邻节点之间传输数据帧。这一层最重要的概念是MAC地址和以太网帧。
涉及的内容:
MAC地址 以太网帧结构 ARP协议(IP到MAC的映射) VLAN(虚拟局域网) 交换机的MAC地址学习和转发 生成树协议(STP)
运维需要关注的点:
ARP表是否正确 是否有ARP欺骗 VLAN配置是否正确 交换机端口是否被阻塞 MAC地址表是否溢出
数据链路层的问题通常发生在局域网内部。最常见的就是ARP相关的问题。
检查数据链路层的命令:
# 查看MAC地址
ip link show eth0 | grep ether
# 查看ARP缓存表
ip neigh show
# 或者用旧命令
arp -a
# 清除ARP缓存
ip neigh flush all
# 手动添加ARP条目(静态绑定)
ip neigh add 192.168.1.100 lladdr 00:11:22:33:44:55 dev eth0
# 查看网桥信息(在容器环境中很有用)
bridge link show
bridge fdb show
# 抓取ARP包
tcpdump -i eth0 arp -nn
# 查看VLAN配置(如果配置了VLAN)
ip -d link show eth0.100
# 查看交换机端口状态(需要在交换机上操作)
# Cisco交换机示例
show mac address-table
show spanning-tree
实战案例:ARP缓存导致的通信故障
前几年遇到过一个问题,某台服务器更换了网卡,MAC地址变了,但同网段的其他服务器上的ARP缓存还是旧的MAC地址。结果就是这台服务器能ping通网关,但ping不通同网段的其他机器。
解决方法很简单,在其他服务器上清除一下ARP缓存,或者等ARP缓存自动过期(一般是几分钟)。但如果是紧急情况,手动清除最快:
# 清除特定IP的ARP缓存
ip neigh del 192.168.1.100 dev eth0
# 或者
arp -d 192.168.1.100
第三层:网络层(Network Layer)
网络层负责端到端的数据包传输,核心就是IP协议。这一层要处理的是IP地址、路由、分片这些事情。
涉及的内容:
IP地址(IPv4、IPv6) 子网划分和CIDR 路由表和路由协议 ICMP协议(ping、traceroute的基础) IP分片和MTU NAT(网络地址转换)
运维需要关注的点:
IP地址配置是否正确 子网掩码是否正确 默认网关是否可达 路由表是否正确 MTU设置是否合适 NAT规则是否生效
网络层是日常运维中打交道最多的一层。IP不通、路由不对、NAT有问题,都属于这一层。
检查网络层的命令:
# 查看IP地址配置
ip addr show
# 查看路由表
ip route show
# 或
route -n
# 查看特定目的IP走哪条路由
ip route get 8.8.8.8
# 添加静态路由
ip route add 10.0.0.0/8 via 192.168.1.1 dev eth0
# 查看NAT规则
iptables -t nat -L -n -v
# 或者用nftables
nft list table nat
# ping测试(ICMP)
ping -c 4 192.168.1.1
# traceroute(跟踪路由路径)
traceroute 8.8.8.8
# 或者用mtr(更好用)
mtr -r -c 10 8.8.8.8
# 查看MTU
ip link show eth0 | grep mtu
# 测试MTU(发送特定大小的包,不允许分片)
ping -M do -s 1472 192.168.1.1
# 如果MTU是1500,去掉IP头20字节和ICMP头8字节,最大负载是1472
# 检查IP转发是否开启
cat /proc/sys/net/ipv4/ip_forward
# 开启IP转发
echo 1 > /proc/sys/net/ipv4/ip_forward
MTU问题深度解析
MTU(Maximum Transmission Unit)是网络层非常重要但经常被忽视的配置。以太网默认MTU是1500字节,但在某些场景下需要调整:
VPN隧道:需要降低MTU来为封装头留空间 VXLAN/GENEVE:同上,需要预留50字节左右 巨型帧(Jumbo Frame):在高性能场景下可以设置到9000
MTU问题最坑的地方在于,小包能通,大包不通。因为小于MTU的包不需要分片,能正常传输;大于MTU的包需要分片,如果路径上有设备不支持或者DF(Don't Fragment)标志被设置,就会丢包。
# 调整MTU
ip link set eth0 mtu 1400
# 检查路径MTU(Path MTU Discovery)
tracepath 8.8.8.8
# 抓包看MTU问题
tcpdump -i eth0 'icmp[0] == 3 and icmp[1] == 4' -nn
# 这个过滤器抓取ICMP "需要分片但DF被设置" 的消息
第四层:传输层(Transport Layer)
传输层负责端到端的连接管理和数据传输控制。主要协议是TCP和UDP,2025年还有QUIC也越来越重要了。
涉及的内容:
TCP协议(三次握手、四次挥手、流控、拥塞控制) UDP协议 QUIC协议(基于UDP,HTTP/3的传输层) 端口号 连接状态(ESTABLISHED、TIME_WAIT等) Socket缓冲区
运维需要关注的点:
端口是否监听 连接状态是否正常 TIME_WAIT是否过多 连接数是否达到上限 TCP参数是否优化 是否有半连接攻击(SYN Flood)
传输层的问题在应用层往往表现为"连接超时"、"连接被拒绝"、"连接被重置"等。
检查传输层的命令:
# 查看监听端口
ss -tlnp
# 或
netstat -tlnp
# 查看所有TCP连接
ss -tan
# 查看连接状态统计
ss -s
# 按状态统计TCP连接
ss -tan | awk '{print $1}' | sort | uniq -c | sort -rn
# 查看特定端口的连接
ss -tan 'sport = :80'
# 查看连接到特定IP的连接
ss -tan 'dst 192.168.1.100'
# 查看TCP连接的详细信息(包括RTT、拥塞窗口等)
ss -ti
# 查看socket统计信息
cat /proc/net/sockstat
# 查看TCP参数
sysctl -a | grep tcp
# 测试端口连通性
nc -zv 192.168.1.100 80
# 或者用telnet
telnet 192.168.1.100 80
# 查看TCP重传统计
netstat -s | grep -i retrans
# 或
ss -ti | grep retrans
TCP连接状态详解
理解TCP连接状态对排查问题非常重要:
状态 描述 常见问题
───────────────────────────────────────────────────────────────
LISTEN 监听状态,等待连接 服务没启动
SYN_SENT 已发送SYN,等待响应 目标不可达
SYN_RECV 收到SYN,已回复SYN+ACK 半连接,可能是攻击
ESTABLISHED 连接已建立 正常
FIN_WAIT_1 已发送FIN,等待ACK 对端没响应
FIN_WAIT_2 收到FIN的ACK,等待对端FIN 对端没关闭
TIME_WAIT 等待2MSL,确保对端收到最后的ACK 高并发短连接场景
CLOSE_WAIT 收到FIN,等待本地关闭 应用没关闭连接(泄漏)
LAST_ACK 已发送FIN,等待最后的ACK 对端没响应
CLOSED 连接已关闭 正常
TIME_WAIT过多的处理
高并发短连接场景(如Nginx反向代理)经常遇到TIME_WAIT过多的问题。TIME_WAIT会占用端口和内存,虽然每个占用的资源不多,但量大了也是问题。
# 查看TIME_WAIT数量
ss -tan state time-wait | wc -l
# 优化TIME_WAIT(注意:这些参数要根据实际情况调整)
# 允许TIME_WAIT状态的socket被重用
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
# 减少TIME_WAIT等待时间(不建议修改,默认60秒是有道理的)
# echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
# 增加本地端口范围
echo"1024 65535" > /proc/sys/net/ipv4/ip_local_port_range
# 增加最大TIME_WAIT数量
echo 262144 > /proc/sys/net/ipv4/tcp_max_tw_buckets
CLOSE_WAIT泄漏问题
CLOSE_WAIT是个危险信号,说明对端已经关闭了连接,但本地应用没有关闭。这通常是应用代码的bug,比如HTTP客户端没有正确关闭响应体。
# 找出CLOSE_WAIT最多的进程
ss -tanp state close-wait | awk '{print $7}' | sort | uniq -c | sort -rn | head
# 查看某个进程的CLOSE_WAIT连接详情
ss -tanp state close-wait | grep "pid=12345"
第五层:会话层(Session Layer)
会话层负责建立、管理和终止会话。在TCP/IP模型中,这一层的功能通常被合并到应用层,但从排查问题的角度,单独理解会话层还是有价值的。
涉及的内容:
会话建立和维护 TLS/SSL握手 身份验证 会话保持(Keep-Alive) 连接池管理
运维需要关注的点:
TLS握手是否成功 证书是否有效 证书链是否完整 Keep-Alive是否正确配置 连接池是否耗尽
检查会话层的命令:
# 测试TLS连接(查看证书信息)
openssl s_client -connect example.com:443 -servername example.com
# 查看证书详情
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -text
# 查看证书过期时间
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
# 验证证书链
openssl s_client -connect example.com:443 -servername example.com -showcerts
# 测试特定TLS版本
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
# 测试支持的加密套件
nmap --script ssl-enum-ciphers -p 443 example.com
# 使用curl测试TLS
curl -vvv https://example.com 2>&1 | grep -A 20 "SSL connection"
TLS证书问题排查
TLS证书问题是运维中非常常见的一类问题。常见的有:
证书过期 证书域名不匹配 证书链不完整 中间证书缺失 客户端不信任CA
# 一个实用的证书检查脚本
#!/bin/bash
HOST=$1
PORT=${2:-443}
echo"=== 检查 $HOST:$PORT 的TLS证书 ==="
# 获取证书信息
cert_info=$(echo | openssl s_client -connect "$HOST:$PORT" -servername "$HOST" 2>/dev/null)
# 检查连接是否成功
if [ $? -ne 0 ]; then
echo"无法建立TLS连接"
exit 1
fi
# 提取证书
cert=$(echo"$cert_info" | openssl x509 2>/dev/null)
# 检查过期时间
echo"--- 证书有效期 ---"
echo"$cert" | openssl x509 -noout -dates
# 检查域名
echo"--- 证书域名 ---"
echo"$cert" | openssl x509 -noout -subject -ext subjectAltName
# 检查颁发者
echo"--- 证书颁发者 ---"
echo"$cert" | openssl x509 -noout -issuer
# 验证证书链
echo"--- 证书链验证 ---"
echo | openssl s_client -connect "$HOST:$PORT" -servername "$HOST" 2>&1 | grep -E "Verify|depth"
第六层:表示层(Presentation Layer)
表示层负责数据的编码、解码、加密、解密、压缩等。这一层在OSI模型中相对独立,但在实际应用中通常与应用层合并。
涉及的内容:
数据编码(UTF-8、Base64等) 数据序列化(JSON、Protocol Buffers、MessagePack) 数据压缩(gzip、brotli、zstd) 数据加密(AES、RSA等)
运维需要关注的点:
字符编码问题 压缩是否生效 序列化/反序列化错误 加密算法兼容性
表示层相关的检查:
# 检查HTTP响应的编码和压缩
curl -H "Accept-Encoding: gzip, deflate, br" -I https://example.com
# 查看响应头中的内容编码
curl -H "Accept-Encoding: gzip" -s -D - https://example.com -o /dev/null | grep -i content-encoding
# 解压gzip响应
curl -H "Accept-Encoding: gzip" -s https://example.com | gunzip
# 检查文件编码
file -bi some_file.txt
# 转换字符编码
iconv -f GBK -t UTF-8 input.txt > output.txt
# 查看二进制数据的十六进制
xxd some_file | head -20
# 或
hexdump -C some_file | head -20
第七层:应用层(Application Layer)
应用层是最接近用户的一层,包含各种应用协议。作为运维,我们需要了解常见的应用层协议。
涉及的内容:
HTTP/HTTPS/HTTP2/HTTP3 DNS FTP/SFTP SMTP/IMAP/POP3 SSH gRPC WebSocket 各种数据库协议(MySQL、PostgreSQL、Redis等)
运维需要关注的点:
HTTP状态码 请求/响应头 DNS解析是否正确 应用协议特定的错误
应用层排查命令:
# HTTP测试
curl -v https://example.com
curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' https://api.example.com/endpoint
# 只看响应头
curl -I https://example.com
# 测试HTTP/2
curl --http2 -I https://example.com
# 测试HTTP/3(需要curl支持)
curl --http3 -I https://example.com
# DNS查询
dig example.com
dig +short example.com
dig @8.8.8.8 example.com # 指定DNS服务器
dig +trace example.com # 追踪DNS解析过程
dig example.com MX # 查询MX记录
dig example.com TXT # 查询TXT记录
# 查看本地DNS缓存(systemd-resolved)
resolvectl statistics
resolvectl query example.com
# Redis测试
redis-cli -h 192.168.1.100 -p 6379 ping
# MySQL测试
mysql -h 192.168.1.100 -u user -p -e "SELECT 1"
# PostgreSQL测试
psql -h 192.168.1.100 -U user -d database -c "SELECT 1"
分层故障排查方法论
讲完了七层模型,下面聊聊实际的排查方法。
自底向上排查法
当你完全不知道问题出在哪里时,建议用自底向上的方法,从物理层开始一层层往上排查。
排查流程:
第一步:物理层检查
├── 网线是否插好?
├── 网口指示灯正常?
├── 网卡识别正常?
└── 链路速率/双工模式正确?
│
└── 如果都正常 → 进入第二步
第二步:数据链路层检查
├── MAC地址正确?
├── ARP表正确?
├── VLAN配置正确?
└── 交换机端口正常?
│
└── 如果都正常 → 进入第三步
第三步:网络层检查
├── IP地址配置正确?
├── 子网掩码正确?
├── 默认网关可达?
├── 路由表正确?
└── ping目标IP通不通?
│
└── 如果都正常 → 进入第四步
第四步:传输层检查
├── 目标端口是否监听?
├── 防火墙是否放行?
├── TCP连接能否建立?
└── 连接状态是否正常?
│
└── 如果都正常 → 进入第五步
第五步:会话层检查
├── TLS握手是否成功?
├── 证书是否有效?
└── 会话是否正常建立?
│
└── 如果都正常 → 进入第六第七步
第六七步:应用层检查
├── 应用协议响应正常?
├── 返回数据正确?
└── 业务逻辑正常?
自顶向下排查法
如果你对系统比较熟悉,或者有一些线索,可以用自顶向下的方法,从应用层开始往下排查。这种方法通常更快,因为大多数问题都出在上层。
# 第一步:应用层检查
curl -v https://api.example.com/health
# 如果返回错误,检查错误类型:
# - 连接超时 → 可能是网络层或传输层问题
# - 连接被拒绝 → 可能是服务没启动或防火墙问题
# - TLS错误 → 会话层/表示层问题
# - HTTP 4xx/5xx → 应用层问题
# 第二步:如果连接超时,检查传输层
nc -zv api.example.com 443
ss -tan | grep api.example.com
# 第三步:如果端口不通,检查网络层
ping api.example.com
traceroute api.example.com
# 第四步:如果ping不通,检查本地网络配置
ip addr show
ip route show
快速定位脚本
下面是一个我常用的网络诊断脚本,能快速完成各层的基本检查:
#!/bin/bash
# network-diag.sh - 网络诊断脚本
# 用法: ./network-diag.sh <目标主机> [端口]
TARGET=$1
PORT=${2:-80}
if [ -z "$TARGET" ]; then
echo"用法: $0 <目标主机> [端口]"
exit 1
fi
echo"========================================="
echo"网络诊断报告 - $(date)"
echo"目标: $TARGET:$PORT"
echo"========================================="
# 解析目标IP
if [[ $TARGET =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TARGET_IP=$TARGET
else
echo""
echo"=== DNS解析 ==="
TARGET_IP=$(dig +short $TARGET | head -1)
if [ -z "$TARGET_IP" ]; then
echo"DNS解析失败!"
echo"尝试其他DNS服务器:"
dig +short @8.8.8.8 $TARGET
exit 1
fi
echo"$TARGET -> $TARGET_IP"
fi
# 第一层:物理层/数据链路层
echo""
echo"=== 本地网络接口状态 ==="
ip -br link show | grep -E "^(eth|ens|enp|eno)"
# 第三层:网络层
echo""
echo"=== 路由信息 ==="
ip route get $TARGET_IP
echo""
echo"=== Ping测试(网络层)==="
ping -c 3 -W 2 $TARGET_IP 2>&1
PING_RESULT=$?
if [ $PING_RESULT -ne 0 ]; then
echo""
echo"=== 路由追踪 ==="
traceroute -m 15 -w 2 $TARGET_IP 2>&1 | head -20
fi
# 第四层:传输层
echo""
echo"=== TCP端口测试(传输层)==="
timeout 5 bash -c "echo > /dev/tcp/$TARGET_IP/$PORT" 2>/dev/null
if [ $? -eq 0 ]; then
echo"端口 $PORT 开放"
else
echo"端口 $PORT 不可达或被过滤"
fi
# 第五层:会话层(如果是HTTPS)
if [ "$PORT" == "443" ]; then
echo""
echo"=== TLS证书检查(会话层)==="
echo | timeout 5 openssl s_client -connect $TARGET:$PORT -servername $TARGET 2>/dev/null | \
openssl x509 -noout -dates -subject 2>/dev/null
fi
# 第七层:应用层(HTTP)
echo""
echo"=== HTTP测试(应用层)==="
if [ "$PORT" == "443" ]; then
PROTOCOL="https"
else
PROTOCOL="http"
fi
curl -s -o /dev/null -w "HTTP状态码: %{http_code}\n连接时间: %{time_connect}s\n总时间: %{time_total}s\n" \
--connect-timeout 5 --max-time 10 "$PROTOCOL://$TARGET:$PORT/"
echo""
echo"========================================="
echo"诊断完成"
echo"========================================="
实战案例分析
下面分享几个我实际工作中遇到的典型案例。
案例一:间歇性丢包的物理层问题
背景:某天收到告警,一台核心服务器的监控数据断断续续的。服务本身没问题,但Prometheus采集数据时有时能成功有时失败。
排查过程:
先检查服务状态,一切正常 从监控机ping这台服务器,发现有1-2%的丢包 检查网络层,路由正常,IP配置正常 检查传输层,TCP连接能建立,但有重传
# 持续ping,观察丢包
ping -c 100 192.168.1.100
# 结果
100 packets transmitted, 98 received, 2% packet loss
# 检查网卡统计
ethtool -S eth0 | grep -E "(error|drop|crc)"
# 发现CRC错误
rx_crc_errors: 1523
CRC错误说明是物理层的问题。
联系机房检查,发现网线水晶头有问题
解决方案:更换网线,问题解决。
经验总结:间歇性丢包如果伴随着CRC错误,基本可以确定是物理层问题。要么是网线,要么是网卡,要么是交换机端口。
案例二:MTU问题导致的VPN连接异常
背景:公司VPN改造后,部分用户反映连上VPN后访问某些内网系统很慢,有时候页面加载不出来,但ping是通的。
排查过程:
用户能ping通目标服务器,延迟正常 telnet 80端口能通 curl访问时,小页面正常,大页面超时
# 小请求正常
curl -o /dev/null -w "%{time_total}\n" http://internal.example.com/api/status
0.123
# 大请求超时
curl -o /dev/null -w "%{time_total}\n" http://internal.example.com/api/large-data
# 超时
# 测试MTU
ping -M do -s 1472 192.168.100.10 # 成功
ping -M do -s 1473 192.168.100.10 # 失败
发现MTU问题。VPN隧道需要额外的封装头,但客户端MTU没有相应调整。
抓包确认
tcpdump -i tun0 -nn 'icmp[0] == 3 and icmp[1] == 4'
# 抓到大量"需要分片但DF被设置"的ICMP消息
解决方案:
方案一:调整VPN客户端MTU
# OpenVPN配置添加
mssfix 1400
方案二:在服务器端调整TCP MSS
# 通过iptables修改MSS
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
经验总结:"小包通大包不通"是MTU问题的典型特征。VPN、隧道、VXLAN等封装场景一定要注意MTU配置。
案例三:CLOSE_WAIT泄漏导致的服务不可用
背景:某Java服务运行一段时间后,开始出现连接超时。重启后恢复正常,但过几天又会出现。
排查过程:
检查服务端口,正常监听 检查连接数
ss -tan | grep :8080 | awk '{print $1}' | sort | uniq -c
# 结果
23 ESTABLISHED
1 LISTEN
15234 CLOSE_WAIT # 大量CLOSE_WAIT!
CLOSE_WAIT数量异常,这是典型的连接泄漏。
找出CLOSE_WAIT连接的对端
ss -tanp state close-wait | grep :8080 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
# 发现都是连接到某个上游服务的
检查代码,发现HTTP客户端没有正确关闭响应
// 问题代码
HttpResponse response = httpClient.execute(request);
String body = EntityUtils.toString(response.getEntity());
// 没有关闭response!
// 修复后
try (CloseableHttpResponse response = httpClient.execute(request)) {
String body = EntityUtils.toString(response.getEntity());
}
解决方案:修复代码,确保HTTP响应被正确关闭。
临时缓解:增加文件描述符限制,设置TCP keepalive
# 增加文件描述符限制
ulimit -n 65535
# 设置TCP keepalive,让系统清理死连接
echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time
echo 30 > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
经验总结:CLOSE_WAIT过多一定是应用程序的bug。对端已经关闭了连接,本地程序没有正确处理。需要从代码层面修复。
案例四:TLS证书链不完整
背景:新上线的API服务,浏览器访问正常,但某些客户端调用报SSL错误。
排查过程:
浏览器访问正常,说明服务端配置没有大问题 用curl测试
curl https://api.example.com/health
# 报错:SSL certificate problem: unable to get local issuer certificate
检查证书链
openssl s_client -connect api.example.com:443 -servername api.example.com
# 输出中看到
depth=0 CN = api.example.com
verify error:num=21:unable to verify the first certificate
证书链不完整!服务器只配置了域名证书,没有配置中间证书。
查看完整证书链应该是什么
# 获取域名证书的颁发者
openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | \
openssl x509 -noout -issuer
# issuer=C = US, O = Let's Encrypt, CN = R3
# 需要添加中间证书 R3
解决方案:
把中间证书和域名证书合并成证书链文件:
# 下载中间证书
wget https://letsencrypt.org/certs/lets-encrypt-r3.pem
# 合并证书
cat domain.crt lets-encrypt-r3.pem > fullchain.crt
# Nginx配置
ssl_certificate /path/to/fullchain.crt;
ssl_certificate_key /path/to/domain.key;
经验总结:浏览器之所以能正常访问,是因为浏览器会缓存中间证书。但其他客户端如果没有缓存,就会报错。配置SSL证书一定要配置完整的证书链。
案例五:DNS解析不一致导致的诡异问题
背景:某服务调用下游API,偶尔报连接超时。但手动curl测试一直是正常的。
排查过程:
应用日志显示连接超时 手动测试正常
curl -w "time_total: %{time_total}\n" https://downstream-api.example.com/health
# 一直正常
检查应用使用的DNS服务器
应用容器使用的是集群内部的CoreDNS,而手动测试用的是宿主机的DNS。
# 在容器内测试
kubectl exec -it app-pod -- nslookup downstream-api.example.com
# 返回了一个旧IP!
# 在宿主机测试
nslookup downstream-api.example.com
# 返回新IP
原来下游服务换了IP,CoreDNS的缓存还是旧的。
检查CoreDNS配置
kubectl get configmap coredns -n kube-system -o yaml
# 发现cache TTL设置过长
解决方案:
方案一:清除CoreDNS缓存(重启CoreDNS Pod)
kubectl rollout restart deployment coredns -n kube-system
方案二:调整缓存TTL
apiVersion:v1
kind:ConfigMap
metadata:
name:coredns
namespace:kube-system
data:
Corefile:|
.:53 {
cache 30 # 降低缓存时间
forward . /etc/resolv.conf
}
经验总结:DNS缓存是很多"灵异事件"的罪魁祸首。排查网络问题时,一定要确认DNS解析结果是否正确,特别是在使用多级DNS(宿主机、容器、Service等)的环境中。
案例六:Kubernetes Service网络问题
背景:微服务A调用微服务B,偶发性超时。两个服务都在同一个Kubernetes集群中。
排查过程:
检查Service和Pod状态
kubectl get svc service-b
kubectl get endpoints service-b
kubectl get pods -l app=service-b
Service和Pod都正常。
在Pod内测试
kubectl exec -it service-a-pod -- curl service-b:8080/health
# 有时成功,有时超时
检查kube-proxy和iptables规则
# 查看Service对应的iptables规则
iptables -t nat -L KUBE-SERVICES -n | grep service-b
# 查看具体规则
iptables -t nat -L KUBE-SVC-XXXXXX -n
发现Service后端有3个Pod,但其中一个Pod的状态有问题。
检查问题Pod
kubectl describe pod service-b-pod-xxx
# 发现Pod的readinessProbe一直失败,但Pod状态显示Running
# 原因是livenessProbe配置不当,没有正确剔除不健康的Pod
抓包确认
# 在问题Pod上抓包
kubectl exec -it service-b-pod-xxx -- tcpdump -i eth0 port 8080 -nn
# 发现请求到达但服务没有响应
解决方案:
修复健康检查配置:
apiVersion:v1
kind:Pod
spec:
containers:
-name:service-b
readinessProbe:
httpGet:
path:/health
port:8080
initialDelaySeconds:5
periodSeconds:10
failureThreshold:3
livenessProbe:
httpGet:
path:/health
port:8080
initialDelaySeconds:15
periodSeconds:20
failureThreshold:3
经验总结:Kubernetes环境的网络问题排查要考虑Service层的负载均衡。健康检查配置不当会导致请求被发送到不健康的Pod。
2025年网络排查新工具
除了传统的tcpdump、netstat这些工具,2025年有一些新工具和技术也值得掌握。
eBPF工具
eBPF(extended Berkeley Packet Filter)是近年来Linux内核最重要的创新之一。它能让我们在不修改内核代码的情况下,动态地跟踪和分析系统行为。
bcc工具集:
# 安装bcc工具
apt install bpfcc-tools
# 跟踪TCP连接
tcpconnect-bpfcc
# 跟踪TCP连接延迟
tcptracer-bpfcc
# 跟踪TCP重传
tcpretrans-bpfcc
# 跟踪TCP连接状态变化
tcpstates-bpfcc
# 跟踪DNS查询
gethostlatency-bpfcc
bpftrace:
# 跟踪所有TCP连接建立
bpftrace -e 'kprobe:tcp_v4_connect { printf("connect: %s\n", comm); }'
# 统计TCP重传
bpftrace -e 'kprobe:tcp_retransmit_skb { @[comm] = count(); }'
# 跟踪网络延迟
bpftrace -e '
tracepoint:net:net_dev_queue { @start[args->skbaddr] = nsecs; }
tracepoint:net:net_dev_xmit {
$latency = nsecs - @start[args->skbaddr];
@us = hist($latency / 1000);
delete(@start[args->skbaddr]);
}'
Cilium网络诊断
在使用Cilium作为CNI的Kubernetes集群中,cilium命令行工具提供了强大的网络诊断功能:
# 查看连接跟踪表
cilium bpf ct list global
# 查看BPF策略
cilium bpf policy list
# 网络诊断
cilium connectivity test
# 查看服务负载均衡
cilium service list
# 监控网络流量
cilium monitor
# hubble流量观测
hubble observe --since 5m
hubble observe --from-pod default/app --to-pod default/db
Istio网络诊断
在Service Mesh环境中,Istio提供了丰富的诊断工具:
# 检查代理配置
istioctl proxy-config all pod-name
# 检查路由配置
istioctl proxy-config routes pod-name
# 检查集群配置
istioctl proxy-config clusters pod-name
# 分析配置问题
istioctl analyze
# 查看代理日志
kubectl logs pod-name -c istio-proxy
# 使用Kiali观察服务网格
kubectl port-forward svc/kiali -n istio-system 20001:20001
现代化的curl替代品
httpie:
# 安装
pip install httpie
# 简洁的HTTP请求
http GET https://api.example.com/users
# POST JSON
http POST https://api.example.com/users name=john age:=30
# 查看请求详情
http -v https://api.example.com/
xh:
# Rust写的高性能HTTP客户端
xh https://api.example.com/
# 只看响应头
xh -h https://api.example.com/
# 跟踪时间
xh --print=hH https://api.example.com/
HTTP/3和QUIC诊断
2025年HTTP/3已经普及,相关的诊断工具也需要掌握:
# curl测试HTTP/3(需要支持QUIC的版本)
curl --http3 -I https://example.com
# 使用quiche的示例客户端
quiche-client https://example.com --no-verify
# 查看HTTP/3支持情况
curl -I https://example.com | grep alt-svc
# 使用ngtcp2测试QUIC
ngtcp2-client -q example.com 443
最佳实践
建立标准化排查流程
网络问题排查标准流程
┌──────────────────────────────────────────────────────────┐
│ 1. 收集信息 │
│ - 问题现象描述 │
│ - 发生时间和频率 │
│ - 影响范围 │
│ - 最近的变更 │
├──────────────────────────────────────────────────────────┤
│ 2. 初步判断 │
│ - 是否网络问题?(排除应用自身问题) │
│ - 影响范围是单点还是全局? │
│ - 问题是持续还是间歇? │
├──────────────────────────────────────────────────────────┤
│ 3. 分层排查 │
│ - 根据现象选择自底向上或自顶向下 │
│ - 使用对应层的工具进行检查 │
│ - 记录每一步的结果 │
├──────────────────────────────────────────────────────────┤
│ 4. 定位问题 │
│ - 确定问题所在的层 │
│ - 确定问题的具体原因 │
├──────────────────────────────────────────────────────────┤
│ 5. 解决问题 │
│ - 临时方案(止血) │
│ - 根本方案(根治) │
├──────────────────────────────────────────────────────────┤
│ 6. 复盘总结 │
│ - 问题原因 │
│ - 解决方案 │
│ - 后续预防措施 │
└──────────────────────────────────────────────────────────┘
建立监控告警体系
网络问题的排查最好是被动变主动,通过完善的监控提前发现问题。
各层监控指标:
# 物理层/数据链路层监控
-网卡流量(in/out)
-网卡错误计数(CRC、帧错误)
-网卡丢包率
-链路状态
# 网络层监控
-ICMP延迟和丢包率
-路由变化
-IP碎片统计
# 传输层监控
-TCP连接数(各状态)
-TCP重传率
-TCPRTT
-端口监听状态
-Socketbuffer使用率
# 会话层监控
-TLS握手成功率
-证书过期时间
-会话建立延迟
# 应用层监控
-HTTP请求量和响应时间
-HTTP状态码分布
-DNS查询延迟
-应用错误率
Prometheus监控配置示例:
# node_exporter指标(物理层/数据链路层)
-alert:NetworkInterfaceErrors
expr:rate(node_network_receive_errs_total[5m])>0
for:5m
labels:
severity:warning
annotations:
summary:"网络接口错误"
description:"{{ $labels.instance }} 的 {{ $labels.device }} 接口有错误"
# TCP连接监控(传输层)
-alert:TooManyTimeWait
expr:node_sockstat_TCP_tw>10000
for:5m
labels:
severity:warning
annotations:
summary:"TIME_WAIT过多"
description:"{{ $labels.instance }} 有 {{ $value }} 个TIME_WAIT连接"
-alert:TooManyCloseWait
expr:node_sockstat_TCP_close_wait>100
for:5m
labels:
severity:critical
annotations:
summary:"CLOSE_WAIT过多"
description:"{{ $labels.instance }} 有 {{ $value }} 个CLOSE_WAIT连接,可能存在连接泄漏"
# TCP重传监控
-alert:HighTCPRetransmission
expr:rate(node_netstat_Tcp_RetransSegs[5m])/rate(node_netstat_Tcp_OutSegs[5m])>0.01
for:10m
labels:
severity:warning
annotations:
summary:"TCP重传率过高"
description:"{{ $labels.instance }} TCP重传率为 {{ $value | humanizePercentage }}"
常用排查命令速查表
层级 命令 用途
──────────────────────────────────────────────────────────────
物理层 ip link show 查看网络接口状态
ethtool eth0 查看网卡详情
ethtool -S eth0 查看网卡统计
数据链路层 ip neigh show 查看ARP表
bridge fdb show 查看MAC地址表
tcpdump -i eth0 arp 抓取ARP包
网络层 ip addr show 查看IP配置
ip route show 查看路由表
ping 测试连通性
traceroute / mtr 追踪路由
tcpdump -i eth0 icmp 抓取ICMP包
传输层 ss -tanp 查看TCP连接
ss -s 连接统计
nc -zv host port 测试端口
tcpdump -i eth0 port 80 抓取指定端口
会话层 openssl s_client 测试TLS连接
curl -v https:// 查看HTTPS详情
应用层 curl HTTP测试
dig / nslookup DNS查询
tcpdump -A 查看应用层数据
网络配置最佳实践
系统级TCP优化:
# /etc/sysctl.conf 或 /etc/sysctl.d/99-network.conf
# 增加TCP缓冲区
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 连接跟踪优化
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 86400
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30
# TIME_WAIT优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_max_tw_buckets = 262144
# TCP keepalive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# 队列优化
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535
# 快速回收和重用
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_slow_start_after_idle = 0
# 启用TCP BBR拥塞控制(2025年推荐)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
应用后:
sysctl -p
总结
写了这么多,最后总结一下核心要点:
第一,理解分层思维。 OSI七层模型不是为了考试,而是一套系统化的排查方法论。网络问题千变万化,但只要你能准确定位问题在哪一层,解决方案自然就清楚了。
第二,掌握每层的核心工具。
物理层:ethtool 数据链路层:ip neigh、bridge、tcpdump arp 网络层:ip、ping、traceroute、mtr 传输层:ss、netstat、tcpdump 会话层:openssl 应用层:curl、dig
第三,建立标准化流程。 不要凭感觉排查,要有系统化的流程。收集信息、初步判断、分层排查、定位问题、解决问题、复盘总结,每一步都不能少。
第四,重视监控告警。 最好的排查是不需要排查,通过完善的监控体系在问题发生前就能发现苗头。
第五,持续学习新工具。 eBPF、Cilium、Istio这些新技术已经成为2025年的标配,不能只停留在传统工具上。
干了这么多年运维,我最大的体会是:网络问题看着复杂,但只要方法对了,没有解决不了的问题。希望这篇文章能帮到正在被网络问题折腾的同行。
(版权归原作者所有,侵删)
免责声明:本文内容来源于网络,所载内容仅供参考。转载仅为学习和交流之目的,如无意中侵犯您的合法权益,请及时联系Docker中文社区!

温馨提示:文章内容系作者个人观点,不代表Docker中文对观点赞同或支持。
版权声明:本文为转载文章,来源于 互联网 ,版权归原作者所有,欢迎分享本文,转载请保留出处!

发表评论