しおしお

IntelliJ IDEAのことなんかを書いてます

Spring Boot 2.3からのCloud Native Buildpacksを試してみる

Spring Boot 2.3からのCloud Native Buildpacksを試してみました。

プロジェクトの作成

Spring Initializrを使って、Spring Bootのバージョンを2.3以降にしてプロジェクトを生成します。
※お試しプロジェクトでは、Dependenciesに Spring WebSpring Boot Actuator を選択してGradleプロジェクトとしています。

Gradleのタスク一覧から、イメージ作成用のタスク(bootBuildImage)が追加されていることが確認できます。

./gradlew tasks --group build

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
bootBuildImage - Builds an OCI image of the application using the output of the bootJar task
bootJar - Assembles an executable jar archive containing the main classes and their dependencies.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

タスクの詳細を確認してみると、オプションでイメージ名を指定できることがわかりますね。

./gradlew help --task bootBuildImage

> Task :help
Detailed task information for bootBuildImage

Path
     :bootBuildImage

Type
     BootBuildImage (org.springframework.boot.gradle.tasks.bundling.BootBuildImage)

Options
     --builder     The name of the builder image to use

     --imageName     The name of the image to generate

Description
     Builds an OCI image of the application using the output of the bootJar task

Group
     build

イメージを作って動かしてみる

bootBuildImageタスクを使うとさくっとイメージを作れます。

./gradlew bootBuildImage                                                                                                                                                                                                                                                                                                                                                                      ✘ 126 

> Task :bootBuildImage
Building image 'docker.io/library/buildpack-example:0.0.1-SNAPSHOT'

 > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' ..................................................
 > Pulled builder image 'gcr.io/paketo-buildpacks/builder@sha256:21a7235c03dbdfcc330e03183a85e0a36b4c0eee6904529e2376502e5e4a84a5'
 > Pulling run image 'gcr.io/paketo-buildpacks/run:base-cnb' ..................................................
 > Pulled run image 'gcr.io/paketo-buildpacks/run@sha256:fb49a85ddb10a93f194cc407b34d5c352def37693c23be5b9f9a9859e7526b78'
 > Executing lifecycle version v0.8.1
 > Using build cache volume 'pack-cache-3949d5df2001.build'

 > Running creator
    [creator]     ===> DETECTING
    [creator]     5 of 16 buildpacks participating
    [creator]     paketo-buildpacks/bellsoft-liberica 2.10.0
    [creator]     paketo-buildpacks/executable-jar    2.0.1
    [creator]     paketo-buildpacks/apache-tomcat     1.3.3
    [creator]     paketo-buildpacks/dist-zip          1.3.7
    [creator]     paketo-buildpacks/spring-boot       2.2.1
    [creator]     ===> ANALYZING
    [creator]     Previous image with name "docker.io/library/buildpack-example:0.0.1-SNAPSHOT" not found
    [creator]     ===> RESTORING
    [creator]     ===> BUILDING
    [creator]     
    [creator]     Paketo BellSoft Liberica Buildpack 2.10.0
    [creator]       https://github.com/paketo-buildpacks/bellsoft-liberica
    [creator]       Build Configuration:
    [creator]         $BP_JVM_VERSION              11.*            the Java version
    [creator]       Launch Configuration:
    [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]       BellSoft Liberica JRE 11.0.8: Contributing to layer
    [creator]         Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.8+10/bellsoft-jre11.0.8+10-linux-amd64.tar.gz
    [creator]         Verifying checksum
    [creator]         Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
    [creator]         Writing env.launch/JAVA_HOME.override
    [creator]         Writing env.launch/MALLOC_ARENA_MAX.override
    [creator]         Writing profile.d/active-processor-count.sh
    [creator]       Memory Calculator 4.1.0: Contributing to layer
    [creator]         Downloading from https://github.com/cloudfoundry/java-buildpack-memory-calculator/releases/download/v4.1.0/memory-calculator-4.1.0.tgz
    [creator]         Verifying checksum
    [creator]         Expanding to /layers/paketo-buildpacks_bellsoft-liberica/memory-calculator
    [creator]         Writing profile.d/memory-calculator.sh
    [creator]       Class Counter: Contributing to layer
    [creator]         Copying to /layers/paketo-buildpacks_bellsoft-liberica/class-counter
    [creator]       JVMKill Agent 1.16.0: Contributing to layer
    [creator]         Downloading from https://github.com/cloudfoundry/jvmkill/releases/download/v1.16.0.RELEASE/jvmkill-1.16.0-RELEASE.so
    [creator]         Verifying checksum
    [creator]         Copying to /layers/paketo-buildpacks_bellsoft-liberica/jvmkill
    [creator]         Writing env.launch/JAVA_OPTS.append
    [creator]       Link-Local DNS: Contributing to layer
    [creator]         Copying to /layers/paketo-buildpacks_bellsoft-liberica/link-local-dns
    [creator]         Writing profile.d/link-local-dns.sh
    [creator]       Java Security Properties: Contributing to layer
    [creator]         Writing env.launch/JAVA_OPTS.append
    [creator]         Writing env.launch/JAVA_SECURITY_PROPERTIES.override
    [creator]       Security Providers Configurer: Contributing to layer
    [creator]         Copying to /layers/paketo-buildpacks_bellsoft-liberica/security-providers-configurer
    [creator]         Writing profile.d/security-providers-classpath.sh
    [creator]         Writing profile.d/security-providers-configurer.sh
    [creator]       OpenSSL Certificate Loader: Contributing to layer
    [creator]         Copying to /layers/paketo-buildpacks_bellsoft-liberica/openssl-security-provider
    [creator]         Writing profile.d/openssl-certificate-loader.sh
    [creator]     
    [creator]     Paketo Executable JAR Buildpack 2.0.1
    [creator]       https://github.com/paketo-buildpacks/executable-jar
    [creator]         Writing env.launch/CLASSPATH
    [creator]       Process types:
    [creator]         executable-jar: java -cp "${CLASSPATH}" ${JAVA_OPTS} org.springframework.boot.loader.JarLauncher
    [creator]         task:           java -cp "${CLASSPATH}" ${JAVA_OPTS} org.springframework.boot.loader.JarLauncher
    [creator]         web:            java -cp "${CLASSPATH}" ${JAVA_OPTS} org.springframework.boot.loader.JarLauncher
    [creator]     
    [creator]     Paketo Spring Boot Buildpack 2.2.1
    [creator]       https://github.com/paketo-buildpacks/spring-boot
    [creator]       Build Configuration:
    [creator]         $BP_BOOT_NATIVE_IMAGE                  the build to create a native image (requires GraalVM)
    [creator]         $BP_BOOT_NATIVE_IMAGE_BUILD_ARGUMENTS  the arguments to pass to the native-image command
    [creator]       Launch Configuration:
    [creator]         $BPL_SPRING_CLOUD_BINDINGS_ENABLED     whether to auto-configure Spring Boot environment properties from bindings
    [creator]       Web Application Type: Contributing to layer
    [creator]         Servlet web application detected
    [creator]         Writing env.launch/BPL_JVM_THREAD_COUNT.default
    [creator]       Spring Cloud Bindings 1.4.0: Contributing to layer
    [creator]         Downloading from https://repo.spring.io/release/org/springframework/cloud/spring-cloud-bindings/1.4.0/spring-cloud-bindings-1.4.0.jar
    [creator]         Verifying checksum
    [creator]         Copying to /layers/paketo-buildpacks_spring-boot/spring-cloud-bindings
    [creator]         Writing profile.d/spring-cloud-bindings.sh
    [creator]       Image labels:
    [creator]         org.springframework.boot.spring-configuration-metadata.json
    [creator]         org.springframework.boot.version
    [creator]     ===> EXPORTING
    [creator]     Adding layer 'launcher'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:class-counter'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:link-local-dns'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:memory-calculator'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:openssl-security-provider'
    [creator]     Adding layer 'paketo-buildpacks/bellsoft-liberica:security-providers-configurer'
    [creator]     Adding layer 'paketo-buildpacks/executable-jar:class-path'
    [creator]     Adding layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
    [creator]     Adding layer 'paketo-buildpacks/spring-boot:web-application-type'
    [creator]     Adding 1/1 app layer(s)
    [creator]     Adding layer 'config'
    [creator]     *** Images (ab7b253dc430):
    [creator]           docker.io/library/buildpack-example:0.0.1-SNAPSHOT

Successfully built image 'docker.io/library/buildpack-example:0.0.1-SNAPSHOT'

コンテナー起動後、actuator/envで確認するとJava11でアプリケーションが実行されているのが確認できますね。

curl http://localhost:8080/actuator/env
# 省略
{
  "value": "11.0.8+10-LTS"
}

Javaのバージョンは、GradleのtargetCompatibilityと連動しているっぽく、以下のようにJava14を指定すると自動的に起動時に使用するバージョンが変わるようです。

java {
    targetCompatibility = JavaVersion.VERSION_14
}

上の状態でイメージ作成後に確認してみると想定通りJava14で起動されていました。

curl http://localhost:8080/actuator/env
# 省略
{
  "value": "14.0.2+13"
}

起動時のオプションを変更してみる

コンテナ起動時に、JAVA_OPTS環境変数を設定することで起動時のJVMオプションを変更できるようです。

例えば、下のようにコンテナを起動するとhogeシステムプロパティにfugaが設定されます。

docker run -p "8080:8080" -e JAVA_OPTS="-Dhoge=fuga"  76747f99e0c7

起動後、actuator/envを叩くことで想定通りシステムプロパティが設定されていることが確認できます。

curl http://localhost:8080/actuator/env
{
  "activeProfiles": [],
  "propertySources": [
    {
      "name": "server.ports",
      "properties": {
        "local.server.port": {
          "value": 8080
        }
      }
    },
    {
      "name": "servletContextInitParams",
      "properties": {}
    },
    {
      "name": "systemProperties",
      "properties": {
        "hoge": {
          "value": "fuga"
        }
    }
  ]
}

イメージ名を指定してビルドしてみる

bootBuildImageタスクのimageNameオプションを指定することで、イメージ名を変更できます。 リモートリポジトリの名前に合わせる場合なんかに使うとよさげですかね。

./gradlew bootBuildImage --imageName siosio/sample:1.0.0

例えば、GitHub ActionsからECRにビルドしたイメージをpushする場合なんかは、こんな感じでいけるかなと思います。

    steps:
      - uses: actions/checkout@v2

      - name: Cache Gradle packages
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
          restore-keys: ${{ runner.os }}-gradle

      - name: setup java
        uses: actions/setup-java@v1
        with:
          java-version: 11
          
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: build image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
        run: |
          IMAGE_TAG=$(echo ${{ github.ref }} | sed 's/refs\/heads\///g' | sed 's/\//-/g')
          ./gradlew bootBuildImage --imageName $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG