在华为云 ECS 上跑容器:host 与 bridge 模式怎么选,端口不通怎么查

举报
yd_42677603 发表于 2026/10/31 17:40:54 2026/10/31
【摘要】 容器在跑、端口也开放了,外网就是连不上?本文梳理云主机上从安全组到容器监听的四层网络关卡,给出 host 与 bridge 两种网络模式的取舍,并用 tcpdump 抓包定性的一套五步排查流程。

一篇真实的排查记录

把服务容器化部署到华为云 ECS 后,最常见的一类故障是:"我明明开放了端口,容器也在跑,为什么外网访问不了?"

我自己在这台 2 vCPU 的 ECS 上排查过这个问题,前后花了小半天,最后发现问题根本不在服务器上,而在安全组那一层。这篇就把整个排查链和结论写清楚,顺带讲讲 host 和 bridge 两种网络模式该怎么选。

一、先把网络层级理清楚

一台运行着容器的 ECS 上,从外到内一共有四道关卡,请求必须逐层通过:

外网请求
   ↓
① 云平台安全组(控制台配置,最容易漏)
   ↓
② 宿主机防火墙(iptables / firewalld / UFW)
   ↓
③ Docker 自身的 NAT 规则(由 dockerd 自动维护)
   ↓
④ 容器内应用是否真的 listen 在 0.0.0.0

关键点:这四层是"与"的关系,任何一层拦截,你看到的现象都完全一样——连接超时或被拒绝。 新手最常见的错误就是只查自己熟悉的那一层。

排查顺序建议从下往上,因为越往下成本越低:先在服务器本机用 curl localhost:端口 确认容器活着,再查宿主机防火墙,最后才查安全组。

二、host 模式:简单但要小心

--net=host 让容器直接共享宿主机的网络栈,没有端口映射这回事,容器里 listen 什么端口,外网就能直接访问什么端口。

docker run -d --name my-service --net=host my-image

优点非常实在:

  • 不需要 -p 做端口映射,容器端口和宿主机端口一一对应,排查时心智负担为零。
  • 网络性能优于 bridge 模式,少一层 NAT 转发。
  • 容器内的进程能直接看到宿主机网络,监控、日志采集类工具用起来方便。

代价也必须说清楚:

  • 端口冲突风险。宿主机上已经有 3000 端口的进程,容器再用 host 模式起 3000 就直接失败,而且是启动即退出。
  • 隔离性差。一个容器监听的端口,等于整个宿主机监听了这个端口。多个容器共存时,端口规划必须提前统一。
  • 不能做端口隔离。host 模式下容器的进程绑定在哪个地址上,服务就出现在哪个地址上。想让服务只对本机开放,得应用自己绑 127.0.0.1——这跟防火墙无关。

我自己的用法是:对外暴露的核心服务用 host(少一层转发、少一类问题),内部工具类容器用 bridge(隔离端口、随便映射到高位端口)。

三、bridge 模式:默认选择,注意两个坑

bridge 是 Docker 的默认网络模式。容器有自己独立的网络命名空间,宿主机通过端口映射把流量转发进去。

docker run -d -p 127.0.0.1:8080:8080 my-image

坑 1:绑定地址不是默认的 0.0.0.0。

默认写法 -p 8080:8080 实际等价于 -p 0.0.0.0:8080:8080,也就是在所有网卡上监听。如果只是想给宿主机本机用(比如一个内部管理后台),应该显式写成 -p 127.0.0.1:8080:8080,这样服务不会出现在公网 IP 上。

坑 2:bridge 模式下,宿主机防火墙规则容易"看起来对但没用"。

原因是 dockerd 启动时会自动维护 iptables 的 DOCKER 链,这套规则会绕过部分常规防火墙的 INPUT 链策略。也就是说,UFW 里没有放行 8080,容器映射出来的端口照样能通。

这会造成一个很混乱的认知:同一个端口,宿主机上的进程占用时必须放行防火墙,容器占用时反而不用。排查时如果搞混这一点,会白白怀疑很久。

我的习惯是:需要外网访问的容器端口,宿主机防火墙和云安全组两边都放行,不依赖 DOCKER 链的隐式行为。行为可预期,排查时也不会有"玄学"。

四、端口不通的完整排查链

这是我最想分享的部分。下面是一套按顺序执行的流程,每一步都有明确的判据。

第 1 步:容器活着吗,端口在里面通吗

docker ps -a                       # STATUS 是不是 Up,PORTS 列显示什么
docker logs --tail 50 my-service   # 有没有启动即退出的报错

进容器再确认一次:

docker exec -it my-service sh -c "netstat -tlnp || ss -tlnp"

判据:容器内必须看到 0.0.0.0:8080 这样的监听地址。 如果看到的是 127.0.0.1:8080,那外部永远访问不到——这是应用自己绑错了地址,改应用配置即可,跟防火墙无关。

第 2 步:宿主机本机通不通

curl -sv http://127.0.0.1:8080/health
curl -sv http://$(hostname -I | awk '{print $1}'):8080/health

第一个用回环地址测,第二个用网卡 IP 测。两者结果不同就说明应用只绑了回环。

第 3 步:宿主机防火墙

ufw status verbose                 # Ubuntu/Debian
firewall-cmd --list-all            # CentOS/EulerOS
iptables -L INPUT -n -v             # 兜底看实际规则和计数

看计数是关键。-v 里的 pkts 列如果一直是 0,说明规则压根没被匹配到——问题不在这一层,而在更外层或者更内层。

第 4 步:抓包定位

这是最有效的手段,能一刀切开"云端拦截"和"本机拦截":

tcpdump -i any -nn port 8080

然后从外部发起一次连接,观察抓包结果:

抓包现象 结论 下一步
完全没有 SYN 包 请求没到服务器 云端安全组或上游网络,去控制台查安全组
看到了 SYN,但没有 SYN-ACK 到了但被本机丢弃 查宿主机防火墙规则
看到 SYN-ACK 但客户端仍超时 回程路径问题 查路由/云平台网络策略
完整握手成功 网络没问题 查应用层和 HTTP 响应

我那次就是靠这一步定性的:抓包 0 packets,说明 SYN 根本没到机器上。UFW 明明放行了,那就是安全组——上控制台一查,果然没有对应端口的入方向规则。

第 5 步:云端安全组

在华为云控制台进入 ECS → 安全组 → 更改安全组规则,确认:

  • 入方向规则里 TCP 端口有对应条目,源地址按需设置(0.0.0.0/0 全开,或限定为你的固定出口 IP)。
  • 规则优先级没有被其他"拒绝"规则压过。安全组是按优先级从上到下匹配,命中即停,一条靠前的 Deny 会让后面所有 Allow 失效。
  • 如果实例绑定了多个安全组,每个安全组都会独立生效,规则是叠加而非覆盖。

安全组改完是立即生效的,不需要重启实例,这一点和部分云厂商不同。

五、几个额外经验

端口规划先于部署。 我在给这台机器规划端口时固定了分段:3000 段给业务网关,3400 段给工具类服务,29000+ 给临时调试。这样半年后回头看 docker ps 的 PORTS 列,一眼就知道哪个容器在干什么。

优先考虑不走公网。 如果只是给自己用,云服务器完全没必要暴露 0.0.0.0。可以走内网访问(同一 VPC 内的其他实例用内网 IP 直连),或者用 Cloudflare Tunnel 这类隧道方案。我实测过同一份服务,直连公网约 30ms,走隧道中转约 1.2~1.7s,差了四十多倍——延迟敏感的内部工具,务必走直连。

别用容器内嵌的数据库当生产存储。 顺手提一句,容器的可写层不是持久存储,容器一删数据就没了。数据库要么挂 named volume,要么挂宿主机目录,这是 Docker 使用里最容易被忽略的一条。

小结

容器化部署在云主机上,端口不通的排查思路可以概括成三句话:

  1. 先分清层级:安全组 → 宿主机防火墙 → Docker NAT → 应用监听,四层逐个排除,不要凭感觉跳。
  2. 用抓包定性:tcpdump 看到 0 个包就是云端问题,看到 SYN 没有 SYN-ACK 就是本机问题。这一步能省掉一半的猜测。
  3. 行为要可预期:不要依赖 Docker 自动维护的 iptables 规则的隐式行为,该放行的两边都显式放行。

把这套流程记下来,下次遇到同类问题,从第 1 步走到第 5 步,一般十分钟内就能定位到具体是哪一层出的问题。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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