Dockerfile 写作精讲 / 07 - EXPOSE 与端口
07 - EXPOSE 与端口:端口声明、映射与健康检查
7.1 EXPOSE 指令概述
EXPOSE 指令用于声明容器运行时监听的端口。它是一个文档性指令,不会实际发布端口。
# 声明容器监听 8080 端口
EXPOSE 8080
# 声明 TCP 和 UDP
EXPOSE 8080/tcp
EXPOSE 5353/udp
# 同时声明多个端口
EXPOSE 8080 8443 9090
EXPOSE 的真正作用
| 作用 | 说明 |
|---|---|
| 文档声明 | 告诉使用者容器监听哪些端口 |
配合 -P | docker run -P 会自动映射 EXPOSE 的端口到随机主机端口 |
| 配合 Compose | docker-compose 可以根据 EXPOSE 自动生成端口配置 |
EXPOSE 不等于端口映射
EXPOSE 8080
# EXPOSE 不会发布端口!
docker run myapp
# 主机无法访问 8080
# 需要使用 -p 显式映射
docker run -p 8080:8080 myapp
# 使用 -P 自动映射 EXPOSE 的端口
docker run -P myapp
# 自动映射: 主机随机端口 → 容器 8080
docker port <container_id>
# 输出: 8080/tcp -> 0.0.0.0:49153
7.2 端口映射详解
-p 参数的多种形式
# 映射到指定主机端口
docker run -p 8080:80 myapp
# 映射到指定 IP 和端口
docker run -p 127.0.0.1:8080:80 myapp
# 映射到随机主机端口
docker run -p 80 myapp
# 映射 UDP 端口
docker run -p 5353:5353/udp myapp
# 同时映射 TCP 和 UDP
docker run -p 5353:5353/tcp -p 5353:5353/udp myapp
# 映射端口范围
docker run -p 8000-8100:8000-8100 myapp
端口映射网络流向
客户端请求 → 主机 IP:主机端口 → Docker NAT → 容器 IP:容器端口
↕
iptables / 用户态代理
注意事项:默认情况下 Docker 使用 iptables 进行端口转发。在生产环境中,建议使用 Docker 的
userland-proxy=false配置以提高性能。
7.3 多端口应用
前后端分离
FROM nginx:alpine
# 前端静态文件
COPY dist/ /usr/share/nginx/html/
# Nginx 配置:反向代理 API
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 前端端口
EXPOSE 80
# 如果同时暴露管理端口
EXPOSE 8080
多服务容器(不推荐但有时需要)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
nginx \
python3 \
supervisor \
&& rm -rf /var/lib/apt/lists/*
COPY nginx.conf /etc/nginx/
COPY app.py /app/
COPY supervisord.conf /etc/supervisor/
# Web 端口
EXPOSE 80
# 管理端口
EXPOSE 9001
CMD ["/usr/bin/supervisord"]
7.4 HEALTHCHECK 指令
HEALTHCHECK 指令告诉 Docker 如何检测容器是否健康运行。
基本语法
# 健康检查
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1
# 带参数
HEALTHCHECK \
--interval=30s \
--timeout=10s \
--start-period=5s \
--retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# 禁用健康检查
HEALTHCHECK NONE
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
--interval | 30s | 两次检查之间的间隔 |
--timeout | 30s | 单次检查超时时间 |
--start-period | 0s | 容器启动后的宽限期(此期间失败不计入重试次数) |
--retries | 3 | 连续失败几次后标记为 unhealthy |
不同应用的健康检查
# Web 服务
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/health || exit 1
# 数据库(PostgreSQL)
HEALTHCHECK --interval=10s --timeout=5s --retries=5 \
CMD pg_isready -U postgres || exit 1
# Redis
HEALTHCHECK --interval=10s --timeout=5s --retries=3 \
CMD redis-cli ping || exit 1
# 自定义脚本
HEALTHCHECK --interval=30s --timeout=10s \
CMD /healthcheck.sh
不使用 curl 的健康检查
# 使用 wget(Alpine 默认没有 curl)
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# 使用 netcat
HEALTHCHECK --interval=30s --timeout=3s \
CMD nc -z localhost 8080 || exit 1
# 使用 Python
HEALTHCHECK --interval=30s --timeout=3s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1
# 使用 Node.js
HEALTHCHECK --interval=30s --timeout=3s \
CMD node -e "require('http').get('http://localhost:8080/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"
查看健康检查状态
# 查看容器健康状态
docker inspect --format='{{.State.Health.Status}}' mycontainer
# 查看健康检查日志
docker inspect --format='{{json .State.Health}}' mycontainer | jq
# docker ps 中显示健康状态
docker ps
# STATUS 列会显示 healthy / unhealthy / starting
7.5 Docker Compose 中的端口与健康检查
services:
web:
build: .
ports:
- "8080:80"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:16
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
7.6 实战:完整的端口与健康检查配置
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
# 创建非 root 用户
RUN addgroup -g 1001 appuser && \
adduser -u 1001 -G appuser -s /bin/sh -D appuser && \
chown -R appuser:appuser /app
USER appuser
# 声明端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
7.7 常见错误与排查
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 端口无法访问 | EXPOSE 不发布端口 | 使用 -p 映射端口 |
port is already allocated | 端口被占用 | 更换端口或停止占用进程 |
| 健康检查始终 unhealthy | 检查命令错误 | 手动在容器内执行检查命令 |
| 健康检查启动期失败 | start-period 太短 | 增加 start-period 值 |
-P 无法映射 | 未使用 EXPOSE | 添加 EXPOSE 指令 |
| localhost 拒绝连接 | 应用绑定 127.0.0.1 | 绑定 0.0.0.0 |
7.8 扩展阅读
上一章:06 - CMD 与 ENTRYPOINT 下一章:08 - USER 与权限 — 非 root 运行、权限管理与文件所有权。