写在前面你是否经历过这样的场景本地开发调试得好好的一部署到测试服务器就各种报错——“JDK版本不对”、“依赖库缺失”、“配置文件路径找不到”……每次都得重复安装环境、调试配置浪费大量时间。Docker的出现彻底改变了这个局面。它让开发者可以将应用与其运行环境一起打包成一个轻量级的镜像无论在哪个平台上只要安装了Docker就能以完全一致的方式运行。在Java开发中最常见的需求就是把我的Spring Boot项目打包成一个镜像然后用docker-compose把应用、MySQL、Redis等依赖服务一起启动。这篇笔记我们从最基础的Dockerfile编写开始一步步掌握镜像构建、多阶段构建优化再到使用docker-compose编排多个容器最终实现一套完整的本地开发/测试环境。不管你是刚接触Docker的初学者还是希望规范化项目交付的开发者这篇文章都能帮你快速上手。1️⃣ Docker核心概念回顾在开始编写Dockerfile之前先理清三个核心概念镜像Image一个只读的模板包含应用及其运行所需的环境操作系统、依赖库、代码等。类似于面向对象中的“类”。容器Container镜像的运行实例是真正运行应用的地方。可以创建、启动、停止、删除。类似于“对象”。仓库Repository存放镜像的地方如Docker Hub、私有Harbor等。关系图2️⃣ Dockerfile基础指令详解与最佳实践Dockerfile是一个文本文件包含一系列指令告诉Docker如何构建镜像。下面是常用的指令及其作用最佳实践原则选择合适的基础镜像生产环境尽量用slim或alpine变体如openjdk:17-jdk-slim体积小漏洞少。减少层数将多条RUN命令合并成一条用连接避免产生太多中间层。利用构建缓存把不容易变化的指令放在前面如COPY pom.xml先于COPY src这样修改代码后不必重装依赖。使用.dockerignore排除不需要的文件如.git、target/、*.log加速构建。指定非root用户创建应用用户运行容器提高安全性。3️⃣ Spring Boot项目打包镜像从jar到镜像假设你有一个Spring Boot项目用Maven构建后生成target/myapp.jar。下面是一个基本的Dockerfile# 第一阶段基础环境 FROM openjdk:17-jdk-slim WORKDIR /app # 复制jar包 COPY target/myapp.jar app.jar # 暴露端口 EXPOSE 8080 # 启动命令 ENTRYPOINT [java, -jar, app.jar]构建镜像# 在项目根目录执行Dockerfile所在位置 docker build -t myapp:1.0 .运行容器docker run -d -p 8080:8080 --name myapp myapp:1.0此时访问http://localhost:8080即可看到应用。问题这个镜像包含了完整的JDK和jar包体积通常在300MB以上。我们可以通过多阶段构建大幅减小体积。4️⃣ 多阶段构建大幅减小镜像体积多阶段构建允许在一个Dockerfile中使用多个FROM指令最终只保留最后一个阶段的产物。对于Spring Boot应用可以在第一阶段用Maven构建可选第二阶段用JRE运行。# 第一阶段构建使用Maven镜像 FROM maven:3.8.6-openjdk-17-slim AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline # 下载依赖利用缓存 COPY src ./src RUN mvn package -DskipTests # 第二阶段运行使用精简JRE FROM openjdk:17-jdk-slim WORKDIR /app COPY --frombuilder /build/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]优势最终镜像中只包含JRE和jar包没有Maven和源代码体积可缩小到100MB左右。还可以使用更极致的eclipse-temurin:17-jre-alpine体积低于80MB。构建命令不变Docker会自动执行多阶段。5️⃣ docker-compose入门定义和运行多容器应用对于一个完整的后端服务通常需要依赖数据库如MySQL、缓存如Redis等。使用docker-compose可以通过一个YAML文件定义多个容器的配置、网络和依赖关系实现一键启动。安装docker-composeDocker Desktop已内置Linux需单独安装sudo apt install docker-compose-plugin。docker-compose.yml基础结构version: 3.8 services: app: build: . # 使用当前目录的Dockerfile构建 ports: - 8080:8080 depends_on: - mysql - redis environment: - SPRING_DATASOURCE_URLjdbc:mysql://mysql:3306/mydb - SPRING_REDIS_HOSTredis mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: mydb ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql # 持久化数据 redis: image: redis:7-alpine ports: - 6379:6379 volumes: mysql_data:启动docker-compose up -d停止docker-compose down加-v会删除数据卷注意服务之间通过服务名互相访问如mysql因为compose会自动创建一个网络并解析服务名。6️⃣ 实战Spring Boot MySQL Redis 完整编排下面我们以一个完整的Spring Boot项目为例演示如何配置环境变量、依赖服务并实现一键启动。步骤1准备Spring Boot应用在application.properties或application.yml中使用环境变量占位符spring: datasource: url: ${SPRING_DATASOURCE_URL:jdbc:mysql://localhost:3306/mydb} username: ${SPRING_DATASOURCE_USERNAME:root} password: ${SPRING_DATASOURCE_PASSWORD:root123} redis: host: ${SPRING_REDIS_HOST:localhost} port: ${SPRING_REDIS_PORT:6379}这样在compose中通过environment覆盖即可。步骤2编写Dockerfile多阶段如上所示放到项目根目录。步骤3编写docker-compose.ymlversion: 3.8 services: app: build: . ports: - 8080:8080 depends_on: mysql: condition: service_healthy # 等待MySQL健康后再启动 redis: condition: service_started environment: - SPRING_DATASOURCE_URLjdbc:mysql://mysql:3306/mydb?useSSLfalseallowPublicKeyRetrievaltrue - SPRING_DATASOURCE_USERNAMEroot - SPRING_DATASOURCE_PASSWORDroot123 - SPRING_REDIS_HOSTredis restart: on-failure mysql: image: mysql:8.0 ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: mydb volumes: - mysql_data:/var/lib/mysql healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] interval: 10s timeout: 5s retries: 5 redis: image: redis:7-alpine ports: - 6379:6379 restart: always volumes: mysql_data:步骤4构建并启动docker-compose up -d --build # --build强制重新构建镜像查看日志docker-compose logs -f app步骤5清理docker-compose down -v # 删除容器和数据卷谨慎7️⃣ 常用docker-compose命令与调试技巧调试技巧单独启动某个服务docker-compose up -d mysql避免一次性启动全部。查看容器内部文件docker-compose exec app ls -la /app复制文件到容器docker cp 本地文件 容器名:目标路径也可以反过来。检查网络连通性docker-compose exec app ping mysql需镜像装有ping。8️⃣ 总结与最佳实践一个更完善的Dockerfile示例FROM eclipse-temurin:17-jre-alpine AS runner WORKDIR /app RUN addgroup -g 1000 app adduser -u 1000 -G app -D app COPY --chownapp:app target/*.jar app.jar USER app EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]在实际项目中你可能会遇到Spring Boot应用连接MySQL时出现“Communications link failure”即使compose中已经定义了depends_on。这是因为MySQL启动完成进程存在并不代表它能接受连接。你是如何解决这个问题的除了使用healthcheck等待你用过哪些更可靠的方案如使用wait-for-it.sh脚本欢迎分享你的经验。