Files
docker_practice/09_network/9.5_port_mapping.md
T
yeasy 51698c89c5 fix(content): correct verified internal inconsistencies across 7 sections
均为书内自证的矛盾,无需外部来源:

- 19.3: golang:1.26-alpine 标注为 ~1GB,但 21.7 将 ~900MB 归给非 alpine 的
  golang:1.26,且 7.17 实测 alpine 版镜像为 295MB——alpine 变体不可能大于其
  非 alpine 版本。改为与 21.7 一致的「golang:1.26 基础镜像的 ~900MB」。
- 20_cases_os: 正文称通用镜像 100-300 MB,紧邻的表格却列 Ubuntu ~80 MB
  (与 4.2 的 ubuntu 24.04 78MB 一致)。正文改为 80-300 MB。
- 4.2: 「查找大于 500MB 的镜像」的 ^[0-9]+GB 不匹配小数,会漏掉 docker 实际
  输出的全部 x.yGB(本书自己的示例即为 2.5GB)。补充可选小数部分。
- 6.2: docker image ls 示例输出把 tag 混入 REPOSITORY 列
  (127.0.0.1:5000/ubuntu:latest + TAG latest),与同块 ubuntu/latest 行及
  本节自述的 tag 格式不符。
- 5.3: 生命周期状态图缺 Stopped --> Running,而 5.3.6 正是讲 docker start
  启动已停止的容器;原图中停止的容器只能被删除。
- 9.5: 端口映射图节点标签 "容器 (Class B: 80)" 语义错乱(Class B 是 IP 地址
  分类,与端口无关),改为「容器 (端口: 80)」。
- appendix/faq/errors.md: 标题「常见错误速查表」与 SUMMARY.md 及 faq/README
  两处链接文字「常见错误处理」不一致(全书 196 篇中唯一一处标题漂移)。
2026-07-16 23:20:25 -07:00

4.2 KiB
Raw Blame History

9.5 外部访问容器

容器运行在自己的隔离网络环境中 (通常是 Bridge 模式)。为了让外部网络访问容器内的服务,我们需要将容器的端口映射到宿主机的端口。

9.5.1 为什么要映射端口

容器的网络访问规则如下:

  • 容器之间:可以通过 IP 或容器名 (自定义网络) 互通。
  • 宿主机访问容器:原生 Linux 环境下可按网络配置访问容器 IP;Docker Desktop 上不能依赖容器 IP,应通过端口映射、容器名(容器间)或 host.docker.internal 等机制访问。
  • 外部网络访问容器 默认无法直接访问。

为了让外部 (如你的浏览器、其他局域网机器) 访问容器内的服务,我们需要将容器的端口 映射 到宿主机的端口。

flowchart TD
    User["外部用户 (Browser)"] --> Host["宿主机 (localhost:8080)"]
    Host --> Proxy["Docker Proxy<br/>端口映射 (8080 -> 80)"]
    Proxy --> Container["容器 (端口: 80)"]

9.5.2 端口映射方式

Docker 提供了多种方式来指定端口映射。

1. 指定映射

使用 -p <宿主机端口>:<容器端口> 格式:

## 将宿主机的 8080 端口映射到容器的 80 端口

$ docker run -d -p 8080:80 nginx

此时访问 http://localhost:8080 即可看到 Nginx 页面。

多种格式

格式 含义 示例
ip:hostPort:containerPort 绑定指定 IP 的特定端口 -p 127.0.0.1:8080:80 (仅 IPv4 本机访问)
ip::containerPort 绑定指定 IP 的随机端口 -p 127.0.0.1::80
hostPort:containerPort 绑定所有地址(通常包括 0.0.0.0[::])的特定端口 -p 8080:80 (默认)
containerPort 绑定所有地址的随机端口 -p 80

2. 随机映射

如果不关心宿主机使用哪个端口,可以使用随机映射。使用 -P (大写) 参数,Docker 会把 Dockerfile 中 EXPOSE 指令暴露的所有端口发布到宿主机的随机高位端口。具体落在哪个端口,取决于宿主机当前可用的临时端口范围。

$ docker run -d -P nginx

查看映射结果:

$ docker ps
CONTAINER ID   PORTS
abc123456      0.0.0.0:49153->80/tcp

此时 Nginx 被映射到了宿主机的一个随机高位端口,例如 49153


9.5.3 查看端口映射

可以使用以下命令查看容器的端口映射:

docker port

运行 docker port 可以查看到指定容器的端口映射情况:

$ docker port mycontainer
80/tcp -> 0.0.0.0:8080
80/tcp -> [::]:8080

docker ps

运行 docker ps 可以查看到所有容器的端口映射列表:

$ docker ps
CONTAINER ID   IMAGE     PORTS                  NAMES
abc123456      nginx     0.0.0.0:8080->80/tcp   web

9.5.4 最佳实践与安全

在配置端口映射时,需要注意以下安全事项:

1. 限制监听 IP

默认情况下,-p 8080:80 会监听所有可用地址,常见输出包括 0.0.0.0:8080[::]:8080。这意味着任何人只要能连接你的宿主机 IP,就能访问该服务。

如果不希望对外暴露 (例如数据库服务),应绑定到回环地址。IPv4 使用 127.0.0.1IPv6 使用 [::1]

## 仅允许本机访问

$ docker run -d -p 127.0.0.1:3306:3306 mysql
$ docker run -d -p '[::1]:3306:3306' mysql

2. 避免端口冲突

如果宿主机 8080 已经被占用了,容器将无法启动。

解决

  • 更换宿主机端口:-p 8081:80
  • 让 Docker 自动分配:-p 80

3. UDP 映射

默认是 TCP 协议。如果要映射 UDP 服务 (如 DNSSyslog)

$ docker run -d -p 53:53/udp dns-server

9.5.5 实现原理

Docker 使用 docker-proxy 进程 (用户态) 或 iptables DNAT 规则 (内核态) 来实现端口转发。

当流量到达宿主机端口时,iptables 规则将其目标地址修改为容器 IP 并转发:

## 简化的 iptables 逻辑

iptables -t nat -A DOCKER -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80

这也是为什么你在容器内部看到的访问来源 IP 通常是网关 IP (如 172.17.0.1),而不是真实的外部 Client IP (除非使用 host 网络模式)。