
Docker已经成为了现代应用部署的标准工具,但从”能用”到”用好”,中间隔着不少坑。这篇文章总结我在实际项目中踩过的那些坑,希望能帮你少走弯路。
一、镜像构建:小即是美
很多人写的Dockerfile,镜像动辄1GB起步。这不是Docker的问题,是构建方式的问题。
❌ 错误示范
FROM openjdk:17
COPY . /app
WORKDIR /app
RUN ./mvnw package
CMD ["java", "-jar", "target/app.jar"]
这样构建出来的镜像包含Maven、源码、编译产物,臃肿且不安全。
✅ 正确做法:多阶段构建
# 构建阶段
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 运行阶段
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
这样最终镜像只有JRE和jar包,体积从1GB+降到150MB左右。
二、日志处理:别让磁盘爆满
Docker默认的日志驱动会把所有stdout输出写到/var/lib/docker/containers下,长期运行后磁盘会被日志撑爆。
解决方案
方法1:配置日志轮转
# /etc/docker/daemon.json
{
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
方法2:使用独立的日志驱动
docker run --log-driver=json-file
--log-opt max-size=10m
--log-opt max-file=5
your-image
或者直接使用syslog、fluentd等专业日志方案。
三、网络陷阱:localhost不是万能的
容器内访问宿主机服务,用localhost往往会失败。这是因为容器有独立的网络命名空间。
正确姿势
- Linux:使用
host.docker.internal或172.17.0.1(docker0网关) - macOS/Windows:Docker Desktop提供了
host.docker.internal域名 - 通用方案:
--network host模式(但失去了网络隔离)
# 连接宿主机的MySQL
spring:
datasource:
url: jdbc:mysql://host.docker.internal:3306/mydb
四、时区问题:为什么时间不对?
容器默认使用UTC时区,这会导致日志时间、业务时间都与本地时间差8小时。
# 方式1:设置环境变量
docker run -e TZ=Asia/Shanghai your-image
# 方式2:挂载时区文件
docker run -v /etc/localtime:/etc/localtime:ro your-image
# Dockerfile中设置
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
五、健康检查:别让僵尸容器活着
容器”活着”不代表应用”正常”。Java应用可能因为OOM或者死锁,进程还在但无法响应请求。
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3
CMD curl -f http://localhost:8080/actuator/health || exit 1
K8s中对应配置:
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
六、资源限制:别让一个容器拖垮整台机器
docker run
--memory="512m" # 内存限制
--memory-swap="1g" # 内存+交换分区限制
--cpus="1.5" # CPU核心数
--cpu-shares=512 # CPU权重
--pids-limit=100 # 进程数限制
your-image
Java应用特别注意:JVM感知不到容器内存限制,可能导致OOM。务必添加JVM参数:
java -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -jar app.jar
七、安全建议:最小权限原则
- 不要用root运行应用:添加非root用户
- 只读文件系统:
--read-only - 限制capabilities:
--cap-drop ALL --cap-add CHOWN - 使用安全扫描:
docker scout quickview
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup target/*.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
写在最后
Docker很强大,但”能跑”和”生产可用”之间,隔着很多细节。希望这些实战经验能帮你避开我踩过的坑。
容器化不是目的,稳定可靠的交付才是。工具是为业务服务的,别为了用而用。
觉得有用就点个赞吧~