以下のソフトウェアをインストールしてください。

この資料は次のバージョンで動作確認しています。

$ java -version              
openjdk version "11.0.10" 2021-01-19 LTS
OpenJDK Runtime Environment (build 11.0.10+9-LTS)
OpenJDK 64-Bit Server VM (build 11.0.10+9-LTS, mixed mode)

$ docker version
Client: Docker Engine - Community
 Cloud integration: 1.0.12
 Version:           20.10.5
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        55c4c88
 Built:             Tue Mar  2 20:13:00 2021
 OS/Arch:           darwin/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.5
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       363e9a8
  Built:            Tue Mar  2 20:15:47 2021
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          1.4.4
  GitCommit:        05f951a3781f4f2c1911b05e61c160e9c30eaa8e
 runc:
  Version:          1.0.0-rc93
  GitCommit:        12644e614e25b05da6fd08a38ffa0cfe1903fdec
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0


$ pack version
0.19.0+git-360dbae.build-2550


TBD


まずはとにかくCloud Native Buildpacksを試してみましょう。Spring Bootは2.3からMaven / Gradle PluginでCloud Native Buildpacksが使えるようになったので非常に簡単に始められます。Spring Bootで簡単なサンプルアプリを作成し、Cloud Native Buildpacksを使ってOCIイメージを作成します。

Prepare a sample app

Spring InitializrでSpring Bootの雛形アプリケーションを作成します。

作業場所としてholディレクトリを作成してください。

mkdir hol
cd hol


次のコマンドで雛形アプリケーションを作成します。

curl -s https://start.spring.io/starter.tgz \
 -d artifactId=vehicle-api \
 -d baseDir=vehicle-api \
 -d dependencies=web,jdbc,postgresql,actuator \
 -d packageName=com.example \
 -d applicationName=VehicleApiApplication | tar -xzvf -

tarコマンドが使えない場合はこのリンクをクリックして"GENERATE"ボタンをクリックして、zipファイルをダウンロードしてholディレクトリで展開してください。

src/main/java/com/example/VehicleController.javaを作成し、次の内容を記述してください。

package com.example;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.sql.PreparedStatement;
import java.util.List;

@RestController
public class VehicleController {

  private final JdbcTemplate jdbcTemplate;

  public VehicleController(JdbcTemplate jdbcTemplate) {
     this.jdbcTemplate = jdbcTemplate;
  }

  @GetMapping(path = "/vehicles")
  public ResponseEntity<?> getVehicles() {
     final List<Vehicle> vehicles = this.jdbcTemplate.query("SELECT id, name FROM vehicle ORDER BY id", (rs, i) -> new Vehicle(rs.getInt("id"), rs.getString("name")));
     return ResponseEntity.ok(vehicles);
  }

  @PostMapping(path = "/vehicles")
  public ResponseEntity<?> postVehicles(@RequestBody Vehicle vehicle) {
     final KeyHolder keyHolder = new GeneratedKeyHolder();
     this.jdbcTemplate.update(connection -> {
        final PreparedStatement statement = connection.prepareStatement("INSERT INTO vehicle(name) VALUES (?)", new String[]{"id"});
        statement.setString(1, vehicle.getName());
        return statement;
     }, keyHolder);
     vehicle.setId(keyHolder.getKey().intValue());
     return ResponseEntity.status(HttpStatus.CREATED).body(vehicle);
  }

  @DeleteMapping(path = "/vehicles/{id}")
  public ResponseEntity<?> deleteVehicle(@PathVariable("id") Integer id) {
     this.jdbcTemplate.update("DELETE FROM vehicle WHERE id = ?", id);
     return ResponseEntity.noContent().build();
  }

  static class Vehicle {

     public Vehicle(Integer id, String name) {
        this.id = id;
        this.name = name;
     }

     private Integer id;

     private String name;

     public Integer getId() {
        return id;
     }

     public void setId(Integer id) {
        this.id = id;
     }

     public String getName() {
        return name;
     }

     public void setName(String name) {
        this.name = name;
     }
  }
}

src/main/resources/schema.sqlを作成し、次の内容を記述してください。

CREATE TABLE IF NOT EXISTS vehicle
(
   id   SERIAL PRIMARY KEY,
   name VARCHAR(16) UNIQUE
);

また、src/main/resources/data.sqlを作成し、次の内容を記述してください。

INSERT INTO vehicle(name) VALUES ('Avalon') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Corolla') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Crown') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Levin') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Yaris') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Vios') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Glanza') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;
INSERT INTO vehicle(name) VALUES ('Aygo') ON CONFLICT ON CONSTRAINT vehicle_name_key DO NOTHING;


次にsrc/main/resources/application.propertiesに次の内容を記述してください。

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/vehicle
spring.datasource.username=vehicle
spring.datasource.password=vehicle
spring.sql.init.mode=always


アプリケーションをビルドしてください。

cd vehicle-api
./mvnw clean package -Dmaven.test.skip=true

ローカルでアプリケーションの動作を確認するためにPostgreSQLをDockerで起動します。新しいターミナルを開いて、次のコマンドを実行してください。

docker run --rm \
 -p 5432:5432 \
 -e POSTGRES_DB=vehicle \
 -e POSTGRES_USER=vehicle \
 -e POSTGRES_PASSWORD=vehicle \
 bitnami/postgresql:11.11.0-debian-10-r59

アプリケーションを起動します。

$ java -jar target/vehicle-api-0.0.1-SNAPSHOT.jar
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-30 21:41:38.665  INFO 67880 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication v0.0.1-SNAPSHOT using Java 11.0.10 on makinoMacBook-Pro.local with PID 67880 (/private/tmp/hol/vehicle-api/target/vehicle-api-0.0.1-SNAPSHOT.jar started by toshiaki in /private/tmp/hol/vehicle-api)
...

起動したアプリケーションにアクセスします。

$ curl -s http://localhost:8080/vehicles | jq .
[
  {
    "id": 1,
    "name": "Avalon"
  },
  {
    "id": 2,
    "name": "Corolla"
  },
  {
    "id": 3,
    "name": "Crown"
  },
  {
    "id": 4,
    "name": "Levin"
  },
  {
    "id": 5,
    "name": "Yaris"
  },
  {
    "id": 6,
    "name": "Vios"
  },
  {
    "id": 7,
    "name": "Glanza"
  },
  {
    "id": 8,
    "name": "Aygo"
  }
]

$ curl -s http://localhost:8080/vehicles -d "{\"name\": \"Lexus\"}" -H "Content-Type: application/json" | jq .
{
  "id": 9,
  "name": "Lexus"
}

$ curl -s http://localhost:8080/vehicles | jq .
[
  {
    "id": 1,
    "name": "Avalon"
  },
  {
    "id": 2,
    "name": "Corolla"
  },
  {
    "id": 3,
    "name": "Crown"
  },
  {
    "id": 4,
    "name": "Levin"
  },
  {
    "id": 5,
    "name": "Yaris"
  },
  {
    "id": 6,
    "name": "Vios"
  },
  {
    "id": 7,
    "name": "Glanza"
  },
  {
    "id": 8,
    "name": "Aygo"
  },
  {
    "id": 9,
    "name": "Lexus"
  }
]


ここまででアプリケーションの準備ができました。Ctrl+Cでアプリケーションを停止してください。

Build with Spring Boot Maven Plugin

Spring Boot Maven Pluginを使い、作成したアプリケーションのOCIイメージを作成します。次のコマンドを実行してください。

./mvnw spring-boot:build-image -Dmaven.test.skip=true

次のようなログが出力がされます。

[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< com.example:vehicle-api >-----------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
...
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.5.2:build-image (default-cli) @ vehicle-api ---
[INFO] Building image 'docker.io/library/vehicle-api:0.0.1-SNAPSHOT'
[INFO] 
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:base' 100%
[INFO]  > Pulled builder image 'paketobuildpacks/builder@sha256:6ad054eb7c2311bd064a458a3a4602efc273043cf86fe21541a5c90b071d4ff2'
[INFO]  > Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' 100%
[INFO]  > Pulled run image 'paketobuildpacks/run@sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d'
[INFO]  > Executing lifecycle version v0.11.3
[INFO]  > Using build cache volume 'pack-cache-13a8f10fbd06.build'
[INFO] 
[INFO]  > Running creator
[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     5 of 18 buildpacks participating
[INFO]     [creator]     paketo-buildpacks/ca-certificates   2.3.2
[INFO]     [creator]     paketo-buildpacks/bellsoft-liberica 8.1.2
[INFO]     [creator]     paketo-buildpacks/executable-jar    5.1.2
[INFO]     [creator]     paketo-buildpacks/dist-zip          4.1.2
[INFO]     [creator]     paketo-buildpacks/spring-boot       4.4.2
[INFO]     [creator]     ===> ANALYZING
[INFO]     [creator]     Restoring metadata for "paketo-buildpacks/ca-certificates:helper" from app image
[INFO]     [creator]     ===> RESTORING
[INFO]     [creator]     ===> BUILDING
[INFO]     [creator]     
[INFO]     [creator]     Paketo CA Certificates Buildpack 2.3.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/ca-certificates
[INFO]     [creator]       Launch Helper: Reusing cached layer
[INFO]     [creator]     
[INFO]     [creator]     Paketo BellSoft Liberica Buildpack 8.1.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/bellsoft-liberica
[INFO]     [creator]       Build Configuration:
[INFO]     [creator]         $BP_JVM_VERSION              11.*            the Java version
[INFO]     [creator]       Launch Configuration:
[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]       BellSoft Liberica JRE 11.0.11: Contributing to layer
[INFO]     [creator]         Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.11+9/bellsoft-jre11.0.11+9-linux-amd64.tar.gz
[INFO]     [creator]         Verifying checksum
[INFO]     [creator]         Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
[INFO]     [creator]         Adding 129 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/MALLOC_ARENA_MAX.default
[INFO]     [creator]       Launch Helper: Contributing to layer
[INFO]     [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/active-processor-count
[INFO]     [creator]         Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
[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/openssl-certificate-loader
[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/security-providers-classpath-9
[INFO]     [creator]       JVMKill Agent 1.16.0: Contributing to layer
[INFO]     [creator]         Downloading from https://github.com/cloudfoundry/jvmkill/releases/download/v1.16.0.RELEASE/jvmkill-1.16.0-RELEASE.so
[INFO]     [creator]         Verifying checksum
[INFO]     [creator]         Copying to /layers/paketo-buildpacks_bellsoft-liberica/jvmkill
[INFO]     [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.append
[INFO]     [creator]         Writing env.launch/JAVA_TOOL_OPTIONS.delim
[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 Executable JAR Buildpack 5.1.2
[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.JarLauncher (direct)
[INFO]     [creator]         task:           java org.springframework.boot.loader.JarLauncher (direct)
[INFO]     [creator]         web:            java org.springframework.boot.loader.JarLauncher (direct)
[INFO]     [creator]     
[INFO]     [creator]     Paketo Spring Boot Buildpack 4.4.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/spring-boot
[INFO]     [creator]       Creating slices from layers index
[INFO]     [creator]         dependencies
[INFO]     [creator]         spring-boot-loader
[INFO]     [creator]         snapshot-dependencies
[INFO]     [creator]         application
[INFO]     [creator]       Launch Helper: Contributing to layer
[INFO]     [creator]         Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
[INFO]     [creator]       Spring Cloud Bindings 1.7.1: Contributing to layer
[INFO]     [creator]         Downloading from https://repo.spring.io/release/org/springframework/cloud/spring-cloud-bindings/1.7.1/spring-cloud-bindings-1.7.1.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]         Servlet web application detected
[INFO]     [creator]         Writing env.launch/BPL_JVM_THREAD_COUNT.default
[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]     Reusing 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/bellsoft-liberica:jvmkill'
[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 5/5 app layer(s)
[INFO]     [creator]     Reusing layer 'launcher'
[INFO]     [creator]     Adding layer 'config'
[INFO]     [creator]     Adding layer '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/vehicle-api:0.0.1-SNAPSHOT...
[INFO]     [creator]     *** Images (919a7f1521f6):
[INFO]     [creator]           docker.io/library/vehicle-api:0.0.1-SNAPSHOT
[INFO]     [creator]     Warning: Failed to export cache: setting cache metadata: creating metadata file '/cache/staging/io.buildpacks.lifecycle.cache.metadata': open /cache/staging/io.buildpacks.lifecycle.cache.metadata: no such file or directory
[INFO] 
[INFO] Successfully built image 'docker.io/library/vehicle-api:0.0.1-SNAPSHOT'
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  23.762 s
[INFO] Finished at: 2021-06-30T21:43:25+09:00
[INFO] ------------------------------------------------------------------------

docker.io/library/vehicle-api:0.0.1-SNAPSHOTという名前のOCIイメージが作成されました。

次のコマンドでイメージ一覧を確認してください。

$ docker images
REPOSITORY                 TAG                     IMAGE ID       CREATED        SIZE
bitnami/postgresql         11.11.0-debian-10-r59   c416eed2ffd6   17 hours ago   258MB
paketobuildpacks/run       base-cnb                8e1ab607b638   6 days ago     87.2MB
vehicle-api                0.0.1-SNAPSHOT          919a7f1521f6   41 years ago    269MB
paketobuildpacks/builder   base                    c8f7e51dbb67   41 years ago   660MB

作成したイメージを実行します。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13099, Headroom: 0%)
Adding 129 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 -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-30 13:40:57.047  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication v0.0.1-SNAPSHOT using Java 11.0.11 on 939a6d82d8f4 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2021-06-30 13:40:57.054  INFO 1 --- [           main] com.example.VehicleApiApplication        : No active profile set, falling back to default profiles: default
2021-06-30 13:40:59.307  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-06-30 13:40:59.322  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-06-30 13:40:59.323  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-06-30 13:40:59.396  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-06-30 13:40:59.397  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2185 ms
2021-06-30 13:40:59.750  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-06-30 13:40:59.959  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-06-30 13:41:00.648  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'
2021-06-30 13:41:00.708  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-06-30 13:41:00.723  INFO 1 --- [           main] com.example.VehicleApiApplication        : Started VehicleApiApplication in 4.661 seconds (JVM running for 5.664)

アプリケーションにアクセスしてください。

$ curl -s http://localhost:8080/vehicles | jq .
[
  {
    "id": 1,
    "name": "Avalon"
  },
  {
    "id": 2,
    "name": "Corolla"
  },
  {
    "id": 3,
    "name": "Crown"
  },
  {
    "id": 4,
    "name": "Levin"
  },
  {
    "id": 5,
    "name": "Yaris"
  },
  {
    "id": 6,
    "name": "Vios"
  },
  {
    "id": 7,
    "name": "Glanza"
  },
  {
    "id": 8,
    "name": "Aygo"
  },
  {
    "id": 9,
    "name": "Lexus"
  }
]

Dockerfileを作成することなく、Spring BootアプリケーションのOCIイメージを作成することができました。

Build with pack CLI

次はpack CLIを使用して作成したアプリケーションのOCIイメージを作成します。

from an executable jar file

まずは事前にビルド済みの実行可能なjarファイルからOCIイメージを作成します。次のコマンドを実行してください。

pack build vehicle-api \
 --path ./target/vehicle-api-0.0.1-SNAPSHOT.jar \
 --builder paketobuildpacks/builder:base

次のようなログが出力されます。

base: Pulling from paketobuildpacks/builder
Digest: sha256:6ad054eb7c2311bd064a458a3a4602efc273043cf86fe21541a5c90b071d4ff2
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
5 of 18 buildpacks participating
paketo-buildpacks/ca-certificates   2.3.2
paketo-buildpacks/bellsoft-liberica 8.1.2
paketo-buildpacks/executable-jar    5.1.2
paketo-buildpacks/dist-zip          4.1.2
paketo-buildpacks/spring-boot       4.4.2
===> ANALYZING
Restoring metadata for "paketo-buildpacks/ca-certificates:helper" from app image
Restoring metadata for "paketo-buildpacks/spring-boot:helper" from app image
Restoring metadata for "paketo-buildpacks/spring-boot:spring-cloud-bindings" from app image
Restoring metadata for "paketo-buildpacks/spring-boot:web-application-type" from app image
===> RESTORING
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper

Paketo BellSoft Liberica Buildpack 8.1.2
  https://github.com/paketo-buildpacks/bellsoft-liberica
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  BellSoft Liberica JRE 11.0.11: Contributing to layer
    Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.11+9/bellsoft-jre11.0.11+9-linux-amd64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
    Adding 129 container CA certificates to JVM truststore
    Writing env.launch/BPI_APPLICATION_PATH.default
    Writing env.launch/BPI_JVM_CACERTS.default
    Writing env.launch/BPI_JVM_CLASS_COUNT.default
    Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
    Writing env.launch/JAVA_HOME.default
    Writing env.launch/MALLOC_ARENA_MAX.default
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/active-processor-count
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/java-opts
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/link-local-dns
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/openssl-certificate-loader
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-configurer
    Creating /layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/security-providers-classpath-9
  JVMKill Agent 1.16.0: Contributing to layer
    Downloading from https://github.com/cloudfoundry/jvmkill/releases/download/v1.16.0.RELEASE/jvmkill-1.16.0-RELEASE.so
    Verifying checksum
    Copying to /layers/paketo-buildpacks_bellsoft-liberica/jvmkill
    Writing env.launch/JAVA_TOOL_OPTIONS.append
    Writing env.launch/JAVA_TOOL_OPTIONS.delim
  Java Security Properties: Contributing to layer
    Writing env.launch/JAVA_SECURITY_PROPERTIES.default
    Writing env.launch/JAVA_TOOL_OPTIONS.append
    Writing env.launch/JAVA_TOOL_OPTIONS.delim

Paketo Executable JAR Buildpack 5.1.2
  https://github.com/paketo-buildpacks/executable-jar
  Class Path: Contributing to layer
    Writing env/CLASSPATH.delim
    Writing env/CLASSPATH.prepend
  Process types:
    executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
    task:           java org.springframework.boot.loader.JarLauncher (direct)
    web:            java org.springframework.boot.loader.JarLauncher (direct)

Paketo Spring Boot Buildpack 4.4.2
  https://github.com/paketo-buildpacks/spring-boot
  Creating slices from layers index
    dependencies
    spring-boot-loader
    snapshot-dependencies
    application
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_spring-boot/helper/exec.d/spring-cloud-bindings
  Spring Cloud Bindings 1.7.1: Reusing cached layer
  Web Application Type: Contributing to layer
    Servlet web application detected
    Writing env.launch/BPL_JVM_THREAD_COUNT.default
  4 application slices
  Image labels:
    org.opencontainers.image.title
    org.opencontainers.image.version
    org.springframework.boot.version
===> EXPORTING
Adding layer 'paketo-buildpacks/ca-certificates:helper'
Adding layer 'paketo-buildpacks/bellsoft-liberica:helper'
Adding layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
Adding layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
Adding layer 'paketo-buildpacks/executable-jar:classpath'
Adding layer 'paketo-buildpacks/spring-boot:helper'
Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
Adding 5/5 app layer(s)
Adding layer 'launcher'
Adding layer 'config'
Reusing layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Adding label 'org.opencontainers.image.title'
Adding label 'org.opencontainers.image.version'
Adding label 'org.springframework.boot.version'
Setting default process type 'web'
Saving vehicle-api...
*** Images (919a7f1521f6):
      vehicle-api
Successfully built image vehicle-api

vehicle-apiという名前のイメージが作成されました。前と同様に、次のコマンドを実行して動作確認ください。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api

作られたイメージはMaven Pluginで作ったものと実質的に同じです。pack CLIを使うかMaven Pluginを使うかは使いやすい方を選択してください。

from source code

Maven PluginがjarファイルまたはwarファイルからのみOCIイメージを作成すると異なり、pack CLIを使用する場合は

のいずれかからOCIイメージを作成できます。

今回はソースコードから直接イメージを作成します。

この場合は、Mavenによるビルドもコンテナ上で行われます。次のコマンドを実行してください。

pack build vehicle-api \
 --builder paketobuildpacks/builder:base

次のようなログが出力されます。

base: Pulling from paketobuildpacks/builder
Digest: sha256:6ad054eb7c2311bd064a458a3a4602efc273043cf86fe21541a5c90b071d4ff2
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
7 of 18 buildpacks participating
paketo-buildpacks/ca-certificates   2.3.2
paketo-buildpacks/bellsoft-liberica 8.1.2
paketo-buildpacks/maven             5.3.2
paketo-buildpacks/executable-jar    5.1.2
paketo-buildpacks/apache-tomcat     5.5.2
paketo-buildpacks/dist-zip          4.1.2
paketo-buildpacks/spring-boot       4.4.2
===> ANALYZING
Restoring metadata for "paketo-buildpacks/ca-certificates:helper" from app image
Restoring metadata for "paketo-buildpacks/bellsoft-liberica:java-security-properties" from app image
Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jre" from app image
Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jvmkill" from app image
Restoring metadata for "paketo-buildpacks/bellsoft-liberica:helper" from app image
Restoring metadata for "paketo-buildpacks/spring-boot:helper" from app image
Restoring metadata for "paketo-buildpacks/spring-boot:spring-cloud-bindings" from app image
Restoring metadata for "paketo-buildpacks/spring-boot:web-application-type" from app image
===> RESTORING
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Reusing cached layer

Paketo BellSoft Liberica Buildpack 8.1.2
  https://github.com/paketo-buildpacks/bellsoft-liberica
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  BellSoft Liberica JDK 11.0.11: Contributing to layer
    Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.11+9/bellsoft-jdk11.0.11+9-linux-amd64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jdk
    Adding 129 container CA certificates to JVM truststore
    Writing env.build/JAVA_HOME.override
    Writing env.build/JDK_HOME.override
  BellSoft Liberica JRE 11.0.11: Reusing cached layer
  Launch Helper: Reusing cached layer
  JVMKill Agent 1.16.0: Reusing cached layer
  Java Security Properties: Reusing cached layer

Paketo Maven Buildpack 5.3.2
  https://github.com/paketo-buildpacks/maven
  Build Configuration:
    $BP_MAVEN_BUILD_ARGUMENTS  -Dmaven.test.skip=true package  the arguments to pass to Maven
    $BP_MAVEN_BUILT_ARTIFACT   target/*.[jw]ar                 the built application artifact explicitly.  Supersedes $BP_MAVEN_BUILT_MODULE
    $BP_MAVEN_BUILT_MODULE                                     the module to find application artifact in
    Creating cache directory /home/cnb/.m2
  Compiled Application: Contributing to layer
    Executing mvnw --batch-mode -Dmaven.test.skip=true package
[INFO] Scanning for projects...
[INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/2.5.2/spring-boot-starter-parent-2.5.2.pom
...
[INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar (500 kB at 200 kB/s)
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:59 min
[INFO] Finished at: 2021-06-30T13:46:40Z
[INFO] ------------------------------------------------------------------------
  Removing source code

Paketo Executable JAR Buildpack 5.1.2
  https://github.com/paketo-buildpacks/executable-jar
  Class Path: Contributing to layer
    Writing env/CLASSPATH.delim
    Writing env/CLASSPATH.prepend
  Process types:
    executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
    task:           java org.springframework.boot.loader.JarLauncher (direct)
    web:            java org.springframework.boot.loader.JarLauncher (direct)

Paketo Spring Boot Buildpack 4.4.2
  https://github.com/paketo-buildpacks/spring-boot
  Creating slices from layers index
    dependencies
    spring-boot-loader
    snapshot-dependencies
    application
  Launch Helper: Reusing cached layer
  Spring Cloud Bindings 1.7.1: Reusing cached layer
  Web Application Type: Contributing to layer
    Servlet web application detected
    Writing env.launch/BPL_JVM_THREAD_COUNT.default
  4 application slices
  Image labels:
    org.opencontainers.image.title
    org.opencontainers.image.version
    org.springframework.boot.version
===> EXPORTING
Reusing layer 'paketo-buildpacks/ca-certificates:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jre'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
Reusing layer 'paketo-buildpacks/executable-jar:classpath'
Reusing layer 'paketo-buildpacks/spring-boot:helper'
Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
Reusing 2/5 app layer(s)
Adding 3/5 app layer(s)
Reusing layer 'launcher'
Adding layer 'config'
Reusing layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Adding label 'org.opencontainers.image.title'
Adding label 'org.opencontainers.image.version'
Adding label 'org.springframework.boot.version'
Setting default process type 'web'
Saving vehicle-api...
*** Images (f5b1d1b182d5):
      vehicle-api
Adding cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Adding cache layer 'paketo-buildpacks/maven:application'
Adding cache layer 'paketo-buildpacks/maven:cache'
Successfully built image vehicle-api

pack buildコマンドを実行後にMavenによるアプリケーションのビルドが実行されました。初回はキャッシュがないため、時間がかかりましたが、二回目のビルド以降はキャッシュが使われるため速くなります。

前と同様に、次のコマンドを実行して動作確認ください。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api

Cloud Native BuildpacksのEcosystemを紹介します。

Builder

Cloud Native Buildpacksでコンテナイメージを作成する場合、まずBuilderを選択します。Builderは以下のコンポーネントから構成されます。

使用するBuilderによって出来上がるイメージが異なりますし、サポートされている言語も異なります。

代表的なBuilderは次のようにpack builder suggestコマンドで確認できます。

$ pack builder suggest
Suggested builders:
        Google:                gcr.io/buildpacks/builder:v1      Ubuntu 18 base image with buildpacks for .NET, Go, Java, Node.js, and Python                                              
        Heroku:                heroku/buildpacks:18              Base builder for Heroku-18 stack, based on ubuntu:18.04 base image                                                        
        Heroku:                heroku/buildpacks:20              Base builder for Heroku-20 stack, based on ubuntu:20.04 base image                                                        
        Paketo Buildpacks:     paketobuildpacks/builder:base     Ubuntu bionic base image with buildpacks for Java, .NET Core, NodeJS, Go, Ruby, NGINX and Procfile                        
        Paketo Buildpacks:     paketobuildpacks/builder:full     Ubuntu bionic base image with buildpacks for Java, .NET Core, NodeJS, Go, PHP, Ruby, Apache HTTPD, NGINX and Procfile     
        Paketo Buildpacks:     paketobuildpacks/builder:tiny     Tiny base image (bionic build image, distroless-like run image) with buildpacks for Java Native Image and Go              

Tip: Learn more about a specific builder with:
        pack builder inspect <builder-image>

このhands-onでは基本的にはpaketobuildpacks/builder:baseを使用します。Spring Boot Maven Pluginでイメージを作った際にもこのBuilderが使用されていました。一部でpaketobuildpacks/builder:tinyを使用します。

Platform

Cloud Native Buildpacksの用語ではPlatformはLifecycleやBuildpackを使ってコンテナイメージを作る機能を含むアプリケーションやシステムです。ここまでで使用したpack CLIやSpring Boot Maven PluginもPlatformの一つです。既に様々なPlatformが存在します。

Local Build

CI

Application Platform

pack inspectコマンドでイメージがどのBuildpackで作成されたかなどの情報を見ることができます。

$ pack inspect docker.io/library/vehicle-api:0.0.1-SNAPSHOT               
Inspecting image: docker.io/library/vehicle-api:0.0.1-SNAPSHOT

REMOTE:
(not present)

LOCAL:

Stack: io.buildpacks.stacks.bionic

Base Image:
  Reference: e30a81410ab1dc84461d098e32611f94fce698ca1212447aa2b6f7f590a2b778
  Top Layer: sha256:24fe3ce6fe4470832bca47951fcbc5ecf499057a671bc3e5e1d6792f4b270eeb

Run Images:
  index.docker.io/paketobuildpacks/run:base-cnb
  gcr.io/paketo-buildpacks/run:base-cnb

Buildpacks:
  ID                                         VERSION        HOMEPAGE
  paketo-buildpacks/ca-certificates          2.3.2          https://github.com/paketo-buildpacks/ca-certificates
  paketo-buildpacks/bellsoft-liberica        8.1.2          https://github.com/paketo-buildpacks/bellsoft-liberica
  paketo-buildpacks/executable-jar           5.1.2          https://github.com/paketo-buildpacks/executable-jar
  paketo-buildpacks/dist-zip                 4.1.2          https://github.com/paketo-buildpacks/dist-zip
  paketo-buildpacks/spring-boot              4.4.2          https://github.com/paketo-buildpacks/spring-boot

Processes:
  TYPE                  SHELL        COMMAND        ARGS
  web (default)                      java           org.springframework.boot.loader.JarLauncher
  executable-jar                     java           org.springframework.boot.loader.JarLauncher
  task                               java           org.springframework.boot.loader.JarLauncher

Run ImageのIDがe30a81410ab1dc84461d098e32611f94fce698ca1212447aa2b6f7f590a2b778であることがわかります。このIDは次のコマンドよりpaketobuildpacks/run:base-cnbのものと一致することがわかります。

$ docker images --no-trunc | grep e30a81410ab1dc84461d098e32611f94fce698ca1212447aa2b6f7f590a2b778
paketobuildpacks/run                                                       base-cnb                            sha256:e30a81410ab1dc84461d098e32611f94fce698ca1212447aa2b6f7f590a2b778   5 days ago      88.3MB

--bomオプションをつけるとそのイメージが使用しているバイナリのバージョンや依存ライブラリ一覧も取得できます。

$ pack inspect docker.io/library/vehicle-api:0.0.1-SNAPSHOT --bom
{
  "remote": null,
  "local": [
    {
      "name": "helper",
      "metadata": {
        "layer": "helper",
        "names": [
          "ca-certificates-helper"
        ],
        "version": "2.3.2"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/ca-certificates",
        "version": "2.3.2"
      }
    },
    {
      "name": "jre",
      "metadata": {
        "layer": "jre",
        "licenses": [
          {
            "type": "GPL-2.0 WITH Classpath-exception-2.0",
            "uri": "https://openjdk.java.net/legal/gplv2+ce.html"
          }
        ],
        "name": "BellSoft Liberica JRE",
        "sha256": "5bbb7b867ab797ace54aa98a76b7abcca6c5fa01338ee2907e97adb21150c414",
        "stacks": [
          "io.buildpacks.stacks.bionic",
          "org.cloudfoundry.stacks.cflinuxfs3"
        ],
        "uri": "https://github.com/bell-sw/Liberica/releases/download/11.0.11+9/bellsoft-jre11.0.11+9-linux-amd64.tar.gz",
        "version": "11.0.11"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/bellsoft-liberica",
        "version": "8.1.2"
      }
    },
    {
      "name": "helper",
      "metadata": {
        "layer": "helper",
        "names": [
          "active-processor-count",
          "java-opts",
          "link-local-dns",
          "memory-calculator",
          "openssl-certificate-loader",
          "security-providers-configurer",
          "security-providers-classpath-9"
        ],
        "version": "8.1.2"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/bellsoft-liberica",
        "version": "8.1.2"
      }
    },
    {
      "name": "jvmkill",
      "metadata": {
        "layer": "jvmkill",
        "licenses": [
          {
            "type": "Apache-2.0",
            "uri": "https://github.com/cloudfoundry/jvmkill/blob/main/LICENSE"
          }
        ],
        "name": "JVMKill Agent",
        "sha256": "a3092627b082cb3cdbbe4b255d35687126aa604e6b613dcda33be9f7e1277162",
        "stacks": [
          "io.buildpacks.stacks.bionic",
          "org.cloudfoundry.stacks.cflinuxfs3"
        ],
        "uri": "https://github.com/cloudfoundry/jvmkill/releases/download/v1.16.0.RELEASE/jvmkill-1.16.0-RELEASE.so",
        "version": "1.16.0"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/bellsoft-liberica",
        "version": "8.1.2"
      }
    },
    {
      "name": "dependencies",
      "metadata": {
        "dependencies": [
          {
            "name": "HdrHistogram",
            "sha256": "9b47fbae444feaac4b7e04f0ea294569e4bc282bc69d8c2ce2ac3f23577281e2",
            "version": "2.1.12"
          },
          {
            "name": "HikariCP",
            "sha256": "7c024aeff1c1063576d74453513f9de6447d8e624d17f8e27f30a2e97688c6c9",
            "version": "4.0.3"
          },
          {
            "name": "LatencyUtils",
            "sha256": "a32a9ffa06b2f4e01c5360f8f9df7bc5d9454a5d373cd8f361347fa5a57165ec",
            "version": "2.0.3"
          },
          {
            "name": "checker-qual",
            "sha256": "729990b3f18a95606fc2573836b6958bcdb44cb52bfbd1b7aa9c339cff35a5a4",
            "version": "3.5.0"
          },
          {
            "name": "jackson-annotations",
            "sha256": "05da0a25bb44a217880a299a1a1e0a301d194b5656a9a07776b77a88f326e7e9",
            "version": "2.12.3"
          },
          {
            "name": "jackson-core",
            "sha256": "baef34fbce041d54f3af3ff4fc917ed8b43ed2a6fa30e0a6abfd9a2b2c3f71e0",
            "version": "2.12.3"
          },
          {
            "name": "jackson-databind",
            "sha256": "94d973062c2fda3dff2c9a85eafce57204821cce9085a99377693dbc9fb8da23",
            "version": "2.12.3"
          },
          {
            "name": "jackson-datatype-jdk8",
            "sha256": "1d131cf0f20c13cffb4f8bb5c2afd68c413c4e1b76fe7384e4fecc8a8c36cb1a",
            "version": "2.12.3"
          },
          {
            "name": "jackson-datatype-jsr310",
            "sha256": "a56dc7dfe15896680d64f746c1105e11f8f18b2331355739b0cdf7794adb4bc3",
            "version": "2.12.3"
          },
          {
            "name": "jackson-module-parameter-names",
            "sha256": "f9655527c39093ba744d02d09d6e254285d1447d3ffbd15ef8f6326907382063",
            "version": "2.12.3"
          },
          {
            "name": "jakarta.annotation-api",
            "sha256": "85fb03fc054cdf4efca8efd9b6712bbb418e1ab98241c4539c8585bbc23e1b8a",
            "version": "1.3.5"
          },
          {
            "name": "jul-to-slf4j",
            "sha256": "97d8b72b4d9cb2b6f40a4c5bb9380e7e8e3c14b45887cab11714d7fc08a86a11",
            "version": "1.7.31"
          },
          {
            "name": "log4j-api",
            "sha256": "8caf58db006c609949a0068110395a33067a2bad707c3da35e959c0473f9a916",
            "version": "2.14.1"
          },
          {
            "name": "log4j-to-slf4j",
            "sha256": "8ba1d1b1c8313731ee053368371b9606c1d71436ac010b0bf91b4c7fc643a1bf",
            "version": "2.14.1"
          },
          {
            "name": "logback-classic",
            "sha256": "fb53f8539e7fcb8f093a56e138112056ec1dc809ebb020b59d8a36a5ebac37e0",
            "version": "1.2.3"
          },
          {
            "name": "logback-core",
            "sha256": "5946d837fe6f960c02a53eda7a6926ecc3c758bbdd69aa453ee429f858217f22",
            "version": "1.2.3"
          },
          {
            "name": "micrometer-core",
            "sha256": "d5eeb0ed9a74ead29d700194e9f0e0d455439fd56c10de92234efa58df8ac25f",
            "version": "1.7.1"
          },
          {
            "name": "postgresql",
            "sha256": "216127fc396598edac446d0e102ac662984b91d77c820916e9189b5fc4074909",
            "version": "42.2.22"
          },
          {
            "name": "slf4j-api",
            "sha256": "cda862aa69683ac072ee2afd0b93d070ceab291ef5b857d318b24c1595b36b8a",
            "version": "1.7.31"
          },
          {
            "name": "snakeyaml",
            "sha256": "35446a1421435d45e4c6ac0de3b5378527d5cc2446c07183e24447730ce1fffa",
            "version": "1.28"
          },
          {
            "name": "spring-aop",
            "sha256": "64d485b9eccd23c085c872c2a4fec7b04c4073f49c914077d8c4cd0a343ee999",
            "version": "5.3.8"
          },
          {
            "name": "spring-beans",
            "sha256": "fbb626312a695294b537150034f7e9fc5563908e02903b8c617d06df60e00380",
            "version": "5.3.8"
          },
          {
            "name": "spring-boot",
            "sha256": "49f4c54d92f16cc91011ba0512007074f6b818b98ba17f8f5165dfb7054f4940",
            "version": "2.5.2"
          },
          {
            "name": "spring-boot-actuator",
            "sha256": "f6af160fe659e4b8ac3baeb086b5071c95f9f7c5e04278e4fdb0246f12ca2a29",
            "version": "2.5.2"
          },
          {
            "name": "spring-boot-actuator-autoconfigure",
            "sha256": "0c5466e93ebd13096fc04516b8312273732041e68e97a59fdb77c6a43489acda",
            "version": "2.5.2"
          },
          {
            "name": "spring-boot-autoconfigure",
            "sha256": "12724e670db3dbf8ec4c56dbc9714b5999af98fe0565ae800954f2b79bfc709c",
            "version": "2.5.2"
          },
          {
            "name": "spring-boot-jarmode-layertools",
            "sha256": "05fda0ce82122e7cd0b48b5a36a7a0408254a35e55bf5df8e7294f10d98311e8",
            "version": "2.5.2"
          },
          {
            "name": "spring-context",
            "sha256": "6fad3f97e8e1e18629bed235c7c1fd21e63ee65718db00c1196d812442fce39f",
            "version": "5.3.8"
          },
          {
            "name": "spring-core",
            "sha256": "e885e8c40baf7cf34d2cf34568da675bb552e62beab57c208df13bf3c782a125",
            "version": "5.3.8"
          },
          {
            "name": "spring-expression",
            "sha256": "5c344c8a7dccc26cb1332f0be8ee1780cab53eb6c98c798909868de966372ad2",
            "version": "5.3.8"
          },
          {
            "name": "spring-jcl",
            "sha256": "a22a2c61c50833d74285b1ade38612301fd3a144e6e9a3c54068feb7f62a7efa",
            "version": "5.3.8"
          },
          {
            "name": "spring-jdbc",
            "sha256": "baa1cf85a5a45ed0b56fb6dc373dfce105aa6480f8e152001c85e38fa8f95d74",
            "version": "5.3.8"
          },
          {
            "name": "spring-tx",
            "sha256": "57faac5b4c07cf7f537d57e0773772896ece6756b5700d3469ad3c7f9d07cf21",
            "version": "5.3.8"
          },
          {
            "name": "spring-web",
            "sha256": "de22ff08947bcb9ee81ef02598d6cdcb7d9c5e881c2b212216084cfe8897f9ee",
            "version": "5.3.8"
          },
          {
            "name": "spring-webmvc",
            "sha256": "05f6d0e32586af83a1ff2c4d98d98afd2882f006752067028b453b20e46e0a2c",
            "version": "5.3.8"
          },
          {
            "name": "tomcat-embed-core",
            "sha256": "2072cc6df5057a0b0a7b4dbf0c2c66ee3e280291b54a5b28358c866f46baf770",
            "version": "9.0.48"
          },
          {
            "name": "tomcat-embed-el",
            "sha256": "f7e786e542a7dbdfc0dc630b2dbf86c6882f0d9a0c5aec09a20fbeeaf5519d61",
            "version": "9.0.48"
          },
          {
            "name": "tomcat-embed-websocket",
            "sha256": "709351999f94aed5a3ec1a5c47b7a51d80efe2ae00a98bcee6ea3d9d76a20de1",
            "version": "9.0.48"
          }
        ],
        "layer": "application"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/spring-boot",
        "version": "4.4.2"
      }
    },
    {
      "name": "helper",
      "metadata": {
        "layer": "helper",
        "names": [
          "spring-cloud-bindings"
        ],
        "version": "4.4.2"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/spring-boot",
        "version": "4.4.2"
      }
    },
    {
      "name": "spring-cloud-bindings",
      "metadata": {
        "layer": "spring-cloud-bindings",
        "licenses": [
          {
            "type": "Apache-2.0",
            "uri": "https://github.com/spring-cloud/spring-cloud-bindings/blob/main/LICENSE"
          }
        ],
        "name": "Spring Cloud Bindings",
        "sha256": "a52c2592d58555b6d70a3b0128be70852c83a0c58b70a7b23c07ebd9631ec47a",
        "stacks": [
          "io.buildpacks.stacks.bionic",
          "org.cloudfoundry.stacks.cflinuxfs3"
        ],
        "uri": "https://repo.spring.io/release/org/springframework/cloud/spring-cloud-bindings/1.7.1/spring-cloud-bindings-1.7.1.jar",
        "version": "1.7.1"
      },
      "buildpacks": {
        "id": "paketo-buildpacks/spring-boot",
        "version": "4.4.2"
      }
    }
  ]
}

Stackはビルドのベースイメージとして使用されるBuild Imageとランタイムのベースイメージとして使用されるRun Imageの組み合わせです。代表的なStackはpack stack suggestコマンドで確認できます。

$ pack stack suggest
Stacks maintained by the community:

    Stack ID: heroku-18
    Description: The official Heroku stack based on Ubuntu 18.04
    Maintainer: Heroku
    Build Image: heroku/pack:18-build
    Run Image: heroku/pack:18

    Stack ID: io.buildpacks.stacks.bionic
    Description: A minimal Paketo stack based on Ubuntu 18.04
    Maintainer: Paketo Project
    Build Image: paketobuildpacks/build:base-cnb
    Run Image: paketobuildpacks/run:base-cnb

    Stack ID: io.buildpacks.stacks.bionic
    Description: A large Paketo stack based on Ubuntu 18.04
    Maintainer: Paketo Project
    Build Image: paketobuildpacks/build:full-cnb
    Run Image: paketobuildpacks/run:full-cnb

    Stack ID: io.paketo.stacks.tiny
    Description: A tiny Paketo stack based on Ubuntu 18.04, similar to distroless
    Maintainer: Paketo Project
    Build Image: paketobuildpacks/build:tiny-cnb
    Run Image: paketobuildpacks/run:tiny-cnb

既にpack inspectコマンドで確認したように、今回作成したイメージにはRun Imageとしてpaketobuildpacks/run:base-cnbが使用されています。

Rebase Stack

例えばRun Imageが更新された場合に、作成したイメージに対してアプリケーションレイヤを変更することなくStackを更新するにはpack rebaseコマンドが使えます。

Image Credit: https://buildpacks.io/docs/concepts/operations/rebase/

次のコマンドを実行してください。

$ pack rebase docker.io/library/vehicle-api:0.0.1-SNAPSHOT  
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
Rebasing docker.io/library/vehicle-api:0.0.1-SNAPSHOT on run image index.docker.io/paketobuildpacks/run:base-cnb
Saving docker.io/library/vehicle-api:0.0.1-SNAPSHOT...
*** Images (919a7f1521f6):
      docker.io/library/vehicle-api:0.0.1-SNAPSHOT
Rebased Image: 919a7f1521f6f33f7709e08248788a975edefbeb662ab8cc70e06b9c362eee33
Successfully rebased image docker.io/library/vehicle-api:0.0.1-SNAPSHOT

Image is up to date for paketobuildpacks/run:base-cnbというメッセージが出ているように、このRun Imageは既に最新なので、この例では実質的には変更がありません。Rebase後のイメージに対してpack inspectコマンドを実行すると確かにRun ImageのIDが依然としてe30a81410ab1dc84461d098e32611f94fce698ca1212447aa2b6f7f590a2b778であることを確認できます。

$ pack inspect docker.io/library/vehicle-api:0.0.1-SNAPSHOT 
Inspecting image: docker.io/library/vehicle-api:0.0.1-SNAPSHOT

REMOTE:
(not present)

LOCAL:

Stack: io.buildpacks.stacks.bionic

Base Image:
  Reference: e30a81410ab1dc84461d098e32611f94fce698ca1212447aa2b6f7f590a2b778
  Top Layer: sha256:24fe3ce6fe4470832bca47951fcbc5ecf499057a671bc3e5e1d6792f4b270eeb

Run Images:
  index.docker.io/paketobuildpacks/run:base-cnb
  gcr.io/paketo-buildpacks/run:base-cnb

Buildpacks:
  ID                                         VERSION        HOMEPAGE
  paketo-buildpacks/ca-certificates          2.3.2          https://github.com/paketo-buildpacks/ca-certificates
  paketo-buildpacks/bellsoft-liberica        8.1.2          https://github.com/paketo-buildpacks/bellsoft-liberica
  paketo-buildpacks/executable-jar           5.1.2          https://github.com/paketo-buildpacks/executable-jar
  paketo-buildpacks/dist-zip                 4.1.2          https://github.com/paketo-buildpacks/dist-zip
  paketo-buildpacks/spring-boot              4.4.2          https://github.com/paketo-buildpacks/spring-boot

Processes:
  TYPE                  SHELL        COMMAND        ARGS
  web (default)                      java           org.springframework.boot.loader.JarLauncher
  executable-jar                     java           org.springframework.boot.loader.JarLauncher
  task                               java           org.springframework.boot.loader.JarLauncher

Update Run Image

ここでRun Imageを自分で更新してみましょう。例えばOpenSSLに脆弱性が見つかり、公式にRun Imageが更新されるよりも先に自分でパッチを当てたいケースを想定します。run.Dockerfileファイルを作成し、次の内容を記述してください。

FROM index.docker.io/paketobuildpacks/run:base-cnb
USER root
RUN apt-get update && \
   apt-get upgrade -y openssl && \
   apt-get clean && \
   rm -rf /var/lib/apt/lists/*
USER cnb

次のコマンドを実行して、Run Imageを作成してください。

$ docker build . -t run-image -f run.Dockerfile
[+] Building 14.7s (6/6) FINISHED                                                                                                                                                                                                                                                                  
 => [internal] load build definition from run.Dockerfile                                                                                                                                                                                                                                      0.0s
 => => transferring dockerfile: 233B                                                                                                                                                                                                                                                          0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                                             0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/paketobuildpacks/run:base-cnb                                                                                                                                                                                                                      0.0s
 => CACHED [1/2] FROM docker.io/paketobuildpacks/run:base-cnb                                                                                                                                                                                                                                 0.0s
 => [2/2] RUN apt-get update &&     apt-get upgrade -y openssl &&     apt-get clean &&     rm -rf /var/lib/apt/lists/*                                                                                                                                                                       14.5s
 => exporting to image                                                                                                                                                                                                                                                                        0.1s 
 => => exporting layers                                                                                                                                                                                                                                                                       0.1s 
 => => writing image sha256:5348813eed51ae53b8a1bfb6ca81708125a18e80b425120c97079df461c01dd6                                                                                                                                                                                                  0.0s 
 => => naming to docker.io/library/run-image  

pack rebaseコマンドに--run-imageオプションを追加して作成したRun Imageを指定します。

$ pack rebase docker.io/library/vehicle-api:0.0.1-SNAPSHOT --run-image run-image
Rebasing docker.io/library/vehicle-api:0.0.1-SNAPSHOT on run image run-image
*** Images (dc92034129ab):
      docker.io/library/vehicle-api:0.0.1-SNAPSHOT
Rebased Image: dc92034129ab45bcacab0c2f11a7eb96dc5d2fb3aa48077494e6a8bc0188c04a
Successfully rebased image docker.io/library/vehicle-api:0.0.1-SNAPSHOT

pack inspectコマンドを実行するとRun ImageのIDが変わっていることがわかります。

$ pack inspect docker.io/library/vehicle-api:0.0.1-SNAPSHOT                     
Inspecting image: docker.io/library/vehicle-api:0.0.1-SNAPSHOT

REMOTE:
(not present)

LOCAL:

Stack: io.buildpacks.stacks.bionic

Base Image:
  Reference: 5348813eed51ae53b8a1bfb6ca81708125a18e80b425120c97079df461c01dd6
  Top Layer: sha256:7100daa8b77f6e5e0479bebad98656d454298701e0559c69d39155affa300e8c

Run Images:
  index.docker.io/paketobuildpacks/run:base-cnb
  gcr.io/paketo-buildpacks/run:base-cnb

Buildpacks:
  ID                                         VERSION        HOMEPAGE
  paketo-buildpacks/ca-certificates          2.3.2          https://github.com/paketo-buildpacks/ca-certificates
  paketo-buildpacks/bellsoft-liberica        8.1.2          https://github.com/paketo-buildpacks/bellsoft-liberica
  paketo-buildpacks/executable-jar           5.1.2          https://github.com/paketo-buildpacks/executable-jar
  paketo-buildpacks/dist-zip                 4.1.2          https://github.com/paketo-buildpacks/dist-zip
  paketo-buildpacks/spring-boot              4.4.2          https://github.com/paketo-buildpacks/spring-boot

Processes:
  TYPE                  SHELL        COMMAND        ARGS
  web (default)                      java           org.springframework.boot.loader.JarLauncher
  executable-jar                     java           org.springframework.boot.loader.JarLauncher
  task                               java           org.springframework.boot.loader.JarLauncher

次のコマンドを実行して、このIDが自分で作成したイメージのものであることがわかります。

$ docker images --no-trunc | grep 5348813eed51ae53b8a1bfb6ca81708125a18e80b425120c97079df461c01dd6
run-image                  latest                  sha256:5348813eed51ae53b8a1bfb6ca81708125a18e80b425120c97079df461c01dd6   About an hour ago   92.2MB

Build a Image with the updated run image

自分で更新したRun Imageを使用してイメージを作成してみましょう。例えば、デフォルトのRun Imageに使用したいコマンドがインストールされていない場合に追加したいケースを想定します。

run.Dockerfileファイルを次の内容を更新してください。ここではImageMagickのインストールを追加します。

FROM index.docker.io/paketobuildpacks/run:base-cnb
USER root
RUN apt-get update && \
   apt-get install -y --no-install-recommends imagemagick && \
   apt-get clean && \
   rm -rf /var/lib/apt/lists/*
USER cnb

次のコマンドを実行して、Run Imageを作成してください。

$ docker build . -t run-image -f run.Dockerfile
[+] Building 20.0s (6/6) FINISHED                                                                                                                                                                                                                                                                  
 => [internal] load build definition from run.Dockerfile                                                                                                                                                                                                                                      0.0s
 => => transferring dockerfile: 261B                                                                                                                                                                                                                                                          0.0s
 => [internal] load .dockerignore                                                                                                                                                                                                                                                             0.0s
 => => transferring context: 2B                                                                                                                                                                                                                                                               0.0s
 => [internal] load metadata for docker.io/paketobuildpacks/run:base-cnb                                                                                                                                                                                                                      0.0s
 => CACHED [1/2] FROM docker.io/paketobuildpacks/run:base-cnb                                                                                                                                                                                                                                 0.0s
 => [2/2] RUN apt-get update &&     apt-get install -y --no-install-recommends imagemagick &&     apt-get clean &&     rm -rf /var/lib/apt/lists/*                                                                                                                                           19.6s
 => exporting to image                                                                                                                                                                                                                                                                        0.3s
 => => exporting layers                                                                                                                                                                                                                                                                       0.3s
 => => writing image sha256:6dccf4e29d3a8d0ec13e30a5053c9ddd85cca268565233b64b9362211e22f4f9                                                                                                                                                                                                  0.0s 
 => => naming to docker.io/library/run-image 

Spring Boot Maven PluginでこのRun Imageを使ってイメージをビルドする場合は、spring-boot.build-image.runImageプロパティにイメージ名を指定すれば良いです。なお、Run ImageがLocalにしかない場合はspring-boot.build-image.pullPolicyプロパティにNEVERを指定しないと、Remoteからの取得を試みエラーが発生します。

./mvnw spring-boot:build-image \
 -Dmaven.test.skip=true \
 -Dspring-boot.build-image.runImage=run-image \
 -Dspring-boot.build-image.pullPolicy=NEVER

次のコマンドで作成したイメージを実行して、ImageMagickのconvertコマンドが使用できることを確認してください。

$ docker run --entrypoint bash --rm docker.io/library/vehicle-api:0.0.1-SNAPSHOT -c 'convert --version'
Version: ImageMagick 6.9.7-4 Q16 x86_64 20170114 http://www.imagemagick.org
Copyright: © 1999-2017 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib djvu fftw fontconfig freetype jbig jng jpeg lcms lqr ltdl lzma openexr pangocairo png tiff wmf x xml zlib


各種Buildpackが提供する色々な機能を使うための設定を行います。

JVM Version

コンテナで使われるJVMのバージョンはpack CLIの場合はデフォルトで11、Spring Boot Maven Pluginの場合はpom.xmlで指定されているJavaのバージョンになります。

変更したい場合はビルド時に環境変数BP_JVM_VERSIONを設定してください。

Java 16を使いたい場合は次のように環境変数を設定してください。

pack build vehicle-api:16 \
 --builder paketobuildpacks/builder:base \
 --env BP_JVM_VERSION=16


次のようなログが出力されます。JDK 16(ビルド用)とJRE 16(実行用)がダウンロードされていることがわかります。

base: Pulling from paketobuildpacks/builder
Digest: sha256:6ad054eb7c2311bd064a458a3a4602efc273043cf86fe21541a5c90b071d4ff2
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
...
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper

Paketo BellSoft Liberica Buildpack 8.1.2
  https://github.com/paketo-buildpacks/bellsoft-liberica
  Build Configuration:
    $BP_JVM_VERSION              16              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  BellSoft Liberica JDK 16.0.1: Contributing to layer
    Downloading from https://github.com/bell-sw/Liberica/releases/download/16.0.1+9/bellsoft-jdk16.0.1+9-linux-amd64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jdk
    Adding 129 container CA certificates to JVM truststore
    Writing env.build/JAVA_HOME.override
    Writing env.build/JDK_HOME.override
  BellSoft Liberica JRE 16.0.1: Contributing to layer
    Downloading from https://github.com/bell-sw/Liberica/releases/download/16.0.1+9/bellsoft-jre16.0.1+9-linux-amd64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
    Adding 129 container CA certificates to JVM truststore
    Writing env.launch/BPI_APPLICATION_PATH.default
    Writing env.launch/BPI_JVM_CACERTS.default
    Writing env.launch/BPI_JVM_CLASS_COUNT.default
    Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
    Writing env.launch/JAVA_HOME.default
    Writing env.launch/MALLOC_ARENA_MAX.default
...
Saving vehicle-api:16...
*** Images (5ccad8c46989):
      vehicle-api:16
Adding cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Adding cache layer 'paketo-buildpacks/maven:application'
Adding cache layer 'paketo-buildpacks/maven:cache'
Successfully built image vehicle-api:16

Spring Boot Maven Pluginの場合は、pom.xmlに次のように設定して./mvnw spring-boot:build-imageを実行してください。

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
     <image>
        <env>
           <BP_JVM_VERSION>16</BP_JVM_VERSION>
        </env>
     </image>
  </configuration>
</plugin>

作成したイメージvehicle-api:16を実行し、ログを確認するとJava 16が使われていることがわかります。

$ docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api:16
Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx449407K -XX:MaxMetaspaceSize=87168K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 12976, Headroom: 0%)
Adding 129 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 -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx449407K -XX:MaxMetaspaceSize=87168K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-30 13:59:31.602  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication v0.0.1-SNAPSHOT using Java 16.0.1 on 6b3137f363e7 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)

...

Memory Calculator

Paketo Buildpackで作成したJavaアプリはメモリ設定が次の図のように自動で行われます。

自動で設定されるのはJavaのMemoryのうちNon Heapの領域です。Non Heapの値が決まった後、

Heap = Container Memory - Non Heap - Headroom

でHeapの最大値(-Xmx)が算出されます。

Non Heapの値は

Non Heap = Metaspace + Reserved CodeCache + Direct Memory + Tread Stack

で決まります。このうち、次項目はデフォルトでは固定値です。

Metaspaceはロードされるクラス数から自動で算出されます。ロードされるクラス数はjarファイルまたはwarファイルに含まれるclassファイルの数 * 0.35で推測されます。

Headroomすなわちコンテナ内でJava以外に使用されるメモリはデフォルトで0です。

ServletアプリではHeapとMetaspace以外に500MB、Spring WebFluxアプリでは300MB固定で確保されることになります。

次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

直後に計算されたメモリ設定が次のようなログとして出力されます。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx449112K -XX:MaxMetaspaceSize=87463K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13028, Headroom: 0%)

ロードされるクラス数が13028と推測され、そこからMetaspaceの値が自動で87463KBと計算されています。コンテナのメモリは1GBと指定されているので、設定されるHeapサイズは1GB - (87463KB + 500MB) - 0B = 449112KBと算出されます。次の図のような構成になります。

Reduce heap

前述の通り、Non Heapサイズは.classファイル数によって自動で決まるため、同一のjarまたはwarファイルに対しては一定値になります。そのため、コンテナのメモリサイズを減らすと必然的にHeapサイズが減ります。

次のコマンドを実行してください。コンテナのメモリサイズを768MBに減らしました。

docker run --rm \
 -p 8080:8080 \
 -m 768m \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx186968K -XX:MaxMetaspaceSize=87463K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 768M, Thread Count: 250, Loaded Class Count: 13028, Headroom: 0%)

コンテナのメモリサイズが1GBの時と比べて、Heapのサイズ(-Xmx)が小さくなっており、それ以外は同じ値であることがわかります。次の図のような構成になります。

ではさらにコンテナのメモリサイズを減らして、512MBにしてみます。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 512m \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

エラーが発生し、次のようなログが出力されます。

unable to calculate memory configuration
fixed memory regions require 599463K which is greater than 512M available for allocation: -XX:MaxDirectMemorySize=10M, -XX:MaxMetaspaceSize=87463K, -XX:ReservedCodeCacheSize=240M, -Xss1M * 250 threads
ERROR: failed to launch: exec.d: failed to execute exec.d file at path '/layers/paketo-buildpacks_bellsoft-liberica/helper/exec.d/memory-calculator': exit status 1

Non Heapに必要なサイズよりもコンテナのメモリサイズが小さく、Heapに割り当てる余地がなくなったためエラーになりました。

Reduce thread count

コンテナサイズを512MBに抑えられるようにNon Heapサイズを変更しましょう。まずはスレッド数を減らしてみます。スレッド数は実行時の環境変数BPL_JVM_THREAD_COUNTで指定できます。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 512m \
 -e BPL_JVM_THREAD_COUNT=30 \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx150104K -XX:MaxMetaspaceSize=87463K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 512M, Thread Count: 30, Loaded Class Count: 13028, Headroom: 0%)

Non Heap用のJVMオプションは変わっていませんが、スレッド数が250から30に減り、スレッドスタックサイズ1MB * 220 = 220MB分のメモリをHeapに割り当てる余地ができてため、エラーが発生することなくアプリケーションが起動しました。次の図のような構成になります。

Configure loaded class count

Metaspaceはロードされるクラス数から決まり、ロードされるクラス数は.classファイル数から推測されます。実際にロードされるクラス数が推測された数よりも多い場合または小さい場合は、実行時の環境変数BPL_JVM_LOADED_CLASS_COUNTで明示することができます。

先の例ではクラス数は13028と推測されましたが、ここでは10000を指定します。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 512m \
 -e BPL_JVM_THREAD_COUNT=30 \
 -e BPL_JVM_LOADED_CLASS_COUNT=10000 \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx167255K -XX:MaxMetaspaceSize=70312K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 512M, Thread Count: 30, Loaded Class Count: 10000, Headroom: 0%)

次の図のような構成になります。

Configure headroom

実行環境によってはJava以外にもメモリが必要になる場合はがあります。JVMはOut of memoryになっていないのにコンテナがOut of memoryになった場合は、Headroomを増やしてください。次のコマンドを実行してください。コンテナのメモリサイズのうち5%をJava以外に割り当てます。

docker run --rm \
 -p 8080:8080 \
 -m 512m \
 -e BPL_JVM_THREAD_COUNT=30 \
 -e BPL_JVM_HEAD_ROOM=5 \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx123890K -XX:MaxMetaspaceSize=87463K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 512M, Thread Count: 30, Loaded Class Count: 13028, Headroom: 5%)

次の図のような構成になります。

Reduce reserved code cache and thread stack

Non Heapのその他のエリアはデフォルトで固定値が設定されますが、環境変数JAVA_TOOL_OPTIONSでオーバーライドできます。これらの値を小さくして、コンテナのメモリサイズを256MBまで減らしてみます。
次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 256m \
 -e BPL_JVM_THREAD_COUNT=30 \
 -e BPL_JVM_HEAD_ROOM=5 \
 -e JAVA_TOOL_OPTIONS="-XX:ReservedCodeCacheSize=64M -Xss512k" \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx70437K -XX:MaxMetaspaceSize=87463K (Total Memory: 256M, Thread Count: 30, Loaded Class Count: 13028, Headroom: 5%)

次の図のような構成になります。

Memory Calculatorの調整方法を説明しました。Memory Calculatorはデフォルトで高負荷時に耐えられる設定を提供してくれます。値を小さくしたい場合は、トレードオフがあることを認識した上で変更してください

Debbuging

Paketo Debug Buildpackを使うと、コンテナのアプリケーションに対してIDEを使ってデバック実行できます。

Debugを有効にするには、ビルド時と実行時の両方に環境変数を設定する必要があります。

まず、コンテナイメージ内にデバッグ実行用のファイル(環境変数JAVA_TOOL_OPTIONSに対してデバッグオプションを追加するコマンド)を追加するためにビルド時に環境変数BP_DEBUG_ENABLEDtrueを設定します。次のコマンドを実行してください。

pack build vehicle-api \
 --builder paketobuildpacks/builder:base \
 --env BP_DEBUG_ENABLED=true


次のようなログが出力されます。Paketo Debug Buildpackに関するログが出力されていることがわかります。

base: Pulling from paketobuildpacks/builder
Digest: sha256:7f2b74ba47b235e5a30393ad83c3e3c12ff5229c190faffd67d842e064b7743e
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:aa9a8a0a4bb607422d3ff59923638f2a6cf3d1e6b404aee9ae027127ea88a5c1
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
8 of 18 buildpacks participating
paketo-buildpacks/ca-certificates   2.3.2
paketo-buildpacks/bellsoft-liberica 8.1.2
paketo-buildpacks/maven             5.3.2
paketo-buildpacks/executable-jar    5.1.2
paketo-buildpacks/apache-tomcat     5.5.2
paketo-buildpacks/dist-zip          4.1.2
paketo-buildpacks/spring-boot       4.4.2
paketo-buildpacks/debug             3.1.2
===> ANALYZING
...
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Reusing cached layer

Paketo BellSoft Liberica Buildpack 8.1.2
  https://github.com/paketo-buildpacks/bellsoft-liberica
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  BellSoft Liberica JDK 11.0.11: Reusing cached layer
  BellSoft Liberica JRE 11.0.11: Reusing cached layer
  Launch Helper: Reusing cached layer
  JVMKill Agent 1.16.0: Reusing cached layer
  Java Security Properties: Reusing cached layer

Paketo Maven Buildpack 5.3.2
  https://github.com/paketo-buildpacks/maven
  Build Configuration:
    $BP_MAVEN_BUILD_ARGUMENTS  -Dmaven.test.skip=true package  the arguments to pass to Maven
    $BP_MAVEN_BUILT_ARTIFACT   target/*.[jw]ar                 the built application artifact explicitly.  Supersedes $BP_MAVEN_BUILT_MODULE
    $BP_MAVEN_BUILT_MODULE                                     the module to find application artifact in
    Creating cache directory /home/cnb/.m2
  Compiled Application: Reusing cached layer
  Removing source code

Paketo Executable JAR Buildpack 5.1.2
  https://github.com/paketo-buildpacks/executable-jar
  Class Path: Contributing to layer
    Writing env/CLASSPATH.delim
    Writing env/CLASSPATH.prepend
  Process types:
    executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
    task:           java org.springframework.boot.loader.JarLauncher (direct)
    web:            java org.springframework.boot.loader.JarLauncher (direct)

Paketo Spring Boot Buildpack 4.4.2
  https://github.com/paketo-buildpacks/spring-boot
  Creating slices from layers index
    dependencies
    spring-boot-loader
    snapshot-dependencies
    application
  Launch Helper: Reusing cached layer
  Spring Cloud Bindings 1.7.1: Reusing cached layer
  Web Application Type: Reusing cached layer
  4 application slices
  Image labels:
    org.opencontainers.image.title
    org.opencontainers.image.version
    org.springframework.boot.version

Paketo Debug Buildpack 3.1.2
  https://github.com/paketo-buildpacks/debug
  Build Configuration:
    $BP_DEBUG_ENABLED   true  whether to contribute debug support
  Launch Configuration:
    $BPL_DEBUG_ENABLED        whether to enable debug support
    $BPL_DEBUG_PORT     8080  what port the debug agent will listen on
    $BPL_DEBUG_SUSPEND  n     whether the JVM will suspend execution until a debugger has attached
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_debug/helper/exec.d/debug
===> EXPORTING
Reusing layer 'paketo-buildpacks/ca-certificates:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jre'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
Reusing layer 'paketo-buildpacks/executable-jar:classpath'
Reusing layer 'paketo-buildpacks/spring-boot:helper'
Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
Adding layer 'paketo-buildpacks/debug:helper'
Reusing 5/5 app layer(s)
Reusing layer 'launcher'
Adding layer 'config'
Reusing layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Adding label 'org.opencontainers.image.title'
Adding label 'org.opencontainers.image.version'
Adding label 'org.springframework.boot.version'
Setting default process type 'web'
Saving vehicle-api...
*** Images (35240563657b):
      vehicle-api
Reusing cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Reusing cache layer 'paketo-buildpacks/maven:application'
Reusing cache layer 'paketo-buildpacks/maven:cache'
Successfully built image vehicle-api

Spring Boot Maven Pluginの場合は、pom.xmlに次のように設定して./mvnw spring-boot:build-imageを実行してください。

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
     <image>
        <env>
           <BP_DEBUG_ENABLED>true</BP_DEBUG_ENABLED>
        </env>
     </image>
  </configuration>
</plugin>

次に環境変数BPL_DEBUG_ENABLEDtrueを設定してコンテナを起動します。デバッグ接続用のポートとして8000を公開します。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -p 8000:8000 \
 -m 1g \
 -e BPL_DEBUG_ENABLED=true \
 -e BPL_DEBUG_PORT="*:8000" \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api


次のようなログが出力されます。環境変数JAVA_TOOL_OPTIONSにデバッグ用のオプションがついていることがわかります。

Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13099, Headroom: 0%)
Adding 129 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Debugging enabled on port *:8000
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true -agentlib:jdwp=transport=dt_socket,server=y,address=*:8000,suspend=n
Listening for transport dt_socket at address: 8000

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)
...

IDEからデバック実行します。ソースコードをIDEで開いてください。
IntelliJ IDEAの場合は次のような設定を行い、デバッグ実行してください。

ソースコードにブレークポイントを設定し、そのコードが実行されるようにアプリケーションにアクセスしてください。


Eclipseの場合はこちらのドキュメントを参照してください。

JMX

Paketo JMX Buildpackを使うことで、アプリケーションのJMX設定を有効にします。仕組みはDebug Buildpackとほぼ同じです。

まず、コンテナイメージ内にJMX設定用のファイル(環境変数JAVA_TOOL_OPTIONSに対してJMXオプションを追加するコマンド)を追加するためにビルド時に環境変数BP_JMX_ENABLEDtrueを設定します。次のコマンドを実行してください。

pack build vehicle-api \
 --builder paketobuildpacks/builder:base \
 --env BP_JMX_ENABLED=true

次のようなログが出力されます。Paketo JMX Buildpackに関するログが出力されていることがわかります。

base: Pulling from paketobuildpacks/builder
Digest: sha256:6ad054eb7c2311bd064a458a3a4602efc273043cf86fe21541a5c90b071d4ff2
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
8 of 18 buildpacks participating
paketo-buildpacks/ca-certificates   2.3.2
paketo-buildpacks/bellsoft-liberica 8.1.2
paketo-buildpacks/maven             5.3.2
paketo-buildpacks/executable-jar    5.1.2
paketo-buildpacks/apache-tomcat     5.5.2
paketo-buildpacks/dist-zip          4.1.2
paketo-buildpacks/spring-boot       4.4.2
paketo-buildpacks/jmx               3.1.2
===> ANALYZING
...
===> RESTORING
Restoring data for "paketo-buildpacks/bellsoft-liberica:jdk" from cache
Restoring data for "paketo-buildpacks/maven:application" from cache
Restoring data for "paketo-buildpacks/maven:cache" from cache
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Reusing cached layer

Paketo BellSoft Liberica Buildpack 8.1.2
  https://github.com/paketo-buildpacks/bellsoft-liberica
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  BellSoft Liberica JDK 11.0.11: Reusing cached layer
  BellSoft Liberica JRE 11.0.11: Reusing cached layer
  Launch Helper: Reusing cached layer
  JVMKill Agent 1.16.0: Reusing cached layer
  Java Security Properties: Reusing cached layer

Paketo Maven Buildpack 5.3.2
  https://github.com/paketo-buildpacks/maven
  Build Configuration:
    $BP_MAVEN_BUILD_ARGUMENTS  -Dmaven.test.skip=true package  the arguments to pass to Maven
    $BP_MAVEN_BUILT_ARTIFACT   target/*.[jw]ar                 the built application artifact explicitly.  Supersedes $BP_MAVEN_BUILT_MODULE
    $BP_MAVEN_BUILT_MODULE                                     the module to find application artifact in
    Creating cache directory /home/cnb/.m2
  Compiled Application: Reusing cached layer
  Removing source code

Paketo Executable JAR Buildpack 5.1.2
  https://github.com/paketo-buildpacks/executable-jar
  Class Path: Contributing to layer
    Writing env/CLASSPATH.delim
    Writing env/CLASSPATH.prepend
  Process types:
    executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
    task:           java org.springframework.boot.loader.JarLauncher (direct)
    web:            java org.springframework.boot.loader.JarLauncher (direct)

Paketo Spring Boot Buildpack 4.4.2
  https://github.com/paketo-buildpacks/spring-boot
  Creating slices from layers index
    dependencies
    spring-boot-loader
    snapshot-dependencies
    application
  Launch Helper: Reusing cached layer
  Spring Cloud Bindings 1.7.1: Reusing cached layer
  Web Application Type: Reusing cached layer
  4 application slices
  Image labels:
    org.opencontainers.image.title
    org.opencontainers.image.version
    org.springframework.boot.version

Paketo JMX Buildpack 3.1.2
  https://github.com/paketo-buildpacks/jmx
  Build Configuration:
    $BP_JMX_ENABLED   true  whether to contribute JMX support
  Launch Configuration:
    $BPL_JMX_ENABLED        whether to enable JMX support
    $BPL_JMX_PORT     5000  what port the JMX Connector will listen on
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_jmx/helper/exec.d/jmx
===> EXPORTING
Reusing layer 'paketo-buildpacks/ca-certificates:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jre'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
Reusing layer 'paketo-buildpacks/executable-jar:classpath'
Reusing layer 'paketo-buildpacks/spring-boot:helper'
Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
Adding layer 'paketo-buildpacks/jmx:helper'
Reusing 5/5 app layer(s)
Reusing layer 'launcher'
Adding layer 'config'
Reusing layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Adding label 'org.opencontainers.image.title'
Adding label 'org.opencontainers.image.version'
Adding label 'org.springframework.boot.version'
Setting default process type 'web'
Saving vehicle-api...
*** Images (f53a218cda7b):
      vehicle-api
Reusing cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Reusing cache layer 'paketo-buildpacks/maven:application'
Reusing cache layer 'paketo-buildpacks/maven:cache'
Successfully built image vehicle-api

Spring Boot Maven Pluginの場合は、pom.xmlに次のように設定して./mvnw spring-boot:build-imageを実行してください。

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
     <image>
        <env>
           <BP_JMX_ENABLED>true</BP_JMX_ENABLED>
        </env>
     </image>
  </configuration>
</plugin>

次に環境変数BPL_JMX_ENABLEDtrueを設定してコンテナを起動します。JMX用のポートとして5000を公開します。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -p 5000:5000 \
 -m 1g \
 -e BPL_JMX_ENABLED=true \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api


次のようなログが出力されます。環境変数JAVA_TOOL_OPTIONSにJMX用のオプションがついていることがわかります。

Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13099, Headroom: 0%)
Adding 129 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
JMX enabled on port 5000
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_bellsoft-liberica/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=5000 -Dcom.sun.management.jmxremote.rmi.port=5000

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)
...

JConsoleを使ってlocalhost:5000にアクセスしてください。

アプリケーションからデータベースへなど外部サービスに接続するための情報を設定する際に環境変数を使うことが多いですが、Bindingは外部サービスのインスタンスをBind可能なリソースとして扱い、このリソースをアプリケーションにBindすることで接続情報を注入する仕組みです。アプリケーション側には明示的に環境変数を設定する必要がありません。

The Twelve-Factor AppIV. Backing servicesの説明で使用されている"Attach"がまさにBindingを表しています。

Image Credit: https://12factor.net/backing-services

Bindingの実装としては

がありますが、前者は後者もカバーし、後者はDeprecatedです。Paketo BuildpackはこのBindingの仕組みに対応しています。様々なサービスへの接続をこのBindingの仕組みで提供します。例えば、Azure Application InsightsGoogle StackdriverのようなAPMへの接続するための設定はこちらです。

サービスへの接続情報は環境変数でなくVolumeMountを経由して取得されます。

PostgreSQL

これまで、アプリケーションがPostgreSQLにアクセスするために環境変数SPRING_DATASOURCE_URLを設定してきましたが、今回はBindingを使用してPostgreSQLへの接続情報を渡します。

Service Binding Specification for Kubernetesの仕様に合わせて、接続情報を作成します。

vehicle-apiディレクトリで次のコマンドを実行してください。

mkdir -p bindings/vehicle-db
echo postgresql > bindings/vehicle-db/type
echo host.docker.internal > bindings/vehicle-db/host
echo 5432 > bindings/vehicle-db/port
echo vehicle > bindings/vehicle-db/username
echo vehicle > bindings/vehicle-db/password
echo vehicle > bindings/vehicle-db/database

次のようなファイル構造になります。vehicle-dbというリソースにBindするための情報がそのディレクトリの下の各ファイルに設定されています。

$ tree bindings 
bindings
`-- vehicle-db
    |-- database
    |-- host
    |-- password
    |-- port
    |-- type
    `-- username

1 directory, 6 files

このファイル構造をSpring Bootのプロパティに変換してくれるライブラリがSpring Cloud Bindingsです。Paketo Java Buildpackでコンテナイメージを作成した場合、Spring Cloud Bindingsのjarは自動で追加されます。

PostgreSQLの接続に対応するプロパティの表は次のリンクを確認してください。

https://github.com/spring-cloud/spring-cloud-bindings#postgresql-rdbms

接続情報を用意したbindingsディレクトリをコンテナの/bindingsにマウントし、このディレクトリをService Binding Specification for Kubernetesの仕様に合わせて環境変数SERVICE_BINDING_ROOTに設定します。次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SERVICE_BINDING_ROOT=/bindings \
 -e MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE="*" \
 -v ${PWD}/bindings:/bindings \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13099, Headroom: 0%)
Adding 129 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 -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-30 14:08:00.813  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication v0.0.1-SNAPSHOT using Java 11.0.11 on da12647c3817 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2021-06-30 14:08:00.817  INFO 1 --- [           main] com.example.VehicleApiApplication        : No active profile set, falling back to default profiles: default
2021-06-30 14:08:00.895  INFO 1 --- [           main] .BindingSpecificEnvironmentPostProcessor : Creating binding-specific PropertySource from Kubernetes Service Bindings
2021-06-30 14:08:02.939  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-06-30 14:08:02.953  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-06-30 14:08:02.953  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-06-30 14:08:03.027  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-06-30 14:08:03.027  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2129 ms
2021-06-30 14:08:03.368  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-06-30 14:08:03.560  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-06-30 14:08:04.286  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 13 endpoint(s) beneath base path '/actuator'
2021-06-30 14:08:04.350  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-06-30 14:08:04.368  INFO 1 --- [           main] com.example.VehicleApiApplication        : Started VehicleApiApplication in 4.29 seconds (JVM running for 4.929)

アプリケーションが起動していれば、Bindingを使用したPostgreSQLへの接続に成功しています。

Spring Cloud Bindingsによって自動設定されたプロパティは次のコマンドで確認できます。

$ curl -s http://localhost:8080/actuator/env | jq ".propertySources[] | select(.name == \"kubernetesServiceBindingSpecific\")" 
{
  "name": "kubernetesServiceBindingSpecific",
  "properties": {
    "spring.datasource.driver-class-name": {
      "value": "org.postgresql.Driver"
    },
    "spring.datasource.username": {
      "value": "vehicle"
    },
    "spring.datasource.url": {
      "value": "jdbc:postgresql://host.docker.internal:5432/vehicle"
    },
    "spring.r2dbc.password": {
      "value": "******"
    },
    "spring.r2dbc.url": {
      "value": "r2dbc:postgresql://host.docker.internal:5432/vehicle"
    },
    "spring.r2dbc.username": {
      "value": "vehicle"
    },
    "spring.datasource.password": {
      "value": "******"
    }
  }
}

また、次のBindingに関する次のプロパティも設定されます。

$ curl -s http://localhost:8080/actuator/env | jq ".propertySources[] | select(.name == \"kubernetesServiceBindingFlattened\")"
{
  "name": "kubernetesServiceBindingFlattened",
  "properties": {
    "k8s.bindings.vehicle-db.password": {
      "value": "******"
    },
    "k8s.bindings.vehicle-db.host": {
      "value": "host.docker.internal"
    },
    "k8s.bindings.vehicle-db.port": {
      "value": "5432"
    },
    "k8s.bindings.vehicle-db.database": {
      "value": "vehicle"
    },
    "k8s.bindings.vehicle-db.username": {
      "value": "vehicle"
    }
  }
}

CA certificates

次は信頼されたCA証明書をBindingの仕組みでコンテナに渡します。vehicle-apiがPostgreSQLに対してTLSで接続するように、PostgreSQLサーバーに設定する証明書のCAを信頼します。

まずはPostgreSQLを自己署名のTLSに対応させます。vehicle-apiディレクトリの下にcertsディレクトリを作成し、そのディレクトリ内にgenerate-certs.shというファイルを作成し、次の内容を記述してください。

#!/bin/bash
set -ex

cd /certs
openssl req -new -nodes -out root.csr \
 -keyout root.key -subj "/CN=docker.internal"
chmod og-rwx root.key

openssl x509 -req -in root.csr -days 3650 \
 -extfile /etc/ssl/openssl.cnf -extensions v3_ca \
 -signkey root.key -out root.crt

openssl req -new -nodes -out server.csr \
 -keyout server.key -subj "/CN=host.docker.internal"
chmod og-rwx server.key

openssl x509 -req -in server.csr -days 3650 \
 -CA root.crt -CAkey root.key -CAcreateserial \
 -out server.crt

vehicle-apiディレクトリで次のコマンドを実行してください。

docker run --rm \
 -v ${PWD}/certs:/certs \
 paketobuildpacks/run:base-cnb \
 sh /certs/generate-certs.sh

次のようなログが出力されます。

+ cd /certs
+ openssl req -new -nodes -out root.csr -keyout root.key -subj /CN=docker.internal
Can't load /home/cnb/.rnd into RNG
139648000373184:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/cnb/.rnd
Generating a RSA private key
.+++++
..................+++++
writing new private key to 'root.key'
-----
+ chmod og-rwx root.key
+ openssl x509 -req -in root.csr -days 3650 -extfile /etc/ssl/openssl.cnf -extensions v3_ca -signkey root.key -out root.crt
Signature ok
subject=CN = docker.internal
Getting Private key
+ openssl req -new -nodes -out server.csr -keyout server.key -subj /CN=host.docker.internal
Can't load /home/cnb/.rnd into RNG
140412413399488:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/cnb/.rnd
Generating a RSA private key
.........+++++
....+++++
writing new private key to 'server.key'
-----
+ chmod og-rwx server.key
+ openssl x509 -req -in server.csr -days 3650 -CA root.crt -CAkey root.key -CAcreateserial -out server.crt
Signature ok
subject=CN = host.docker.internal
Getting CA Private Key

certsディレクトリ以下に次のファイルが作成されていることを確認してください。

$ ls -l certs 
total 64
-rw-r--r--  1 toshiaki  staff   499 Apr 10 02:14 generate-certs.sh
-rw-r--r--  1 toshiaki  staff  1131 Apr 10 02:14 root.crt
-rw-r--r--  1 toshiaki  staff   899 Apr 10 02:14 root.csr
-rw-------  1 toshiaki  staff  1704 Apr 10 02:14 root.key
-rw-r--r--  1 toshiaki  staff    41 Apr 10 02:14 root.srl
-rw-r--r--  1 toshiaki  staff  1013 Apr 10 02:14 server.crt
-rw-r--r--  1 toshiaki  staff   907 Apr 10 02:14 server.csr
-rw-------  1 toshiaki  staff  1708 Apr 10 02:14 server.key

この証明書を使い、PostgreSQLをTLS対応させます。次のコマンドを実行してください。

docker run --rm \
 -p 5432:5432 \
 -e POSTGRES_DB=vehicle \
 -e POSTGRES_USER=vehicle \
 -e POSTGRES_PASSWORD=vehicle \
 -e POSTGRESQL_ENABLE_TLS=yes \
 -e POSTGRESQL_TLS_CERT_FILE=/certs/server.crt \
 -e POSTGRESQL_TLS_KEY_FILE=/certs/server.key \
 -e POSTGRESQL_TLS_CA_FILE=/certs/root.crt \
 -e POSTGRESQL_PGHBA_REMOVE_FILTERS=hostssl \
 -v ${PWD}/certs:/certs \
 bitnami/postgresql:11.11.0-debian-10-r59

まずはBindingなしで、PostgreSQLにTLSで接続します。次のコマンドを実行してください。sslModeがverify-fullなので、接続するサーバーが信頼されていないといけません。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLMODE=verify-full \
 -e SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLFACTORY=org.postgresql.ssl.DefaultJavaSSLFactory \
 -e SERVICE_BINDING_ROOT=/bindings \
 -v ${PWD}/bindings:/bindings \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

自己署名の証明書は信頼されていないため、次のログのようにunable to find valid certification path to requested targetというエラーメッセージが出力されます。

Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13099, Headroom: 0%)
Adding 129 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 -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-30 14:30:48.551  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication v0.0.1-SNAPSHOT using Java 11.0.11 on 6e7e43e2496b with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2021-06-30 14:30:48.555  INFO 1 --- [           main] com.example.VehicleApiApplication        : No active profile set, falling back to default profiles: default
2021-06-30 14:30:48.634  INFO 1 --- [           main] .BindingSpecificEnvironmentPostProcessor : Creating binding-specific PropertySource from Kubernetes Service Bindings
2021-06-30 14:30:50.347  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-06-30 14:30:50.360  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-06-30 14:30:50.360  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-06-30 14:30:50.424  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-06-30 14:30:50.425  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1788 ms
2021-06-30 14:30:50.815  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-06-30 14:30:52.327 ERROR 1 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Exception during pool initialization.

org.postgresql.util.PSQLException: SSL error: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at org.postgresql.ssl.MakeSSL.convert(MakeSSL.java:43) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.core.v3.ConnectionFactoryImpl.enableSSL(ConnectionFactoryImpl.java:534) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.core.v3.ConnectionFactoryImpl.tryConnect(ConnectionFactoryImpl.java:149) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:213) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:223) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.Driver.makeConnection(Driver.java:465) ~[postgresql-42.2.22.jar:42.2.22]
        at org.postgresql.Driver.connect(Driver.java:264) ~[postgresql-42.2.22.jar:42.2.22]
        at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138) ~[HikariCP-4.0.3.jar:na]
...

次にこのCA証明書を信頼するようにBindingを作成します。次のコマンドを実行してください。CA Certificates Buildpackの仕様に合わせます。

mkdir -p bindings/trusted-certs
echo ca-certificates > bindings/trusted-certs/type
cp certs/root.crt bindings/trusted-certs/

次のようなファイル構造になります。

$ tree bindings 
bindings
|-- trusted-certs
|   |-- root.crt
|   `-- type
`-- vehicle-db
    |-- database
    |-- host
    |-- password
    |-- port
    |-- type
    `-- username

2 directories, 8 files

再度次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 1g \
 -e SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLMODE=verify-full \
 -e SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLFACTORY=org.postgresql.ssl.DefaultJavaSSLFactory \
 -e SERVICE_BINDING_ROOT=/bindings \
 -v ${PWD}/bindings:/bindings \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

Added 1 additional CA certificate(s) to system truststore
Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13099, Headroom: 0%)
Adding 130 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 -agentpath:/layers/paketo-buildpacks_bellsoft-liberica/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448710K -XX:MaxMetaspaceSize=87865K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-06-30 14:33:05.663  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication v0.0.1-SNAPSHOT using Java 11.0.11 on aaa964c376a6 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2021-06-30 14:33:05.667  INFO 1 --- [           main] com.example.VehicleApiApplication        : No active profile set, falling back to default profiles: default
2021-06-30 14:33:05.728  INFO 1 --- [           main] .BindingSpecificEnvironmentPostProcessor : Creating binding-specific PropertySource from Kubernetes Service Bindings
2021-06-30 14:33:07.561  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-06-30 14:33:07.576  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-06-30 14:33:07.577  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-06-30 14:33:07.642  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-06-30 14:33:07.642  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1912 ms
2021-06-30 14:33:07.964  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-06-30 14:33:08.575  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-06-30 14:33:09.463  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'
2021-06-30 14:33:09.531  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-06-30 14:33:09.557  INFO 1 --- [           main] com.example.VehicleApiApplication        : Started VehicleApiApplication in 4.676 seconds (JVM running for 5.345)

これまでAdding 129 container CA certificates to JVM truststoreというメッセージが出力されていましたが、今回はAdding 130 container CA certificates to JVM truststoreというメッセージが出力され、1件CA証明書がTruststoreに追加されたことが分かります。アプリケーションも起動し、PostgreSQLにsslModeをverify-fullの設定で接続することができました。

Paketo BuildpackのJava Buildpackでは環境変数SSL_CERT_DIRに追加したいCA証明書のディレクトリを指定すれば、そのディレクトリ内の証明書を起動時にJavaのTruststoreに追加してくれます。CA certificate Buildpackはtypeがca-certificatesなBindingがある場合に、その値を環境変数SSL_CERT_DIRに追加してくれます。

PostgreSQLのServiceをBindした際とCA CertificatesのService Bindをした際にアプリケーションに設定するService Binidngに関する環境変数は変わっていません。Bindする情報が増えただけで、この情報は自動でアプリケーションに注入されます。

今回はRuntimeでのBindingを試しましたが、BindingはBuild時に行うこともできます。


次のコマンドを実行してください。

pack build vehicle-api \
 --path ./target/vehicle-api-0.0.1-SNAPSHOT.jar \
 --builder paketobuildpacks/builder:base \
 --volume ${PWD}/bindings/trusted-certs:/platform/bindings/trusted-certs

次のようなログが出力されます。

base: Pulling from paketobuildpacks/builder
Digest: sha256:6ad054eb7c2311bd064a458a3a4602efc273043cf86fe21541a5c90b071d4ff2
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
===> DETECTING
5 of 18 buildpacks participating
paketo-buildpacks/ca-certificates   2.3.2
paketo-buildpacks/bellsoft-liberica 8.1.2
paketo-buildpacks/executable-jar    5.1.2
paketo-buildpacks/dist-zip          4.1.2
paketo-buildpacks/spring-boot       4.4.2
===> ANALYZING
...
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Reusing cached layer
  CA Certificates: Contributing to layer
    Added 1 additional CA certificate(s) to system truststore
    Writing env.build/SSL_CERT_DIR.append
    Writing env.build/SSL_CERT_DIR.delim
    Writing env.build/SSL_CERT_FILE.default

Paketo BellSoft Liberica Buildpack 8.1.2
  https://github.com/paketo-buildpacks/bellsoft-liberica
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  BellSoft Liberica JRE 11.0.11: Contributing to layer
    Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.11+9/bellsoft-jre11.0.11+9-linux-amd64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
    Adding 130 container CA certificates to JVM truststore
    Writing env.launch/BPI_APPLICATION_PATH.default
    Writing env.launch/BPI_JVM_CACERTS.default
    Writing env.launch/BPI_JVM_CLASS_COUNT.default
    Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
    Writing env.launch/JAVA_HOME.default
    Writing env.launch/MALLOC_ARENA_MAX.default
  Launch Helper: Reusing cached layer
  JVMKill Agent 1.16.0: Reusing cached layer
  Java Security Properties: Reusing cached layer

Paketo Executable JAR Buildpack 5.1.2
  https://github.com/paketo-buildpacks/executable-jar
  Class Path: Contributing to layer
    Writing env/CLASSPATH.delim
    Writing env/CLASSPATH.prepend
  Process types:
    executable-jar: java org.springframework.boot.loader.JarLauncher (direct)
    task:           java org.springframework.boot.loader.JarLauncher (direct)
    web:            java org.springframework.boot.loader.JarLauncher (direct)

Paketo Spring Boot Buildpack 4.4.2
  https://github.com/paketo-buildpacks/spring-boot
  Creating slices from layers index
    dependencies
    spring-boot-loader
    snapshot-dependencies
    application
  Launch Helper: Reusing cached layer
  Spring Cloud Bindings 1.7.1: Reusing cached layer
  Web Application Type: Contributing to layer
    Servlet web application detected
    Writing env.launch/BPL_JVM_THREAD_COUNT.default
  4 application slices
  Image labels:
    org.opencontainers.image.title
    org.opencontainers.image.version
    org.springframework.boot.version
===> EXPORTING
Reusing layer 'paketo-buildpacks/ca-certificates:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:helper'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
Reusing layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
Reusing layer 'paketo-buildpacks/executable-jar:classpath'
Reusing layer 'paketo-buildpacks/spring-boot:helper'
Reusing layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
Reusing 2/5 app layer(s)
Adding 3/5 app layer(s)
Reusing layer 'launcher'
Adding layer 'config'
Reusing layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Adding label 'org.opencontainers.image.title'
Adding label 'org.opencontainers.image.version'
Adding label 'org.springframework.boot.version'
Setting default process type 'web'
Saving vehicle-api...
*** Images (cc08b2e48a7e):
      vehicle-api
Successfully built image vehicle-api

pack build--volume引数にtrusted-certs Service Bindingを指定しました。CA Certificates BuildpackのログにAdded 1 additional CA certificate(s) to system truststoreが出力されていることを確認できます。

Spring Boot Maven Pluginの場合は、pom.xmlに次のように設定して./mvnw spring-boot:build-imageを実行してください。

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <image>
      <bindings>
        <binding>${project.basedir}/bindings/trusted-certs:/platform/bindings/trusted-certs</binding>
      </bindings>
    </image>
  </configuration>                                
</plugin>

GraalVMを使うことでJavaアプリケーションをnative imageにコンパイルでき、JVMに比べてアプリケーションの起動を非常に高速にすることができます。
Spring Nativeを使うことでSpring Bootアプリケーションがnative image化する場合に必要な作業を大幅に省略できるようになります。vehicle-apiをGraal VMでnative image化しましょう。

Paketo GraalVM Buildpackを使うことでLaptop上にGraalVMをインストールすることなく、native imageのコンテナイメージを作成できます。

Spring Nativeを使うために、アプリケーションのpom.xmlを更新します。次のコマンドを実行してください。

curl -s https://start.spring.io/pom.xml \
 -d artifactId=vehicle-api \
 -d baseDir=vehicle-api \
 -d dependencies=web,jdbc,postgresql,actuator,native \
 -d packageName=com.example >pom.xml

spring-nativeライブラリの追加と、paketobuildpacks/builder:tinyを使用するための設定に更新されます。

次のコマンドを実行してください。

./mvnw clean spring-boot:build-image -Dmaven.test.skip=true

次のようなログが出力がされます。GraalVMがダウンロードされ、native imageを作成するための処理が実行されていることがわかります。

実行環境のSpecによって異なりますが、ビルド時間は5分以上になるでしょう。

[INFO] Scanning for projects...
...
[INFO] 
[INFO] >>> spring-boot-maven-plugin:2.5.2:build-image (default-cli) > package @ vehicle-api >>>
[INFO] 
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ vehicle-api ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 2 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ vehicle-api ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /private/tmp/hol/vehicle-api/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ vehicle-api ---
[INFO] Not copying test resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ vehicle-api ---
[INFO] Not compiling test sources
[INFO] 
[INFO] --- spring-aot-maven-plugin:0.10.1:test-generate (test-generate) @ vehicle-api ---
WARNING: Unable to find jar '/private/tmp/hol/vehicle-api/target/test-classes' whilst scanning filesystem
[INFO] Spring Native operating mode: native
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.security.config.annotation.web.configuration.AutowiredWebSecurityConfigurersIgnoreParents it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: io.netty.channel.socket.nio.NioSocketChannel it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.messaging.handler.annotation.MessageMapping it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: javax.transaction.Transactional it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL10Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL95Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL94Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL93Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL92Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL91Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL9Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgresPlusDialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL82Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL81Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.web.reactive.socket.server.upgrade.TomcatRequestUpgradeStrategy it will be skipped
[INFO] Not compiling test sources
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 6 resources
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ vehicle-api ---
[INFO] Tests are skipped.
[INFO] 
[INFO] --- spring-aot-maven-plugin:0.10.1:generate (generate) @ vehicle-api ---
[INFO] Spring Native operating mode: native
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.security.config.annotation.web.configuration.AutowiredWebSecurityConfigurersIgnoreParents it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: io.netty.channel.socket.nio.NioSocketChannel it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.messaging.handler.annotation.MessageMapping it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: javax.transaction.Transactional it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL10Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL95Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL94Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL93Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL92Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL91Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL9Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgresPlusDialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL82Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.hibernate.dialect.PostgreSQL81Dialect it will be skipped
[WARNING] Failed verification check: this type was requested to be added to configuration but is not resolvable: org.springframework.web.reactive.socket.server.upgrade.TomcatRequestUpgradeStrategy it will be skipped
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 24 source files to /private/tmp/hol/vehicle-api/target/classes
[INFO] /private/tmp/hol/vehicle-api/target/generated-sources/spring-aot/src/main/java/org/springframework/aot/StaticSpringFactories.java: ????????????????API???????????????????
[INFO] /private/tmp/hol/vehicle-api/target/generated-sources/spring-aot/src/main/java/org/springframework/aot/StaticSpringFactories.java: ????-Xlint:deprecation???????????????????????
[INFO] /private/tmp/hol/vehicle-api/target/generated-sources/spring-aot/src/main/java/org/springframework/aot/StaticSpringFactories.java: /private/tmp/hol/vehicle-api/target/generated-sources/spring-aot/src/main/java/org/springframework/aot/StaticSpringFactories.java???????????????????????
[INFO] /private/tmp/hol/vehicle-api/target/generated-sources/spring-aot/src/main/java/org/springframework/aot/StaticSpringFactories.java: ????-Xlint:unchecked???????????????????????
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 6 resources
[INFO] 
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ vehicle-api ---
[INFO] Building jar: /private/tmp/hol/vehicle-api/target/vehicle-api-0.0.1-SNAPSHOT.jar
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.5.2:repackage (repackage) @ vehicle-api ---
[INFO] Replacing main artifact with repackaged archive
[INFO] 
[INFO] <<< spring-boot-maven-plugin:2.5.2:build-image (default-cli) < package @ vehicle-api <<<
[INFO] 
[INFO] 
[INFO] --- spring-boot-maven-plugin:2.5.2:build-image (default-cli) @ vehicle-api ---
[INFO] Building image 'docker.io/library/vehicle-api:0.0.1-SNAPSHOT'
[INFO] 
[INFO]  > Pulling builder image 'docker.io/paketobuildpacks/builder:tiny' 100%
[INFO]  > Pulled builder image 'paketobuildpacks/builder@sha256:68e1e2ba37188e5f003904ae64382c8f8ead45b304943ec19f2b9e2b44255338'
[INFO]  > Pulling run image 'docker.io/paketobuildpacks/run:tiny-cnb' 100%
[INFO]  > Pulled run image 'paketobuildpacks/run@sha256:23752df63300f9144d13a041c27558bebcd82ed809cc0a19948ed4b5e7df4703'
[INFO]  > Executing lifecycle version v0.11.3
[INFO]  > Using build cache volume 'pack-cache-13a8f10fbd06.build'
[INFO] 
[INFO]  > Running creator
[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     5 of 12 buildpacks participating
[INFO]     [creator]     paketo-buildpacks/ca-certificates 2.3.2
[INFO]     [creator]     paketo-buildpacks/graalvm         6.2.2
[INFO]     [creator]     paketo-buildpacks/executable-jar  5.1.2
[INFO]     [creator]     paketo-buildpacks/spring-boot     4.4.2
[INFO]     [creator]     paketo-buildpacks/native-image    4.1.2
[INFO]     [creator]     ===> ANALYZING
[INFO]     [creator]     Restoring metadata for "paketo-buildpacks/ca-certificates:helper" from app image
[INFO]     [creator]     ===> RESTORING
[INFO]     [creator]     ===> BUILDING
[INFO]     [creator]     
[INFO]     [creator]     Paketo CA Certificates Buildpack 2.3.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/ca-certificates
[INFO]     [creator]       Launch Helper: Reusing cached layer
[INFO]     [creator]     
[INFO]     [creator]     Paketo GraalVM Buildpack 6.2.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/graalvm
[INFO]     [creator]       Build Configuration:
[INFO]     [creator]         $BP_JVM_VERSION              11.*            the Java version
[INFO]     [creator]       Launch Configuration:
[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]       GraalVM JDK 11.0.11: Contributing to layer
[INFO]     [creator]         Downloading from https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/graalvm-ce-java11-linux-amd64-21.1.0.tar.gz
[INFO]     [creator]         Verifying checksum
[INFO]     [creator]         Expanding to /layers/paketo-buildpacks_graalvm/jdk
[INFO]     [creator]         Adding 129 container CA certificates to JVM truststore
[INFO]     [creator]       GraalVM Native Image Substrate VM 11.0.11
[INFO]     [creator]         Downloading from https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.1.0/native-image-installable-svm-java11-linux-amd64-21.1.0.jar
[INFO]     [creator]         Verifying checksum
[INFO]     [creator]         Installing substrate VM
[INFO]     [creator]     Processing Component archive: /tmp/1ab725616fede21ad5ae900d00ec6d45753db6f6f4fe51a836d538c79d79614a/native-image-installable-svm-java11-linux-amd64-21.1.0.jar
[INFO]     [creator]     Installing new component: Native Image (org.graalvm.native-image, version 21.1.0)
[INFO]     [creator]         Writing env.build/JAVA_HOME.override
[INFO]     [creator]         Writing env.build/JDK_HOME.override
[INFO]     [creator]     
[INFO]     [creator]     Paketo Executable JAR Buildpack 5.1.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/executable-jar
[INFO]     [creator]       Class Path: Contributing to layer
[INFO]     [creator]         Writing env.build/CLASSPATH.delim
[INFO]     [creator]         Writing env.build/CLASSPATH.prepend
[INFO]     [creator]     
[INFO]     [creator]     Paketo Spring Boot Buildpack 4.4.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/spring-boot
[INFO]     [creator]       Class Path: Contributing to layer
[INFO]     [creator]         Writing env.build/CLASSPATH.append
[INFO]     [creator]         Writing env.build/CLASSPATH.delim
[INFO]     [creator]       Image labels:
[INFO]     [creator]         org.opencontainers.image.title
[INFO]     [creator]         org.opencontainers.image.version
[INFO]     [creator]         org.springframework.boot.version
[INFO]     [creator]     
[INFO]     [creator]     Paketo Native Image Buildpack 4.1.2
[INFO]     [creator]       https://github.com/paketo-buildpacks/native-image
[INFO]     [creator]       Build Configuration:
[INFO]     [creator]         $BP_NATIVE_IMAGE                  true  enable native image build
[INFO]     [creator]         $BP_NATIVE_IMAGE_BUILD_ARGUMENTS        arguments to pass to the native-image command
[INFO]     [creator]       Native Image: Contributing to layer
[INFO]     [creator]         GraalVM 21.1.0 Java 11 CE (Java Version 11.0.11+8-jvmci-21.1-b05)
[INFO]     [creator]         Executing native-image -H:+StaticExecutableWithDynamicLibC -H:Name=/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication -cp /workspace:/workspace/BOOT-INF/classes:/workspace/BOOT-INF/lib/spring-boot-2.5.2.jar:/workspace/BOOT-INF/lib/spring-boot-autoconfigure-2.5.2.jar:/workspace/BOOT-INF/lib/logback-classic-1.2.3.jar:/workspace/BOOT-INF/lib/logback-core-1.2.3.jar:/workspace/BOOT-INF/lib/log4j-to-slf4j-2.14.1.jar:/workspace/BOOT-INF/lib/log4j-api-2.14.1.jar:/workspace/BOOT-INF/lib/jul-to-slf4j-1.7.31.jar:/workspace/BOOT-INF/lib/jakarta.annotation-api-1.3.5.jar:/workspace/BOOT-INF/lib/snakeyaml-1.28.jar:/workspace/BOOT-INF/lib/spring-boot-actuator-autoconfigure-2.5.2.jar:/workspace/BOOT-INF/lib/spring-boot-actuator-2.5.2.jar:/workspace/BOOT-INF/lib/jackson-databind-2.12.3.jar:/workspace/BOOT-INF/lib/jackson-annotations-2.12.3.jar:/workspace/BOOT-INF/lib/jackson-core-2.12.3.jar:/workspace/BOOT-INF/lib/jackson-datatype-jsr310-2.12.3.jar:/workspace/BOOT-INF/lib/micrometer-core-1.7.1.jar:/workspace/BOOT-INF/lib/HdrHistogram-2.1.12.jar:/workspace/BOOT-INF/lib/LatencyUtils-2.0.3.jar:/workspace/BOOT-INF/lib/HikariCP-4.0.3.jar:/workspace/BOOT-INF/lib/slf4j-api-1.7.31.jar:/workspace/BOOT-INF/lib/spring-jdbc-5.3.8.jar:/workspace/BOOT-INF/lib/spring-beans-5.3.8.jar:/workspace/BOOT-INF/lib/spring-tx-5.3.8.jar:/workspace/BOOT-INF/lib/jackson-datatype-jdk8-2.12.3.jar:/workspace/BOOT-INF/lib/jackson-module-parameter-names-2.12.3.jar:/workspace/BOOT-INF/lib/tomcat-embed-core-9.0.48.jar:/workspace/BOOT-INF/lib/tomcat-embed-el-9.0.48.jar:/workspace/BOOT-INF/lib/tomcat-embed-websocket-9.0.48.jar:/workspace/BOOT-INF/lib/spring-web-5.3.8.jar:/workspace/BOOT-INF/lib/spring-webmvc-5.3.8.jar:/workspace/BOOT-INF/lib/spring-aop-5.3.8.jar:/workspace/BOOT-INF/lib/spring-context-5.3.8.jar:/workspace/BOOT-INF/lib/spring-expression-5.3.8.jar:/workspace/BOOT-INF/lib/spring-native-0.10.1.jar:/workspace/BOOT-INF/lib/postgresql-42.2.22.jar:/workspace/BOOT-INF/lib/checker-qual-3.5.0.jar:/workspace/BOOT-INF/lib/spring-core-5.3.8.jar:/workspace/BOOT-INF/lib/spring-jcl-5.3.8.jar:/workspace/BOOT-INF/lib/spring-boot-jarmode-layertools-2.5.2.jar com.example.VehicleApiApplication
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]    classlist:   5,157.88 ms,  0.94 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]        (cap):     484.32 ms,  0.94 GB
[INFO]     [creator]     WARNING: Method com.sun.management.OperatingSystemMXBean.getCpuLoad() not found.
...
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]     (clinit):   1,767.76 ms,  2.88 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]   (typeflow):  48,849.19 ms,  2.88 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]    (objects):  69,331.31 ms,  2.88 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]   (features):   5,463.47 ms,  2.88 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]     analysis: 128,848.89 ms,  2.88 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]     universe:   3,442.48 ms,  2.88 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]      (parse):  17,018.72 ms,  3.54 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]     (inline):  16,105.52 ms,  4.66 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]    (compile):  57,093.11 ms,  5.02 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]      compile:  94,370.96 ms,  5.02 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]        image:  11,014.74 ms,  4.75 GB
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]        write:   1,153.88 ms,  4.75 GB
[INFO]     [creator]     # Printing build artifacts to: com.example.VehicleApiApplication.build_artifacts.txt
[INFO]     [creator]     [/layers/paketo-buildpacks_native-image/native-image/com.example.VehicleApiApplication:172]      [total]: 248,010.89 ms,  4.75 GB
[INFO]     [creator]       Removing bytecode
[INFO]     [creator]       Process types:
[INFO]     [creator]         native-image: /workspace/com.example.VehicleApiApplication (direct)
[INFO]     [creator]         task:         /workspace/com.example.VehicleApiApplication (direct)
[INFO]     [creator]         web:          /workspace/com.example.VehicleApiApplication (direct)
[INFO]     [creator]     ===> EXPORTING
[INFO]     [creator]     Reusing layer 'paketo-buildpacks/ca-certificates:helper'
[INFO]     [creator]     Adding 1/1 app layer(s)
[INFO]     [creator]     Reusing layer 'launcher'
[INFO]     [creator]     Adding layer 'config'
[INFO]     [creator]     Reusing layer '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/vehicle-api:0.0.1-SNAPSHOT...
[INFO]     [creator]     *** Images (d4d202bb227d):
[INFO]     [creator]           docker.io/library/vehicle-api:0.0.1-SNAPSHOT
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/graalvm:jdk'
[INFO]     [creator]     Adding cache layer 'paketo-buildpacks/native-image:native-image'
[INFO] 
[INFO] Successfully built image 'docker.io/library/vehicle-api:0.0.1-SNAPSHOT'
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  04:59 min
[INFO] Finished at: 2021-07-01T22:42:50+09:00
[INFO] ------------------------------------------------------------------------

次のコマンドを実行して、作成したイメージを確認してください。paketobuildpacks/builder:tiny Builderはベースイメージとして最小構成のUbuntuを使用するため、JVMを使ったイメージよりもサイズが小さくなります。

$ docker images | grep vehicle
vehicle-api                     0.0.1-SNAPSHOT          d4d202bb227d   41 years ago   127MB
vehicle-api                     16                      6cc5e40ea762   41 years ago   291MB
vehicle-api                     latest                  048a01061034   41 years ago   266MB

次のコマンドを実行してください。native imageはJVMと比べて消費メモリも小さいので、ここではメモリサイズを150MBにします。

docker run --rm \
 -p 8080:8080 \
 -m 150m \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。非常に高速に起動していることを確認してください。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-07-01 13:54:39.317  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication using Java 11.0.11 on 6d72f004f7ca with PID 1 (/workspace/com.example.VehicleApiApplication started by cnb in /workspace)
2021-07-01 13:54:39.317  INFO 1 --- [           main] com.example.VehicleApiApplication        : No active profile set, falling back to default profiles: default
2021-07-01 13:54:39.383  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
Jul 01, 2021 1:54:39 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Jul 01, 2021 1:54:39 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Jul 01, 2021 1:54:39 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/9.0.48]
Jul 01, 2021 1:54:39 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring embedded WebApplicationContext
2021-07-01 13:54:39.385  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 67 ms
2021-07-01 13:54:39.388  WARN 1 --- [           main] i.m.c.i.binder.jvm.JvmGcMetrics          : GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM
2021-07-01 13:54:39.407  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-07-01 13:54:39.446  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-07-01 13:54:39.503  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'
Jul 01, 2021 1:54:39 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
2021-07-01 13:54:39.510  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-07-01 13:54:39.512  INFO 1 --- [           main] com.example.VehicleApiApplication        : Started VehicleApiApplication in 0.204 seconds (JVM running for 0.207)

次はPostgreSQLに対してTLSで通信します。


Buildpackでnative imageを作る場合、現時点ではこれまで紹介してきたBuildpackの機能やService Bindingが使えません。CA証明書の設定はPostgreSQL JDBC Driverの機能を使用しします。sslFactoryorg.postgresql.ssl.LibPQFactory(デフォルト)に変更し、$HOME/.postgreql/root.crtに配置します。

次のコマンドを実行してください。

docker run --rm \
 -p 8080:8080 \
 -m 150m \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 -e SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLMODE=verify-full \
 -e SPRING_DATASOURCE_HIKARI_DATASOURCEPROPERTIES_SSLFACTORY=org.postgresql.ssl.LibPQFactory \
 -v ${PWD}/certs/root.crt:/home/cnb/.postgresql/root.crt \
 docker.io/library/vehicle-api:0.0.1-SNAPSHOT

次のようなログが出力されます。

2021-07-01 15:30:55.175  INFO 1 --- [           main] o.s.nativex.NativeListener               : This application is bootstrapped with code generated with Spring AOT

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

2021-07-01 15:30:55.176  INFO 1 --- [           main] com.example.VehicleApiApplication        : Starting VehicleApiApplication using Java 11.0.11 on 7569501cc780 with PID 1 (/workspace/com.example.VehicleApiApplication started by cnb in /workspace)
2021-07-01 15:30:55.176  INFO 1 --- [           main] com.example.VehicleApiApplication        : No active profile set, falling back to default profiles: default
2021-07-01 15:30:55.244  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
Jul 01, 2021 3:30:55 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Jul 01, 2021 3:30:55 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Jul 01, 2021 3:30:55 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/9.0.48]
Jul 01, 2021 3:30:55 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring embedded WebApplicationContext
2021-07-01 15:30:55.246  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 69 ms
2021-07-01 15:30:55.249  WARN 1 --- [           main] i.m.c.i.binder.jvm.JvmGcMetrics          : GC notifications will not be available because MemoryPoolMXBeans are not provided by the JVM
2021-07-01 15:30:55.269  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-07-01 15:30:55.304  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-07-01 15:30:55.349  INFO 1 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 1 endpoint(s) beneath base path '/actuator'
Jul 01, 2021 3:30:55 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
2021-07-01 15:30:55.356  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-07-01 15:30:55.358  INFO 1 --- [           main] com.example.VehicleApiApplication        : Started VehicleApiApplication in 0.191 seconds (JVM running for 0.193)

アプリケーションにアクセスできることを確認してください。

Paketo BuildpacksのBuilderでJavaアプリのコンテナイメージを作る場合はデフォルトでPaketo BellSoft Liberica Buildpackが使われ、Bellsoft LibericaのJREが組み込まれます。その他のDistributionを使う方法を紹介します。

Adopt OpenJDK

Paketo AdoptOpenJDK Buildpackを使うことでAdopt OpenJDKを使うことができます。Builderから提供されるBuildpack一式を使わず、使用するBuildpackをpack CLIに全て渡すことで差し替えることができます。

次のコマンドを実行してください。

pack build vehicle-api:adopt-openjdk \
 --builder paketobuildpacks/builder:base \
 --buildpack paketo-buildpacks/ca-certificates@2.3.2 \
 --buildpack paketo-buildpacks/adopt-openjdk \
 --buildpack paketo-buildpacks/maven \
 --buildpack paketo-buildpacks/executable-jar \
 --buildpack paketo-buildpacks/spring-boot

次のようなログが出力されます。Adopt OpenJDKがダウンロードされていることが確認できます。

base: Pulling from paketobuildpacks/builder
Digest: sha256:7f2b74ba47b235e5a30393ad83c3e3c12ff5229c190faffd67d842e064b7743e
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:aa9a8a0a4bb607422d3ff59923638f2a6cf3d1e6b404aee9ae027127ea88a5c1
Status: Image is up to date for paketobuildpacks/run:base-cnb
gcr.io/paketo-buildpacks/adopt-openjdk@sha256:9fd288108e7b34bea89986446966f885bbf08e7dd80d07277ff3b229549fb6da: Pulling from paketo-buildpacks/adopt-openjdk
Digest: sha256:9fd288108e7b34bea89986446966f885bbf08e7dd80d07277ff3b229549fb6da
Status: Image is up to date for gcr.io/paketo-buildpacks/adopt-openjdk@sha256:9fd288108e7b34bea89986446966f885bbf08e7dd80d07277ff3b229549fb6da
===> DETECTING
paketo-buildpacks/ca-certificates 2.1.0
paketo-buildpacks/adopt-openjdk   7.1.0
paketo-buildpacks/maven           5.0.0
paketo-buildpacks/executable-jar  5.0.0
paketo-buildpacks/spring-boot     4.2.0
===> ANALYZING
Previous image with name "vehicle-api:adopt-openjdk" not found
===> RESTORING
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper

Paketo AdoptOpenJDK Buildpack 8.0.2
  https://github.com/paketo-buildpacks/adopt-openjdk
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  AdoptOpenJDK JDK 11.0.11: Contributing to layer
    Downloading from https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11%2B9/OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_adopt-openjdk/jdk
    Adding 129 container CA certificates to JVM truststore
    Writing env.build/JAVA_HOME.override
    Writing env.build/JDK_HOME.override
  AdoptOpenJDK JRE 11.0.11: Contributing to layer
    Downloading from https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.11%2B9/OpenJDK11U-jre_x64_linux_hotspot_11.0.11_9.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_adopt-openjdk/jre
    Adding 129 container CA certificates to JVM truststore
    Writing env.launch/BPI_APPLICATION_PATH.default
    Writing env.launch/BPI_JVM_CACERTS.default
    Writing env.launch/BPI_JVM_CLASS_COUNT.default
    Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
    Writing env.launch/JAVA_HOME.default
    Writing env.launch/MALLOC_ARENA_MAX.default
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/active-processor-count
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/java-opts
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/link-local-dns
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/memory-calculator
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/openssl-certificate-loader
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/security-providers-configurer
    Creating /layers/paketo-buildpacks_adopt-openjdk/helper/exec.d/security-providers-classpath-9
  JVMKill Agent 1.16.0: Reusing cached layer
  Java Security Properties: Contributing to layer
    Writing env.launch/JAVA_SECURITY_PROPERTIES.default
    Writing env.launch/JAVA_TOOL_OPTIONS.append
    Writing env.launch/JAVA_TOOL_OPTIONS.delim

...
Saving vehicle-api:adopt-openjdk...
*** Images (8460e75ee0fc):
      vehicle-api:adopt-openjdk
Adding cache layer 'paketo-buildpacks/adopt-openjdk:jdk'
Adding cache layer 'paketo-buildpacks/maven:application'
Adding cache layer 'paketo-buildpacks/maven:cache'
Successfully built image vehicle-api:adopt-openjdk

次のコマンドを実行してください。

docker run --rm -p 8080:8080 \
 -m 1g \
 -e MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE="*" \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api:adopt-openjdk

次のようなログが出力されます。Adopt OpenJDKが使用されていることがわかります。

Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx448954K -XX:MaxMetaspaceSize=87621K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13056, Headroom: 0%)
Adding 129 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_adopt-openjdk/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_adopt-openjdk/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx448954K -XX:MaxMetaspaceSize=87621K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
...

次のコマンドを実行してください。実際に稼働しているJavaのベンダー名やバージョンを確認できます。

$ curl -s http://localhost:8080/actuator/env | jq ".propertySources[] | select(.name == \"systemProperties\").properties[\"java.vendor.version\"]"
{
  "value": "AdoptOpenJDK-11.0.11+9"
}

$ curl -s http://localhost:8080/actuator/env | jq ".propertySources[] | select(.name == \"systemProperties\").properties[\"java.vm.version\"]"
{
  "value": "11.0.11+9"
}

Azul Zulu

Paketo Azul Zulu Buildpackを使うことでAzul Zuluを使うことができます。Adopt OpenJDKの場合と同じように次のコマンドで利用可能です。

pack build vehicle-api:azul \
 --builder paketobuildpacks/builder:base \
 --buildpack paketo-buildpacks/ca-certificates \
 --buildpack paketo-buildpacks/azul-zulu \
 --buildpack paketo-buildpacks/maven \
 --buildpack paketo-buildpacks/executable-jar \
 --buildpack paketo-buildpacks/spring-boot

Azul Zuluの場合は、Buildpackの集合であるMeta BuildpackのPaketo Azure Java Buildpackに含まれています。このBuildpackはAzure上で使うことを想定したBuildpack一式を定義してうため、Buildpackを一つ一つ指定する必要がなくなります。次のコマンドを実行してください。

次のコマンドを実行してください。

pack build vehicle-api:azul \
 --builder paketobuildpacks/builder:base \
 --buildpack paketo-buildpacks/java-azure

次のようなログが出力されます。Azul Zuluがダウンロードされていることがわかります。

base: Pulling from paketobuildpacks/builder
Digest: sha256:d2b4295b20506411a6f97273e40a8326d31f058fc4dc42af44bc7970734de9cd
Status: Image is up to date for paketobuildpacks/builder:base
base-cnb: Pulling from paketobuildpacks/run
Digest: sha256:47481aa496959d425f4a7d306875c0e17babe3981cc98a406b177f1491e5a27d
Status: Image is up to date for paketobuildpacks/run:base-cnb
gcr.io/paketo-buildpacks/java-azure@sha256:25d2658e84933d9df6b21404e8c585b1e35d55c6444d0dad86abffca2be16504: Pulling from paketo-buildpacks/java-azure
Digest: sha256:25d2658e84933d9df6b21404e8c585b1e35d55c6444d0dad86abffca2be16504
Status: Downloaded newer image for gcr.io/paketo-buildpacks/java-azure@sha256:25d2658e84933d9df6b21404e8c585b1e35d55c6444d0dad86abffca2be16504

===> DETECTING
7 of 17 buildpacks participating
paketo-buildpacks/ca-certificates 2.3.2
paketo-buildpacks/azul-zulu       8.2.2
paketo-buildpacks/maven           5.3.2
paketo-buildpacks/executable-jar  5.1.2
paketo-buildpacks/apache-tomcat   5.5.2
paketo-buildpacks/dist-zip        4.1.2
paketo-buildpacks/spring-boot     4.4.2
===> ANALYZING
...
===> RESTORING
===> BUILDING

Paketo CA Certificates Buildpack 2.3.2
  https://github.com/paketo-buildpacks/ca-certificates
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_ca-certificates/helper/exec.d/ca-certificates-helper

Paketo Azul Zulu Buildpack 8.2.2
  https://github.com/paketo-buildpacks/azul-zulu
  Build Configuration:
    $BP_JVM_VERSION              11              the Java version
  Launch Configuration:
    $BPL_JVM_HEAD_ROOM           0               the headroom in memory calculation
    $BPL_JVM_LOADED_CLASS_COUNT  35% of classes  the number of loaded classes in memory calculation
    $BPL_JVM_THREAD_COUNT        250             the number of threads in memory calculation
    $JAVA_TOOL_OPTIONS                           the JVM launch flags
  Azul Zulu JDK 11.0.11: Contributing to layer
    Downloading from https://repos.azul.com/azure-only/zulu/packages/zulu-11/11.0.11/zulu-11-azure-jdk_11.48.21-11.0.11-linux_x64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_azul-zulu/jdk
    Adding 129 container CA certificates to JVM truststore
    Writing env.build/JAVA_HOME.override
    Writing env.build/JDK_HOME.override
  Azul Zulu JRE 11.0.11: Contributing to layer
    Downloading from https://repos.azul.com/azure-only/zulu/packages/zulu-11/11.0.11/zulu-11-azure-jre_11.48.21-11.0.11-linux_x64.tar.gz
    Verifying checksum
    Expanding to /layers/paketo-buildpacks_azul-zulu/jre
    Adding 129 container CA certificates to JVM truststore
    Writing env.launch/BPI_APPLICATION_PATH.default
    Writing env.launch/BPI_JVM_CACERTS.default
    Writing env.launch/BPI_JVM_CLASS_COUNT.default
    Writing env.launch/BPI_JVM_SECURITY_PROVIDERS.default
    Writing env.launch/JAVA_HOME.default
    Writing env.launch/MALLOC_ARENA_MAX.default
  Launch Helper: Contributing to layer
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/active-processor-count
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/java-opts
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/link-local-dns
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/memory-calculator
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/openssl-certificate-loader
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/security-providers-configurer
    Creating /layers/paketo-buildpacks_azul-zulu/helper/exec.d/security-providers-classpath-9
  JVMKill Agent 1.16.0: Reusing cached layer
  Java Security Properties: Contributing to layer
    Writing env.launch/JAVA_SECURITY_PROPERTIES.default
    Writing env.launch/JAVA_TOOL_OPTIONS.append
    Writing env.launch/JAVA_TOOL_OPTIONS.delim

...

Saving vehicle-api:azul...
*** Images (22a250b93d3f):
      vehicle-api:azul
Adding cache layer 'paketo-buildpacks/azul-zulu:jdk'
Adding cache layer 'paketo-buildpacks/maven:application'
Adding cache layer 'paketo-buildpacks/maven:cache'
Successfully built image vehicle-api:azul

次のコマンドを実行してください。

docker run --rm -p 8080:8080 \
 -m 1g \
 -e MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE="*" \
 -e SPRING_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/vehicle \
 vehicle-api:azul

次のようなログが出力されます。Azul Zuluがダウンロードされていることがわかります。

Setting Active Processor Count to 4
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx447509K -XX:MaxMetaspaceSize=89066K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 13311, Headroom: 0%)
Adding 129 container CA certificates to JVM truststore
Spring Cloud Bindings Enabled
Picked up JAVA_TOOL_OPTIONS: -Djava.security.properties=/layers/paketo-buildpacks_azul-zulu/java-security-properties/java-security.properties -agentpath:/layers/paketo-buildpacks_azul-zulu/jvmkill/jvmkill-1.16.0-RELEASE.so=printHeapHistogram=1 -XX:ActiveProcessorCount=4 -XX:MaxDirectMemorySize=10M -Xmx447509K -XX:MaxMetaspaceSize=89066K -XX:ReservedCodeCacheSize=240M -Xss1M -Dorg.springframework.cloud.bindings.boot.enable=true
...

次のコマンドを実行してください。実際に稼働しているJavaのベンダー名やバージョンを確認できます。

$ curl -s http://localhost:8080/actuator/env | jq ".propertySources[] | select(.name == \"systemProperties\").properties[\"java.vendor\"]"    
{
  "value": "Azul Systems, Inc."
}

$ curl -s http://localhost:8080/actuator/env | jq ".propertySources[] | select(.name == \"systemProperties\").properties[\"java.vm.version\"]"
{
  "value": "11.0.11+9-LTS"
}


TBD