JibでMicronautのアプリをビルドしてGCRにpush

昨日MicronautとDockerを触りながら「そういえばJibってのがあったけど触ってなかったなぁ」と思ったので触ってみた。感想は「気の利くやつだな」。

## Jib?

Googleの提供してるコンテナ化ツール。

github.com

去年の夏頃発表されたんだっけな。

cloud.google.com

通常、Javaのアプリをコンテナ化するときって、例えばSpringBootとかMicronautだと

  1. GradleとかMavenでビルドしてFatJAR(依存ライブラリー全部入りのJARファイル)を作る
  2. そのJARファイルとDockerfileを使ってコンテナをビルドする
  3. レジストリーにpushする

という手順になるのだけど、Jibを使うとDockerfileを書かなくても空気を読んでGradleやMavenでコンテナのビルドやpushまでコマンドひとつでやってくれる。*1

それと、Dockerデーモンがなくてもビルドできるみたい。ローカルだとDockerは入れておくだろうけど、CIサーバーとかだとDockerなくても大丈夫ってのは良さそう。

## MicronautのアプリをGCRにpushしてみる

この辺を読みながらやってみた。

https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin

### build.gradle

まずは、Micronautのプロジェクトを作るところから始めてみた*2

❯ mn create-app hello-jib --features spock                                                                                                                                                             
| Generating Java project...                                                                                                                                                                           
| Application created at /home/bufferings/workspace/micronaut/hello-jib    

で、build.gradleにjibの設定を入れる。プラグインの部分と、生成するイメージの名前。

plugins {
    id "io.spring.dependency-management" version "1.0.6.RELEASE"
    id "com.github.johnrengelman.shadow" version "4.0.2"
    id "application"
    id "groovy"
    id "java"
    id "net.ltgt.apt-eclipse" version "0.21"
    id "net.ltgt.apt-idea" version "0.21"

    // これ
    id 'com.google.cloud.tools.jib' version '1.0.2'
}
// これ
jib.to.image = 'asia.gcr.io/bufferings/my-app'

Google Container Registryにpushしてみようかなと思ったので、イメージ名はそうしといた。

### GCR用のクレデンシャルを用意

Make sure you have the docker-credential-gcr command line tool. Jib automatically uses docker-credential-gcr for obtaining credentials. See Authentication Methods for other ways of authenticating.

って書いてあるので、設定しといた。

❯ docker-credential-gcr configure-docker                                                                                                                                                               
/home/bufferings/.docker/config.json configured to use this credential helper for GCR registries  

### 実行!

のためのGradleタスクはこの2つ

  • ./gradlew jib → ビルドしてレジストリーにpushする
  • ./graldew jibDockerBuild → ローカルのDockerデーモンに対してイメージのビルドをする

とりあえず、前者の ./gradlew jib を実行してみたら、エラーになった。

❯ ./gradlew jib                                                                                                                                                                                        
                                                                                                                                                                                                       
> Task :compileJava                                                                                                                                                                                    
warning: Supported source version 'RELEASE_11' from annotation processor 'org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor' less than -source '12'                               
warning: Supported source version 'RELEASE_11' from annotation processor 'org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor' less than -source '12'                               
warning: Supported source version 'RELEASE_11' from annotation processor 'org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor' less than -source '12'                               
3 warnings                                                                                                                                                                                             
                                                                                                                                                                                                       
> Task :jib FAILED                                                                                                                                                                                     
                                                                                                                                                                                                       
FAILURE: Build failed with an exception.                                                                                                                                                               
                                                                                                                                                                                                       
* What went wrong:                                                                                                                                                                                     
Execution failed for task ':jib'.                                                                                                                                                                      
> Your project is using Java 12 but the base image is for Java 11, perhaps you should configure a Java 12-compatible base image using the 'jib.from.image' parameter, or set targetCompatibility = 11 o
r below in your build configuration                                                                                                                                                                    
                                                                                                                                                                                                       
* Try:                                                                                                                                                                                                 
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 4s
3 actionable tasks: 3 executed

Java 12使ってたけど、Base Imageは11じゃよ。って怒られた。JibはデフォルトでDistroless/javaを使うらしいんだけど、それがJava 11ってことか。そっか。

❯ sdk use java 11.0.2-open

Using java version 11.0.2-open in this shell.

これでいけるかな。

❯ ./gradlew jib
Starting a Gradle Daemon, 1 incompatible and 2 stopped Daemons could not be reused, use --status for details

Containerizing application to asia.gcr.io/bufferings/my-app...

Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, hello.jib.Application]

Built and pushed image as asia.gcr.io/bufferings/my-app
Executing tasks:
[==============================] 100.0% complete


BUILD SUCCESSFUL in 2m 4s
3 actionable tasks: 2 executed, 1 up-to-date

レジストリー見てみたら、ちゃんとpushされてるー。(∩´∀`)∩ワーイ

f:id:bufferings:20190414152207p:plain:w300

### pullしてみるか

じゃ、pullして実行できるの確認しとこかな?と思ったけど、そのまえに、そもそも、実はビルド時にローカルにも登録されてたりしないのかな?と思って見てみた。

❯ docker images | grep gcr
asia.gcr.io/bufferings/quickstart-image   tag1                       e1ddd7948a1c        8 months ago        1.16MB
gcr.io/distroless/java                    latest                     d53055d7a4da        49 years ago        118MB

ないね。じゃ、やってみよ。

❯ docker run --rm -p 8080:8080 asia.gcr.io/bufferings/my-app
Unable to find image 'asia.gcr.io/bufferings/my-app:latest' locally
latest: Pulling from bufferings/my-app
f6045256ec3f: Pull complete
5f5edd681dcb: Pull complete
3e010093287c: Pull complete
e6476c6c5a02: Pull complete
4988a9948f57: Pull complete
ffc771f05eaa: Pull complete
45a6b543af72: Pull complete
Digest: sha256:eeb9e44406fb12dc4ed318c2ca56d36db065a8dbbae32d435526a50e65f33d39
Status: Downloaded newer image for asia.gcr.io/bufferings/my-app:latest
01:30:48.626 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2115ms. Server Running: http://a1151605f84c:8080
^C01:32:10.753 [Thread-2] INFO  io.micronaut.runtime.Micronaut - Embedded Application shutting down

OK。

## 中身が気になる

中身が気になるなー。どうなってるんだろうなー?

### Entrypoint

まずはEntrypointを見てみた。さっきビルドしたときにも出てきたな。

❯ docker inspect asia.gcr.io/bufferings/my-app | jq '.[] | .Config.Entrypoint'
[
  "java",
  "-cp",
  "/app/resources:/app/classes:/app/libs/*",
  "hello.jib.Application"
]

ふーん。

### 中に入ってみる

Distrolessにはshも入ってないから、デバッグ用に中に入って色々見てみたりしたいならdebugタグを使ってねってFAQに書いてある。

https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#where-is-bash

If you would like to include a shell for debugging, set the base image to gcr.io/distroless/java:debug instead. The shell will be located at /busybox/sh. Note that :debug images are not recommended for production use.

おすおす。こんな感じに書き換えてみた。

    id 'com.google.cloud.tools.jib' version '1.0.2'
}
jib {
    from {
        // これを足した
        image = 'gcr.io/distroless/java:debug'
    }
    to {
        image = 'asia.gcr.io/bufferings/my-app'
    }
}

で、こんどはpushしなくてもいいからローカル用に jibDockerBuild でビルド。したら、エラー。

❯ ./gradlew jibDockerBuild                                                                                                                                                                             
> Task :jibDockerBuild FAILED                                                                                                                                                                          
                                                                                                                                                                                                       
FAILURE: Build failed with an exception.                                                                                                                                                               
                                                                                                                                                                                                       
* What went wrong:                                                                                                                                                                                     
Execution failed for task ':jibDockerBuild'.                                                                                                                                                           
> Your project is using Java 11 but the base image is for Java 8, perhaps you should configure a Java 11-compatible base image using the 'jib.from.image' parameter, or set targetCompatibility = 8 or 
below in your build configuration                                                                                                                                                                      
                                                                                                                                                                                                       
* Try:                                                                                                                                                                                                 
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.                                                   
                                                                                                                                                                                                       
* Get more help at https://help.gradle.org                                                                                                                                                             

BUILD FAILED in 1s
3 actionable tasks: 1 executed, 2 up-to-date

あら。debugタグはJava 8なのか。

❯ sdk use java 8.0.202.hs-adpt

Using java version 8.0.202.hs-adpt in this shell.

8に変えて、ビルド。

❯ ./gradlew jibDockerBuild
Starting a Gradle Daemon, 2 incompatible Daemons could not be reused, use --status for details

Containerizing application to Docker daemon as asia.gcr.io/bufferings/my-app...

Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, hello.jib.Application]

Built image to Docker daemon as asia.gcr.io/bufferings/my-app
Executing tasks:
[==============================] 100.0% complete


BUILD SUCCESSFUL in 35s
3 actionable tasks: 2 executed, 1 up-to-date

おっけー。

じゃ、中に入ってみてみるかー。

❯ docker run --rm -ti --entrypoint /busybox/sh asia.gcr.io/bufferings/my-app
/ # find /app
/app
/app/libs
/app/libs/netty-codec-4.1.30.Final.jar
/app/libs/netty-transport-4.1.30.Final.jar
/app/libs/micronaut-http-client-1.1.0.jar
/app/libs/jackson-core-2.9.8.jar
/app/libs/micronaut-core-1.1.0.jar
/app/libs/netty-handler-4.1.30.Final.jar
/app/libs/netty-buffer-4.1.30.Final.jar
/app/libs/micronaut-http-1.1.0.jar
/app/libs/micronaut-aop-1.1.0.jar
/app/libs/jackson-databind-2.9.8.jar
/app/libs/netty-codec-socks-4.1.30.Final.jar
/app/libs/netty-handler-proxy-4.1.30.Final.jar
/app/libs/validation-api-2.0.1.Final.jar
/app/libs/javax.annotation-api-1.3.2.jar
/app/libs/jackson-annotations-2.9.8.jar
/app/libs/netty-resolver-4.1.30.Final.jar
/app/libs/jsr305-3.0.2.jar
/app/libs/slf4j-api-1.7.25.jar
/app/libs/micronaut-validation-1.1.0.jar
/app/libs/netty-common-4.1.30.Final.jar
/app/libs/micronaut-http-netty-1.1.0.jar
/app/libs/javax.inject-1.jar
/app/libs/micronaut-http-server-netty-1.1.0.jar
/app/libs/micronaut-runtime-1.1.0.jar
/app/libs/logback-core-1.2.3.jar
/app/libs/jackson-datatype-jsr310-2.9.8.jar
/app/libs/reactive-streams-1.0.2.jar
/app/libs/micronaut-buffer-netty-1.1.0.jar
/app/libs/micronaut-websocket-1.1.0.jar
/app/libs/logback-classic-1.2.3.jar
/app/libs/jackson-datatype-jdk8-2.9.8.jar
/app/libs/rxjava-2.2.6.jar
/app/libs/snakeyaml-1.23.jar
/app/libs/netty-codec-http-4.1.30.Final.jar
/app/libs/micronaut-inject-1.1.0.jar
/app/libs/micronaut-http-server-1.1.0.jar
/app/libs/micronaut-router-1.1.0.jar
/app/classes
/app/classes/hello
/app/classes/hello/jib
/app/classes/hello/jib/Application.class
/app/resources
/app/resources/application.yml
/app/resources/logback.xml

ふーん。libsに依存ライブラリーが入って、classesにクラスファイルが、resourcesにはリソースファイルが入るのか。なるほどなー。気の利くやつだな。

## その他

細かく色々設定できるみたい。

https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin

環境変数とかは普通に外から渡せば良さそう。

https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#how-do-i-set-parameters-for-my-image-at-runtime

おしまい。これくらいメモ残しておけば、また使いたくなったときに思い出せるかな。

*1:だからといって何も中身を知らなくていいという訳ではないよなー

*2:例によってSpockは好みでつけてるだけ