Linux 排查:ss -tlnp 与端口占用的三种典型场景

举报
yd_282015185 发表于 2026/10/25 11:06:06 2026/10/25
【摘要】 端口问题看着都像“端口有问题”,根因却分三种:bind 占用、绑错网卡、连接风暴。用 ss -tlnp 一条命令先分方向:-p 的权限陷阱、Local Address 里 127.0.0.1 与 0.0.0.0 的区别、TIME_WAIT 该不该调参。附真实命令、真实报错和容器 namespace 的坑。

Linux 排查:ss -tlnp 与端口占用的三种典型场景

端口问题大概是运维日常里最高频的一类:服务起不来、起了连不上、连上了越跑越慢。这三种现象看着都像"端口有问题",但根因完全不同,用同一条命令 ss -tlnp 就能分出方向。

先把参数拆开:-t TCP、-l 只看监听、-n 不解析服务名和域名、-p 显示进程。四个字母组合起来,就是"谁在监听哪个 TCP 端口"这一个问题的最短答案。

$ ss -tlnp
State   Recv-Q  Send-Q  Local Address:Port   Peer Address:Port  Process
LISTEN  0       128     0.0.0.0:22           0.0.0.0:*          users:(("sshd",pid=812,fd=3))
LISTEN  0       511     127.0.0.1:8080       0.0.0.0:*          users:(("java",pid=20431,fd=87))
LISTEN  0       128     [::]:443             [::]:*             users:(("nginx",pid=1203,fd=6))

场景一:服务起不来,报 bind: Address already in use

这是最好处理的一种,因为有明确报错。

$ ./app
listen tcp :8080: bind: address already in use

第一步确认占用者:

$ ss -tlnp | grep :8080
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("java",pid=20431,fd=87))

这里有个很容易踩的坑:-p 能不能显示进程,取决于你的权限。 用普通用户跑,占端口的又是别人的进程,Process 列会直接是空的,只留一个 users:(("java",pid=...)) 都没有的空行。这时候别怀疑命令坏了,加 sudo 再跑一次:

$ sudo ss -tlnp | grep :8080

如果连 ss 都没有(精简镜像里常见),有两个等价替代:

$ sudo lsof -i:8080 -sTCP:LISTEN
$ sudo fuser -n tcp 8080

拿到 pid 之后,先看清楚它是什么再动手:

$ ps -o pid,ppid,user,cmd -p 20431
  PID  PPID USER   CMD
20431     1  app  java -jar /opt/app/gateway.jar --server.port=8080

结论: 这一步的产出不是"杀掉进程",而是"这个端口到底该不该归它"。我见过不止一次,真正的故障是某个历史遗留的 systemd 服务还在开机自启,把端口占了,重启业务当然起不来——systemctl is-enabled 比 kill -9 有用。


场景二:端口在监听,但外部连不上

这种最费时间,因为本机 curl 是通的,很容易先去怀疑防火墙和安全组。其实 ss 输出里的 Local Address 已经把答案写在那儿了。

对比两行:

LISTEN 0 511 127.0.0.1:8080 0.0.0.0:*  users:(("java",pid=20431,fd=87))
LISTEN 0 511 0.0.0.0:8080   0.0.0.0:*  users:(("java",pid=20431,fd=87))
  • 127.0.0.1:8080 —— 只绑在回环网卡上,只有本机能连。别的机器、甚至同宿主机上的另一个容器都连不进来。
  • 0.0.0.0:8080 —— 所有 IPv4 网卡都监听。
  • [::]:8080 和 [::1]:8080 —— 是 IPv6 版本,同样有"只回环"和"全部"的区别。

所以"本机通、外部不通"的第一步不是查安全组,是确认绑定地址:

$ curl -sI http://127.0.0.1:8080/ | head -1
HTTP/1.1 200 OK

$ curl -sI --connect-timeout 3 http://10.0.0.12:8080/ | head -1
curl: (7) Failed to connect to 10.0.0.12 port 8080: Connection refused

本机 200、内网 IP 拒绝,那就是绑错了。改配置重绑 0.0.0.0 即可。确认这一步之后再去查安全组,能省掉一大半误判。

顺便记住 Connection refused 和超时的区别:

  • refused —— 包到了机器,但没有进程在这个端口上监听(或被本机防火墙 reject)。方向是"服务/绑定"。
  • 超时 —— 包根本没回来。方向才是"安全组、路由、对端没开"。

场景三:连接数跑满、TIME_WAIT 一堆

前两种情况是"起不来",这种是"跑着跑着不行了"。先看总量:

$ ss -s
Total: 412
TCP:   389 (estab 42, closed 301, orphaned 0, timewait 287)

再按状态拆:

$ ss -tan state time-wait | wc -l
287

$ ss -tan state established | awk '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
     38 10.0.0.12

这里我要泼一盆冷水:TIME_WAIT 是 TCP 的正常收尾状态,不是故障。 主动关闭连接的一方必然进入 TIME_WAIT,持续 2MSL(Linux 上 60 秒)。287 个 TIME_WAIT 在一台正常跑业务的小机器上完全不算多。

真正要看的指标是 closed 涨得快不快,以及 estab 有没有顶到上限:

$ ss -tan state established | wc -l
$ ss -tan state syn-recv | wc -l     # 有人疯狂建连但握不上手

如果 closed 很大、estab 却很小,说明业务在疯狂建短连接——典型场景是每次请求都新建 HTTP 连接、或者数据库连接没走池子。正确解法是加连接池 / 复用连接,不是去调 net.ipv4.tcp_tw_reuse 或 tcp_max_tw_buckets。 后两个参数在 NAT 环境里还可能引入更隐蔽的问题,属于治标不治本。


一个容易翻车的细节:在容器里看不到宿主机进程

这个坑我踩过。在容器里跑 ss -tlnp,宿主机上明明有进程占了 8080,容器里却完全看不到——因为容器有自己的 network namespace,ss 只列当前 namespace 的 socket。

要看宿主机的,得借宿主机的 namespace:

$ nsenter -t <宿主机上某进程pid> -n ss -tlnp

或者直接从宿主机侧查,再对应到容器:

$ sudo ss -tlnp | grep :8080
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=30122,fd=4))
$ docker ps --filter publish=8080

看到 docker-proxy 就说明端口是被容器映射走的,去查容器而不是查进程树。


三种场景的排查顺序

现象 先看什么 常见根因
bind: Address already in use sudo ss -tlnp | grep :端口 历史服务残留、开机自启、上一个实例没退干净
本机能连、外部连不上 ss -tlnp 的 Local Address 绑了 127.0.0.1;确认后才查安全组
越跑越慢、建连失败 ss -s 与 ss -tan state xxx 短连接过多、连接池没配好、SYN 队列满

一句话总结:ss -tlnp 回答"谁在听",Local Address 回答"谁能连",ss -s 与状态过滤回答"为什么连不上"。 把这三件事分开看,端口类的故障基本就不会走弯路。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。