如何公开 Docker 端口
厘清 EXPOSE、--expose 与 -p 的区别,理解 Docker 端口暴露。

Docker 的网络模型会将容器与主机系统隔离。每个容器都有独立的网络命名空间和端口范围。
Docker 不会自动通过主机网络公开容器端口。要让外部流量进入容器,需要明确暴露容器使用的端口,并将目标端口绑定到主机网络接口。
“暴露”和“绑定(发布)”描述的是不同操作。本文将通过示例说明两者的区别,以及如何查看和管理容器端口。
主要内容包括:
- 什么是在 Docker 中公开端口?
- 如何在 Dockerfile 中公开端口
- 如何在创建 Docker 容器时公开端口
- 公开 Docker 端口 - 示例
- 如何公开多个 Docker 端口
- 如何绑定公开的端口
什么是在 Docker 中公开端口?
公开 Docker 端口表示容器化工作负载正在使用这个端口。Docker 会记录并显示这些信息,帮助你判断应该将哪些容器端口绑定到主机。
需要注意,公开端口并不会自动将其绑定到主机上。公开端口只会添加元数据,用于说明容器中的应用正在监听哪些端口。
例如,Web 服务器的 Dockerfile 可以包含以下指令:
EXPOSE 80
这条指令表示容器中的应用监听 80 端口。要从容器外部访问服务器,还需要将这个端口绑定到主机;容器启动时不会因为该指令自动建立映射。
即使可以直接绑定一个未公开的端口,公开端口仍然具有文档作用。它能明确说明容器需要使用哪些端口,让镜像更容易理解和管理。
通过公开端口列表,你可以快速判断应该绑定哪些端口,也能发现已经分配给容器但没有使用的主机端口。启动容器时,还可以自动绑定所有已公开端口,减少手动配置。
公开端口与绑定端口
公开端口是一种被动行为,只表示容器能够监听该端口。要让外部流量进入容器,需要将主机端口绑定到容器端口,建立主机与容器之间的映射。
日常表达中,绑定端口也经常被称为“公开”端口,因为绑定后可以通过主机网络访问容器。但两者并不等价:端口可以只公开而不绑定,也可以不先公开就直接绑定。
- 公开端口:记录元数据,说明容器中的应用或服务会监听该端口。
- 绑定/发布端口:将主机端口映射到容器端口,允许通过主机网络接口与容器通信。
如何在 Dockerfile 中公开端口
Docker 提供两种主要方式公开端口:在 Dockerfile 中添加 EXPOSE,或者在通过 docker run 启动容器时传入 --expose。
在 Dockerfile 中,需要为每个端口添加一条 EXPOSE 指令:
EXPOSE 80
默认情况下,Docker 会按 TCP 公开端口。如果需要 UDP,可以明确指定协议:
EXPOSE 80/udp
如果同一个端口需要同时支持 TCP 和 UDP,应分别添加两条 EXPOSE 指令:
EXPOSE 80/tcpEXPOSE 80/udp
使用包含 EXPOSE 指令的镜像启动容器时,Docker 会自动记录其中列出的全部公开端口。
如何在创建 Docker 容器时公开端口
如果需要在启动容器时手动公开端口,可以在 docker run 中添加 --expose:
docker run my-container:latest --expose 80
重复传入该标志可以公开多个端口。与 EXPOSE 类似,--expose 支持 <port>/tcp 和 <port>/udp 格式;只提供端口号时,默认使用 TCP。
实际使用中,--expose 并不常见。容器需要监听的端口通常已经定义在镜像的 Dockerfile 中。如果应用没有监听额外端口,即使在启动时将它们公开,也不会产生实际作用。
公开 Docker 端口 - 示例
下面通过一个简单示例,演示如何公开端口并查看结果。
使用 Dockerfile EXPOSE 公开端口
首先,创建一个包含公开端口的 Dockerfile:
FROM alpine:latestEXPOSE 80CMD ["sleep", "300"]
然后构建镜像,并使用它启动容器:
docker build -t demo-image:latest .docker run -d --name demo docker-image:latest
使用 Docker run expose
如果无法修改镜像的 Dockerfile,可以在启动容器时手动公开端口:
docker run -d --name demo-run --expose 443 demo-image:latest
查看公开的端口
使用 docker ps 可以查看容器公开的端口:
docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES2fdfd038a368 demo-image:latest "sleep 300" 2 seconds ago Up Less than a second 80/tcp, 443/tcp demo-rundd50cd05e515 demo-image:latest "sleep 300" About a minute ago Up About a minute 80/tcp demo
如何公开多个 Docker 端口
在 docker ps 的输出中,每个容器公开的端口都会显示在 PORTS 一栏。
要为一个容器公开多个端口,可以重复使用 Dockerfile 中的 EXPOSE,或者重复传入 docker run 的 --expose。
如果同时使用 EXPOSE 和 --expose,Docker 会合并两处定义的端口。上面的输出显示,示例容器同时公开了 Dockerfile 中定义的 80 端口,以及启动容器时通过 --expose 添加的 443 端口。
如何绑定公开的端口
要让外部流量进入容器,需要将容器端口绑定到主机。使用 docker run 的 -p 标志可以建立端口映射:
docker run -d --name nginx -p 80:80 nginx:latest
这个示例将主机的 80 端口映射到容器的 80 端口。完成映射后,可以通过主机网络接口访问容器中的应用:
curl http://localhost:80<!DOCTYPE html><html><head><title>Welcome to nginx!</title>
绑定结果会显示在 docker ps 输出的 PORTS 一栏。只公开端口时,Docker 仅显示 80/tcp 这类容器端口;绑定后,输出会包含主机端口到容器端口的映射:
docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMEScafc1f042132 nginx:latest "/docker-entrypoint.…" About an hour ago Up About an hour 0.0.0.0:80->80/tcp, :::80->80/tcp nginx
重映射已绑定的端口
主机端口不需要与容器监听的端口相同。下面将主机的 8080 端口映射到容器的 80 端口:
docker run -d --name nginx -p 8080:80 nginx:latestcur http://localhost:8080<!DOCTYPE html><html><head><title>Welcome to nginx!</title>
将端口绑定到特定主机接口(如:localhost)
前面的端口绑定会作用于主机的所有网络接口,因此网络中的其他设备也可能访问容器。
如果服务只应该提供给主机上的其他进程,可以使用 host_address:host_port:container_port 格式,将端口限制在指定的主机接口:
docker run -d --name nginx -p 127.0.0.1:8080:80 nginx:latest
这里将 Docker 主机 127.0.0.1(localhost)的 8080 端口映射到容器的 80 端口。从主机其他 IP 地址进入该端口的流量不会到达容器。
自动绑定公开的端口
公开端口与绑定端口可以独立使用。你可以绑定未公开的端口,也可以只公开端口而不建立任何主机映射。
如果希望绑定容器公开的所有端口,可以在 docker run 中添加 -P (--publish-all):
docker run -d -P --name demo demo-image:latest
使用这个标志后,不需要先检查镜像公开了哪些端口,再逐个通过 -p 手动绑定。Docker 会为每个公开的容器端口随机分配一个主机端口。
使用 docker ps 可以确认实际映射结果:
docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESd66f376e3860 demo-image:latest "sleep 300" 2 seconds ago Up 1 second 0.0.0.0:32768->80/tcp, :::32768->80/tcp demo
示例 Dockerfile 中通过 EXPOSE 定义的容器端口 80,被随机映射到主机端口 32768。这是启动容器时使用 -P 的结果。
要点
暴露端口只会记录容器应用使用的端口,是镜像或容器的元数据。绑定或发布端口则会建立主机端口到容器端口的映射,让外部流量能够进入容器。
Dockerfile 中的 EXPOSE 用于声明镜像使用的端口,运行容器时的 --expose 可以补充公开端口。需要建立明确映射时使用 -p;需要自动绑定全部公开端口时,则可以使用 -P。