これは、なにをしたくて書いたもの?
前に、Spring BootのCloud Native Buildpacksサポートで遊んでみました。
Spring BootのCloud Native Buildpacksサポートを試す - CLOVER🍀
今回は、Cloud Native Buildpacksそのものに焦点を当てて見てみようかな、と。
Cloud Native Buildpacks · Cloud Native Buildpacks
ビルドパック(Buildpacks)がCloud Native Computing Foundationのプロジェクトに。HerokuやCloud Foundryなどが開発 - Publickey
Cloud Native Buildpackで めんどうなコンテナイメージ作成を 自動化しよう - Speaker Deck
JavaアプリケーションのコンテナイメージをCloud Native Buildpacksで作ってみようかなと思います。
Cloud Native Buildpacks
Cloud Native Buildpacksは、アプリケーションをどのクラウドでも実行できるイメージに変換するツールだそうです。
Cloud Native Buildpacks transform your application source code into images that can run on any cloud.
Cloud Native Buildpacks · Cloud Native Buildpacks
イメージとは、OCIイメージを指します。
2011年にHerokuが始め、その後Pivotalでも採用し、2018年にCloud Native Buildpacksになったようです(Pivotal+Heroku)。

こちらに、特徴が書いてあります。
Features · Cloud Native Buildpacks
- Advanced Caching
- パフォーマンスを向上させるためのキャッシュ
- Bill-of-Materials
- アプリケーションのイメージのための知見
- Modular / Pluggable
- 複数のbuildpacksの利用
- Multi-language
- 多くのプログラミング言語のサポート
- Multi-process
- イメージは、オペレーションモードによって複数のエントリーポイントを持つことができる
- Minimal app image
- イメージには、必要なものだけが含まれる
- Rebasing
- 再構築なしで、ベースイメージを即座に更新できる
- Reproducibility
- ビルドを再実行すると、同じアプリケーションイメージのダイジェストを再現する
- Reusability
- コミュニティによってメンテナンスされている、production-readyなbuildpacksを活用できる
ドキュメントは、こちら。
Getting Started · Cloud Native Buildpacks
Cloud Native Buildpacksのコンセプト
こちらに、Cloud Native Buildpacksのコンセプトが書かれているので、少し見てみましょう。
Concepts · Cloud Native Buildpacks
5つのコンポーネントと、2つの操作がコンセプトのようです。
- Components
- Builder
- Buildpack
- Lifecycle
- Platform
- Stack
- Operations
- Build
- Rebase
Components
コンポーネントについては、以下に説明があります。
Components · Cloud Native Buildpacks
- Builder · Cloud Native Buildpacks
- Buildpack、Lifecycleの実装、PlatformがLifecycleの実行時に使用する可能性があるビルド時の環境、アプリケーションのビルドに関する情報をバンドルしたイメージのこと
- Buildpacks、Lifecycle、Stackで構成される
- Buildpack · Cloud Native Buildpacks
- アプリケーションのソースコードを検査し、アプリケーションのビルドと実行の計画を作る作業単位のこと
- 主に、
buildpack.toml、bin/detect、bin/buildの3ファイルで構成される - 以下の2つのフェーズがある
- Lifecycle · Cloud Native Buildpacks
- Buildpackの実行を調整し、アーティファクトを最終的なアプリケーションイメージに組み上げる
- 以下の4つのフェーズのこと
- Detection
- Analysis
- Build
- Export
- Platform · Cloud Native Buildpacks
- Stack · Cloud Native Buildpacks
- BuildpackのLifecycleに、ビルド時および実行時の環境をイメージとして提供する
- Builderの設定ファイル内で、
[stack]として設定し、ビルドや実行時のイメージを指定する
Operations
操作については、以下に説明があります。
Operations · Cloud Native Buildpacks
- Build · Cloud Native Buildpacks
- アプリケーションのソースコードに対して、1つまたは複数のbuildpacksを実行し、実行可能なOCIイメージを作成するプロセスのこと
- Rebase · Cloud Native Buildpacks
- アプリケーション開発者または運用者は、Stackの実行イメージが変更された時に、アプリケーションイメージを素早く更新できる
つまり?
すごいざっくりと言うと、イメージ生成までのプロセスは
- Buildpackというビルド、実行に関する2つのイメージを持ったコンポーネントがあり、かつ言語検出機能を持っている
- 複数のBuildpackの中から、適切なもの(最初に選ばれたもの)でアプリケーションイメージのビルドを行う
- ビルドに使うイメージと、実行に使うイメージは分かれている
という感じですかね。
アプリケーションのビルドからイメージ作成まで、プロセスが決められているところが特徴的ですね。
とまあ、ここまで見てきたところで、実際に試してみましょう。
環境
今回の環境は、こちら。
$ uname -srvmpio Linux 5.4.0-52-generic #57-Ubuntu SMP Thu Oct 15 10:57:00 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.1 LTS Release: 20.04 Codename: focal
Dockerも必要になります。
$ docker version Client: Docker Engine - Community Version: 19.03.13 API version: 1.40 Go version: go1.13.15 Git commit: 4484c46d9d Built: Wed Sep 16 17:02:52 2020 OS/Arch: linux/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 19.03.13 API version: 1.40 (minimum version 1.12) Go version: go1.13.15 Git commit: 4484c46d9d Built: Wed Sep 16 17:01:20 2020 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.3.7 GitCommit: 8fba4e9a7d01810a393d5d25a3621dc101981175 runc: Version: 1.0.0-rc10 GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd docker-init: Version: 0.18.0 GitCommit: fec3683
Getting Startedで始めてみる
ドキュメントのGetting Startedをまずは眺めてみます。
An App's Brief Journey from Source to Image · Cloud Native Buildpacks
どうやら、packというツールを最初にインストールする必要があるようです。
packをインストールする
ドキュメントに従い、packをインストールします。
Pack · Cloud Native Buildpacks
Linux向けのマニュアルインストール手順を実行。
$ (curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.14.2/pack-v0.14.2-linux.tgz" | sudo tar -C /usr/local/bin/ --no-same-owner -xzv pack)
インストールされました。
$ which pack /usr/local/bin/pack $ pack --version 0.14.2+git-0fd189d.build-1450
ヘルプを確認。
$ pack -h
CLI for building apps using Cloud Native Buildpacks
Usage:
pack [command]
Available Commands:
build Generate app image from source code
rebase Rebase app image with latest run image
inspect-image Show information about a built image
inspect-buildpack Show information about a buildpack
set-run-image-mirrors Set mirrors to other repositories for a given run image
set-default-builder Set default builder used by other commands
inspect-builder Show information about a builder
suggest-builders Display list of recommended builders
trust-builder Trust builder
untrust-builder Stop trusting builder
list-trusted-builders List Trusted Builders
create-builder Create builder image
package-buildpack Package buildpack in OCI format.
suggest-stacks Display list of recommended stacks
version Show current 'pack' version
report Display useful information for reporting an issue
completion Outputs completion script location
help Help about any command
Flags:
-h, --help Help for 'pack'
--no-color Disable color output
-q, --quiet Show less output
--timestamps Enable timestamps in output
-v, --verbose Show more output
--version Show current 'pack' version
Use "pack [command] --help" for more information about a command.
bashのauto-completionがあるようなので、有効にしておきましょう。
. $(pack completion)
その他の情報については、packのドキュメントへ。
pack · Cloud Native Buildpacks
サンプルアプリケーションを作る
Getting Startedを見ているとサンプルアプリケーションが用意されているようなのですが、ここは自分で作成しておきましょう。
Javaで作るので、環境を書いておきましょう。ローカルの情報ですが。
$ java --version openjdk 11.0.9 2020-10-20 OpenJDK Runtime Environment (build 11.0.9+11-Ubuntu-0ubuntu1.20.04) OpenJDK 64-Bit Server VM (build 11.0.9+11-Ubuntu-0ubuntu1.20.04, mixed mode, sharing) $ mvn --version Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 11.0.9, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-52-generic", arch: "amd64", family: "unix"
あとでわかりますが、packを使ったビルドはコンテナ内で行われます。
今回は、Vert.x Webを使い、Maven Shade Pluginも合わせて実行可能JARファイルを作成するMavenプロジェクトを作ります。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>sample-app</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> <version>3.9.4</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>org.littlewings.buildpacks.App</mainClass> </transformer> </transformers> </configuration> </plugin> </plugins> </build> </project>
ソースコード。至ってシンプルです。
src/main/java/org/littlewings/buildpacks/App.java
package org.littlewings.buildpacks; import java.time.LocalDateTime; import java.util.concurrent.TimeUnit; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerResponse; public class App { public static void main(String... args) throws InterruptedException { Vertx vertx = Vertx.vertx(); HttpServer server = vertx.createHttpServer(); try { server.requestHandler(request -> { HttpServerResponse response = request.response(); response.putHeader("Content-Type", "text/plain"); response.end(String.format("[%s] Hello World", LocalDateTime.now())); }); server.listen(8080); System.out.printf("[%s] Server startup.", LocalDateTime.now()); while (true) { TimeUnit.SECONDS.sleep(5L); } } finally { server.close(); vertx.close(); } } }
ビルドして
$ mvn package
動作確認。
$ java -jar target/sample-app-0.0.1-SNAPSHOT.jar [2020-10-31T21:53:02.876200] Server startup.
OKですね。
$ curl localhost:8080 [2020-10-31T21:53:15.596597] Hello World
cleanしておきます。
$ mvn clean
以降、カレントディレクトリはMavenプロジェクトのルートディレクトリとします。
こういう状態ですね。
$ find . -type f ./pom.xml ./src/main/java/org/littlewings/buildpacks/App.java
samplesの手順をマネてみる
Getting Startedに戻って、その手順をそのまま実行してみましょう。
本来は、こちらのサンプルコードを使うようです。
samples/apps/java-maven at main · buildpacks/samples · GitHub
とりあえず、なにも考えずにコマンドをそのまま実行してみます。
$ pack build myapp --builder cnbs/sample-builder:bionic
Javaのバージョンが合わず、エラーになりました。
[builder] [INFO] Changes detected - recompiling the module! [builder] [INFO] Compiling 1 source file to /workspace/target/classes [builder] [INFO] ------------------------------------------------------------------------ [builder] [INFO] BUILD FAILURE [builder] [INFO] ------------------------------------------------------------------------ [builder] [INFO] Total time: 48.397 s [builder] [INFO] Finished at: 2020-10-31T08:03:37Z [builder] [INFO] ------------------------------------------------------------------------ [builder] [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project sample-app: Fatal error compiling: invalid target release: 11 -> [Help 1] [builder] [ERROR] [builder] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [builder] [ERROR] Re-run Maven using the -X switch to enable full debug logging. [builder] [ERROR] [builder] [ERROR] For more information about the errors and possible solutions, please read the following articles: [builder] [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException [builder] ERROR: failed to build: exit status 1 ERROR: failed to build: executing lifecycle. This may be the result of using an untrusted builder: failed with status code: 145
このディレクトリの中のスクリプトを見ると、Java 8を想定していそうな感じです。
https://github.com/buildpacks/samples/tree/main/buildpacks/java-maven
さて、どうしましょう。
Paketo Buildpacks
ここで、Paketo Buildpacksを見てみることにします。
Paketo Buildpacks - Paketo Buildpacks
Paketo Buildpacksは、Cloud FoundryコミュニティによるCloud Native Buildpacksの実装です。
Java、Java Native Image、.NET Core、Node.js、Go、PHP、Ruby、nginxのBuildpacksが提供されています。
ドキュメントは、こちら。
Getting Started - Paketo Buildpacks
Dockerを要求するのは、Paketo Buildpacksでした…。
Builderはfull、base、tinyの3種類があるようです。
pack suggest-buildersで、Paketo BuildpacksのBuilderも確認することができます。
$ pack suggest-builders
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 heroku-18 base image with buildpacks for Ruby, Java, Node.js, Python, Golang, & PHP
Paketo Buildpacks: paketobuildpacks/builder:base Ubuntu bionic base image with buildpacks for Java, NodeJS and Golang
Paketo Buildpacks: paketobuildpacks/builder:full Ubuntu bionic base image with buildpacks for Java, .NET, NodeJS, Golang, PHP, HTTPD and NGINX
Paketo Buildpacks: paketobuildpacks/builder:tiny Tiny base image (bionic build image, distroless run image) with buildpacks for Golang
Tip: Learn more about a specific builder with:
pack inspect-builder <builder-image>
inspect-builderで、どのようなものが含まれているかを確認することもできます。
$ pack inspect-builder paketobuildpacks/builder:base
では、デフォルトのBuilderをJava向けのイメージも含まれているpaketobuildpacks/builder:baseとしましょう。
$ pack set-default-builder paketobuildpacks/builder:base Builder paketobuildpacks/builder:base is now the default builder
これで、pack build時にBuilderを指定しない場合は、paketobuildpacks/builder:baseがBuilderとして使われます。
$ pack build kazuhira/sample-app
buildに続くのは作成するイメージ名なので、タグ付けも好きに行うことができます。
$ pack build kazuhira/sample-app:latest $ pack build kazuhira/sample-app:0.0.1
また、デフォルトのBuilder以外を使いたくなった場合は、--builderで他のBuilderを指定することもできます。
$ pack build kazuhira/sample-app --builder paketobuildpacks/builder:base
Getting Startedの例が、--builderを使っていましたね。
これが、「Operation」の「Build」なわけですねぇ…。
で、ビルドしてみると、初回はBuilderのDockerイメージのダウンロードが行われ
$ pack build kazuhira/sample-app base: Pulling from paketobuildpacks/builder 〜省略〜
Detect、Analyzeが行われ
===> DETECTING 6 of 17 buildpacks participating paketo-buildpacks/bellsoft-liberica 4.0.0 paketo-buildpacks/maven 3.1.1 paketo-buildpacks/executable-jar 3.1.1 paketo-buildpacks/apache-tomcat 2.3.0 paketo-buildpacks/dist-zip 2.2.0 paketo-buildpacks/spring-boot 3.2.1 ===> ANALYZING Previous image with name "kazuhira/sample-app" not found Restoring metadata for "paketo-buildpacks/bellsoft-liberica:jdk" from cache Restoring metadata for "paketo-buildpacks/maven:application" from cache Restoring metadata for "paketo-buildpacks/maven:cache" from cache Restoring metadata for "paketo-buildpacks/maven:maven" from cache ===> 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 Restoring data for "paketo-buildpacks/maven:maven" from cache ===> BUILDING
選択されたBuildpackが次々に現れます。
Paketo BellSoft Liberica Buildpack 4.0.0
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.8: Reusing cached layer
BellSoft Liberica JRE 11.0.8: Contributing to layer
Downloading from https://github.com/bell-sw/Liberica/releases/download/11.0.8+10/bellsoft-jre11.0.8+10-linux-amd64.tar.gz
Verifying checksum
Expanding to /layers/paketo-buildpacks_bellsoft-liberica/jre
Adding 127 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
Writing profile.d/helper
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 Maven Buildpack 3.1.1
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
Apache Maven 3.6.3: Reusing cached layer
Creating cache directory /home/cnb/.m2
Compiled Application: Contributing to layer
Executing mvn -Dmaven.test.skip=true package
Mavenビルドが行われ
[INFO] Scanning for projects... [INFO] [INFO] ---------------------< org.littlewings:sample-app >--------------------- [INFO] Building sample-app 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]---------------------------------
ビルド完了。
[INFO] Replacing original artifact with shaded artifact. [INFO] Replacing /workspace/target/sample-app-0.0.1-SNAPSHOT.jar with /workspace/target/sample-app-0.0.1-SNAPSHOT-shaded.jar [INFO] Dependency-reduced POM written at: /workspace/dependency-reduced-pom.xml [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.057 s [INFO] Finished at: 2020-10-31T13:00:25Z [INFO] ------------------------------------------------------------------------ Removing source code
さらに、もうひとつBuildpackが実行されて完了。
Paketo Executable JAR Buildpack 3.1.1
https://github.com/paketo-buildpacks/executable-jar
Writing env.launch/CLASSPATH.delim
Writing env.launch/CLASSPATH.prepend
Process types:
executable-jar: java org.littlewings.buildpacks.App
task: java org.littlewings.buildpacks.App
web: java org.littlewings.buildpacks.App
===> EXPORTING
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:class-path'
Adding 1/1 app layer(s)
Adding layer 'launcher'
Adding layer 'config'
Adding layer 'process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Setting default process type 'web'
*** Images (2038d8c3b916):
kazuhira/sample-app
Reusing cache layer 'paketo-buildpacks/bellsoft-liberica:jdk'
Adding cache layer 'paketo-buildpacks/maven:application'
Reusing cache layer 'paketo-buildpacks/maven:cache'
Reusing cache layer 'paketo-buildpacks/maven:maven'
Successfully built image kazuhira/sample-app
Dockerイメージができあがりました。
$ docker image ls | grep sample kazuhira/sample-app latest 2038d8c3b916 40 years ago 254MB
実行してみます。
$ docker container run -it --rm --name sample -p 8080:8080 kazuhira/sample-app:latest Setting Active Processor Count to 8 WARNING: Container memory limit unset. Configuring JVM for 1G container. Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx466790K -XX:MaxMetaspaceSize=69785K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 9907, Headroom: 0%) Adding 127 container CA certificates to JVM truststore 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=8 -XX:MaxDirectMemorySize=10M -Xmx466790K -XX:MaxMetaspaceSize=69785K -XX:ReservedCodeCacheSize=240M -Xss1M [2020-10-31T13:07:41.661609] Server startup.
OKですね。
$ curl localhost:8080 [2020-10-31T13:07:52.735419] Hello World
ビルドは、Dockerコンテナ内で行われているようで、初回のMavenビルド時は大量のJARファイルがダウンロードされます。
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom (8.1 kB at 6.0 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom (9.2 kB at 35 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/22/maven-parent-22.pom (30 kB at 83 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/org/apache/apache/11/apache-11.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/apache/11/apache-11.pom (15 kB at 61 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.jar
2回目以降は起こらなくなりますが、これはキャッシュとしてvolumeに保存されているようです。
$ docker volume ls DRIVER VOLUME NAME local pack-cache-387b2463780f.build local pack-cache-387b2463780f.launch
イメージ名を変えると、キャッシュは再利用されなくなるようで
$ pack build kazuhira/sample-app:0.0.1
volumeも別になります。
$ docker volume ls DRIVER VOLUME NAME local pack-cache-24b92486e761.build local pack-cache-24b92486e761.launch local pack-cache-387b2463780f.build local pack-cache-387b2463780f.launch
ここまで出てきたBuildpackを見てみる
今回、Builderとしてはpaketobuildpacks/builder:baseを選択しましたが、この中からJava向けのBuildpackが使われたことになります。
Java Buildpack - Paketo Buildpacks
さらにJava向けのBuildpackはいくつかあり、今回はこの中から選択されたことになります。
Spring Boot用のものもあったりしますね。
これらは、ビルド対象のアプリケーションのファイルの種類から決定(Detect)されます。
ビルド時のログは、こう出力されていました。
Paketo BellSoft Liberica Buildpack 4.0.0 Paketo Maven Buildpack 3.1.1 Paketo Executable JAR Buildpack 3.1.1
というわけで、今回使われたのは以下の3つのBuildpackです。
このうち、Paketo BellSoft Liberica BuildpackはJavaでは必須になっているみたいですね。
ソースコードを見てみると、DetectやBuildはGoで表現されていて面白いです。
https://github.com/paketo-buildpacks/maven/blob/v3.1.1/maven/detect.go
https://github.com/paketo-buildpacks/maven/blob/v3.1.1/maven/build.go
実行コマンド。
https://github.com/paketo-buildpacks/maven/blob/v3.1.1/cmd/main/main.go
こうやって、どのBuildpackを使うかをアプリケーションのソースコードから検出して、ビルドもこの中で行ってしまうのが
Buildpackです、と。
ホスト側に言語環境がなくても、ビルドできるってことになりますね。
PaaSとかでのビルドには、確かに便利そうですね。この仕組み内でビルドできることが強制されますし。
設定を行う
ところで、ビルドには成功しましたが設定方法なども気になるところです。
Buildpackには、環境変数(ビルド時、実行時)、buildpack.yml、Binding、Procfileの4つの設定があるようです。
今回は、環境変数を見てみましょう。
JavaのBuildpackで、ビルド時の設定。
たとえば、Mavenビルドの設定を変えてみましょう。
BP_MAVEN_BUILD_ARGUMENTSを使うと、Mavenに渡すオプションを設定することができます。
pack buildでは、--envで指定します。
$ pack build kazuhira/sample-app --env BP_MAVEN_BUILD_ARGUMENTS='-DskipTests=true package'
結果。
Paketo Maven Buildpack 3.1.1
https://github.com/paketo-buildpacks/maven
Build Configuration:
$BP_MAVEN_BUILD_ARGUMENTS -DskipTests=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
Apache Maven 3.6.3: Reusing cached layer
Creating cache directory /home/cnb/.m2
Compiled Application: Contributing to layer
Executing mvn -DskipTests=true package
この部分に反映されましたね。
Executing mvn -DskipTests=true package
この環境変数の値は、デフォルトでは-Dmaven.test.skip=true packageになっています。
Paketo Maven Buildpack 3.1.1
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
Apache Maven 3.6.3: Reusing cached layer
Creating cache directory /home/cnb/.m2
Compiled Application: Contributing to layer
Executing mvn -Dmaven.test.skip=true package
実行時に関する設定は、こちら。
実行時の引数を指定する
実行するコンテナイメージの引数は、そのままコンテナ起動時に渡せばOKです。
Providing Additional Arguments
たとえば、サンプルアプリケーションを引数を受け取るように修正してみましょう。
src/main/java/org/littlewings/buildpacks/App.java
package org.littlewings.buildpacks; import java.time.LocalDateTime; import java.util.concurrent.TimeUnit; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerResponse; public class App { public static void main(String... args) throws InterruptedException { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } Vertx vertx = Vertx.vertx(); HttpServer server = vertx.createHttpServer(); try { server.requestHandler(request -> { HttpServerResponse response = request.response(); response.putHeader("Content-Type", "text/plain"); response.end(String.format("[%s] Hello World", LocalDateTime.now())); }); server.listen(port); System.out.printf("[%s] Server startup, port = %d%n", LocalDateTime.now(), port); while (true) { TimeUnit.SECONDS.sleep(5L); } } finally { server.close(); vertx.close(); } } }
リッスンポートを指定できるようにしました。
public static void main(String... args) throws InterruptedException { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; }
そして、リッスンポートをログ出力します。
server.listen(port);
System.out.printf("[%s] Server startup, port = %d%n", LocalDateTime.now(), port);
再度ビルド。
$ pack build kazuhira/sample-app
引数を指定しない場合。
$ docker container run -it --rm --name sample kazuhira/sample-app:latest Setting Active Processor Count to 8 WARNING: Container memory limit unset. Configuring JVM for 1G container. Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx466790K -XX:MaxMetaspaceSize=69785K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 9907, Headroom: 0%) Adding 127 container CA certificates to JVM truststore 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=8 -XX:MaxDirectMemorySize=10M -Xmx466790K -XX:MaxMetaspaceSize=69785K -XX:ReservedCodeCacheSize=240M -Xss1M [2020-10-31T13:25:08.819790] Server startup, port = 8080
引数を指定した場合。
$ docker container run -it --rm --name sample kazuhira/sample-app:latest 9000 Setting Active Processor Count to 8 WARNING: Container memory limit unset. Configuring JVM for 1G container. Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -Xmx466790K -XX:MaxMetaspaceSize=69785K -XX:ReservedCodeCacheSize=240M -Xss1M (Total Memory: 1G, Thread Count: 250, Loaded Class Count: 9907, Headroom: 0%) Adding 127 container CA certificates to JVM truststore 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=8 -XX:MaxDirectMemorySize=10M -Xmx466790K -XX:MaxMetaspaceSize=69785K -XX:ReservedCodeCacheSize=240M -Xss1M [2020-10-31T13:26:07.587287] Server startup, port = 9000
反映されましたね。
ところで、ベースイメージは?
ビルドに使われているBuildpackはわかりましたが、そういえば実行時に使われているイメージがわかりません。
Stackの説明を見ると、Builderの設定ファイルに含まれていそうな気がします。
Packeto Executable JAR Buildpackの[[stack]]を見てみます。
[[stacks]] id = "io.buildpacks.stacks.bionic" [[stacks]] id = "io.paketo.stacks.tiny" [[stacks]] id = "org.cloudfoundry.stacks.cflinuxfs3"
https://github.com/paketo-buildpacks/executable-jar/blob/v3.1.1/buildpack.toml
Buildpackをinspect-buildpackしてみます。
$ pack inspect-buildpack gcr.io/paketo-buildpacks/executable-jar
Inspecting buildpack: gcr.io/paketo-buildpacks/executable-jar
REMOTE IMAGE:
Stacks:
ID: io.buildpacks.stacks.bionic
Mixins:
(omitted)
ID: io.paketo.stacks.tiny
Mixins:
(omitted)
ID: org.cloudfoundry.stacks.cflinuxfs3
Mixins:
(omitted)
Buildpacks:
ID VERSION HOMEPAGE
paketo-buildpacks/executable-jar 3.1.1 https://github.com/paketo-buildpacks/executable-jar
Detection Order:
└ Group #1:
└ paketo-buildpacks/executable-jar@3.1.1
よくわかりませんね…。
suggest-stacksを見てみます。
$ pack suggest-stacks
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
今回、Builderはbaseを選んだので、イメージはこちらですね。
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
まとめ
Cloud Native Buildpacksを試してみました。
Dockerfileを使ったビルドとだいぶ違いますが、これはこれで便利ですねぇ。代わりに、イメージ作成の際の自由度は低い…というか、
Buildpackによって決められたことしかできない感じもします。
いろいろ学ぶことが多そうですけど、Spring Bootでも使われていますし、多少は知っておいた方がいいのかな、と。