これは、なにをしたくて書いたもの?
前に、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でも使われていますし、多少は知っておいた方がいいのかな、と。