Docker 端口映射怎么配置?-p、Compose 和排查怎么做?
Docker 端口映射的作用,是把容器网络命名空间里的端口发布到宿主机上。最常见的写法是 docker run -p 8080:80 nginx:外部访问宿主机的 8080 端口,请求会被转发到容器里的 80 端口。
这里最容易混淆的是:EXPOSE 只是镜像或 Dockerfile 里的声明,告诉别人“这个容器通常会监听哪些端口”;真正让宿主机能访问容器端口的,是 -p/--publish 或 Compose 里的 ports。
-p 的完整写法是什么?
-p 的通用格式是:
bashdocker run -p [hostIP:]hostPort:containerPort[/protocol] image
几个常见例子:
bash# 宿主机 8080 转发到容器 80 docker run -p 8080:80 nginx # 只允许本机访问宿主机 8080 docker run -p 127.0.0.1:8080:80 nginx # 绑定所有网卡的 8080 docker run -p 0.0.0.0:8080:80 nginx # 映射多个端口 docker run -p 8080:80 -p 8443:443 nginx # 映射 UDP 端口 docker run -p 5353:5353/udp some-image
默认协议是 TCP。如果服务用的是 UDP,比如 DNS、游戏服务、部分实时通信服务,必须显式写 /udp,否则你映射的是 TCP,访问当然不通。
0.0.0.0、127.0.0.1 和安全性有什么区别?
0.0.0.0:8080:80 表示监听宿主机所有网卡。只要机器的公网 IP、内网 IP 能被访问,别人就可能连到这个端口。
127.0.0.1:8080:80 只绑定本机回环地址,通常只有宿主机自己能访问,适合数据库、管理后台、本地调试服务这类不该直接暴露的端口。
所以生产环境里不要随手写:
bashdocker run -p 0.0.0.0:3306:3306 mysql
这等于把数据库端口暴露在宿主机所有网卡上。更稳妥的方式是只绑定本地地址,再通过反向代理、堡垒机、VPN 或内网访问控制处理入口。
为什么映射了端口还是访问不了?
端口映射只负责把流量送到容器端口,但容器里的应用也要真的监听在正确地址上。
如果应用在容器内只监听 127.0.0.1,外部流量通常到不了它。因为 Docker 转发过来的目标地址是容器的网络地址,不是容器里的 loopback。容器里的 Web 服务应监听:
bash0.0.0.0:80
而不是:
bash127.0.0.1:80
很多 Node、Python、Go 开发服务默认只监听 localhost,本地直接跑没问题,放进容器再做端口映射就会踩坑。
EXPOSE、-P 和 -p 有什么区别?
EXPOSE 不会自动发布端口,例如:
dockerfileEXPOSE 80
它只是元数据。你运行容器时仍然需要:
bashdocker run -p 8080:80 image
-P 是大写 P,会把镜像里 EXPOSE 声明的端口随机映射到宿主机端口:
bashdocker run -P nginx
这种方式适合临时调试,不适合稳定对外服务,因为宿主机端口不固定。要知道实际映射到了哪个端口,可以查:
bashdocker port 容器名
Docker Compose 里怎么写?
Compose 里最常见的是 ports:
yamlservices: web: image: nginx ports: - "8080:80" - "127.0.0.1:8443:443"
ports 会把容器端口发布到宿主机。
expose 不会发布到宿主机,只是让同一个 Docker 网络里的其他服务知道这个服务使用了哪些端口:
yamlservices: api: image: my-api expose: - "3000"
在同一个 Compose 网络里,服务之间通常不需要端口映射。比如 web 访问 api:3000 即可,Docker 内部 DNS 会把服务名解析到对应容器。只有当宿主机或外部用户需要访问容器时,才需要 ports。
常用排查命令有哪些?
先看 Docker 是否真的发布了端口:
bashdocker port 容器名
再看容器是否在运行:
bashdocker ps
检查宿主机端口是否监听:
bashss -lntp | grep 8080
如果是 macOS 没有 ss,可以用:
bashlsof -i :8080
从宿主机访问测试:
bashcurl -v http://127.0.0.1:8080
进容器里看应用是否监听正确地址:
bashdocker exec -it 容器名 sh ss -lntp
如果 Docker 映射没问题、容器服务也在监听,但外部机器访问不了,继续查三件事:
- 宿主机防火墙是否放行端口,比如
ufw、firewalld、云服务器安全组; - 是否绑定成了
127.0.0.1,导致只能本机访问; - 协议是否写错,UDP 服务不能只映射 TCP。
端口映射配置时要记住什么?
-p 8080:80 的方向是“宿主机端口:容器端口”,不要写反。EXPOSE 只是声明,-p 或 ports 才是真正发布端口。对外服务可以绑定 0.0.0.0,但数据库、后台管理、调试端口更适合绑定 127.0.0.1 或放在 Docker 内部网络里。
大多数端口映射问题最后都落在四处:Docker 没发布、应用没监听 0.0.0.0、宿主机端口被占用、防火墙或安全组没放行。按这个顺序查,通常很快能定位。