Dockerfiles per Service
One Image per Service
Every service is built into its own Docker image and versioned on its own: shopease/order-service:1.4.2, shopease/payment-service:2.0.1. The image contains the JRE and the app, so it runs the same on a laptop, in CI and in production.
Tip: Docker basics (images, containers, layers, volumes) are covered in the Careeroza Docker material, and a first Spring Boot Dockerfile in the Spring Boot advance level. Here we build images that are small, secure and fast to rebuild, for many services.
A Production Dockerfile (Multi-Stage, Layered)
# ---------- build stage ----------
FROM eclipse-temurin:21-jdk AS build
WORKDIR /workspace
COPY mvnw pom.xml ./
COPY .mvn .mvn
RUN ./mvnw -q dependency:go-offline # cached until pom.xml changes
COPY src src
RUN ./mvnw -q package -DskipTests
# split the fat jar into layers: dependencies change rarely, application code often
RUN java -Djarmode=tools -jar target/order-service.jar extract --layers --launcher --destination extracted
# ---------- runtime stage ----------
FROM eclipse-temurin:21-jre
RUN useradd --system --uid 1001 spring
WORKDIR /app
COPY --from=build /workspace/extracted/dependencies/ ./
COPY --from=build /workspace/extracted/spring-boot-loader/ ./
COPY --from=build /workspace/extracted/snapshot-dependencies/ ./
COPY --from=build /workspace/extracted/application/ ./
# never run as root
USER spring
EXPOSE 8082
ENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError"
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]- Multi-stage: Maven and the JDK stay in the build stage; the final image only has the JRE (~200 MB smaller).
- Layers: after a code change only the small
applicationlayer (~100 KB) is rebuilt and pushed, not 80 MB of dependencies. -Djarmode=toolsis the Spring Boot 3.3+ way (older versions use-Djarmode=layertools). Set<finalName>order-service</finalName>in the pom so the jar name is fixed.MaxRAMPercentage=75sizes the heap from the container memory limit, leaving room for metaspace and threads.
Build and Run
Example
docker build -t shopease/order-service:1.4.2 ./order-service
docker images shopease/order-service
docker run -d --name order -p 8082:8082 -e SPRING_PROFILES_ACTIVE=docker shopease/order-service:1.4.2Example output
[+] Building 41.3s (17/17) FINISHED => [build 6/7] RUN ./mvnw -q package -DskipTests 31.2s => [stage-1 6/7] COPY --from=build /workspace/extracted/application/ ./ 0.1s => naming to docker.io/shopease/order-service:1.4.2 REPOSITORY TAG IMAGE ID CREATED SIZE shopease/order-service 1.4.2 3f9c1b2a7d4e 10 seconds ago 312MB # rebuild after changing one Java file: [+] Building 9.8s (dependency layers CACHED)
Tip: No Dockerfile at all: ./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=shopease/order-service:1.4.2 uses Cloud Native Buildpacks to create a layered, non-root image. Add a .dockerignore with target/, .git/ and *.log to keep the build context small.