Skip to content
VDS · Docker 与容器

Docker 网络与端口转发

容器之间如何互相看到,发布端口与简单的 EXPOSE 有何不同,以及为什么 Docker 会绕过 ufw 规则。

更新于 2026年8月23日

容器问题有一半都和网络有关:服务连不上数据库、端口被占用、控制面板突然暴露在整个互联网上。下面我们来拆解一下它的工作原理。

三种网络类型

  • bridge —— 默认类型。容器获得内部地址并相互通信;它们通过宿主机的 NAT 访问外部。
  • host —— 容器直接使用宿主机的网络;端口映射既不需要也不起作用。速度快,但没有隔离:容器的 80 端口就是宿主机的 80 端口。
  • none —— 完全没有网络。适用于不需要网络的任务。

由您或 Compose 创建的自定义网络与默认的 bridge 有一个重要区别:在其中名称解析可以正常工作。app 容器可以通过名称 db 访问 db 容器。

docker network create backend
docker network ls
docker network inspect backend

在 Compose 中,这样的网络会为每个项目自动创建。

端口映射

ports:
  - "127.0.0.1:8080:8080"   # accessible only from the machine itself
  - "80:80"                 # accessible to the whole internet

左边是宿主机上的端口,右边是容器内部的端口。镜像中的 EXPOSE 指令不会映射任何端口:它只是说明应用程序监听哪个端口。

规则很简单:只映射必须从外部访问的端口。数据库、控制面板和内部 API 绑定到 127.0.0.1,而带证书的 Web 服务器则将它们暴露到外部。

Docker 会自行写入 iptables 规则,而且是在 ufw 管理的链之前写入。以 -p 8080:8080 映射的端口即使 ufw 显示“deny”,也能从互联网访问。不要依赖防火墙——请将端口绑定到 127.0.0.1。

容器之间的通信

services:
  app:
    networks: [frontend, backend]
  db:
    networks: [backend]
  proxy:
    networks: [frontend]
    ports:
      - "80:80"
      - "443:443"

networks:
  frontend:
  backend:

在这里,数据库只对应用程序可访问,只有 Web 服务器面向外部。这是任何技术栈的正确配置。

在容器内部,通过服务名称访问相邻容器:postgres://db:5432/app。容器内的 localhost 地址指的是容器本身,而不是宿主机——这是仅次于 latest 的第二常见错误。

要从容器访问运行在宿主机本身上的服务,请使用名称 host.docker.internal,并添加:

extra_hosts:
  - "host.docker.internal:host-gateway"

诊断

docker compose exec app ping -c2 db
docker compose exec app getent hosts db
sudo ss -tulpn | grep LISTEN
docker port <container>

ss 会显示端口实际监听在哪个接口上:0.0.0.0:8080 —— 对外开放,127.0.0.1:8080 —— 不对外开放。

常见错误

  • “端口已被占用”。 它被另一个容器或宿主机服务占用。sudo ss -tulpn | grep :80 可以找出罪魁祸首。
  • 不同 Compose 项目中的容器互相看不到。 每个项目都有自己的网络。请通过 networks: { shared: { external: true } } 显式连接它们。
  • 应用程序在容器内监听 127.0.0.1。 那么端口映射也无济于事:您无法从容器外部访问它。请将应用程序配置为监听 0.0.0.0。
  • 带宽。 每台宿主机共享最高 500 Mbps。文件分发会与其他所有流量共享它。