きっかけ
SpringBoot 2.3.0 M1がリリースされました。
いろいろな改善や、不要機能の削除などがされていますが、リリースノートを見ていて気になった、Build Docker images with Cloud Native Buildpacksを試してみたいと思います。
リリースノートには、以下記載がありました。
Support for building Docker images using Cloud Native Buildpacks has been added to the Maven and Gradle plugins via the spring-boot:build-image goal and the bootBuildImage task.
MavenかGradleプラグインでDockerイメージが作れるようになるようです。
早速試したいと思い、その活動のまとめとなります。
How to Get Started
前回と同じ設定で
Spring Initializr でアプリ雛形を作ります。
作成後に、環境情報を取得したかったので、SpringBoot Actuatorも有効にしておきます。
早速DockerImageを作ります。イメージを作成する環境ではDockerDeamonを起動しておく必要があるので、その点は注意してください。
これまでSpringBootアプリケーションをDockerImageビルドする場合は以下のステップを踏んでました。
-
mvn package
でアプリのJarファイルを作成 - どのようなDockerImageを作るのかをDockerfileに記載
- docker buildコマンドでイメージを作成
- Dockerfileの設定例
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Build Docker images with Cloud Native Buildpacksでは、上記手順を踏まずに1ステップでDockerImageまで作ることができます。
Mavenの場合は、mvn spring-boot:build-image
、Gradleの場合はgradle bootBuildImage
を叩くだけです。Cloud Native Buildpacksの機能で、ソースの中身を読み取ってDockerfileに設定が必要な内容を補完してくれます。
$ mvn spring-boot:build-image
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.3.0.M1:build-image (default-cli) > package @ demo >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ demo ---
[INFO] Nothing to compile - all classes are up to date
中略
[INFO] > Running builder
[INFO] [builder]
[INFO] [builder] Cloud Foundry OpenJDK Buildpack v1.0.80
[INFO] [builder] OpenJDK JRE 11.0.5: Reusing cached layer
[INFO] [builder]
[INFO] [builder] Cloud Foundry JVM Application Buildpack v1.0.113
[INFO] [builder] Executable JAR: Reusing cached layer
[INFO] [builder] Process types:
[INFO] [builder] executable-jar: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[INFO] [builder] task: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[INFO] [builder] web: java -cp $CLASSPATH $JAVA_OPTS org.springframework.boot.loader.JarLauncher
[INFO] [builder]
[INFO] [builder] Cloud Foundry Spring Boot Buildpack v1.0.157
[INFO] [builder] Spring Boot 2.3.0.M1: Reusing cached layer
[INFO] [builder] Process types:
[INFO] [builder] spring-boot: java -cp $CLASSPATH $JAVA_OPTS com.example.demo.DemoApplication
[INFO] [builder] task: java -cp $CLASSPATH $JAVA_OPTS com.example.demo.DemoApplication
[INFO] [builder] web: java -cp $CLASSPATH $JAVA_OPTS com.example.demo.DemoApplication
[INFO] [builder]
[INFO] [builder] Cloud Foundry Spring Auto-reconfiguration Buildpack v1.0.159
[INFO] [builder] Spring Auto-reconfiguration 2.11.0: Reusing cached layer
[INFO]
[INFO] > Running exporter
[INFO] [exporter] Reusing layer 'app'
[INFO] [exporter] Reusing layer 'config'
[INFO] [exporter] Reusing layer 'launcher'
[INFO] [exporter] Reusing layer 'org.cloudfoundry.openjdk:openjdk-jre'
[INFO] [exporter] Reusing layer 'org.cloudfoundry.jvmapplication:executable-jar'
[INFO] [exporter] Reusing layer 'org.cloudfoundry.springboot:spring-boot'
[INFO] [exporter] Reusing layer 'org.cloudfoundry.springautoreconfiguration:auto-reconfiguration'
[INFO] [exporter] *** Images (89a7e99f9c15):
[INFO] [exporter] docker.io/library/demo:0.0.1-SNAPSHOT
[INFO]
[INFO] > Running cacher
[INFO] [cacher] Reusing layer 'org.cloudfoundry.openjdk:2f08c469c9a8adea1b6ee3444ba2a8242a7e99d87976a077faf037a9eb7f884b'
[INFO] [cacher] Reusing layer 'org.cloudfoundry.jvmapplication:executable-jar'
[INFO] [cacher] Reusing layer 'org.cloudfoundry.springboot:spring-boot'
[INFO] [cacher] Reusing layer 'org.cloudfoundry.springautoreconfiguration:46ab131165317d91fd4ad3186abf755222744e2d277dc413def06f3ad45ab150'
[INFO]
[INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:01 min
[INFO] Finished at: 2020-02-08T15:30:43+09:00
[INFO] ------------------------------------------------------------------------
初回はベースイメージのダウンロードとかで時間がかかりますが、2回目以上は上記の時間ぐらいでできます。
さて、ほんとにDockerImageができているのか、確認してみましょう。
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
demo 0.0.1-SNAPSHOT 89a7e99f9c15 About a minute ago 226MB
ちゃんといますね。起動は通常のDockerImageと同じです。ここではdocker run -it -p8080:8080 demo:0.0.1-SNAPSHOT
で起動させます。
docker run -it -p8080:8080 demo:0.0.1-SNAPSHOT
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.0.M1)
2020-02-08 06:33:43.540 WARN 1 --- [ main] pertySourceApplicationContextInitializer : Skipping 'cloud' property source addition because not in a cloud
2020-02-08 06:33:43.570 WARN 1 --- [ main] nfigurationApplicationContextInitializer : Skipping reconfiguration because not in a cloud
2020-02-08 06:33:43.635 INFO 1 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on 40fbed8bc770 with PID 1 (/workspace/BOOT-INF/classes started by cnb in /workspace)
2020-02-08 06:33:43.636 INFO 1 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2020-02-08 06:33:48.387 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-02-08 06:33:48.456 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-02-08 06:33:48.466 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.30]
2020-02-08 06:33:48.738 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-02-08 06:33:48.740 INFO 1 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 4841 ms
2020-02-08 06:33:50.208 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-02-08 06:33:50.900 INFO 1 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 14 endpoint(s) beneath base path '/actuator'
2020-02-08 06:33:51.122 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-02-08 06:33:51.142 INFO 1 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 9.779 seconds (JVM running for 12.428)
無事、起動できています。前回アプリを使っているので、Actuatorも有効のままです。envの情報を取得してみましょう。
curl localhost:8080/actuator/env | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 8753 0 8753 0 0 10130 0 --:--:-- --:--:-- --:--:-- 10130
{
"activeProfiles": [],
"propertySources": [
{
"name": "server.ports",
"properties": {
"local.server.port": {
"value": 8080
}
}
},
{
"name": "servletContextInitParams",
"properties": {}
},
{
"name": "systemProperties",
"properties": {
"awt.toolkit": {
"value": "sun.awt.X11.XToolkit"
},
"java.specification.version": {
"value": "11"
},
"sun.cpu.isalist": {
"value": ""
},
"sun.jnu.encoding": {
"value": "ANSI_X3.4-1968"
},
"java.class.path": {
"value": "/layers/org.cloudfoundry.springautoreconfiguration/auto-reconfiguration/java-buildpack-auto-reconfiguration-2.11.0.RELEASE.jar:/workspace/BOOT-INF/classes:/workspace/BOOT-INF/lib/HdrHistogram-2.1.11.jar:/workspace/BOOT-INF/lib/LatencyUtils-2.0.3.jar:/workspace/BOOT-INF/lib/jackson-annotations-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-core-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-databind-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-datatype-jdk8-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-datatype-jsr310-2.10.1.jar:/workspace/BOOT-INF/lib/jackson-module-parameter-names-2.10.1.jar:/workspace/BOOT-INF/lib/jakarta.annotation-api-1.3.5.jar:/workspace/BOOT-INF/lib/jakarta.el-3.0.3.jar:/workspace/BOOT-INF/lib/jul-to-slf4j-1.7.29.jar:/workspace/BOOT-INF/lib/log4j-api-2.12.1.jar:/workspace/BOOT-INF/lib/log4j-to-slf4j-2.12.1.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/micrometer-core-1.3.3.jar:/workspace/BOOT-INF/lib/slf4j-api-1.7.29.jar:/workspace/BOOT-INF/lib/snakeyaml-1.25.jar:/workspace/BOOT-INF/lib/spring-aop-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-beans-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-boot-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-actuator-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-actuator-autoconfigure-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-autoconfigure-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-actuator-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-json-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-logging-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-tomcat-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-boot-starter-web-2.3.0.M1.jar:/workspace/BOOT-INF/lib/spring-context-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-core-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-expression-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-jcl-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-web-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/spring-webmvc-5.2.3.RELEASE.jar:/workspace/BOOT-INF/lib/tomcat-embed-core-9.0.30.jar:/workspace/BOOT-INF/lib/tomcat-embed-websocket-9.0.30.jar:/workspace"
},
"java.vm.vendor": {
"value": "AdoptOpenJDK"
},
"sun.arch.data.model": {
"value": "64"
},
"java.vendor.url": {
"value": "https://adoptopenjdk.net/"
},
"catalina.useNaming": {
"value": "false"
},
"user.timezone": {
"value": "GMT"
},
"os.name": {
"value": "Linux"
},
"java.vm.specification.version": {
"value": "11"
},
"sun.java.launcher": {
"value": "SUN_STANDARD"
},
"user.country": {
"value": "US"
},
"sun.boot.library.path": {
"value": "/layers/org.cloudfoundry.openjdk/openjdk-jre/lib"
},
"sun.java.command": {
"value": "******"
},
"jdk.debug": {
"value": "release"
},
"sun.cpu.endian": {
"value": "little"
},
"user.home": {
"value": "/home/cnb"
},
"user.language": {
"value": "en"
},
"java.specification.vendor": {
"value": "Oracle Corporation"
},
"java.version.date": {
"value": "2019-10-15"
},
"java.home": {
"value": "/layers/org.cloudfoundry.openjdk/openjdk-jre"
},
"file.separator": {
"value": "/"
},
"java.vm.compressedOopsMode": {
"value": "32-bit"
},
"line.separator": {
"value": "\n"
},
"java.specification.name": {
"value": "Java Platform API Specification"
},
"java.vm.specification.vendor": {
"value": "Oracle Corporation"
},
"java.awt.graphicsenv": {
"value": "sun.awt.X11GraphicsEnvironment"
},
"java.awt.headless": {
"value": "true"
},
"sun.management.compiler": {
"value": "HotSpot 64-Bit Tiered Compilers"
},
"java.runtime.version": {
"value": "11.0.5+10"
},
"user.name": {
"value": "cnb"
},
"path.separator": {
"value": ":"
},
"os.version": {
"value": "4.9.184-linuxkit"
},
"java.runtime.name": {
"value": "OpenJDK Runtime Environment"
},
"file.encoding": {
"value": "ANSI_X3.4-1968"
},
"spring.beaninfo.ignore": {
"value": "true"
},
"java.vm.name": {
"value": "OpenJDK 64-Bit Server VM"
},
"java.vendor.version": {
"value": "AdoptOpenJDK"
},
"java.vendor.url.bug": {
"value": "https://github.com/AdoptOpenJDK/openjdk-build/issues"
},
"java.io.tmpdir": {
"value": "/tmp"
},
"catalina.home": {
"value": "/tmp/tomcat.9057283283640867778.8080"
},
"java.version": {
"value": "11.0.5"
},
"user.dir": {
"value": "/workspace"
},
"os.arch": {
"value": "amd64"
},
"java.vm.specification.name": {
"value": "Java Virtual Machine Specification"
},
"PID": {
"value": "1"
},
"java.awt.printerjob": {
"value": "sun.print.PSPrinterJob"
},
"sun.os.patch.level": {
"value": "unknown"
},
"catalina.base": {
"value": "/tmp/tomcat.9057283283640867778.8080"
},
"java.library.path": {
"value": "/layers/org.cloudfoundry.openjdk/openjdk-jre/lib:/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib"
},
"java.vendor": {
"value": "AdoptOpenJDK"
},
"java.vm.info": {
"value": "mixed mode"
},
"java.vm.version": {
"value": "11.0.5+10"
},
"sun.io.unicode.encoding": {
"value": "UnicodeLittle"
},
"java.class.version": {
"value": "55.0"
}
}
後略
Javaの実装はAdoptOpenJDKの11が使われていますね。OSはLinuxのようです。
まとめ
Dockerfileを書かなくてもよいので、Dockerの知識がなくても簡単にDockerImageを作成できました。仕様や細かい動きを確認しないと本番へすぐ投入とはいかなそうですが、気軽にDockerImage作成できることは、メリットとなるのではないでしょうか?
使ったソースコードは以下に配置しておきました。
ご参考です。