光环编程技术写作

光环编程技术写作

Halo 为什么没有采用 CNB 构建 Docker 镜像

2
2024-10-23

背景

我们都知道 Spring Boot 是可以利用 Cloud Native Buildpacks(CNB)构建 Docker 镜像,只需要简单执行命令 ./gradlew bootBuildImage 就可以轻松构建一个完整可用的 Docker 镜像。但是 Halo 目前仍然采用传统的 Dockerfile + BuildKit 构建 Docker 镜像。

优点

对于 Halo 来说,这种方式构建的镜像有以下优点:

  1. 更安全。镜像用户都是非 root 用户,可以参考官方文档

  2. 更方便。无需提前打包,无需手动编写 Dockerfile

  3. 镜像默认包含了内存计算器。容器在启动的时候,会进行自动计算最优的 JVM 内存参数,这对于小白用户来说非常友好。

缺点

既然通过 Spring Boot Gradle 插件构建 Docker 镜像有这么多优点,Halo 为什么迟迟没有采用呢?

  1. 历史遗留问题。在 Halo 2 诞生的时候,镜像运行用户一直是 root,如果我们修改为非 root 用户,将会带来比较严重的破坏性更新。

  2. 多架构 Docker 镜像难以支持。Halo 2 同时支持这些系统架构的 Docker 镜像:linux/amd64、linux/arm/v7、linux/arm64/v8、linux/ppc64le、linux/s390x。这一点 Buildpacks 目前似乎无法满足要求。直到现在,The Paketo Buildpack for Spring Boot 还不支持构建多架构 Docker 镜像。尽管我们可以自定义 builderbuildpacks 并指定 imagePlatform 来实现构建 ARM64 架构的 Docker 镜像,但仍然只是临时解决方案,我们期待上游能够彻底解决这个问题。最后,多架构镜像构建是在 Spring Boot 3.4.0-M2 中才得以实现,实现细节可参考这里

尝试采用 CNB 构建 Docker 镜像

尽管我们目前无法直接应用,但这并不方案我们往这个方向去探索。下面我将稍微介绍一下如何利用 Spring Boot Gradle 插件为 Halo 2.20.6 构建 ARM64 架构(可根据自己的机器自定选择系统架构)的 Docker 镜像。

需要注意的是,Halo 2.20.6 依赖的是 Spring Boot 3.4.0-M3。

  1. 克隆 Halo 源码并切换到 Halo 2.20.6

    gh repo clone halo-dev/halo
    git checkout v2.20.6
  1. 配置 Gradle 任务 bootBuildImage

    tasks.named('bootBuildImage') {
        environment["BP_JVM_VERSION"] = "21"
        imageName = "johnniang/halo:${project.version}"
        imagePlatform = 'linux/arm64'
        builder = 'paketobuildpacks/builder-jammy-buildpackless-tiny'
        buildpacks = ['paketobuildpacks/java']
    }
  1. 开始构建

    ./gradlew bootBuildImage -Pversion=2.20.6-alpha.1
    ❯ ./gradlew bootBuildImage -Pversion=2.20.6-alpha.1
    
    > Task :application:bootBuildImage
    Building image 'docker.io/johnniang/halo:2.20.6-alpha.1'
    
     > Pulling builder image 'docker.io/paketobuildpacks/builder-jammy-buildpackless-tiny:latest' for platform 'linux/arm64' ..................................................
     > Pulled builder image 'paketobuildpacks/builder-jammy-buildpackless-tiny@sha256:4ca31d2c8e8ab91b31470734cf563f636e6217773dab7d5dadfa558c34d469d7'
     > Pulling run image 'docker.io/paketobuildpacks/run-jammy-tiny:latest' for platform 'linux/arm64' ..................................................
     > Pulled run image 'paketobuildpacks/run-jammy-tiny@sha256:402b925a81a4c6985438fd37d0b22022ca688e528bbd46a38831a3702067cced'
     > Pulling buildpack image 'docker.io/paketobuildpacks/java:latest' for platform 'linux/arm64' ..................................................
     > Pulled buildpack image 'paketobuildpacks/java@sha256:a447ec782abc8c334842f91a69f670d352d86746b710b9dfe6dbd8a5631aab1b'
     > Executing lifecycle version v0.20.3
     > Using build cache volume 'pack-cache-af29c58f1d33.build'
    
     > Running creator
        [creator]     ===> ANALYZING
        [creator]     Image with name "docker.io/johnniang/halo:2.20.6-alpha.1" not found
        [creator]     ===> DETECTING
        [creator]     target distro name/version labels not found, reading /etc/os-release file
        [creator]     6 of 26 buildpacks participating
        [creator]     paketo-buildpacks/ca-certificates   3.8.6
        [creator]     paketo-buildpacks/bellsoft-liberica 10.9.0
        [creator]     paketo-buildpacks/syft              2.1.0
        [creator]     paketo-buildpacks/executable-jar    6.11.3
        [creator]     paketo-buildpacks/dist-zip          5.8.5
        [creator]     paketo-buildpacks/spring-boot       5.31.2
        [creator]     ===> RESTORING
        [creator]     ===> BUILDING
        [creator]     target distro name/version labels not found, reading /etc/os-release file
        [creator]     
        [creator]     Paketo Buildpack for CA Certificates 3.8.6
        [creator]       https://github.com/paketo-buildpacks/ca-certificates
        [creator]       Build Configuration:
        [creator]         $BP_EMBED_CERTS                    false  Embed certificates into the image
        [creator]         $BP_ENABLE_RUNTIME_CERT_BINDING    true   Deprecated: Enable/disable certificate helper layer to add certs at runtime
        [creator]         $BP_RUNTIME_CERT_BINDING_DISABLED  false  Disable certificate helper layer to add certs at runtime
        [creator]       Launch Helper: Contributing to layer
        [creator]         Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper
        [creator]     
        [creator]     Paketo Buildpack for BellSoft Liberica 10.9.0
        [creator]       https://github.com/paketo-buildpacks/bellsoft-liberica
        [creator]       Build Configuration:
        [creator]         $BP_JVM_JLINK_ARGS           --no-man-pages --no-header-files --strip-debug --compress=1  configure custom link arguments (--output must be omitted)
        [creator]         $BP_JVM_JLINK_ENABLED        false                                                        enables running jlink tool to generate custom JRE
        [creator]         $BP_JVM_TYPE                 JRE                                                          the JVM type - JDK or JRE
        [creator]         $BP_JVM_VERSION              21                                                           the Java version
        [creator]       Launch Configuration:
        [creator]         $BPL_DEBUG_ENABLED           false                                                        enables Java remote debugging support
        [creator]         $BPL_DEBUG_PORT              8000                                                         configure the remote debugging port
        [creator]         $BPL_DEBUG_SUSPEND           false                                                        configure whether to suspend execution until a debugger has attached
        [creator]         $BPL_HEAP_DUMP_PATH                                                                       write heap dumps on error to this path
        [creator]         $BPL_JAVA_NMT_ENABLED        true                                                         enables Java Native Memory Tracking (NMT)
        [creator]         $BPL_JAVA_NMT_LEVEL          summary                                                      configure level of NMT, summary or detail
        [creator]         $BPL_JFR_ARGS                                                                             configure custom Java Flight Recording (JFR) arguments
        [creator]         $BPL_JFR_ENABLED             false                                                        enables Java Flight Recording (JFR)
        [creator]         $BPL_JMX_ENABLED             false                                                        enables Java Management Extensions (JMX)
        [creator]         $BPL_JMX_PORT                5000                                                         configure the JMX port
        [creator]         $BPL_JVM_HEAD_ROOM           0                                                            the headroom in memory calculation
        [creator]         $BPL_JVM_LOADED_CLASS_COUNT  35% of classes                                               the number of loaded classes in memory calculation
        [creator]         $BPL_JVM_THREAD_COUNT        250                                                          the number of threads in memory calculation
        [creator]         $JAVA_TOOL_OPTIONS                                                                        the JVM launch flags
        [creator]         Using Java version 21 from BP_JVM_VERSION
        [creator]       BellSoft Liberica JRE 21.0.4: Contributing to layer
        [creator]         Downloading from https://github.com/bell-sw/Liberica/releases/download/21.0.4+9/bellsoft-jre21.0.4+9-linux-aarch64.tar.gz
        [creator]         Verifying checksum
        [creator]         Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
        [creator]         Adding 146 container CA certificates to JVM truststore
        [creator]         Writing env.launch/BPI_APPLICATION_PATH.default
        [creator]         Writing env.launch/BPI_JVM_CACERTS.default
        [creator]         Writing env.launch/BPI_JVM_CLASS_COUNT.default
        [creator]         Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
        [creator]         Writing env.launch/JAVA_HOME.default
        [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.append
        [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.delim
        [creator]         Writing env.launch/MALLOC_ARENA_MAX.default
        [creator]       Launch Helper: Contributing to layer
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jvm-heap
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jmx
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/jfr
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/debug-9
        [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/nmt
        [creator]       Java Security Properties: Contributing to layer
        [creator]         Writing env.launch/JAVA_SECURITY_PROPERTIES.default
        [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.append
        [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.delim
        [creator]     
        [creator]     Paketo Buildpack for Syft 2.1.0
        [creator]       https://github.com/paketo-buildpacks/syft
        [creator]         Downloading from https://github.com/anchore/syft/releases/download/v1.12.2/syft_1.12.2_linux_arm64.tar.gz
        [creator]         Verifying checksum
        [creator]         Writing env.build/SYFT_CHECK_FOR_APP_UPDATE.default
        [creator]     
        [creator]     Paketo Buildpack for Executable JAR 6.11.3
        [creator]       https://github.com/paketo-buildpacks/executable-jar
        [creator]       Class Path: Contributing to layer
        [creator]         Writing env/CLASSPATH.delim
        [creator]         Writing env/CLASSPATH.prepend
        [creator]       Process types:
        [creator]         executable-jar: java org.springframework.boot.loader.launch.JarLauncher (direct)
        [creator]         task:           java org.springframework.boot.loader.launch.JarLauncher (direct)
        [creator]         web:            java org.springframework.boot.loader.launch.JarLauncher (direct)
        [creator]     
        [creator]     Paketo Buildpack for Spring Boot 5.31.2
        [creator]       https://github.com/paketo-buildpacks/spring-boot
        [creator]       Build Configuration:
        [creator]         $BPL_JVM_CDS_ENABLED                 false  whether to enable CDS optimizations at runtime
        [creator]         $BPL_SPRING_AOT_ENABLED              false  whether to enable Spring AOT at runtime
        [creator]         $BP_JVM_CDS_ENABLED                  false  whether to enable CDS & perform JVM training run
        [creator]         $BP_SPRING_AOT_ENABLED               false  whether to enable Spring AOT
        [creator]         $BP_SPRING_CLOUD_BINDINGS_DISABLED   false  whether to contribute Spring Boot cloud bindings support
        [creator]         $BP_SPRING_CLOUD_BINDINGS_VERSION    1      default version of Spring Cloud Bindings library to contribute
        [creator]       Launch Configuration:
        [creator]         $BPL_SPRING_CLOUD_BINDINGS_DISABLED  false  whether to auto-configure Spring Boot environment properties from bindings
        [creator]         $BPL_SPRING_CLOUD_BINDINGS_ENABLED   true   Deprecated - whether to auto-configure Spring Boot environment properties from bindings
        [creator]       Creating slices from layers index
        [creator]         dependencies (88.3 MB)
        [creator]         spring-boot-loader (394.4 KB)
        [creator]         snapshot-dependencies (0.0 B)
        [creator]         application (7.4 MB)
        [creator]       Spring Cloud Bindings 2.0.3: Contributing to layer
        [creator]         Downloading from https://repo1.maven.org/maven2/org/springframework/cloud/spring-cloud-bindings/2.0.3/spring-cloud-bindings-2.0.3.jar
        [creator]         Verifying checksum
        [creator]         Copying to /layers/paketo-buildpacks_spring-boot/spring-cloud-bindings
        [creator]       Web Application Type: Contributing to layer
        [creator]         Reactive web application detected
        [creator]         Writing env.launch/BPL_JVM_THREAD_COUNT.default
        [creator]       Launch Helper: Contributing to layer
        [creator]         Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
        [creator]       4 application slices
        [creator]       Image labels:
        [creator]         org.opencontainers.image.title
        [creator]         org.opencontainers.image.version
        [creator]         org.springframework.boot.version
        [creator]     ===> EXPORTING
        [creator]     Adding layer 'paketo-buildpacks/ca-certificates:helper'
        [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:helper'
        [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
        [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
        [creator]     Adding layer 'paketo-buildpacks/executable-jar:classpath'
        [creator]     Adding layer 'paketo-buildpacks/spring-boot:helper'
        [creator]     Adding layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
        [creator]     Adding layer 'paketo-buildpacks/spring-boot:web-application-type'
        [creator]     Adding layer 'buildpacksio/lifecycle:launch.sbom'
        [creator]     Added 5/5 app layer(s)
        [creator]     Adding layer 'buildpacksio/lifecycle:launcher'
        [creator]     Adding layer 'buildpacksio/lifecycle:config'
        [creator]     Adding layer 'buildpacksio/lifecycle:process-types'
        [creator]     Adding label 'io.buildpacks.lifecycle.metadata'
        [creator]     Adding label 'io.buildpacks.build.metadata'
        [creator]     Adding label 'io.buildpacks.project.metadata'
        [creator]     Adding label 'org.opencontainers.image.title'
        [creator]     Adding label 'org.opencontainers.image.version'
        [creator]     Adding label 'org.springframework.boot.version'
        [creator]     Setting default process type 'web'
        [creator]     Saving docker.io/johnniang/halo:2.20.6-alpha.1...
        [creator]     *** Images (416133f64f56):
        [creator]           docker.io/johnniang/halo:2.20.6-alpha.1
        [creator]     Adding cache layer 'paketo-buildpacks/syft:syft'
        [creator]     Adding cache layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
        [creator]     Adding cache layer 'buildpacksio/lifecycle:cache.sbom'
    
    Successfully built image 'docker.io/johnniang/halo:2.20.6-alpha.1'
    
    
    Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
    
    You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
    
    For more on this, please refer to https://docs.gradle.org/8.10.2/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
    
    BUILD SUCCESSFUL in 39s
    18 actionable tasks: 7 executed, 11 up-to-date
  2. 尝试运行

    docker run --rm -it --name halo -p 8090:8090 -v ./halo-workdir:/home/cnb/.halo2 -m 1g --cpus 2 johnniang/halo:2.20.6-alpha.1
    Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx575357K -XX:MaxMetaspaceSize=166018K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 50, Loaded Class Count: 26897, Headroom: 0%)
    Enabling Java Native Memory Tracking
    Adding 146 container CA certificates to JVM truststore
    Spring Cloud Bindings Enabled
    Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -XX:+ExitOnOutOfMemoryError -XX:MaxDirectMemorySize=10M -Xmx575357K -XX:MaxMetaspaceSize=166018K -XX:ReservedCodeCacheSize=240M -Xss1M -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -Dorg.springframework.cloud.bindings.boot.enable=true
    
        __  __      __
       / / / /___ _/ /___
      / /_/ / __ `/ / __ \
     / __  / /_/ / / /_/ /
    /_/ /_/\__,_/_/\____/
    
    Version: 2.20.6-alpha.1
    2024-10-22T16:22:57.232Z  INFO 1 --- [           main] run.halo.app.Application                 : Starting Application v2.20.6-alpha.1 using Java 21.0.4 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
    2024-10-22T16:22:57.234Z  INFO 1 --- [           main] run.halo.app.Application                 : No active profile set, falling back to 1 default profile: "default"
    2024-10-22T16:22:57.366Z  WARN 1 --- [           main] o.s.c.a.AnnotationBeanNameGenerator      : Support for convention-based stereotype names is deprecated and will be removed in a future version of the framework. Please annotate the 'value' attribute in @run.halo.app.theme.finders.Finder with @AliasFor(annotation=Component.class) to declare an explicit alias for @Component's 'value' attribute.
    2024-10-22T16:22:58.131Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
    2024-10-22T16:22:58.132Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data R2DBC repositories in DEFAULT mode.
    2024-10-22T16:22:58.236Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 94 ms. Found 1 R2DBC repository interface.
    2024-10-22T16:22:59.621Z  INFO 1 --- [           main] r.h.a.s.a.impl.RsaKeyService             : Generating RSA keys for PAT.
    2024-10-22T16:23:00.269Z  INFO 1 --- [           main] r.h.a.s.a.impl.RsaKeyService             : Wrote RSA keys for PAT into /home/cnb/.halo2/keys/pat_id_rsa and /home/cnb/.halo2/keys/pat_id_rsa.pub
    2024-10-22T16:23:00.454Z  INFO 1 --- [           main] org.pf4j.DefaultPluginStatusProvider     : Enabled plugins: []
    2024-10-22T16:23:00.454Z  INFO 1 --- [           main] org.pf4j.DefaultPluginStatusProvider     : Disabled plugins: []
    2024-10-22T16:23:00.455Z  INFO 1 --- [           main] org.pf4j.DefaultPluginManager            : PF4J version 3.12.0 in 'deployment' mode
    WARNING: A restricted method in java.lang.foreign.Linker has been called
    WARNING: java.lang.foreign.Linker::downcallHandle has been called by the unnamed module
    WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for this module
    
    2024-10-22T16:23:01.536Z  INFO 1 --- [           main] o.a.l.s.MemorySegmentIndexInputProvider  : Using MemorySegmentIndexInput and native madvise support with Java 21 or later; to disable start with -Dorg.apache.lucene.store.MMapDirectory.enableMemorySegments=false
    2024-10-22T16:23:01.537Z  INFO 1 --- [           main] r.h.a.search.lucene.LuceneSearchEngine   : Initialized lucene search engine
    2024-10-22T16:23:02.156Z  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 34 endpoints beneath base path '/actuator'
    2024-10-22T16:23:02.857Z  INFO 1 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8090 (http)
    

总结

我其实是更看重优点 3 的,可惜无法实现。或许我们应该寻找替代方案来实现这一目标。Buildpacks 社区支持更多的系统架构还需要很长时间,我们仍然需要持续关注它的发展。最后,期望 Halo 3 能够利用 CNB 构建 Docker 镜像。