1. 从命令输出看懂Linux网络状态:三张表是基本功
上次聊完Linux网络基础的第一部分,配套讲的基本都是"配置怎么改":改IP、改网关、改DNS、重启网卡服务。那种操作属于"能把机器联网"的范畴,但真正进入网络基础2这个阶段,首先得跨过一个坎——从看懂命令输出,到能根据命令输出反推系统当前的网络状态。这中间的载体,就是三张表:接口地址表、路由表、连接表。
很多刚接触Linux的人有个习惯,查IP还在用ifconfig,查连接还在用netstat,甚至查路由还在用route命令。不是说这些命令不能用,而是它们在现代Linux发行版里基本已经被ip、ss这套iproute2工具集取代了。更重要的是,ip命令输出出来的信息密度,远远高于老命令,但是默认格式不友好,需要训练自己的眼睛。
1.1 网卡与地址:ip addr输出的信息分层
先看最基础的,ip addr(缩写ip a)。它的输出通常长这样:
$ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic eth0 valid_lft 86382sec preferred_lft 86382sec inet6 fe80::5054:ff:fe12:3456/64 scope link valid_lft forever preferred_lft forever看起来很长,但核心信息就几块。第一是尖括号里的状态标志,UP代表这个接口是启用的,LOWER_UP代表物理链路是通的(网线插着、交换机端口正常)。如果网线拔了,你会看到NO-CARRIER,这时候别急着查配置,物理层先查一遍。第二是inet那行,这是IPv4地址、掩码、作用域和获取方式。第三行的dynamic说明这是DHCP获取的,static就是手动配置的。
我排查网络问题时,第一步永远是ip addr,为什么?因为配置层面和物理链路层的问题在这里一眼就能分辨。如果你看到IP地址正常但state DOWN,那问题在接口没启用;如果接口UP但NO-CARRIER,那问题在物理链路。这基本上能把一半的"上不了网"问题分流掉。
1.2 路由表:数据包出网的路线图
第二张表是路由表。ip route(缩写ip r)的输出。这是很多新手会跳过的一步,但恰恰是网络不通时最需要看的一张表。
$ ip route default via 192.168.1.1 dev eth0 proto dhcp metric 100 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100这里就两行,但信息量很大。第一行default via 192.168.1.1是默认路由,所有目标不在本地子网的数据包,都走192.168.1.1这个网关。第二行是本地子网的路由,目标是192.168.1.0/24的包直接走eth0,不需要网关。
排查不通的问题,光看默认路由是否存在是不够的,还得确认网关本身通不通。我见过太多情况:默认路由在,但网关配置错了(比如网关IP写成别的机器的IP),结果所有流量都发给了错误的设备,自然上不了网。这时候ping网关IP是一个极快的验证手段。
1.3 连接表:用ss看端口与连接状态
第三张表是连接表。老派做法是用netstat -tunlp,但现在我更推荐ss -tunlp。原因很简单,ss的性能比netstat好一个量级,尤其在连接数多的服务器上,netstat可能会卡半天甚至跑不完,ss基本秒出。
$ ss -tunlp Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3)) tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=5678,fd=6))看待监听地址时要特别小心0.0.0.0:80和127.0.0.1:80的区别,前者是监听所有网卡,后者只监听回环地址。很多安全类面试题就藏在这类细节里:服务监听在内网地址和监听在所有地址,暴露面完全不一样。
平时做网络基础排查或者Linux面试准备,这三张表的顺序基本就是固定套路:先看接口有没有地址,再看路由有没有默认网关,最后看端口有没有在监听。把这三步练熟了,能覆盖80%的网络基础运维场景。
2. TCP/IP协议栈的行为特征:握手、断开与状态机
聊完了命令,得往下一层走。网络基础2这个阶段,真正卡住人的往往不是命令记不熟,而是不理解协议栈为什么要这样设计。TCP/IP里很多反直觉的地方,恰恰是排查间歇性故障的钥匙。
2.1 三次握手与SYN队列的核心逻辑
TCP建立连接的三次握手,理论大家都背得出:SYN、SYN-ACK、ACK。但实际排查的时候,光背这个过程没用,你得知道握手是分两条路走的。
第一条路是半连接队列(SYN队列),内核收到SYN包后,把连接放到这里,等ACK确认。第二条路是全连接队列(accept队列),三次握手完成后,连接被移到这里,等应用程序调用accept()取走。
这两条队列都有长度上限,而且上限机制在Linux上发生过变化。早期内核net.ipv4.tcp_max_syn_backlog管SYN队列,net.core.somaxconn管全连接队列。但现代内核引入了tcp_syncookies之后,SYN队列满了会启用syncookie机制,行为又不一样。这里不展开太多,只说一句经验之谈:当你的服务出现"端口通但连不上"的情况,优先查全连接队列溢出,也就是ss -lnt里Send-Q那一列的值就是accept队列长度上限。
2.2 TIME_WAIT与端口耗尽的那笔账
TIME_WAIT可能是TCP状态机里最被误解的一个状态。很多运维看到netstat里一堆TIME_WAIT就紧张,其实在主动关闭连接多的场景下(典型如Nginx代理、短连接服务),TIME_WAIT出现是很正常的。
TIME_WAIT的作用有两个:一是确保最后的ACK能到达对端,万一丢了可以重发;二是让属于旧连接的报文在网络里自然消失,避免污染新连接。所以TIME_WAIT会持续2个MSL(最大报文生存时间)。在Linux上,TIME_WAIT的默认保留时间是60秒,这也就是为什么短连接服务每秒新建连接超过一定数量后,会出现大量TIME_WAIT堆积。
那怎么判断TIME_WAIT是不是"病态"?看这条命令的输出:
$ ss -s Total: 128 (kernel 0) TCP: 42 (estab 8, closed 0, orphaned 0, synrecv 0, timewait 32/0), ports 0如果timewait后面的数字长期占TCP连接总数的相当比例且一直不降,可能引发的问题是端口耗尽。因为每条TCP连接对应一个四元组,客户端主动发起的连接会占本地端口,默认net.ipv4.ip_local_port_range是32768-60999,总共两万多个端口,如果TIME_WAIT都占着不放,新连接就没有端口可用了,表现就是服务突然大量报错Can't assign requested address。
这里有个重要的认知:TIME_WAIT是主动关闭方的事情。如果你的服务只做被动关闭(比如Nginx作为服务端,由客户端主动断开),TIME_WAIT大概率不会成为问题。真正需要调优的是那些充当反向代理、短连接压力大的场景,这时候可以开net.ipv4.tcp_tw_reuse配合timestamp,让内核安全复用TIME_WAIT状态的连接。
2.3 连接状态排查的实战读法
理解了状态机,再看ss -state的输出就不一样了。一次典型的后端连接排查,对应状态大概是这样的链路:
SYN-SENT → ESTABLISHED →(数据传输)→ FIN-WAIT-1 → FIN-WAIT-2 → TIME_WAIT如果对方端口不通,连接会卡在SYN-SENT,表现为请求超时;如果对方进程崩了但内核还活着,可能直接收到RST,表现为Connection refused;如果中间网络设备丢了包,连接可能长期卡在ESTABLISHED但数据不通,这时候就得从应用层做超时控制。
提示:协议栈排查最有价值的经验不是背状态,而是把状态变化和具体场景对上号。看到SYN_RECV堆积,想的是握手包回不去;看到FIN_WAIT_2堆积,想的是对端不想理你了;看到CLOSE_WAIT堆积,想的是你这边应用代码没调用close()。
3. DNS解析的完整链路与系统的解析行为
网络基础2里DNS是重头戏,但这里我讲的不是怎么配DNS服务器,而是理解一台Linux机器从敲下域名到拿到IP,中间发生了什么,以及哪些环节会出问题。这个理解比记住几条配置命令值钱得多。
3.1 解析器的优先级:nsswitch.conf的串联逻辑
当你执行ping baidu.com,程序第一件事是调用getaddrinfo(),而这个函数的行为由/etc/nsswitch.conf里hosts那一行决定。默认情况下通常是:
hosts: files dns myhostname意思是先查/etc/hosts文件,查不到再走DNS解析,最后再匹配自己的主机名。这个顺序很有讲究:files优先意味着你可以通过改/etc/hosts实现本机级别的域名覆盖,比如屏蔽某些站点或者把内网域名映射到内网IP,而完全不需要动DNS服务器。
实际工作中,/etc/hosts的坑往往出在格式上。正确的格式是IP地址加空格加域名,一行一个。但有些人会把注释写错位置,或者把IP和域名顺序写反了,导致解析行为变得诡异。我见过最典型的例子是有人把127.0.0.1 localhost删了,结果本机连回环地址都解析不了。
3.2 resolv.conf的坑:超时与重试机制
/etc/resolv.conf是最容易被改坏的文件之一。一个常见的配置是:
nameserver 8.8.8.8 nameserver 114.114.114.114 options timeout:2 attempts:2这个文件有几个行为特性值得注意。第一,nameserver最多可以配三个,但解析器按顺序尝试,只有前一个超时才用下一个,并不是多个同时查询。第二,timeout和attempts两个参数控制的是第一个nameserver超时后的行为:timeout是等待单个查询的秒数,attempts是重试次数。如果第一个DNS出现了丢包,一个解析请求可能耗掉数秒,这对在线业务是致命的。
有些发行版默认不写options行,导致解析器使用内置默认值,可能是timeout 5秒、attempts 2次。这意味着一个DNS请求最坏情况可以拖10秒。所以做服务器初始化时,我会习惯性地在resolv.conf里加上options timeout:2 attempts:1,缩短等待时间,快速失败。
3.3 用dig判断问题在哪一层
判断DNS问题,最核心的命令是dig。注意它默认不带搜索域和配置文件的干扰,完全按照你给的参数做原始查询,所以适合定位问题。
$ dig @114.114.114.114 www.example.com这行命令强制使用114这个nameserver查询,绕开系统配置,如果这个能查出来但系统解析失败,问题就在resolv.conf配置或nsswitch顺序上;如果这个也查不通,那就要考虑网络到该DNS服务器的连通性或者DNS服务本身。
另外记得看dig输出末尾的查询耗时和状态字段。状态是NOERROR表示域名存在,NXDOMAIN表示域名不存在,SERVFAIL表示服务器内部故障,REFUSED表示被拒绝。这些状态码本身就是排查线索。
注意:不要一上来就ping域名测DNS,ping里看到的解析结果和DNS链路测试之间隔了好多层。先dig,再ping IP,再ping域名,一层层剥开,才是合格的做法。
4. 多网卡与策略路由:一张路由表搞不定的现实
单网卡、单路由表的场景其实很好理解,真正让人头疼的是多网卡机器。嵌入式Linux设备、双线上网的服务器、堡垒机,都容易碰到这种情况。
4.1 多网卡场景的核心矛盾
想象一台机器有两块网卡:eth0接内网,eth1接外网。内网IP是192.168.1.10,外网IP是203.0.113.10。默认网关只能有一个,如果指向eth1的网关,那去内网的流量也会被丢给外网网关,结果内网不通;如果指向eth0的网关,那外网流量就出不去。
这个问题本质上是主路由表的表达能力不够:Linux默认只有一张main路由表,虽然路由规则支持按目标网段分流,但遇到"从哪个接口进来的流量就从哪个接口回去"这种需求时,就得引入策略路由。
4.2 ip rule与独立路由表的配合
策略路由的设计思路是:先匹配规则,再根据规则选路由表。你可以为不同的来源IP或mark值定义不同的路由表,而且每个表里可以有自己的默认网关。配置过程大概是这样的。
先为主路由表之外的新表建规则,比如表100用于内网:
ip rule add from 192.168.1.10 table 100然后在表100里声明独立的默认路由:
ip route add default via 192.168.1.1 dev eth0 table 100 ip route add 192.168.1.0/24 dev eth0 table 100再把主路由表里的默认路由删掉,否则流量还是会优先走主表。这样,来自内网地址的流量会先匹配rule,进表100,走eth0的网关;来自其他地址的流量走main表,默认路由指向eth1。
这套思路在linux 网口转串口服务器这类嵌入式设备上非常常见。网口转串口设备往往同时有业务网口和管理网口,两条链路不能互相干扰,策略路由就是最干净的解法。配置策略路由时必须注意,表ID和表名的对应关系写在/etc/iproute2/rt_tables里,建议加注释说明每个表的作用,不然三个月后你自己都看不懂。
4.3 策略路由的验证方法
配完路由后别急着说"好了",先用两条命令验证。
$ ip route get 192.168.2.5 from 192.168.1.10 $ ip route get 8.8.8.8 from 203.0.113.10ip route get是测试路由选择的利器,它会按照源地址、目的地址、规则优先级,实际走一遍查表流程并告诉你该走哪个接口、哪个网关。如果结果跟预期不一致,再逐条检查rule的优先级(rule的priority数值越小优先级越高,默认是按添加顺序从0开始递增)。
5. 防火墙过滤链路:iptables与nftables的排查要点
说网络基础2,绕不开防火墙。这里不教大家从头配置一套完整的企业防火墙策略,那个篇幅不够,只讲清楚一件事:当业务连不上服务时,怎么判断是不是防火墙在拦,以及五链的基本走向。
5.1 五链的数据包走向
iptables有五条内置链:PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。一个数据包从网卡进来,先过PREROUTING,然后判断目的地址——如果是本机就进INPUT,送给本地进程;如果是要转发的就进FORWARD。本地进程发出的包,先过OUTPUT,再过POSTROUTING出去。
我见过很多系统的排查误区,就是分不清INPUT和FORWARD。比如一台Linux路由器,两个网口之间的流量不通,有人习惯性看INPUT链,但INPUT只管发往本机的包,转发流量根本不会经过它。这种时候应该在FORWARD链里找原因。
$ iptables -L INPUT -n -v $ iptables -L FORWARD -n -v-n禁止反解,-v显示计数,配合起来可以看到每条规则匹配了多少包。如果计数器长时间不增长,说明流量根本没走到这条规则,问题得往上游找。
5.2 快速定位防火墙拦截的三板斧
判断是不是防火墙拦截,我的习惯操作是三条命令:
$ iptables -L -n --line-numbers $ iptables -S $ systemctl status firewalld先看有什么规则(-S能看到完整的iptables命令格式,包括删除的规则),再看防火墙服务是否开启。很多发行版默认用firewalld管理iptables,firewalld和直接改iptables的规则偶尔会互相覆盖,所以改规则之前先确认你在跟哪个管理端说话。
如果怀疑规则太长不好排查,有个临时方案:加一条放行规则在不合适的地方,观察计数器变化。比如你想知道http流量是不是被后面的规则拦了,可以临时在INPUT链开头插一条ACCEPT tcp dport 80,如果业务立刻恢复,那说明确实是被后面的某条规则DROP了。定位完再删掉这条临时规则,恢复原状。
5.3 nftables迁移要注意的思路差异
新版系统越来越倾向用nftables,命令风格完全不同。iptables是"链+规则"模型,nftables是"表+链+规则"的统一框架,理论上表达能力更强,但排查思路要从"看链"变成"看表和链的关联"。
举一个常见坑:nftables默认有一个规则集,可能存在多个表,流量可能在某个表的INPUT链被drop。你用iptables -L查是空的,就以为没有防火墙,其实规则都在nftables的table里。所以在新系统上排查时,我一般直接跑nft list ruleset,一次性看全所有规则,而不是用iptables命令去查一个已经被替代的框架。
6. 真实故障排查链路:一个跨网段访问超时的复盘
这部分写一个我印象深刻的实际排查过程。当时一台应用服务器连着一段内网,所有内网访问都很正常,但访问某台跨网段的数据库时,延迟极高且有间歇性超时。整个排查链路走完后,发现是一件很小但很隐蔽的事。
6.1 排查起点:先分层还是先分段
有人排查网络故障喜欢按OSI模型从物理层一层层往上查,理论没错但效率太低。我的习惯是先分段:客户端到服务端的路径上,先确认哪一段不通。
当时我先在应用服务器上ping数据库IP,结果丢包率很高,Ping值在几百毫秒波动。这说明网络链路有问题,但还不能确定是在应用服务器到自己网关这一段,还是在中间链路。于是我在同一网段的另一台机器上再ping数据库,结果完全正常。这就把问题圈定在应用服务器到网关这一段。
6.2 定位到网卡队列:一个没人注意的配置
接着查应用服务器的网卡状态。ip -s link show eth0输出里,RX错误计数高得离谱,而且有大量的dropped。同一个交换机的另一台机器没有这个问题,所以交换机端口大概率没问题。再查网卡驱动和中断配置,发现网卡多队列功能没启用,中断全部打在一个CPU核心上,而该核心还承载着应用进程的大量软中断处理,处理不过来就开始丢包。
解释一下原理:现代网卡普遍支持多队列(RSS),就是把收包队列分散到多个CPU核心上并行处理。如果队列只有一个,碰到高并发或流量突增,单一CPU核心的软中断就会成为瓶颈,表现为网卡层面出现丢包,但物理链路完全正常。
6.3 修复与验证
修法也很直接。如果网卡支持,用ethtool打开多队列:
$ ethtool -L eth0 combined 4或者确认irqbalance服务是否在跑,让它自动均衡中断到多个核心:
$ systemctl status irqbalance改完之后再做同样的ping测试,丢包消失。这次故障的根因不是IP、不是路由、不是网关,而是流量处理带宽跑在了网卡中断上。如果不是靠计数器的提示,光想三层以上的问题,永远摸不到这个点。
提示:Linux网络排查有个原则——从现象往下挖,但别只盯着一种可能性。ping不通可能是防火墙拦了ICMP,电路没问题但业务超时可能是MTU导致分片,甚至可能是nginx的worker连接数满了。多用计数器观察,少用猜测试错。
7. 网络基础2的进阶清单:把这些命令练成肌肉记忆
以上都是场景化的知识点。把它们拆成一份可以照着练的清单,我按使用频率排个序,建议对着真实机器操作几遍,别光看不练。
ip addr/ip route/ss -tunlp:三张表,看一切网络状态的入口ping -c 3 <ip>:测连通性,注意要ping IP,别ping域名,否则混入DNS变量dig @<ns> <domain>:DNS专项分析,比nslookup详细得多traceroute -n <ip>或者mtr -n <ip>:看每一跳的延迟和丢包,定位中间链路问题ip -s link:看网卡的错误计数、丢包计数,驱动层问题的第一线索ethtool eth0:看网卡速率、双工模式,排查物理层协商问题iptables -L -n -v或nft list ruleset:看防火墙规则和计数器ip rule:看策略路由规则,多网卡场景的必备命令ss -s:看TCP统计总量,快速判断TIME_WAIT等状态是否异常sysctl net.ipv4.*:看内核协议栈参数,端口范围、超时、转发开关都在这里
每条命令背后的逻辑,本文前面已经展开过了,这里不再重复。关键是练的时候要思考输出对应的是哪一层:网卡层、网络层、传输层、应用层,还是防火墙层。
8. 从命令到经验:最常见的三个认知误区
最后聊几个容易被带偏的认知,这些在Linux面试题里经常出现,但答案跟很多人理解的完全不一样。
误区一:ping不通就是网络不通。很多服务器出于安全考虑禁用了ICMP,但TCP业务完全正常。用ping测出来的"不通"可能只是防火墙丢弃了ICMP报文。正确做法是同时用nc -vz <ip> <port>测一下具体端口,看能不能建立TCP连接。
误区二:DNS配置了就能解析。DNS解析链条很长,resolv.conf只是第一步。程序也可能绕过系统解析器,直接用自己内置的DNS配置(比如很多容器应用、nginx resolver指令),或者被/etc/hosts里的记录干扰。改完resolv.conf后,最好用getent hosts <domain>验证一下系统层面解析结果,而不是只看到dig输出正常就认为好了。
误区三:丢包一定发生在物理链路。前面那个网卡队列的案例就是典型:物理链路完全正常,丢包发生在网卡驱动和内核之间。处理不过来时,网卡会先把包丢掉。此外,iptables的DROP规则、tc限速、socket缓冲区满,都可能表现为丢包。判断丢包在哪一层,最直观的就是看各层计数器的变化。
我个人体会:网络基础2这类进阶内容,学的不是单个命令的用法,而是排错的分层思路。数据包从网卡到应用,每一层都有自己的计数器测量点和日志线索。什么时候看哪一层,取决于现象的特征。把这一套分层排查的逻辑装进脑子里,比你背一百条命令都管用。
篇幅有限,这次先聊到这里。后面有机会可以专门写一篇iptables生产配置实例,把那些规则怎么dump、怎么备份、怎么原子性重载都展开讲讲。