Docker端口暴露原理与实战:从EXPOSE到iptables全链路解析
1. 项目概述:Docker端口暴露不是“开个口子”那么简单
你刚跑起一个Nginx容器, docker run -d nginx ,浏览器打不开 localhost:80 ——这几乎是每个Docker新手在第三分钟就会遇到的“灵魂拷问”。但问题从来不在Nginx本身,而在于你根本没让它的80端口“见光”。所谓 Expose a Docker Port ,表面看是加个 -p 8080:80 参数的事,实则牵扯到Linux网络栈、iptables规则链、Docker daemon的守护进程调度、容器命名空间隔离机制,甚至宿主机防火墙策略的协同响应。我带过二十多期DevOps实训班,92%的学员第一次失败,不是因为命令敲错,而是把 EXPOSE 指令当成“端口开通指令”,结果 docker build 完一跑就502—— EXPOSE 只是元数据声明,它不触发任何网络动作,就像你在租房合同里写“本屋有窗户”,但不等于你真把窗帘拉开、玻璃擦亮、百叶窗调成45度角让阳光照进来。
这个主题的核心价值,远不止于“让网页能打开”。它直接决定你能否:
- 在本地快速验证微服务API(比如Spring Boot的
/actuator/health); - 将容器化数据库(PostgreSQL/Redis)安全接入本地IDE或客户端工具;
- 构建CI/CD流水线中可复现的集成测试环境;
- 在Kubernetes集群外调试Ingress路由前的Service流量走向;
- 甚至为家庭NAS、自建笔记系统(如Obsidian Sync Server)提供可控的外网访问入口。
适合谁来读?如果你正卡在“容器启动了但连不上”、“端口映射后访问超时”、“同一宿主机多个容器端口冲突”这类问题上,或者你已会用 -p 但说不清 -P 和 -p 的本质区别、搞不懂 host 网络模式为什么能绕过NAT却丧失隔离性——那这篇就是为你写的。它不讲Docker安装,不教 docker ps 基础命令,只聚焦“端口怎么通”这件事的底层逻辑、实操陷阱和真实场景解法。
我不会堆砌 man docker-run 的参数说明,而是带你重走一遍从容器进程监听→内核网络栈接收→iptables转发→宿主机防火墙放行→客户端请求抵达的全链路。每一个环节,都有我踩过的坑、改过的配置、抓过的包、删过的规则。接下来的内容,你可以当操作手册抄,也可以当原理图读——但请记住:Docker端口暴露,本质是一场宿主机与容器之间的网络协商,而你,必须同时听懂双方的语言。
2. 端口暴露的三种路径:为什么不能只靠 -p 一条路走到底
Docker提供端口暴露能力,并非单一技术方案,而是分层设计的三套机制: 容器内声明(EXPOSE)、运行时绑定(-p/-P)、网络模式切换(--network) 。它们分工明确,互不可替代,强行混用只会制造混乱。下面我用实际案例拆解每种路径的适用边界、技术原理和典型误用。
2.1 EXPOSE :容器镜像的“端口说明书”,不是“端口开关”
EXPOSE 是Dockerfile中的指令,例如:
FROM python:3.9-slim
COPY app.py /app/
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]
很多人以为加了这行,容器启动后5000端口就自动对外可访问。错。 EXPOSE 仅做两件事:
- 向镜像元数据写入端口声明 :执行
docker inspect <image-id>能看到"ExposedPorts": {"5000/tcp": {}}字段; - 为
docker run --expose或docker run --link提供上下文 (后者已废弃,但历史遗留系统仍有使用)。
它 完全不修改iptables规则,不创建端口映射,不监听宿主机任何端口 。你可以把它理解为“容器自述文档”——告诉别人“我内部监听5000端口”,但不承诺“你能从外面连上我”。
提示:
EXPOSE对-p参数无任何影响。你删掉Dockerfile里的EXPOSE 5000,再用docker run -p 8000:5000 myapp,照样能通。反之,只写EXPOSE 5000不加-p,容器内服务正常运行,但宿主机curl localhost:5000必然失败。
我曾帮一家电商公司排查API网关容器无法被前端调用的问题。运维坚称“Dockerfile写了EXPOSE 8080,肯定没问题”,结果发现他们漏掉了 -p 8080:8080 。花两小时查日志、抓包、重装Docker,最后发现是 EXPOSE 被当成了功能开关。这种认知偏差,在中小团队中极其普遍。
2.2 -p (publish):最常用也最容易翻车的“端口搬运工”
-p 是运行时参数,格式为 -p [HOST_IP:]HOST_PORT:CONTAINER_PORT[/PROTOCOL] 。它才是真正打通宿主机与容器网络的关键动作。其背后是Docker daemon调用 iptables 在 nat 表的 DOCKER 链中插入DNAT(目标地址转换)规则。以 docker run -p 8080:80 nginx 为例,Docker会执行:
# 自动添加的iptables规则(简化版)
iptables -t nat -A DOCKER ! -i docker0 -p tcp --dport 8080 -j DNAT --to-destination 172.17.0.2:80
iptables -t filter -A DOCKER ! -i docker0 -o docker0 -p tcp --dport 80 -j ACCEPT
这里 172.17.0.2 是容器在docker0网桥上的IP(由Docker分配), 8080 是宿主机监听端口, 80 是容器内进程监听端口。
但 -p 的坑远不止于此:
- 端口冲突检测不智能 :
docker run -p 3000:3000 myapp,若宿主机3000端口已被Node.js进程占用,Docker默认报错Bind for 0.0.0.0:3000 failed: port is already allocated。但如果你用-p 0:3000(让系统自动分配空闲端口),Docker会成功启动,却返回0.0.0.0:32768->3000/tcp——这个32768端口你得手动查docker port才能知道,CI脚本里硬编码就崩了; - 协议类型需显式声明 :
-p 5432:5432默认是TCP,但PostgreSQL支持TCP+UDP(用于流复制心跳),若需UDP,必须写-p 5432:5432/udp,否则pg_basebackup可能因UDP包被丢弃而失败; - HOST_IP限制访问范围 :
-p 127.0.0.1:8080:80只允许本机访问,-p 0.0.0.0:8080:80(默认)允许所有IP,但若宿主机启用了UFW防火墙,0.0.0.0绑定仍可能被UFW拦截——这是很多“本地能通,局域网不通”的根源。
注意:
-p绑定的是 宿主机网络命名空间 的端口,而非容器。这意味着:
- 同一宿主机上,
docker run -p 8080:80 nginx和docker run -p 8080:3000 node-app会因8080端口冲突而后者启动失败;- 但
docker run -p 127.0.0.1:8080:80 nginx和docker run -p 192.168.1.100:8080:3000 node-app可以共存,因为绑定IP不同。
2.3 --network 模式:放弃NAT,直连宿主机网络栈
当 -p 无法满足需求时(如需要容器内获取真实客户端IP、低延迟要求、或调试iptables规则本身),就得切换网络模式。Docker提供四种模式,但真正影响端口暴露的只有两种:
-
--network host:容器直接共享宿主机的网络命名空间。此时容器内进程监听0.0.0.0:80,等同于宿主机进程监听0.0.0.0:80。-p参数失效(Docker会警告Warning: Published ports are discarded when using host network mode),因为无需NAT转换。- 优势:零网络开销,
netstat -tuln | grep :80直接看到容器进程PID; - 劣势:完全丧失网络隔离,容器可随意操作宿主机iptables、修改
/etc/hosts,生产环境禁用; - 典型场景:本地开发调试Fluentd日志收集器(需监听宿主机
/var/log并上报到ES),或运行tcpdump抓包分析。
- 优势:零网络开销,
-
--network container:<name|id>:让新容器共享已有容器的网络命名空间。例如docker run --network container:nginx-proxy busybox wget -qO- http://localhost:80,此时busybox的localhost:80就是nginx-proxy容器的80端口——这比-p更轻量,且避免端口冲突。
实操心得:我在部署Prometheus监控栈时,将
prometheus、alertmanager、grafana三个容器用--network container:prometheus串联,只暴露prometheus的9090端口。这样既保证内部通信走localhost(毫秒级延迟),又避免为每个服务单独开防火墙端口,还杜绝了-p 9090:9090、-p 9093:9093、-p 3000:3000的端口管理混乱。
这三种路径不是递进关系,而是并列选项。选哪条,取决于你的场景:日常开发用 -p ,调试网络用 host ,微服务内部通信用 container 网络。死记硬背不如理解本质—— EXPOSE 是说明书, -p 是搬运工, --network 是换房子。
3. 深度解析 -p 参数:从命令行到iptables规则的完整链路
-p 看似简单,但它是Docker网络中最常出问题的环节。要真正掌控它,必须穿透Docker CLI的封装,看到它背后调用的系统级操作。下面我以一次完整的 docker run -p 8000:3000 my-node-app 执行过程为例,逐层拆解每一步发生了什么、为什么这样设计、以及哪里容易出错。
3.1 Docker daemon如何解析 -p 参数并生成iptables规则
当你输入 docker run -p 8000:3000 my-node-app ,Docker CLI将参数序列化为HTTP请求发送给Docker daemon(默认监听 unix:///var/run/docker.sock )。daemon收到后,执行以下关键步骤:
第一步:端口可用性检查
Docker daemon会调用 netstat 或 ss 命令检查宿主机 0.0.0.0:8000 是否空闲。注意:它检查的是 所有IP绑定的8000端口 ,包括 127.0.0.1:8000 和 192.168.1.100:8000 。如果 sudo lsof -i :8000 显示有进程占用,daemon立即返回错误。但这里有个陷阱:某些程序(如VS Code Remote-SSH)会绑定 ::1:8000 (IPv6回环),而Docker的检查可能忽略IPv6,导致启动后 curl localhost:8000 失败——因为IPv4的 127.0.0.1 和IPv6的 ::1 是两个独立地址。
第二步:容器网络初始化
Daemon为容器创建网络命名空间,启动 docker0 网桥(如果不存在),并为容器分配IP(如 172.17.0.3/16 )。此时容器内可通过 ip addr show 看到该IP,但宿主机尚无法路由到它——因为 docker0 是Linux bridge,需配合 iptables 实现NAT。
第三步:注入iptables规则
这才是 -p 的核心。Docker daemon调用 iptables 命令,在 nat 表的 DOCKER 链中插入两条规则:
# 规则1:DNAT(目标地址转换)——将发往宿主机8000端口的TCP包,目标IP改为容器IP,端口改为3000
iptables -t nat -A DOCKER ! -i docker0 -p tcp --dport 8000 -j DNAT --to-destination 172.17.0.3:3000
# 规则2:FORWARD链放行——允许从非docker0接口进入、目标为docker0接口的包通过
iptables -t filter -A FORWARD -i ! docker0 -o docker0 -p tcp --dport 3000 -j ACCEPT
这两条规则缺一不可。规则1负责“改地址”,规则2负责“放行包”。如果宿主机启用了UFW(Ubuntu默认防火墙),UFW的 FORWARD 策略默认是 DROP ,那么即使DNAT成功,包也会在 FORWARD 链被丢弃,导致连接超时。这就是为什么 ufw status verbose 里必须看到 Forward: ALLOW 。
提示:你可以用
iptables-save | grep DOCKER随时查看Docker自动生成的规则。每次docker stop容器,Docker会自动清理对应规则;但若Docker daemon异常退出,规则可能残留,导致新容器端口映射失败——此时执行iptables -t nat -F DOCKER && iptables -t filter -F DOCKER即可清空。
3.2 多端口映射与复杂绑定的实操细节
单端口映射( -p 8000:3000 )很直观,但真实项目往往需要暴露多个端口,或绑定特定IP。以下是高频场景的正确写法与避坑指南:
场景1:同时暴露Web端口(80)和管理端口(8080)
错误写法: docker run -p 8000:80 -p 8080:8080 myapp
问题:两个 -p 参数独立工作,但若宿主机8080端口被占用,第二个 -p 会失败,整个容器启动中断。
正确做法:用 --publish 多次指定,或更推荐——在 docker-compose.yml 中定义:
services:
myapp:
image: myapp
ports:
- "8000:80" # Web
- "8081:8080" # 管理端口(避开宿主机8080)
docker-compose 会按顺序执行端口检查,任一失败即停止,比CLI更可控。
场景2:绑定到特定网卡IP,而非所有接口
需求:让容器只响应局域网请求( 192.168.1.100:8000 ),拒绝公网IP(如云服务器的 10.0.0.5:8000 )访问。
写法: docker run -p 192.168.1.100:8000:3000 myapp
原理:iptables规则中的 --dport 条件不变,但 -A DOCKER 链的匹配条件增加了源IP范围限制。此时 curl 127.0.0.1:8000 会失败,因为 127.0.0.1 不匹配 192.168.1.100 。
场景3:UDP端口映射(如DNS、Syslog) -p 默认TCP,UDP需显式声明:
# DNS服务(53端口需TCP+UDP)
docker run -p 53:53/udp -p 53:53/tcp bind9
# Syslog接收(514端口)
docker run -p 514:514/udp rsyslog
漏掉 /udp 会导致UDP包被内核丢弃, tcpdump -i any port 514 看不到任何UDP包。
场景4:动态端口分配与端口查询
当使用 -p 0:3000 时,Docker随机分配宿主机端口(如32768)。获取方式:
# 启动时获取
CONTAINER_ID=$(docker run -d -p 0:3000 myapp)
PORT=$(docker port $CONTAINER_ID 3000 | cut -d':' -f2)
echo "App accessible at http://localhost:$PORT"
# 或直接查
docker port <container-name-or-id>
# 输出:3000/tcp -> 0.0.0.0:32768
实操心得:在CI/CD中,我习惯用
docker run -d --rm -p 0:3000 myapp启动临时测试容器,然后用curl -s http://localhost:$(docker port $(hostname) 3000 | cut -d':' -f2)/health做健康检查。--rm确保容器退出后自动清理,避免端口残留。
3.3 宿主机防火墙(UFW/iptables)与Docker规则的协同
Docker的iptables规则运行在 nat 和 filter 表,而UFW(Uncomplicated Firewall)是iptables的前端封装。两者共存时,规则顺序决定命运。UFW默认策略是:
INPUT链:ACCEPT(允许所有入站)FORWARD链:DROP(拒绝所有转发)
但Docker的 FORWARD 规则(允许 !docker0 -> docker0 )必须在UFW的 DROP 之前生效,否则包在 FORWARD 链就被拦下。Docker 20.10+版本已修复此问题,默认在UFW规则前插入自己的链。但旧版本(<19.03)或手动配置UFW的用户,仍需干预:
解决方案1(推荐):配置UFW允许Docker流量
# 编辑UFW配置
sudo nano /etc/default/ufw
# 将 DEFAULT_FORWARD_POLICY="DROP" 改为 DEFAULT_FORWARD_POLICY="ACCEPT"
sudo ufw reload
解决方案2:手动插入iptables规则(高级用户)
# 确保DOCKER-USER链存在(Docker 17.06+新增,优先级高于DOCKER链)
sudo iptables -N DOCKER-USER
sudo iptables -I FORWARD -j DOCKER-USER
sudo iptables -I DOCKER-USER -i eth0 -o docker0 -j ACCEPT
DOCKER-USER 链是Docker预留的用户自定义链,所有规则在此链中执行,不受Docker daemon重启影响。
注意:不要用
ufw allow 8000代替-p!ufw allow只开放INPUT链,对FORWARD链无效。Docker容器流量走的是FORWARD链(从eth0到docker0),所以ufw allow 8000对容器端口暴露毫无帮助。
4. 实战排障:从“Connection refused”到“Connection timed out”的精准定位
端口暴露失败,症状相似,病因各异。我整理了过去三年处理的137个真实案例,归纳出四大类故障,每类给出 现象-原因-诊断命令-解决方法 的闭环方案。你不需要背命令,只需按顺序执行,90%的问题5分钟内定位。
4.1 现象:“Connection refused”(连接被拒)
典型表现 :
$ curl http://localhost:8000
curl: (7) Failed to connect to localhost port 8000: Connection refused
核心原因 :TCP三次握手的第一步(SYN包)发出后,对方直接回复RST(复位)包,表明“此端口无人监听”。
诊断流程 :
-
确认容器内服务是否真在监听
# 进入容器 docker exec -it <container-id> sh # 查看监听端口(注意:-t参数显示TCP,-u显示UDP,-l显示监听状态) netstat -tuln | grep :3000 # 或用ss(更现代) ss -tuln | grep :3000如果无输出,说明应用未启动或监听错了地址(如只监听
127.0.0.1:3000而非0.0.0.0:3000)。Node.js常见错误:app.listen(3000)默认监听127.0.0.1,需改为app.listen(3000, '0.0.0.0')。 -
确认Docker是否成功创建iptables规则
# 查看nat表的DOCKER链 sudo iptables -t nat -L DOCKER -n # 应看到类似:DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 dpt:8000 to:172.17.0.3:3000若无此规则,检查Docker daemon日志:
sudo journalctl -u docker.service | tail -20,常见错误是port is already allocated。 -
确认宿主机端口是否被其他进程占用
sudo lsof -i :8000 # 或 sudo ss -tuln | grep :8000如果显示
node或python进程,说明端口冲突,要么杀掉原进程,要么换端口启动容器。
终极解决 :
- 容器内服务监听
0.0.0.0; docker run -p 8000:3000中宿主机端口空闲;iptables -t nat -L DOCKER确认规则存在。
4.2 现象:“Connection timed out”(连接超时)
典型表现 :
$ curl http://localhost:8000
curl: (28) Failed to connect to localhost port 8000: Connection timed out
核心原因 :SYN包发出后,对方无任何响应(既不SYN-ACK也不RST),通常是因为包在网络中被丢弃,最常见于 防火墙拦截 或 路由不可达 。
诊断流程 :
-
确认Docker的FORWARD链是否放行
# 查看FORWARD链策略 sudo iptables -t filter -L FORWARD -n | head -5 # 如果Policy是DROP,且无ACCEPT规则,则问题在此 # 检查Docker的FORWARD规则 sudo iptables -t filter -L FORWARD -n | grep docker0应看到类似:
ACCEPT all -- !docker0 docker0 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED。若无,执行:sudo iptables -I FORWARD -i ! docker0 -o docker0 -j ACCEPT -
检查UFW状态(Ubuntu/Debian)
sudo ufw status verbose # 关键看Forward: 的值,必须是ALLOW # 若是DISABLED,启用UFW:sudo ufw enable # 若是DENY,修改/etc/default/ufw中DEFAULT_FORWARD_POLICY="ACCEPT" -
抓包验证包流向 (终极手段)
# 在宿主机抓docker0网桥的包(容器侧) sudo tcpdump -i docker0 port 3000 -nn # 在宿主机抓eth0网卡的包(宿主机侧) sudo tcpdump -i eth0 port 8000 -nn- 如果
eth0能看到SYN包,但docker0看不到,说明包在FORWARD链被丢弃; - 如果
docker0能看到SYN包,但容器内netstat无监听,说明应用未启动; - 如果
docker0能看到SYN包,容器内也能看到(docker exec -it ... tcpdump),但无SYN-ACK回复,说明应用崩溃或配置错误。
- 如果
终极解决 :
sudo ufw default allow forwarded(UFW用户);- 或手动添加
iptables -I FORWARD -i ! docker0 -o docker0 -j ACCEPT; - 确保
docker0网桥存在且UP:ip link show docker0。
4.3 现象:“502 Bad Gateway”或“Empty reply from server”
典型表现 :
Nginx反向代理容器,上游服务返回502;或 curl 返回空响应。
核心原因 :Docker的DNAT规则生效,但容器内服务返回了错误响应,或健康检查失败。
诊断流程 :
-
跳过Nginx,直连容器IP
# 获取容器IP docker inspect <container-id> | grep '"IPAddress"' | head -1 # 直连(假设IP是172.17.0.3) curl http://172.17.0.3:3000如果直连成功,说明问题在Nginx配置(如
proxy_pass http://172.17.0.3:3000写错);如果直连也失败,回到4.1节排查。 -
检查容器健康状态
docker ps --format "table {{.ID}}\t{{.Status}}\t{{.Names}}" | grep myapp # Status应为"Up 2 minutes",而非"Up 2 minutes (unhealthy)" # 查看健康检查日志 docker inspect <container-id> | jq '.[0].State.Health' -
检查Docker DNS解析 (微服务间调用失败)
# 进入容器 docker exec -it <container-id> sh # 测试DNS nslookup my-db-service # 如果失败,检查docker-compose.yml中service名称是否匹配
终极解决 :
- 微服务间调用用
service-name:port(Docker内置DNS),不用localhost:port; - Nginx配置中
proxy_pass指向容器IP或service名,而非127.0.0.1; - 健康检查脚本返回正确的HTTP状态码(如
curl -f http://localhost:3000/health || exit 1)。
4.4 常见问题速查表
| 现象 | 最可能原因 | 快速验证命令 | 解决方案 |
|---|---|---|---|
curl localhost:8000 返回 Connection refused |
容器内服务未监听 0.0.0.0 |
docker exec -it <id> netstat -tuln | grep :3000 |
修改应用代码,监听 0.0.0.0:3000 |
curl localhost:8000 返回 Connection timed out |
UFW/FORWARD链拦截 | sudo ufw status verbose |
sudo ufw default allow forwarded |
docker port <id> 无输出 |
容器未用 -p 启动 |
docker ps -a | grep <id> |
重新 docker run -p 8000:3000 |
局域网机器无法访问 http://192.168.1.100:8000 |
宿主机防火墙(如Windows Defender)阻止 | sudo ufw status (Linux)或检查Windows防火墙 |
开放对应端口或禁用防火墙 |
同一宿主机启动两个 -p 8000:80 容器失败 |
端口冲突 | sudo lsof -i :8000 |
第二个容器用 -p 8001:80 |
EXPOSE 80 后 docker run 不加 -p , curl 失败 |
EXPOSE 不是端口开关 |
docker inspect <image> 查看 ExposedPorts |
必须加 -p 或 -P |
实操心得:我给自己定了一条铁律—— 任何端口问题,先
docker exec进容器,用netstat和curl验证内部是否正常;再iptables-save看规则;最后tcpdump抓包 。跳过任何一步,都可能浪费数小时在错误方向上。曾经有个客户坚持认为是Docker bug,结果我docker exec进去发现应用配置文件里把端口写成了8080,而-p映射的是8000:3000,根本对不上号。
5. 高级技巧与生产环境最佳实践
掌握基础 -p 用法后,真正的挑战在于如何在复杂环境中稳定、安全、高效地暴露端口。以下是我在金融、电商、SaaS公司落地的6个高级技巧,涵盖性能优化、安全加固、自动化运维和故障预防。
5.1 性能优化:绕过iptables,用 host 网络模式提升吞吐
某支付公司风控服务要求API延迟<10ms,但 -p 映射后平均延迟15ms。抓包发现 iptables DNAT 引入了约3ms内核处理延迟。解决方案:
- 改用
--network host:
此时服务直接监听宿主机docker run --network host -d my-risk-service0.0.0.0:8080,curl http://localhost:8080延迟降至6ms。 - 安全补偿 :
- 容器内禁止执行
iptables、ip等命令(--cap-drop=NET_ADMIN --cap-drop=NET_RAW); - 用
--read-only挂载根文件系统,防止篡改; - 通过
--user指定非root用户运行进程。
- 容器内禁止执行
注意:
host模式下,容器与宿主机共用127.0.0.1,因此容器内localhost:3306就是宿主机MySQL,无需-p映射数据库端口——这既是便利也是风险,务必确保数据库绑定了127.0.0.1而非0.0.0.0。
5.2 安全加固:用 DOCKER-USER 链实现细粒度访问控制
Docker默认的 DOCKER 链规则是“全通”,生产环境需限制来源IP。 DOCKER-USER 链是Docker 17.06+引入的用户自定义链,优先级高于 DOCKER ,可用于添加白名单:
# 只允许192.168.1.0/24网段访问容器8000端口
sudo iptables -I DOCKER-USER -i eth0 -o docker0 -p tcp --dport 8000 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -I DOCKER-USER -i eth0 -o docker0 -p tcp --dport 8000 -j DROP
这样, 192.168.1.50 能访问, 10.0.0.5 (云服务器内网)则被拒绝。规则永久生效需保存:
sudo iptables-save > /etc/iptables/rules.v4 # Ubuntu/Debian
sudo service netfilter-persistent save # 启用持久化
5.3 自动化运维:用 docker-compose 的 ports 实现环境差异化配置
开发、测试、生产环境端口策略不同:
- 开发:
-p 3000:3000,方便本地调试; - 测试:
-p 8080:3000,避免与Jenkins端口冲突; - 生产:不暴露端口,由Nginx Ingress统一入口。
docker-compose.yml 支持环境变量覆盖:
version: '3.8'
services:
web:
image: myapp:${APP_VERSION:-latest}
ports:
- "${WEB_PORT:-3000}:3000"
# 生产环境设 WEB_PORT=0,不暴露端口
启动时:
# 开发
docker-compose up -d
# 生产(不暴露端口)
WEB_PORT=0 docker-compose up -d
5.4 故障预防:用 healthcheck 避免端口暴露但服务未就绪
容器启动快,但应用加载慢
更多推荐
所有评论(0)