一、概述
1.1 背景介绍
TCP/IP 网络通信的本质是进程间通信。两台机器之间的数据传输,最终落到"某个 IP 地址的某个端口"这个粒度。端口号是传输层的概念,范围 0-65535,其中 0-1023 为系统保留端口(Well-Known Ports),1024-49151 为注册端口(Registered Ports),49152-65535 为动态端口(Ephemeral Ports)。
当运维人员说"端口不通"时,实际可能指若干种完全不同的故障:
服务未监听:目标端口上没有进程在监听,内核直接返回 RST
防火墙拦截:数据包被 iptables/nftables/firewalld 规则丢弃或拒绝
安全策略阻断:SELinux 阻止进程绑定非标准端口
应用层协议不匹配:TCP 连接建立成功但应用层握手失败
这五种情况的排查方法完全不同。如果不分层次地乱查,可能花几个小时还找不到原因。
TCP 连接建立过程
理解端口不通的排查逻辑,首先需要理解 TCP 三次握手:
客户端 服务端 | | | SYN (seq=x) | |------------------------------>| | | | SYN-ACK (seq=y, ack=x+1) | |<------------------------------| | | | ACK (seq=x+1, ack=y+1) | |------------------------------>| | | | 连接建立完成 |
三次握手中任何一步出问题都会导致"端口不通"的表象,但根因截然不同:
| 阶段 | 现象 | 常见原因 |
|---|---|---|
| SYN 发出后无响应 | 连接超时(通常 30-120 秒) | 防火墙 DROP、路由不可达、目标主机宕机 |
| SYN 发出后收到 RST | 连接立即被拒绝 | 端口未监听、防火墙 REJECT |
| SYN-ACK 已收到但 ACK 丢失 | 连接超时 | 客户端出方向防火墙、NAT 问题 |
| 三次握手成功但随后断开 | 连接重置 | 应用层拒绝、SSL/TLS 握手失败 |
UDP 的特殊性
UDP 没有连接建立过程,判断"通不通"比 TCP 更复杂:
UDP 端口未监听时,内核返回 ICMP Port Unreachable
如果防火墙丢弃了 ICMP 响应,客户端无法区分"端口不通"和"数据包丢失"
UDP 探测的可靠方法是发送应用层协议数据包并观察是否有响应
1.2 端口不通的七个排查层级
按照从近到远、从简单到复杂的原则,端口不通的排查可以分为七个层级:
第一层:服务监听检查(本机) ↓ 第二层:本地防火墙(iptables/nftables/firewalld) ↓ 第三层:SELinux 端口策略 ↓ 第四层:本机连通性测试(telnet/nc/curl) ↓ 第五层:网络路径排查(traceroute/mtr) ↓ 第六层:云安全组 / 网络 ACL ↓ 第七层:应用层协议检查
每一层只要确认没问题,就往下一层走。这种分层排查法避免了无头苍蝇式的排查。
1.3 适用场景
部署新服务后外部无法访问
服务迁移后端口不通
网络变更后部分端口受影响
容器化部署中的端口映射问题
跨云/跨机房的连通性故障
自动化巡检中的端口健康检查
1.4 环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 24.04 LTS / Rocky Linux 9.5 | 内核 6.12+ |
| iproute2 | 6.x | 提供 ss 命令 |
| nftables | 1.1.x | 新一代防火墙框架 |
| firewalld | 2.x | 高级防火墙管理 |
| nmap | 7.95+ | 端口扫描与探测 |
| ncat/nc | nmap 附带 | 连通性测试 |
| mtr | 0.95+ | 网络路径分析 |
# Ubuntu 24.04 安装必要工具 sudo apt install -y iproute2 nmap netcat-openbsd traceroute mtr-tiny tcpdump # Rocky Linux 9.5 安装 sudo dnf install -y iproute nmap ncat traceroute mtr tcpdump
二、详细步骤
2.1 准备工作
在排查端口不通之前,先明确几个关键信息:
# 确认目标 IP 和端口 TARGET_IP="192.168.1.100" TARGET_PORT="8080" # 确认本机 IP ip addr show | grep"inet "| grep -v 127.0.0.1 # 确认本机到目标的基本网络连通性 ping -c 3$TARGET_IP
如果 ping 不通,问题可能在更底层(网络层),需要先解决 IP 可达性问题。但注意:ping 不通不代表端口不通——很多环境禁止 ICMP 但允许 TCP。
2.2 第一层:服务是否监听
这是最常见的原因,也是最先排查的。
使用 ss 命令(推荐)
# 查看所有 TCP 监听端口
ss -tlnp
# 查看特定端口是否在监听
ss -tlnp | grep":8080"
# 输出示例
# LISTEN 0 4096 *:8080 *:* users:(("java",pid=12345,fd=88))
ss命令参数解释:
-t:只显示 TCP
-l:只显示 LISTEN 状态
-n:数字格式显示端口(不做 DNS 解析)
-p:显示进程信息
监听地址的区别
# 查看监听地址 ss -tlnp | grep":8080"
监听地址有三种情况,含义完全不同:
| 监听地址 | 含义 | 外部是否可访问 |
|---|---|---|
| 0.0.0.0:8080 | 监听所有 IPv4 地址 | 可以 |
| *:8080 或:::8080 | 监听所有地址(含 IPv6) | 可以 |
| 127.0.0.1:8080 | 只监听本地回环 | 不可以 |
| 192.168.1.100:8080 | 只监听特定网卡 | 仅该网卡可达 |
最常见的坑:应用配置文件里 bind 地址写了127.0.0.1或localhost,导致只能本机访问。
# 典型的配置错误排查 # Nginx grep -r"listen"/etc/nginx/nginx.conf /etc/nginx/conf.d/ # MySQL grep"bind-address"/etc/mysql/mysql.conf.d/mysqld.cnf # Redis grep"bind"/etc/redis/redis.conf # Java 应用的 application.yml grep -r"server.address"/path/to/app/
使用 netstat(兼容方案)
# 部分老系统还在用 netstat netstat -tlnp | grep":8080"
netstat已经被ss取代,但在某些极简容器镜像中可能只有netstat。功能上两者等价。
服务未监听的常见原因
| 原因 | 排查命令 | 解决方法 |
|---|---|---|
| 服务未启动 |
systemctl status |
启动服务 |
| 服务启动失败 |
journalctl -u |
查看错误日志 |
| 端口被其他进程占用 |
ss -tlnp | grep ": |
停止冲突进程或更换端口 |
| 配置文件端口写错 | 检查服务配置文件 | 修正端口配置 |
| bind 地址错误 | 检查 bind/listen 配置 | 改为 0.0.0.0 |
2.3 第二层:本地防火墙
确认服务已经在监听后,下一步检查本机防火墙是否放行了该端口。
firewalld(CentOS/Rocky Linux 默认)
# 查看 firewalld 是否运行 systemctl status firewalld # 查看当前活动区域和规则 firewall-cmd --get-active-zones firewall-cmd --zone=public --list-all # 查看是否放行了特定端口 firewall-cmd --zone=public --query-port=8080/tcp # 放行端口(临时,重启失效) sudo firewall-cmd --zone=public --add-port=8080/tcp # 放行端口(永久) sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent sudo firewall-cmd --reload # 放行服务(推荐方式,比端口号更语义化) sudo firewall-cmd --zone=public --add-service=http --permanent sudo firewall-cmd --reload
nftables(新一代框架)
Ubuntu 24.04 默认使用 nftables 作为底层框架:
# 查看当前规则集 sudo nft list ruleset # 查看特定链的规则 sudo nft list chain inet filter input # 添加放行规则 sudo nft add rule inet filter input tcp dport 8080 accept # 查看规则编号(用于删除) sudo nft -a list chain inet filter input # 删除指定规则 sudo nft delete rule inet filter input handle 15
iptables(传统方式)
# 查看所有规则(带行号) sudo iptables -L -n -v --line-numbers # 查看 INPUT 链 sudo iptables -L INPUT -n -v --line-numbers # 检查是否有 DROP/REJECT 规则拦截了目标端口 sudo iptables -L INPUT -n | grep -E"DROP|REJECT" # 添加放行规则(插入到 INPUT 链的第一条) sudo iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT # 持久化规则 sudo netfilter-persistent save # Ubuntu/Debian sudo service iptables save # CentOS/RHEL
防火墙排查要点
# 快速判断是否是防火墙问题的方法:临时关闭防火墙测试 # 注意:仅在测试环境使用,生产环境禁止关闭防火墙 # 查看 iptables 规则计数器,确认数据包是否命中了 DROP 规则 sudo iptables -L -n -v | grep -i drop # 实时监控防火墙日志 sudo journalctl -f -t kernel | grep -i"iptables|nft|BLOCKED"
一个容易忽略的坑:Docker 会自动在 iptables 中创建 DOCKER 链和 NAT 规则。如果你在 Docker 宿主机上排查端口问题,需要同时检查 DOCKER-USER 链:
# Docker 相关的 iptables 规则 sudo iptables -L DOCKER -n -v sudo iptables -L DOCKER-USER -n -v sudo iptables -t nat -L -n -v
2.4 第三层:SELinux 端口策略
在 Rocky Linux / CentOS 等开启 SELinux 的系统上,即使防火墙放行了端口,SELinux 也可能阻止服务绑定非标准端口。
# 查看 SELinux 状态 getenforce # 输出 Enforcing 表示开启,Permissive 表示仅记录不阻断 # 查看 SELinux 允许的 HTTP 端口 semanage port -l | grep http_port_t # http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 # 如果需要 Nginx 监听 8080 端口 sudo semanage port -a -t http_port_t -p tcp 8080 # 查看 SELinux 拒绝日志 sudo ausearch -m avc -ts recent # 查看 audit.log 中的 SELinux 拒绝记录 sudo grep"denied"/var/log/audit/audit.log | tail -10 # 生成允许策略(排查时使用) sudo ausearch -m avc -ts recent | audit2allow -w
SELinux 导致端口不通的典型特征:
ss -tlnp看不到服务在目标端口监听
服务日志报 "Permission denied" 或 "Address already in use"(误导性信息)
audit.log中有type=AVC msg=audit(...): avc: denied记录
2.5 第四层:本机连通性测试
服务监听正常、防火墙已放行,接下来从其他机器测试连通性。
telnet
# 最基础的 TCP 端口测试 telnet 192.168.1.100 8080 # 连接成功的输出 # Trying 192.168.1.100... # Connected to 192.168.1.100. # Escape character is '^]'. # 连接失败(被拒绝)的输出 # Trying 192.168.1.100... # telnet: Unable to connect to remote host: Connection refused # 连接超时的输出 # Trying 192.168.1.100... # (长时间无响应)
nc (netcat)
# TCP 端口测试(推荐,比 telnet 更灵活) nc -zv 192.168.1.100 8080 # 设置超时时间(避免长时间等待) nc -zv -w 5 192.168.1.100 8080 # 扫描端口范围 nc -zv 192.168.1.100 8080-8090 # UDP 端口测试 nc -zuv -w 3 192.168.1.100 53 # 参数说明 # -z: 只扫描,不发送数据 # -v: 详细输出 # -w: 超时时间(秒) # -u: UDP 模式
curl(HTTP/HTTPS 服务)
# HTTP 服务测试
curl -v http://192.168.1.100:8080/health
# 只测试连通性,不关心响应内容
curl -o /dev/null -s -w"HTTP Code: %{http_code}
Time: %{time_total}s
"http://192.168.1.100:8080/
# HTTPS 服务测试(忽略证书验证)
curl -vk https://192.168.1.100:443/
# 指定超时
curl --connect-timeout 5 --max-time 10 http://192.168.1.100:8080/
nmap(全面端口扫描)
# 扫描单个端口 nmap -p 8080 192.168.1.100 # 扫描多个端口 nmap -p 80,443,8080 192.168.1.100 # 扫描端口范围 nmap -p 1-1024 192.168.1.100 # 扫描结果解读 # PORT STATE SERVICE # 8080/tcp open http-proxy <-- 端口开放 # 8081/tcp closed tproxy <-- 端口关闭(RST) # 8082/tcp filtered unknown <-- 被防火墙过滤(DROP)
nmap 端口状态含义:
| 状态 | 含义 | 对应情况 |
|---|---|---|
| open | 端口开放 | 有服务监听且防火墙放行 |
| closed | 端口关闭 | 无服务监听但防火墙放行(返回 RST) |
| filtered | 被过滤 | 防火墙 DROP,无任何响应 |
| unfiltered | 未过滤 | ACK 扫描可达但无法确定开放/关闭 |
2.6 第五层:网络路径排查
从客户端能 ping 通目标但端口不通时,问题可能在中间路径。
traceroute
# 基于 ICMP 的路由追踪 traceroute 192.168.1.100 # 基于 TCP 的路由追踪(穿透防火墙) sudo traceroute -T -p 8080 192.168.1.100 # 基于 UDP 的路由追踪 traceroute -U -p 8080 192.168.1.100
mtr(推荐)
# 实时路由追踪+丢包统计 mtr 192.168.1.100 # 基于 TCP 的 mtr sudo mtr --tcp -P 8080 192.168.1.100 # 报告模式(非交互) mtr -r -c 100 192.168.1.100 # 输出示例 # Loss% Snt Last Avg Best Wrst StDev # 1. gateway 0.0% 100 0.5 0.6 0.3 2.1 0.3 # 2. 10.0.0.1 0.0% 100 1.2 1.3 0.8 3.5 0.4 # 3. ??? 100.0% 100 0.0 0.0 0.0 0.0 0.0 # 4. 192.168.1.1 0.0% 100 2.1 2.3 1.5 5.2 0.6 # 5. 192.168.1.100 0.0% 100 2.3 2.5 1.8 5.8 0.7
mtr 结果解读:
某一跳 100% Loss 但后续正常:该设备不响应探测包,不代表故障
某一跳开始 Loss 升高且后续持续高丢包:该跳可能是瓶颈
最后一跳丢包:目标主机或其前面的设备有问题
tcpdump 辅助判断
在目标服务器上抓包,确认 SYN 包是否到达:
# 在目标服务器上抓包 sudo tcpdump -i any -nn port 8080 -c 20 # 如果能看到 SYN 包到达但没有 SYN-ACK 响应,说明是本机防火墙或服务问题 # 如果完全看不到 SYN 包,说明数据包在到达目标服务器之前就被拦截了 # 输出示例(正常连接) # 1701.123456 IP 10.0.0.5.54321 > 192.168.1.100.8080: Flags [S], seq 123456 # 1701.123567 IP 192.168.1.100.8080 > 10.0.0.5.54321: Flags [S.], seq 789012, ack 123457 # 1701.124012 IP 10.0.0.5.54321 > 192.168.1.100.8080: Flags [.], ack 789013 # 输出示例(SYN 到达但被 RST) # 1701.123456 IP 10.0.0.5.54321 > 192.168.1.100.8080: Flags [S], seq 123456 # 1701.123567 IP 192.168.1.100.8080 > 10.0.0.5.54321: Flags [R.], seq 0, ack 123457
2.7 第六层:云安全组 / 网络 ACL
在云环境中(AWS/阿里云/腾讯云),安全组是独立于操作系统防火墙之外的网络层控制。
# 阿里云 CLI 查看安全组规则 aliyun ecs DescribeSecurityGroupAttribute --SecurityGroupId sg-xxxxx # AWS CLI 查看安全组 aws ec2 describe-security-groups --group-ids sg-xxxxx # 腾讯云 CLI tccli cvm DescribeSecurityGroupPolicies --cli-unfold-argument --SecurityGroupId sg-xxxxx
云安全组排查要点:
| 检查项 | 说明 |
|---|---|
| 入站规则 | 是否允许目标端口的入站流量 |
| 出站规则 | 是否允许响应流量的出站 |
| 协议类型 | TCP/UDP 是否匹配 |
| 源地址范围 | 是否限制了客户端 IP 段 |
| 规则优先级 | 多条规则冲突时的优先级 |
| 安全组关联 | 实例是否绑定了正确的安全组 |
常见的云安全组陷阱:
安全组默认拒绝所有入站流量
修改安全组规则后不需要重启实例,但生效可能有几秒延迟
VPC 网络 ACL 和安全组是两层控制,两者都要放行
2.8 第七层:应用层协议检查
TCP 连接能建立但业务层面不通,问题在应用层协议。
# HTTP 应用层检查 curl -v http://192.168.1.100:8080/api/health 2>&1 # 检查返回状态码 # HTTP/1.1 200 OK -> 正常 # HTTP/1.1 502 Bad Gateway -> 后端服务不可达 # HTTP/1.1 503 Service Unavailable -> 服务过载 # SSL/TLS 握手检查 openssl s_client -connect 192.168.1.100:443 -servername example.com # 查看证书信息 openssl s_client -connect 192.168.1.100:443 2>/dev/null | openssl x509 -noout -dates -subject # gRPC 服务检查 grpcurl -plaintext 192.168.1.100:9090 list # MySQL 连接测试 mysql -h 192.168.1.100 -P 3306 -u root -p -e"SELECT 1" # Redis 连接测试 redis-cli -h 192.168.1.100 -p 6379 ping
2.9 验证与确认
完成排查并修复后,做一次完整的验证:
#!/bin/bash
# 端口连通性验证脚本
# port_verify.sh
TARGET=$1
PORT=$2
echo"========== 端口连通性验证 =========="
echo"目标:${TARGET}:${PORT}"
echo"时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo"===================================="
# 1. DNS 解析
echo-e"
[1] DNS 解析"
host$TARGET2>/dev/null ||echo"非域名或解析失败"
# 2. ICMP 连通性
echo-e"
[2] ICMP Ping"
ping -c 3 -W 2$TARGET2>/dev/null
if[ $? -ne 0 ];then
echo"ICMP 不通(可能被禁止)"
fi
# 3. TCP 端口测试
echo-e"
[3] TCP 端口测试"
nc -zv -w 5$TARGET$PORT2>&1
# 4. 路由追踪
echo-e"
[4] TCP 路由追踪"
sudo traceroute -T -p$PORT-m 15$TARGET2>/dev/null
echo-e"
========== 验证完成 =========="
三、示例代码和配置
3.1 完整的端口排查流程脚本
#!/bin/bash
# port_diagnose.sh - 端口不通问题自动诊断脚本
# 用法: ./port_diagnose.sh <目标IP> <端口号>
set-euo pipefail
# 颜色定义
RED='�33[0;31m'
GREEN='�33[0;32m'
YELLOW='�33[1;33m'
NC='�33[0m'
TARGET_IP="${1:?用法: $0 <目标IP> <端口号>}"
TARGET_PORT="${2:?用法: $0 <目标IP> <端口号>}"
log_pass() {echo-e"${GREEN}[PASS]${NC}$1"; }
log_fail() {echo-e"${RED}[FAIL]${NC}$1"; }
log_warn() {echo-e"${YELLOW}[WARN]${NC}$1"; }
log_info() {echo-e"[INFO]$1"; }
echo"============================================"
echo" 端口连通性诊断工具"
echo" 目标:${TARGET_IP}:${TARGET_PORT}"
echo" 时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo"============================================"
# 第一层:检查是否为本机
IS_LOCAL=false
ifip addr show | grep -q"${TARGET_IP}";then
IS_LOCAL=true
log_info"目标 IP 是本机地址"
fi
# 第二层:如果是本机,检查服务监听
if$IS_LOCAL;then
echo-e"
--- 第一层:服务监听检查 ---"
LISTEN_INFO=$(ss -tlnp | grep":${TARGET_PORT}"||true)
if[ -n"$LISTEN_INFO"];then
log_pass"端口${TARGET_PORT}有服务在监听"
echo" $LISTEN_INFO"
# 检查监听地址
LISTEN_ADDR=$(echo"$LISTEN_INFO"| awk'{print $4}'| cut -d: -f1)
if["$LISTEN_ADDR"="127.0.0.1"];then
log_warn"服务仅监听 127.0.0.1,外部无法访问"
fi
else
log_fail"端口${TARGET_PORT}没有服务在监听"
log_info"可能原因:服务未启动、配置端口不对、端口被占用"
fi
# 检查防火墙
echo-e"
--- 第二层:防火墙检查 ---"
ifcommand-v firewall-cmd &>/dev/null;then
ifsystemctl is-active --quiet firewalld;then
FW_RESULT=$(firewall-cmd --query-port=${TARGET_PORT}/tcp 2>/dev/null ||true)
if["$FW_RESULT"="yes"];then
log_pass"firewalld 已放行端口${TARGET_PORT}/tcp"
else
log_fail"firewalld 未放行端口${TARGET_PORT}/tcp"
fi
else
log_info"firewalld 未运行"
fi
fi
# 检查 iptables
ifcommand-v iptables &>/dev/null;then
DROP_RULES=$(sudo iptables -L INPUT -n 2>/dev/null | grep -c"DROP|REJECT"||true)
if["$DROP_RULES"-gt 0 ];then
log_warn"iptables INPUT 链有${DROP_RULES}条 DROP/REJECT 规则"
else
log_pass"iptables INPUT 链无 DROP/REJECT 规则"
fi
fi
# 检查 SELinux
echo-e"
--- 第三层:SELinux 检查 ---"
ifcommand-v getenforce &>/dev/null;then
SELINUX_STATUS=$(getenforce)
if["$SELINUX_STATUS"="Enforcing"];then
log_warn"SELinux 处于 Enforcing 模式"
AVC_DENIED=$(sudo ausearch -m avc -ts recent 2>/dev/null | grep -c"denied"||true)
if["$AVC_DENIED"-gt 0 ];then
log_fail"发现${AVC_DENIED}条 SELinux 拒绝记录"
fi
else
log_info"SELinux 状态:$SELINUX_STATUS"
fi
else
log_info"SELinux 未安装"
fi
fi
# 第四层:连通性测试
echo-e"
--- 第四层:连通性测试 ---"
# TCP 测试
ifnc -zv -w 5"$TARGET_IP""$TARGET_PORT"2>&1 | grep -q"succeeded|Connected|open";then
log_pass"TCP 连接到${TARGET_IP}:${TARGET_PORT}成功"
else
log_fail"TCP 连接到${TARGET_IP}:${TARGET_PORT}失败"
# 进一步诊断
echo-e"
--- 第五层:网络路径分析 ---"
log_info"执行路由追踪..."
ifcommand-v mtr &>/dev/null;then
mtr -r -c 10 --tcp -P"$TARGET_PORT""$TARGET_IP"2>/dev/null ||
mtr -r -c 10"$TARGET_IP"2>/dev/null
else
traceroute -m 15"$TARGET_IP"2>/dev/null ||true
fi
fi
# nmap 扫描
echo-e"
--- 端口状态扫描 ---"
ifcommand-v nmap &>/dev/null;then
NMAP_RESULT=$(nmap -p"$TARGET_PORT""$TARGET_IP"2>/dev/null | grep"^${TARGET_PORT}"||true)
if[ -n"$NMAP_RESULT"];then
log_info"nmap 结果:$NMAP_RESULT"
ifecho"$NMAP_RESULT"| grep -q"filtered";then
log_warn"端口被防火墙过滤(DROP),数据包未到达目标"
fi
fi
fi
echo-e"
============================================"
echo" 诊断完成"
echo"============================================"
3.2 案例 1:firewalld 规则遗漏导致端口不通
场景:部署了一个新的 Java 应用,监听 8080 端口,本机 curl 正常,但外部访问超时。
排查过程:
# 1. 确认服务在监听
ss -tlnp | grep":8080"
# LISTEN 0 4096 *:8080 *:* users:(("java",pid=15234,fd=88))
# 结论:服务正常监听在所有接口
# 2. 本机测试
curl -s -o /dev/null -w"%{http_code}"http://127.0.0.1:8080/health
# 200
# 结论:本机访问正常
# 3. 检查防火墙
systemctl status firewalld
# Active: active (running)
firewall-cmd --zone=public --list-all
# public (active)
# target: default
# services: cockpit dhcpv6-client ssh
# ports:
# 结论:firewalld 运行中,未放行 8080 端口
# 4. 放行端口
sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
sudo firewall-cmd --reload
# 5. 验证
firewall-cmd --zone=public --query-port=8080/tcp
# yes
# 从外部再次测试
# curl http://192.168.1.100:8080/health
# 200 OK
3.3 案例 2:SELinux 阻止非标准端口
场景:将 Nginx 的监听端口从 80 改为 9090 后,Nginx 启动失败。
# 1. 查看 Nginx 状态 sudo systemctl status nginx # nginx.service - The nginx HTTP and reverse proxy server # Active: failed # 2. 查看错误日志 sudo journalctl -u nginx --no-pager -n 20 # nginx: [emerg] bind() to 0.0.0.0:9090 failed (13: Permission denied) # 3. 检查 SELinux getenforce # Enforcing # 4. 查看 SELinux 允许的 HTTP 端口 sudo semanage port -l | grep http_port_t # http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 # 结论:9090 不在允许列表中 # 5. 添加 9090 到 HTTP 端口类型 sudo semanage port -a -t http_port_t -p tcp 9090 # 6. 重启 Nginx sudo systemctl start nginx sudo systemctl status nginx # Active: active (running) # 7. 验证 ss -tlnp | grep":9090" # LISTEN 0 511 0.0.0.0:9090 0.0.0.0:* users:(("nginx",...))
3.4 案例 3:Docker 容器端口映射异常
场景:运行 Docker 容器时指定了-p 8080:80,但外部无法访问 8080 端口。
# 1. 确认容器运行状态 docker ps | grep myapp # abc123 myapp:latest "nginx -g ..." Up 5 minutes 0.0.0.0:8080->80/tcp myapp # 2. 检查端口映射 docker port myapp # 80/tcp -> 0.0.0.0:8080 # 3. 容器内部测试 dockerexecmyapp curl -s http://127.0.0.1:80/ # 正常响应 # 4. 宿主机测试 curl -s http://127.0.0.1:8080/ # 正常响应 # 5. 外部测试失败,检查 Docker 的 iptables 规则 sudo iptables -L DOCKER -n -v # Chain DOCKER (1 references) # pkts bytes target prot opt in out source destination # 125 6500 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:80 # 6. 检查 DOCKER-USER 链(用户自定义规则会在这里) sudo iptables -L DOCKER-USER -n -v # Chain DOCKER-USER (1 references) # pkts bytes target prot opt in out source destination # 0 0 DROP all -- eth0 * 0.0.0.0/0 0.0.0.0/0 # 结论:DOCKER-USER 链有一条 DROP 规则阻止了所有从 eth0 进入的流量 # 7. 修复:删除错误的 DROP 规则 sudo iptables -D DOCKER-USER -i eth0 -j DROP # 添加正确的规则(只允许特定端口) sudo iptables -I DOCKER-USER -i eth0 -p tcp --dport 80 -j ACCEPT sudo iptables -A DOCKER-USER -j RETURN # 8. 验证 curl -s http://192.168.1.100:8080/ # 正常响应
3.5 端口连通性批量检测脚本
#!/bin/bash
# batch_port_check.sh - 批量端口检测脚本
# 用法: ./batch_port_check.sh <配置文件>
# 配置文件格式: IP:端口:服务名(每行一个)
CONFIG_FILE="${1:?用法: $0 <配置文件>}"
TIMEOUT=5
TIMESTAMP=$(date'+%Y-%m-%d %H:%M:%S')
TOTAL=0
PASS=0
FAIL=0
RED='�33[0;31m'
GREEN='�33[0;32m'
NC='�33[0m'
echo"=========================================="
echo" 批量端口连通性检测"
echo" 时间:${TIMESTAMP}"
echo"=========================================="
echo""
printf"%-20s %-8s %-15s %-10s
""目标""端口""服务""状态"
printf"%-20s %-8s %-15s %-10s
""----""----""----""----"
whileIFS=:read-r ip port service;do
# 跳过空行和注释
[[ -z"$ip"||"$ip"=~ ^# ]] && continue
TOTAL=$((TOTAL + 1))
ifnc -zv -w$TIMEOUT"$ip""$port"&>/dev/null;then
printf"%-20s %-8s %-15s${GREEN}%-10s${NC}
""$ip""$port""$service""通"
PASS=$((PASS + 1))
else
printf"%-20s %-8s %-15s${RED}%-10s${NC}
""$ip""$port""$service""不通"
FAIL=$((FAIL + 1))
fi
done< "$CONFIG_FILE"
echo ""
echo "=========================================="
echo " 检测完成: 总计 ${TOTAL}, 通过 ${PASS}, 失败 ${FAIL}"
echo "=========================================="
# 退出码:有失败则返回 1
[ $FAIL -eq 0 ] && exit 0 || exit 1
配置文件示例:
# port_check.conf # 格式: IP:端口:服务名 192.168.1.100Nginx-HTTP 192.168.1.100Nginx-HTTPS 192.168.1.101MySQL 192.168.1.101Redis 192.168.1.102Prometheus 192.168.1.102Grafana 10.0.0.50App-Server-1 10.0.0.51App-Server-2
四、最佳实践和注意事项
4.1 最佳实践
防火墙规则管理规范
# 1. 使用服务名而非端口号(firewalld) # 推荐:语义化更强 sudo firewall-cmd --add-service=http --permanent # 不推荐:端口号不直观 sudo firewall-cmd --add-port=80/tcp --permanent # 2. 使用 rich rules 实现精细控制 # 只允许特定 IP 段访问 MySQL sudo firewall-cmd --permanent --add-rich-rule=' rule family="ipv4" source address="10.0.0.0/24" port port="3306" protocol="tcp" accept' # 3. 防火墙规则版本化管理 # 导出当前规则 sudo iptables-save > /etc/iptables/rules.v4.$(date +%Y%m%d) sudo nft list ruleset > /etc/nftables.conf.$(date +%Y%m%d) # 4. 变更前备份 sudo cp /etc/firewalld/zones/public.xml /etc/firewalld/zones/public.xml.bak
端口变更 Checklist
| 序号 | 检查项 | 负责人 | 完成 |
|---|---|---|---|
| 1 | 新端口在应用配置中已修改 | 开发 | |
| 2 | 操作系统防火墙已放行 | 运维 | |
| 3 | SELinux 策略已更新 | 运维 | |
| 4 | 云安全组/网络 ACL 已更新 | 运维 | |
| 5 | 负载均衡器后端端口已更新 | 运维 | |
| 6 | DNS 记录已更新(如有) | 运维 | |
| 7 | 监控探测端口已更新 | 运维 | |
| 8 | 文档已更新 | 运维 | |
| 9 | 回滚方案已准备 | 运维 |
安全加固建议
# 1. 只开放必要的端口,默认拒绝所有
sudo firewall-cmd --set-default-zone=drop
sudo firewall-cmd --zone=drop --add-service=ssh --permanent
# 2. 限制 SSH 来源 IP
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="10.0.0.0/8"
service name="ssh"
accept'
# 3. 定期审查开放端口
ss -tlnp | awk'NR>1 {print $4}'| sort -u
# 4. 关闭不需要的服务
sudo systemctldisable--now rpcbind
sudo systemctldisable--now avahi-daemon
4.2 注意事项
常见错误
| 错误现象 | 原因分析 | 解决方案 |
|---|---|---|
| firewall-cmd 规则添加后不生效 | 未加 --permanent 或未 reload | 加上 --permanent 并执行 --reload |
| iptables 规则重启后丢失 | 未持久化规则 | 使用 netfilter-persistent save |
| Docker 端口映射不通 | DOCKER-USER 链有 DROP 规则 | 检查并修复 DOCKER-USER 链 |
| telnet 超时但 ping 通 | 防火墙 DROP TCP 但放行 ICMP | 检查防火墙 TCP 规则 |
| 本机 curl 通但外部不通 | 服务绑定 127.0.0.1 | 修改 bind 地址为 0.0.0.0 |
| SELinux 拒绝非标准端口 | 端口不在 SELinux 策略中 | semanage port 添加端口 |
| nmap 显示 filtered | 中间设备 DROP 数据包 | 检查云安全组/网络 ACL |
| 端口状态时通时断 | 连接数超限或负载均衡问题 | 检查 conntrack 表和负载均衡 |
兼容性注意事项
ss是iproute2包的组成部分,在所有现代 Linux 发行版中默认安装
netstat属于net-tools包,在最小安装中可能不存在
nftables是 iptables 的替代者,但 iptables 命令会自动转换为 nftables 规则
firewalld 2.x 默认使用 nftables 后端
Docker 仍然依赖 iptables 规则,即使系统使用 nftables
容器环境特殊注意
# Kubernetes 中的端口排查 # 1. 检查 Service 端口映射 kubectl get svc -n# 2. 检查 Endpoints kubectl get endpoints -n # 3. 检查 Pod 端口 kubectl get pod -n -o jsonpath='{.spec.containers[*].ports}' # 4. 检查 NetworkPolicy kubectl get networkpolicy -n # 5. 在 Pod 内部测试 kubectlexec-it -n -- nc -zv
五、故障排查和监控
5.1 故障排查
日志查看
# firewalld 日志 sudo journalctl -u firewalld -n 50 --no-pager # iptables 日志(需要先添加 LOG 规则) sudo iptables -I INPUT -p tcp --dport 8080 -j LOG --log-prefix"PORT8080: " sudo journalctl -k | grep"PORT8080" # SELinux 拒绝日志 sudo ausearch -m avc -ts recent sudo sealert -a /var/log/audit/audit.log # 内核网络栈日志 dmesg | grep -i"nf_conntrack|dropped|martian"
conntrack 表排查
# 查看连接跟踪表大小 cat /proc/sys/net/netfilter/nf_conntrack_count cat /proc/sys/net/netfilter/nf_conntrack_max # 如果 count 接近 max,新连接会被丢弃 # 临时增大连接跟踪表 sudo sysctl -w net.netfilter.nf_conntrack_max=1048576 # 查看特定端口的连接跟踪记录 sudo conntrack -L -p tcp --dport 8080 2>/dev/null | head -20 # 清除特定连接 sudo conntrack -D -p tcp --dport 8080
常见问题排查表
| 问题 | 快速诊断命令 | 预期结果 |
|---|---|---|
| 服务是否监听 |
ss -tlnp | grep : |
看到 LISTEN 行 |
| 防火墙是否放行 |
firewall-cmd --query-port= |
yes |
| SELinux 是否阻止 | ausearch -m avc -ts recent | 无相关拒绝记录 |
| 数据包是否到达 |
tcpdump -i any port |
看到 SYN 包 |
| 路径是否可达 |
mtr --tcp -P |
最后一跳无丢包 |
| conntrack 是否满 | cat /proc/sys/net/netfilter/nf_conntrack_count | 远小于 max 值 |
5.2 性能监控
Blackbox Exporter 端口探测
Prometheus 的 blackbox_exporter 是端口连通性监控的标准方案。
# blackbox.yml - blackbox_exporter 配置 modules: tcp_connect: prober:tcp timeout:5s tcp: preferred_ip_protocol:"ip4" tcp_connect_tls: prober:tcp timeout:5s tcp: preferred_ip_protocol:"ip4" tls:true tls_config: insecure_skip_verify:false http_2xx: prober:http timeout:10s http: valid_http_versions:["HTTP/1.1","HTTP/2.0"] valid_status_codes:[200,204] method:GET preferred_ip_protocol:"ip4" icmp_check: prober:icmp timeout:5s icmp: preferred_ip_protocol:"ip4"
# prometheus.yml - Prometheus 抓取配置 scrape_configs: -job_name:'blackbox-tcp' metrics_path:/probe params: module:[tcp_connect] static_configs: -targets: -'192.168.1.100:8080' -'192.168.1.101:3306' -'192.168.1.102:6379' relabel_configs: -source_labels:[__address__] target_label:__param_target -source_labels:[__param_target] target_label:instance -target_label:__address__ replacement:'blackbox-exporter:9115' -job_name:'blackbox-http' metrics_path:/probe params: module:[http_2xx] static_configs: -targets: -'http://192.168.1.100:80/health' -'https://192.168.1.100:443/health' relabel_configs: -source_labels:[__address__] target_label:__param_target -source_labels:[__param_target] target_label:instance -target_label:__address__ replacement:'blackbox-exporter:9115'
Prometheus 告警规则
# port_alerts.yml
groups:
-name:port_connectivity
rules:
-alert:PortUnreachable
expr:probe_success{job="blackbox-tcp"}==0
for:2m
labels:
severity:critical
annotations:
summary:"端口不可达:{{ $labels.instance }}"
description:"目标{{ $labels.instance }}的端口连通性检测失败,已持续 2 分钟"
-alert:PortHighLatency
expr:probe_duration_seconds{job="blackbox-tcp"}>1
for:5m
labels:
severity:warning
annotations:
summary:"端口响应延迟高:{{ $labels.instance }}"
description:"目标{{ $labels.instance }}的 TCP 连接耗时超过 1 秒"
-alert:SSLCertExpiringSoon
expr:probe_ssl_earliest_cert_expiry-time()< 86400 * 30
for: 1h
labels:
severity: warning
annotations:
summary: "SSL 证书即将过期: {{ $labels.instance }}"
description: "证书将在 {{ $value | humanizeDuration }} 后过期"
- alert: HTTPStatusError
expr: probe_http_status_code{job="blackbox-http"} != 200
for: 3m
labels:
severity: critical
annotations:
summary: "HTTP 状态异常: {{ $labels.instance }}"
description: "HTTP 状态码: {{ $value }}"
关键监控指标
| 指标名称 | 含义 | 正常范围 | 告警阈值 |
|---|---|---|---|
| probe_success | 探测是否成功 | 1 | == 0 持续 2 分钟 |
| probe_duration_seconds | 探测耗时 | < 0.1s | > 1s 持续 5 分钟 |
| probe_dns_lookup_time_seconds | DNS 解析耗时 | < 0.05s | > 0.5s |
| probe_ssl_earliest_cert_expiry | SSL 证书过期时间 | > 30 天 | < 30 天 |
| node_netfilter_conntrack_entries | conntrack 条目数 | < max 的 70% | > max 的 80% |
5.3 备份与恢复
防火墙规则备份
#!/bin/bash
# firewall_backup.sh - 防火墙规则备份脚本
BACKUP_DIR="/opt/backup/firewall"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p"$BACKUP_DIR"
# 备份 iptables 规则
ifcommand-v iptables-save &>/dev/null;then
sudo iptables-save >"${BACKUP_DIR}/iptables_${DATE}.rules"
sudo ip6tables-save >"${BACKUP_DIR}/ip6tables_${DATE}.rules"
echo"iptables 规则已备份"
fi
# 备份 nftables 规则
ifcommand-v nft &>/dev/null;then
sudo nft list ruleset >"${BACKUP_DIR}/nftables_${DATE}.conf"
echo"nftables 规则已备份"
fi
# 备份 firewalld 配置
if[ -d /etc/firewalld ];then
tar czf"${BACKUP_DIR}/firewalld_${DATE}.tar.gz"/etc/firewalld/
echo"firewalld 配置已备份"
fi
# 保留最近 30 天的备份
find"$BACKUP_DIR"-name"*.rules"-mtime +30 -delete
find"$BACKUP_DIR"-name"*.conf"-mtime +30 -delete
find"$BACKUP_DIR"-name"*.tar.gz"-mtime +30 -delete
echo"备份完成:${BACKUP_DIR}"
规则恢复
# 恢复 iptables 规则 sudo iptables-restore < /opt/backup/firewall/iptables_20260313.rules # 恢复 nftables 规则 sudo nft -f /opt/backup/firewall/nftables_20260313.conf # 恢复 firewalld 配置 sudo tar xzf /opt/backup/firewall/firewalld_20260313.tar.gz -C / sudo firewall-cmd --reload
六、总结
6.1 技术要点回顾
端口不通问题需要分层排查:服务监听 → 防火墙 → SELinux → 网络路径 → 云安全组 → 应用层
ss -tlnp是确认服务监听的第一选择,重点关注监听地址(0.0.0.0 vs 127.0.0.1)
防火墙排查需要同时检查 firewalld/iptables/nftables,Docker 环境还要检查 DOCKER-USER 链
SELinux 在 Enforcing 模式下会阻止非标准端口绑定,使用semanage port添加
nmap的端口状态(open/closed/filtered)能精确反映问题所在层级
云环境需要额外检查安全组和网络 ACL,这些是独立于操作系统之外的控制
使用 blackbox_exporter 做端口连通性的持续监控
6.2 进阶学习方向
eBPF 网络追踪:使用 bpftrace 追踪内核网络栈中数据包的处理路径,精确定位丢包位置
Kubernetes 网络策略:NetworkPolicy、Calico/Cilium 网络插件的端口控制机制
服务网格:Istio/Linkerd 环境中 sidecar 代理对端口通信的影响
零信任网络:基于身份而非 IP/端口的访问控制架构
6.3 参考资料
Linux man pages: ss(8), iptables(8), nft(8), firewall-cmd(1)
nmap 官方文档: https://nmap.org/book/
Prometheus Blackbox Exporter: https://github.com/prometheus/blackbox_exporter
firewalld 官方文档: https://firewalld.org/documentation/
Red Hat SELinux 管理指南: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/using_selinux
附录
A. 命令速查表
# 服务监听检查 ss -tlnp # 查看所有 TCP 监听 ss -tlnp | grep":" # 查看特定端口 ss -ulnp # 查看所有 UDP 监听 # 防火墙操作 firewall-cmd --list-all # 查看当前规则 firewall-cmd --add-port=8080/tcp --permanent # 添加端口 firewall-cmd --reload # 重载规则 sudo iptables -L -n -v # iptables 规则 sudo nft list ruleset # nftables 规则 # SELinux getenforce # 查看状态 semanage port -l | grep # 查看端口策略 semanage port -a -t -p tcp # 添加端口 # 连通性测试 nc -zv -w 5 # TCP 测试 nmap -p # 端口扫描 curl -v http:// : / # HTTP 测试 telnet # 基础测试 # 路径分析 mtr --tcp -P # TCP 路由追踪 traceroute -T -p # TCP traceroute tcpdump -i any port -nn # 抓包确认 # conntrack conntrack -L -p tcp --dport # 查看连接跟踪 conntrack -D -p tcp --dport # 清除连接 cat /proc/sys/net/netfilter/nf_conntrack_count # 当前连接数
B. 配置参数详解
firewalld 区域默认行为
| 区域 | 默认行为 | 适用场景 |
|---|---|---|
| drop | 丢弃所有入站,不响应 | 高安全需求 |
| block | 拒绝所有入站,返回 ICMP 拒绝 | 安全需求 |
| public | 只允许指定服务 | 公网服务器 |
| external | NAT 转发 | 网关服务器 |
| dmz | 只允许指定服务 | DMZ 区域 |
| work | 信任同事网络 | 办公网络 |
| home | 信任家庭网络 | 家庭环境 |
| internal | 信任内部网络 | 内网服务器 |
| trusted | 允许所有流量 | 完全信任网络 |
nmap 常用扫描类型
| 参数 | 扫描类型 | 原理 | 适用场景 |
|---|---|---|---|
| -sT | TCP Connect | 完整三次握手 | 普通用户 |
| -sS | TCP SYN | 半开扫描 | 需要 root |
| -sU | UDP | 发送 UDP 包 | UDP 服务 |
| -sA | TCP ACK | 判断防火墙规则 | 防火墙检测 |
| -sN | TCP Null | 不设置任何标志 | 绕过简单过滤 |
C. 术语表
| 术语 | 英文 | 解释 |
|---|---|---|
| 三次握手 | Three-Way Handshake | TCP 建立连接的过程:SYN → SYN-ACK → ACK |
| 连接跟踪 | Connection Tracking | 内核记录网络连接状态的机制(conntrack) |
| 有状态防火墙 | Stateful Firewall | 能识别连接状态的防火墙,只需放行首包 |
| 安全组 | Security Group | 云平台的虚拟防火墙,控制实例级别的网络访问 |
| ACL | Access Control List | 网络访问控制列表,控制子网级别的流量 |
| BPF | Berkeley Packet Filter | 内核数据包过滤框架,用于 tcpdump 等工具 |
| RST | Reset | TCP 连接重置标志,表示拒绝连接 |
| DNAT | Destination NAT | 目标地址转换,用于端口映射/转发 |
| Well-Known Ports | 知名端口 | 0-1023 范围内的系统保留端口 |
| Ephemeral Ports | 临时端口 | 49152-65535 范围内的动态分配端口 |
-
TCP
+关注
关注
8文章
1432浏览量
83757 -
端口
+关注
关注
4文章
1107浏览量
34037 -
网络通信
+关注
关注
4文章
845浏览量
32651
原文标题:「运维必备」端口连接异常排查全流程思路
文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
Linux系统CPU占用率100%的排查思路
科普小课堂|LCD 问题排查思路解析
端口连接异常排查全流程思路
评论