99 Ngày Spring — Ngày 89: Docker hóa Spring Boot
Đóng một file jar vào image chỉ cần ba dòng Dockerfile, nhưng image đó thường to, build lại chậm và chạy bằng quyền root. Một Dockerfile chuẩn cho Spring Boot giải quyết ba việc: tách môi trường build khỏi môi trường chạy, chia jar thành các layer để tận dụng cache, và chạy bằng người dùng không có đặc quyền.

1. Nhiều stage: build một nơi, chạy một nơi
Stage đầu dùng image có JDK để biên dịch; stage cuối chỉ cần JRE và những file phải có lúc chạy. Mã nguồn, Maven và cache build không đi vào image cuối, nên image nhỏ hơn và ít bề mặt tấn công hơn. Ghim tag của base image theo phiên bản cụ thể để build lặp lại được, và thêm .dockerignore để không gửi target, .git vào build context.
FROM eclipse-temurin:21-jdk AS build
WORKDIR /workspace
COPY . .
RUN ./mvnw -q -DskipTests package
FROM eclipse-temurin:21-jre AS extract
WORKDIR /workspace
COPY --from=build /workspace/target/*.jar app.jar
RUN java -Djarmode=tools -jar app.jar extract --layers --destination extracted2. Layered jar: đổi code không tải lại thư viện
Fat jar là một file duy nhất: sửa một dòng code thì cả trăm megabyte thư viện bị coi là thay đổi. Spring Boot biết chia jar thành bốn layer theo tần suất thay đổi: dependencies, spring-boot-loader, snapshot-dependencies và application. COPY từng layer theo thứ tự đó thì các layer thư viện được lấy từ cache, chỉ layer application nhỏ phải build và đẩy lại.
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN useradd --system --uid 1001 spring
USER spring
COPY --from=extract /workspace/extracted/dependencies/ ./
COPY --from=extract /workspace/extracted/spring-boot-loader/ ./
COPY --from=extract /workspace/extracted/snapshot-dependencies/ ./
COPY --from=extract /workspace/extracted/application/ ./
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "-jar", "app.jar"]3. Chạy đúng trong container
Chạy bằng user không phải root để một lỗ hổng trong ứng dụng không trao luôn quyền root trong container. ENTRYPOINT viết dạng mảng để tiến trình java nhận trực tiếp tín hiệu SIGTERM; kết hợp server.shutdown=graceful, ứng dụng xử lý xong request đang dở trước khi tắt. JVM tự nhận giới hạn bộ nhớ của container; MaxRAMPercentage chừa chỗ cho phần bộ nhớ ngoài heap.
Không đưa secret vào image hay ARG; truyền lúc chạy.
Cấu hình đến từ biến môi trường, cùng một image dùng cho mọi môi trường.
Dùng health endpoint của Actuator cho probe của nền tảng chạy container.
4. Buildpacks: không cần Dockerfile
Plugin Spring Boot có thể tạo image OCI bằng Cloud Native Buildpacks chỉ với một lệnh. Buildpack tự chọn JRE, chia layer, chạy bằng user không phải root và tính sẵn tham số bộ nhớ cho JVM. Bạn mất một phần quyền kiểm soát chi tiết nhưng được một image theo thông lệ tốt mà không phải bảo trì Dockerfile.
./mvnw spring-boot:build-image \
-Dspring-boot.build-image.imageName=registry.example/orders:1.4.0
docker run --rm -p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shop \
registry.example/orders:1.4.0Một image tốt nhỏ, build lại nhanh, không chứa secret và không chạy bằng root. Ngày 90 dùng Docker cho việc gần gũi hơn: dựng môi trường dev bằng Docker Compose.
Tài liệu đối chiếu
https://docs.spring.io/spring-boot/reference/packaging/container-images/dockerfiles.html
https://docs.spring.io/spring-boot/reference/packaging/container-images/cloud-native-buildpacks.html
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


