สำหรับวันนี้พอดีสงสัยได้ไปส่อง Repo อื่นๆ แล้วเวลาทำ Container Image มันต้องมี Dockerfile แต่มา Project นึง (เพิ่งได้เข้ามาดู เพราะคนเดิมออกไป) เลยอ๋อ นอกจากทำ Dockerfile แล้วมันสามารถกำหนด Config ในตัว Maven หรือ gradle ได้ด้วยนะ เลยมา Recap ลง Blog ไว้นิดหน่อยทำ Second Brain อิอิ
Table of Contents
Cloud Native Buildpacks
เป็น Project ที่ทาง Pivotal กับ Heroku ทำขึ้นมา เพื่อให้การทำ Container Image สะดวกขึ้น และได้ส่งต่อ CNCF ดูแลต่อ โดยเจ้า Buildpacks มันเข้ามาช่วยลดภาระในการมาจัดการดูแลตัว Dockerfile ของแต่ละ EndPoint ถ้ามีเยอะก็ดูแลลำบาก และยังช่วย Optimize ให้ในตัวด้วย รวมถึงเป็น Container Image ที่เข้ากับมาตรฐาน OCI ด้วย
แล้วที่นี้ใน maven หรือ gradle มันพร้อมใช้อยู่แล้วด้วย โดยใน Blog นี้จะเน้นไปในส่วนของ maven ครับ
Set up the project
ลองสร้าง Project ด้วย Spring Initializr + Spring Web เพิ่มเข้าไปเป็น Dependency และทำ Controller เพื่ออ่าน version ของ app
import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/status") public class ApplicationStatusController { @Value("${api.version}") private String apiVersion; @GetMapping public Status getStatus() { return new Status(this.apiVersion, "running"); } class Status { private String apiVersion; private String status; public Status(String apiVersion, String status) { this.apiVersion = apiVersion; this.status = status; } public String getApiVersion() { return apiVersion; } public String getStatus() { return status; } } }
จากนั้นกำหนด application.properties ใน folder resource เพิ่ม property api.version=1.0 เข้าไป
สร้าง Container Image โดยใช้ buildpacks
ใน maven หรือ gradle รองรับตัว buildpacks อยู่แล้ว สำหรับใน maven ให้ลองหา section build
ได้เลยครับ มี Code ปรมาณนี้
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
ปกติแล้ว เราสามารถ Run mvn spring-boot:build-image
หรือ mvnw.cmd spring-boot:build-image
ได้เลย มันจะไปอ่านในส่วน artifactId
ของ maven project
แต่ถ้าเจอ Error failed: Unable to parse name "dsInsuranceApi". Image name must be in the form
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.4.5:build-image (default-cli) on project dsInsuranceApi: Execution default-cli of goal org.springframework.boot:spring-boot-maven-plugin:3.4.5:build-image failed: Unable to parse name "dsInsuranceApi". Image name must be in the form '[domainHost:port/][path/]name', with 'path' and 'name' containing only [a-z0-9][.][_][-] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
แสดงว่าชื่อ artifactId
เอาไปใช้เป็น container image ไม่ได้ เงื่อนไขต้อง lowercase letters/ numbers / hyphens (-
)/ underscores (_
) / periods (.
) ตรงนี้สามารถใช้ tag configuration > image > name แทนครับ
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <name>ds_insurance/api</name> </image> </configuration> </plugin> </plugins> </build>
ถ้าลองสั่ง Build อีกรอบ ลองสังเกตุดูมันจะพยายามดึง Base Image มาให้ด้วย
[INFO] Building image 'docker.io/library/ds-insurance-api:0.0.1-SNAPSHOT' [INFO] [INFO] > Pulling builder image 'docker.io/paketobuildpacks/builder-jammy-java-tiny:latest' 100% [INFO] > Pulled builder image 'paketobuildpacks/builder-jammy-java-tiny@sha256:cbd65c21bc53832bd81932f9f52e650b275a6aa82fd30702f523907385a4788e' [INFO] > Pulling run image 'docker.io/paketobuildpacks/run-jammy-tiny:latest' for platform 'linux/amd64' 100%
จากนั้น เข้า Process การ Build และทำเก็บข้อมูล รวมถึงการ Inject Config ต่างๆ เข้าไป BP_xx รวมถึงแอบเก็บ Cache ด้วย
INFO] > Running creator [INFO] [creator] ===> ANALYZING [INFO] [creator] Image with name "docker.io/library/ds-insurance-api:0.0.1-SNAPSHOT" not found [INFO] [creator] ===> DETECTING [INFO] [creator] target distro name/version labels not found, reading /etc/os-release file [INFO] [creator] 6 of 26 buildpacks participating [INFO] [creator] paketo-buildpacks/ca-certificates 3.10.1 [INFO] [creator] paketo-buildpacks/bellsoft-liberica 11.2.1 [INFO] [creator] paketo-buildpacks/syft 2.12.0 [INFO] [creator] paketo-buildpacks/executable-jar 6.13.0 [INFO] [creator] paketo-buildpacks/dist-zip 5.10.0 [INFO] [creator] paketo-buildpacks/spring-boot 5.33.0 [INFO] [creator] ===> RESTORING [INFO] [creator] ===> BUILDING [INFO] [creator] target distro name/version labels not found, reading /etc/os-release file [INFO] [creator] [INFO] [creator] Paketo Buildpack for CA Certificates 3.10.1 [INFO] [creator] https://github.com/paketo-buildpacks/ca-certificates [INFO] [creator] Build Configuration: [INFO] [creator] $BP_EMBED_CERTS false Embed certificates into the image [INFO] [creator] $BP_ENABLE_RUNTIME_CERT_BINDING true Deprecated: Enable/disable certificate helper layer to add certs at runtime [INFO] [creator] $BP_RUNTIME_CERT_BINDING_DISABLED false Disable certificate helper layer to add certs at runtime [INFO] [creator] Launch Helper: Contributing to layer [INFO] [creator] Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper [INFO] [creator] [INFO] [creator] Paketo Buildpack for BellSoft Liberica 11.2.1 [INFO] [creator] https://github.com/paketo-buildpacks/bellsoft-liberica [INFO] [creator] Build Configuration: [INFO] [creator] $BP_JVM_JLINK_ARGS --no-man-pages --no-header-files --strip-debug --compress=1 configure custom link arguments (--output must be omitted) [INFO] [creator] $BP_JVM_JLINK_ENABLED false enables running jlink tool to generate custom JRE [INFO] [creator] $BP_JVM_TYPE JRE the JVM type - JDK or JRE [INFO] [creator] $BP_JVM_VERSION 21 the Java version [INFO] [creator] Launch Configuration: [INFO] [creator] $BPL_DEBUG_ENABLED false enables Java remote debugging support [INFO] [creator] $BPL_DEBUG_PORT 8000 configure the remote debugging port [INFO] [creator] $BPL_DEBUG_SUSPEND false configure whether to suspend execution until a debugger has attached [INFO] [creator] $BPL_HEAP_DUMP_PATH write heap dumps on error to this path [INFO] [creator] $BPL_JAVA_NMT_ENABLED true enables Java Native Memory Tracking (NMT) [INFO] [creator] $BPL_JAVA_NMT_LEVEL summary configure level of NMT, summary or detail [INFO] [creator] $BPL_JFR_ARGS configure custom Java Flight Recording (JFR) arguments [INFO] [creator] $BPL_JFR_ENABLED false enables Java Flight Recording (JFR) [INFO] [creator] $BPL_JMX_ENABLED false enables Java Management Extensions (JMX) [INFO] [creator] $BPL_JMX_PORT 5000 configure the JMX port [INFO] [creator] $BPL_JVM_HEAD_ROOM 0 the headroom in memory calculation [INFO] [creator] $BPL_JVM_LOADED_CLASS_COUNT 35% of classes the number of loaded classes in memory calculation [INFO] [creator] $BPL_JVM_THREAD_COUNT 250 the number of threads in memory calculation [INFO] [creator] $JAVA_TOOL_OPTIONS the JVM launch flags [INFO] [creator] Using Java version 21 extracted from MANIFEST.MF [INFO] [creator] BellSoft Liberica JRE 21.0.7: Contributing to layer [INFO] [creator] Downloading from https://github.com/bell-sw/Liberica/releases/download/21.0.7+9/bellsoft-jre21.0.7+9-linux-amd64.tar.gz [INFO] [creator] Verifying checksum [INFO] [creator] Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre [INFO] [creator] Adding 146 container CA certificates to JVM truststore [INFO] [creator] Writing env.launch/BPI_APPLICATION_PATH.default [INFO] [creator] Writing env.launch/BPI_JVM_CACERTS.default [INFO] [creator] Writing env.launch/BPI_JVM_CLASS_COUNT.default [INFO] [creator] Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default [INFO] [creator] Writing env.launch/JAVA_HOME.default [INFO] [creator] Writing env.launch/JAVA_TOOL_OPTIONS.append [INFO] [creator] Writing env.launch/JAVA_TOOL_OPTIONS.delim [INFO] [creator] Writing env.launch/MALLOC_ARENA_MAX.default [INFO] [creator] Launch Helper: Contributing to layer [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jvm-heap [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jmx [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jfr [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9 [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/debug-9 [INFO] [creator] Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/nmt [INFO] [creator] Java Security Properties: Contributing to layer [INFO] [creator] Writing env.launch/JAVA_SECURITY_PROPERTIES.default [INFO] [creator] Writing env.launch/JAVA_TOOL_OPTIONS.append [INFO] [creator] Writing env.launch/JAVA_TOOL_OPTIONS.delim [INFO] [creator] [INFO] [creator] Paketo Buildpack for Syft 2.12.0 [INFO] [creator] https://github.com/paketo-buildpacks/syft [INFO] [creator] Downloading from https://github.com/anchore/syft/releases/download/v1.23.0/syft_1.23.0_linux_amd64.tar.gz [INFO] [creator] Verifying checksum [INFO] [creator] Writing env.build/SYFT_CHECK_FOR_APP_UPDATE.default [INFO] [creator] [INFO] [creator] Paketo Buildpack for Executable JAR 6.13.0 [INFO] [creator] https://github.com/paketo-buildpacks/executable-jar [INFO] [creator] Class Path: Contributing to layer [INFO] [creator] Writing env/CLASSPATH.delim [INFO] [creator] Writing env/CLASSPATH.prepend [INFO] [creator] Process types: [INFO] [creator] executable-jar: java org.springframework.boot.loader.launch.WarLauncher (direct) [INFO] [creator] task: java org.springframework.boot.loader.launch.WarLauncher (direct) [INFO] [creator] web: java org.springframework.boot.loader.launch.WarLauncher (direct) [INFO] [creator] [INFO] [creator] Paketo Buildpack for Spring Boot 5.33.0 [INFO] [creator] https://github.com/paketo-buildpacks/spring-boot [INFO] [creator] Build Configuration: [INFO] [creator] $BPL_JVM_CDS_ENABLED false whether to enable CDS optimizations at runtime [INFO] [creator] $BPL_SPRING_AOT_ENABLED false whether to enable Spring AOT at runtime [INFO] [creator] $BP_JVM_CDS_ENABLED false whether to enable CDS & perform JVM training run [INFO] [creator] $BP_SPRING_AOT_ENABLED false whether to enable Spring AOT [INFO] [creator] $BP_SPRING_CLOUD_BINDINGS_DISABLED false whether to contribute Spring Boot cloud bindings support [INFO] [creator] $BP_SPRING_CLOUD_BINDINGS_VERSION 1 default version of Spring Cloud Bindings library to contribute [INFO] [creator] Launch Configuration: [INFO] [creator] $BPL_SPRING_CLOUD_BINDINGS_DISABLED false whether to auto-configure Spring Boot environment properties from bindings [INFO] [creator] $BPL_SPRING_CLOUD_BINDINGS_ENABLED true Deprecated - whether to auto-configure Spring Boot environment properties from bindings [INFO] [creator] Creating slices from layers index [INFO] [creator] dependencies (64.7 MB) [INFO] [creator] spring-boot-loader (459.3 KB) [INFO] [creator] snapshot-dependencies (0.0 B) [INFO] [creator] application (66.6 KB) [INFO] [creator] Spring Cloud Bindings 2.0.4: Contributing to layer [INFO] [creator] Downloading from https://repo1.maven.org/maven2/org/springframework/cloud/spring-cloud-bindings/2.0.4/spring-cloud-bindings-2.0.4.jar [INFO] [creator] Verifying checksum [INFO] [creator] Copying to /layers/paketo-buildpacks_spring-boot/spring-cloud-bindings [INFO] [creator] Web Application Type: Contributing to layer [INFO] [creator] Non-web application detected [INFO] [creator] Writing env.launch/BPL_JVM_THREAD_COUNT.default [INFO] [creator] Launch Helper: Contributing to layer [INFO] [creator] Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings [INFO] [creator] 4 application slices [INFO] [creator] Image labels: [INFO] [creator] org.opencontainers.image.title [INFO] [creator] org.opencontainers.image.version [INFO] [creator] org.springframework.boot.version [INFO] [creator] ===> EXPORTING [INFO] [creator] Adding layer 'paketo-buildpacks/ca-certificates:helper' [INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:helper' [INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties' [INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:jre' [INFO] [creator] Adding layer 'paketo-buildpacks/executable-jar:classpath' [INFO] [creator] Adding layer 'paketo-buildpacks/spring-boot:helper' [INFO] [creator] Adding layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings' [INFO] [creator] Adding layer 'paketo-buildpacks/spring-boot:web-application-type' [INFO] [creator] Adding layer 'buildpacksio/lifecycle:launch.sbom' [INFO] [creator] Added 5/5 app layer(s) [INFO] [creator] Adding layer 'buildpacksio/lifecycle:launcher' [INFO] [creator] Adding layer 'buildpacksio/lifecycle:config' [INFO] [creator] Adding layer 'buildpacksio/lifecycle:process-types' [INFO] [creator] Adding label 'io.buildpacks.lifecycle.metadata' [INFO] [creator] Adding label 'io.buildpacks.build.metadata' [INFO] [creator] Adding label 'io.buildpacks.project.metadata' [INFO] [creator] Adding label 'org.opencontainers.image.title' [INFO] [creator] Adding label 'org.opencontainers.image.version' [INFO] [creator] Adding label 'org.springframework.boot.version' [INFO] [creator] Setting default process type 'web' [INFO] [creator] Saving docker.io/library/ds-insurance-api:0.0.1-SNAPSHOT... [INFO] [creator] *** Images (0fa27a0042c5): [INFO] [creator] docker.io/library/ds-insurance-api:0.0.1-SNAPSHOT [INFO] [creator] Adding cache layer 'paketo-buildpacks/syft:syft' [INFO] [creator] Adding cache layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings' [INFO] [creator] Adding cache layer 'buildpacksio/lifecycle:cache.sbom' [INFO] [INFO] Successfully built image 'docker.io/library/ds-insurance-api:0.0.1-SNAPSHOT' [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 39.951 s [INFO] Finished at: 2025-05-04T15:26:48+07:00
ถ้าลอง docker images จะพบ image ของเรา
docker images REPOSITORY TAG IMAGE ID CREATED SIZE ds-insurance-api 0.0.1-SNAPSHOT 0fa27a0042c5 3 minute ago 314MB
เพียงเท่านี้เราเอา image ไปใช้งานใน docker compose ต่อได้แล้ว อย่างผมใช้วิธีให้มันลบทิ้ง pack และ run compose ขึ้นมาใช้งานครับ
docker compose -f docker/docker-compose.yaml down --remove-orphans docker rmi best_insurance/api:latest .\mvnw spring-boot:build-image docker compose -f docker/docker-compose.yaml up
ที่เหลือสำหรับการตั้งค่าในเคสอื่นๆ
- Set Specific Java version
กำหนด env > BP_JVM_VERSION ตามตัวอย่าง
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <name>ds_insurance/api</name> <env> <BP_JVM_VERSION>17</BP_JVM_VERSION> <JAVA_TOOL_OPTIONS>-XX:+UseZGC</JAVA_TOOL_OPTIONS> </env> </image> </configuration> </plugin> </plugins> </build>
- Configure the JVM
กำหนด param ของ JVM เช่น พวก Memory, Garbage Collector, heap size เป็นต้น กำหนดได้จาก JAVA_TOOL_OPTIONS
- ปกติทำประมาณนี้
JAVA_TOOL_OPTIONS="-XX:+UseZGC -Xms512m" mvn spring-boot:build-image
- เติมไว้ใน pom ก็ได้นะ
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <environment> <JAVA_TOOL_OPTIONS>-XX:+UseZGC -Xms512m</JAVA_TOOL_OPTIONS> </environment> </configuration> </plugin> </plugins> </build>
จริงมันกำหนดตอน Run ได้นะ เติม -e JAVA_TOOL_OPTIONS="-XX:+UseZGC -Xms512m" หรือ จะลองแปลงเป็น env ใน deployment ของ k8s ก็ได้นะ
... env: - name: JAVA_TOOL_OPTIONS value: "-XX:+UseZGC -Xms512m" ...
- Select Builder เพื่อลดขนาด image
มันมีหลายเจ้า หรือ จะ Custom ได้ เช่น Alpaquita Buildpacks based on Liberica JDK (มันเป็น Image เค้าแนะนำว่าสำหรับ Spring ก็ลองได้ตามนี้
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <builder>bellsoft/buildpacks.builder:musl</builder> </image> </configuration> </plugin> </plugins> </build>
- ถ้าจะใช้ AOT / CDS เปิด option ตามนี้ครับ
- CDS-Class Data Sharing
- AOT-Ahead-of-time
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <env> <BP_SPRING_AOT_ENABLED>true</BP_SPRING_AOT_ENABLED> <BP_JVM_CDS_ENABLED>true</BP_JVM_CDS_ENABLED> </env> </image> </configuration> <executions> <execution> <goals> <goal>process-aot</goal> </goals> </execution> </executions> </plugin>
จริงๆ แล้วตัว Build Packs รองรับได้ภาษา Go / dotnet / Node ก็มีนะ ลองดูเพิ่มได้ที่ https://paketo.io/docs/
Reference
- spring.io: Cloud Native Buildpacks
- https://paketo.io/docs/howto/java/
- https://github.com/paketo-buildpacks/bellsoft-liberica
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.