SpringBootはおしごとで使うのにDockerイメージ作ったことないなと思って、やっておこうかと。
Google先生に聞いたらSpring 公式ブログがDockerイメージの作り方について書いてました。2つほど見つけましたが、下の記事のほうが詳しく書かれています。このあたりを参考にDockerイメージを作ってみます。
最終的に出来上がったものはGitHubに載せました。(Springのサンプルを今後も作ると思うので、Gradleマルチプロジェクト構成にしています。)
プロジェクト構成
プロジェクトの構成は以下です。
. ├── README.md ├── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts ├── simple-spring-boot-docker │ ├── Dockerfile │ ├── README.md │ └── src │ └── main │ ├── java │ │ └── com │ │ └── example │ │ └── spring │ │ └── docker │ │ └── Application.java │ └── resources └── spring-boot-docker-multi-stage-build ├── Dockerfile ├── README.md └── src └── main └── java └── com └── example └── spring └── docker └── Application.java
simple-spring-boot-docker
がビルドしたJarをコピーしてDockerイメージを作る例。spring-boot-docker-multi-stage-build
はDockerマルチステージビルドを使って、Jarを作るステップを含んだDockerfileを書いています。
Gradleのマルチプロジェクトの構成をとっているので、ルートプロジェクトの build.gradle.kts
にプラグインや依存関係を定義してます。 subproject
ブロックを使うことでサブプロジェクトの設定が一箇所で行なえます。プラグインは以下のようにルートプロジェクトで定義したあと、サブプロジェクトごとに apply
で適用していく必要があるようです。
spring-examples/build.gradle.kts at master · BooookStore/spring-examples · GitHub
plugins { id("org.springframework.boot") version "2.3.1.RELEASE" apply false id("io.spring.dependency-management") version "1.0.9.RELEASE" apply false id("java") } subprojects { apply(plugin = "org.springframework.boot") apply(plugin = "io.spring.dependency-management") apply(plugin = "java") java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } repositories { mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") } }
Dockerイメージを作ってみる
ビルドしたJarをコピーしてDockerイメージを作るのは非常にシンプルです。以下がそのDockerfileですが COPY
を使ってJarをコピーしたあと、 ENTRYPOINT
でアプリケーションを起動するだけです。
spring-examples/Dockerfile at master · BooookStore/spring-examples · GitHub
FROM openjdk:11-jre-slim WORKDIR /app COPY ./build/libs/simple-spring-boot-docker.jar . ENTRYPOINT ["java", "-jar", "simple-spring-boot-docker.jar"]
マルチステージビルドを使う場合はちょっと複雑な印象です。Dockerfileが以下になります。
spring-examples/Dockerfile at master · BooookStore/spring-examples · GitHub
# Build Application FROM openjdk:11-jdk-slim AS builder WORKDIR /app COPY gradlew . COPY gradle gradle COPY build.gradle.kts build.gradle.kts COPY settings.gradle.kts settings.gradle.kts COPY spring-boot-docker-multi-stage-build spring-boot-docker-multi-stage-build RUN ["./gradlew", "spring-boot-docker-multi-stage-build:build"] # Build Docker Image FROM openjdk:11-jre-slim WORKDIR /app COPY --from=builder /app/spring-boot-docker-multi-stage-build/build/libs/spring-boot-docker-multi-stage-build.jar . ENTRYPOINT ["java", "-jar", "spring-boot-docker-multi-stage-build.jar"]
ステップが2つに分かれています。最初にビルドに必要なリソースをコピーしてJarを作ります。次に最終的なイメージを作ります。
マルチステージビルドを利用するメリットとしてはビルドする環境も含めて統一化できる点かなと思います。こっちのPCだと動くという状況をなくすためにはいいと思いますね。(CI環境とかがあるのであればそちらで担保できますけどね)
このDockerfileを実行するときに知ったのですが、Dockerfileが置いてあるディレクトリの外のファイルをコピーすることはできないみたいです。コピーしようとするとこんなエラーが出てしまいます。
COPY failed: Forbidden path outside the build context: ../ ()
このような場合、docker build
コマンドを叩くディレクトリをコピーしたいファイルよりも上位のディレクトリにすればOKです。その場合はDockerfileは docker build
コマンドを叩くディレクトリからの相対パスを記述する必要があります。また、コマンドの引数にファイルを指定する必要もあります。