Docker踩坑实录:上线第一个月,我遇到的5个生产环境问题(2026版)
Docker踩坑实录:上线第一个月,我遇到的5个生产环境问题(2026版)
Docker用过都说好,用好都是踩过来的。我的服务器上跑了8个容器,上线第一个月,镜像构建挂了3次、容器启动顺序乱了2次、磁盘被日志撑爆了1次。这篇把我踩过的5个坑整理出来,每个都有完整解决方案。
坑1:镜像构建体积过大
现象
部署的时候,镜像传了20分钟还没传完。一看镜像大小:3.2GB。Node.js应用而已,怎么这么大?
原因
问题出在Dockerfile上。我的原始写法是这样的:
FROM node:18
WORKDIR /app
COPY . . # 把整个项目包括node_modules都复制进去
RUN npm install # 然后再装一遍依赖
EXPOSE 3000
CMD ["node", "server.js"]
问题在哪?COPY . . 把 node_modules 也复制进去了(里面可能有本地编译的二进制模块,体积巨大),然后 npm install 又重新装了一遍。最后镜像里会有两份依赖。
解决
使用多阶段构建,只复制必要文件:
# 构建阶段
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# 运行阶段
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
另外,加上 .dockerignore 文件,排除不需要的文件:
node_modules
.git
*.log
dist
coverage
镜像体积从3.2GB降到280MB,构建时间从4分钟降到45秒。
坑2:容器启动顺序问题
现象
用 docker-compose up 启动时,Java应用容器报了连接错误:
Caused by: java.net.ConnectException: Connection refused
(in java.net.PlainSocketImpl)
但等10秒再试,又好了。
原因
MySQL容器还没完成初始化(数据库启动、安全加固、创建表),Java应用容器就已经开始连接了。docker-compose up 默认不等待容器"就绪",只等待容器"启动"。
解决
方案1:用wait-for脚本(最通用)
# docker-compose.yml
version: "3.8"
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 10
app:
image: my-java-app
depends_on:
db:
condition: service_healthy # 等待db healthcheck通过
command: ["./wait-for-it.sh", "db:3306", "--", "java", "-jar", "app.jar"]
方案2:用healthcheck+condition(Docker Compose原生)
在db服务加 healthcheck,在app服务的 depends_on 加 condition: service_healthy。
方案3:用depends_on + sleep(不推荐,但简单)
不推荐是因为sleep时间是拍脑袋的,网络慢的时候还是会挂。
depends_on 只保证启动顺序,不保证服务就绪。用 healthcheck + condition: service_healthy 才能真正解决。
坑3:内存限制不生效
现象
Java应用设置了Docker内存限制为512MB,但应用启动时JVM报告使用了8GB内存,最终OOM崩溃。
原因
JVM默认使用物理机的内存,而不是容器限制的内存。-Xmx 如果不设置,JVM会探测物理机总内存然后给自己分配一个比例,通常是1/4。容器限制对JVM不起作用。
解决
正确传递容器内存限制给JVM:
# 方式1:用环境变量(推荐)
FROM openjdk:17-slim
# 不设置-Xmx,让JVM自动检测容器内存
# 但需要JDK 10+,并且加上 -XX:+UseContainerSupport
CMD java -XX:+UseContainerSupport -jar app.jar
# 方式2:显式指定(精确控制)
CMD java -XX:MaxRAM=$(echo $((512*1024*1024))) -jar app.jar
Spring Boot 2.4+ 自带容器内存感知,不需要额外配置。
JVM不自动识别容器内存限制。JDK 10以上加 -XX:+UseContainerSupport,或者手动传 -XX:MaxRAM。
坑4:跨平台构建导致的不兼容
现象
Mac M1上构建的Node.js镜像,传到Linux服务器上跑,报错:
Illegal instruction
原因
Mac M1是ARM64架构,Linux服务器是AMD64(x86_64)架构。用Docker默认构建,镜像的平台就是当前机器的平台。M1上构建的镜像是 linux/arm64,但服务器CPU不认识ARM64指令。
解决
方法1:指定平台构建(推荐)
# 构建时指定目标平台
docker buildx build --platform linux/amd64 -t my-app:latest .
# 或者在docker-compose.yml里指定
services:
app:
build:
context: .
platform: linux/amd64 # 指定目标平台
方法2:使用alpine基础镜像
# 用 alpine 版本,基本兼容所有平台
FROM node:18-alpine
构建机器和运行机器平台不一致,是最隐蔽的坑。始终用 --platform linux/amd64 构建,或者用 alpine 基础镜像。
坑5:日志撑爆磁盘
现象
部署第二天,服务器SSH连不上了。一查,磁盘100%满了。
原因
Docker默认把容器日志写入 /var/lib/docker/containers/<container-id>/<container-id>-json.log,并且没有轮转。日志一直写,越积越多。我的Java应用debug日志开着,一天能写10GB。
解决
方法1:配置日志轮转(生产环境推荐)
修改 /etc/docker/daemon.json(需要sudo):
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
然后重启Docker:
sudo systemctl restart docker
方法2:在docker-compose里配置(更灵活)
services:
app:
image: my-app
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
方法3:定期清理日志(补充手段)
# 清理超过7天的日志文件
find /var/lib/docker/containers -name "*.log" -mtime +7 -delete
日志磁盘占满是生产环境的常见事故。部署第一天就配置好日志轮转,不要等磁盘满了再处理。
完整Dockerfile示例(Node.js生产环境)
# ========== 构建阶段 ==========
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# ========== 运行阶段 ==========
FROM node:18-alpine
WORKDIR /app
# 从builder阶段复制依赖
COPY --from=builder /app/node_modules ./node_modules
COPY . .
# 非root用户运行(安全最佳实践)
RUN addgroup -g 1001 -S nodejs && adduser -S nodeapp -u 1001
USER nodeapp
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
总结
| 坑 | 原因 | 解决方案 |
|---|---|---|
| 镜像体积过大 | COPY . . + 多余依赖 | 多阶段构建 + .dockerignore |
| 启动顺序 | depends_on 不等就绪 | healthcheck + condition |
| 内存限制不生效 | JVM不识别容器限制 | -XX:+UseContainerSupport |
| 跨平台不兼容 | 构建平台与运行平台不一致 | --platform linux/amd64 |
| 日志撑爆磁盘 | 无日志轮转 | daemon.json + max-size |
Docker的坑大多数是"看起来能用,但上线就挂"类型的。建议每条都提前预防,不要等踩了再补。
更多推荐


所有评论(0)