> For the complete documentation index, see [llms.txt](https://yeasy.gitbook.io/docker_practice/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yeasy.gitbook.io/docker_practice/di-er-bu-fen-jin-jie-pian/09_network/9.5_port_mapping.md).

# 9.5 外部访问容器

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

## 9.5.1 为什么要映射端口

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

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

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

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

***

## 9.5.2 端口映射方式

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

### 1. 指定映射

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

```bash
## 将宿主机的 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` 指令暴露的所有端口发布到宿主机的随机高位端口。具体落在哪个端口，取决于宿主机当前可用的临时端口范围。

```bash
$ docker run -d -P nginx
```

查看映射结果：

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

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

***

## 9.5.3 查看端口映射

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

### docker port

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

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

### docker ps

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

```bash
$ 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.1`，IPv6 使用 `[::1]`：

```bash
## 仅允许本机访问

$ 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 服务（如 DNS，Syslog）：

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

***

## 9.5.5 实现原理

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

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

```bash
## 简化的 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 网络模式）。

***
