Docker には、イメージの「レイヤー」を指定するために使用するシンプルな "Dockerfile" (英語) ファイル形式があります。Spring Boot プロジェクトで次の Dockerfile を作成します。
例 1: Dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Gradle を使用する場合は、次のコマンドで実行できます。
docker build --build-arg JAR_FILE=build/libs/\*.jar -t springio/gs-spring-boot-docker .
Maven を使用する場合は、次のコマンドで実行できます。
docker build -t springio/gs-spring-boot-docker .
このコマンドはイメージを構築し、springio/gs-spring-boot-docker
としてタグ付けします。
この Dockerfile は非常にシンプルですが、Java と JAR ファイルだけで、Spring Boot アプリを無駄なく実行できます。ビルドにより、アプリケーションを実行するための Spring ユーザーと Spring グループが作成されます。次に、プロジェクト JAR ファイルが app.jar
としてコンテナーにコピーされ (COPY
コマンドによって)、ENTRYPOINT
で実行されます。Java プロセスをラップするシェルがないように、Dockerfile ENTRYPOINT
の配列形式が使用されます。Docker のトピックガイドでは、このトピックについてさらに詳しく説明します。
ユーザー権限でアプリケーションを実行すると、いくつかのリスクを軽減できます (例: StackExchange のスレッド (英語) を参照)。Dockerfile
の重要な改善点は、アプリケーションを非ルートユーザーとして実行することです。
例 2: Dockerfile
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
アプリケーションをビルドして実行すると、アプリケーションの起動ログにユーザー名が表示されます。
docker build -t springio/gs-spring-boot-docker .
docker run -p 8080:8080 springio/gs-spring-boot-docker
最初の INFO
ログエントリの started by
に注意してください。
:: Spring Boot :: (v2.2.1.RELEASE)
2020-04-23 07:29:41.729 INFO 1 --- [ main] hello.Application : Starting Application on b94c86e91cf9 with PID 1 (/app started by spring in /)
...
また、Spring Boot ファット JAR ファイルでは依存関係とアプリケーションリソースが明確に分離されており、この事実を利用してパフォーマンスを向上させることができます。鍵となるのは、コンテナーファイルシステムにレイヤーを作成することです。レイヤーはビルド時と実行時 (ほとんどの実行時) の両方でキャッシュされるため、最も頻繁に変更されるリソース (通常はアプリケーション自体のクラスリソースと静的リソース) は、変更が遅いリソースの後にレイヤー化する必要があります。そのため、Dockerfile のわずかに異なる実装を使用します。
例 3: Dockerfile
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]
この Dockerfile には、fat JAR を解凍したディレクトリを指す DEPENDENCY
パラメーターがあります。Gradle で DEPENDENCY
パラメーターを使用するには、次のコマンドを実行します。
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
Maven で DEPENDENCY
パラメーターを使用するには、次のコマンドを実行します。
mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
正しく実行されていれば、依存関係 JAR を含む BOOT-INF/lib
ディレクトリと、アプリケーションクラスを含む BOOT-INF/classes
ディレクトリがすでに含まれています。アプリケーション独自のメインクラスである hello.Application
を使用していることに注意してください。(これは、ファット JAR ランチャーによって提供される間接参照を使用するよりも高速です。)
| boot2docker を使用する場合は、Docker コマンドラインまたはビルドツールで何かを行う前に、まず boot2docker を実行する必要があります (仮想マシン内で作業を処理するデーモンプロセスが実行されます)。 |
Gradle ビルドでは、Docker コマンドラインに明示的なビルド引数を追加する必要があります。
docker build --build-arg DEPENDENCY=build/dependency -t springio/gs-spring-boot-docker .
Maven でイメージをビルドするには、よりシンプルな Docker コマンドラインを使用できます。
docker build -t springio/gs-spring-boot-docker .
| Gradle のみを使用する場合は、Dockerfile を変更して、DEPENDENCY のデフォルト値を解凍されたアーカイブの場所と一致させることができます。 |
Docker コマンドラインでビルドする代わりに、ビルドプラグインを使用することもできます。Spring Boot は、独自のビルドプラグインを使用して、Maven または Gradle からコンテナーをビルドすることをサポートしています。Google には、Maven および Gradle プラグインを備えた Jib [GitHub] (英語) というオープンソースツールもあります。おそらく、このアプローチの最も興味深い点は、Dockerfile
が必要ないことです。docker build
から取得するのと同じ標準コンテナー形式を使用してイメージをビルドできます。また、docker がインストールされていない環境でも機能します (ビルドサーバーでは珍しいことではありません)。
| デフォルトでは、デフォルトの buildpacks によって生成されたイメージは、アプリケーションをルートとして実行しません。デフォルト設定を変更する方法については、Gradle または Maven の構成ガイドを確認してください。 |
Gradle で Docker イメージを構築する
1 つのコマンドで、Gradle を含むタグ付き docker イメージを構築できます。
./gradlew bootBuildImage --imageName=springio/gs-spring-boot-docker
Maven で Docker イメージを構築する
すぐに開始するには、pom.xml
を変更せずに Spring Boot イメージジェネレーターを実行できます (Dockerfile
がまだある場合は無視されることに注意してください)。
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=springio/gs-spring-boot-docker
Docker レジストリにプッシュするには、プッシュ権限が必要ですが、デフォルトではこの権限がありません。Docker を実行する前に、イメージのプレフィックスを自分の Dockerhub ID と docker login
に変更して、認証されていることを確認してください。
プッシュ後
例の docker push
は失敗します (Dockerhub の "springio" 組織に属している場合を除く)。ただし、自分の docker ID と一致するように構成を変更すると、成功するはずです。すると、新しいタグ付きイメージがデプロイされます。
ローカルでビルドされた docker イメージを実行するために、docker に登録したり、何かを公開したりする必要はありません。Docker (コマンドラインまたは Spring Boot から) を使用してビルドした場合は、ローカルでタグ付けされたイメージがまだあるため、次のように実行できます。
$ docker run -p 8080:8080 -t springio/gs-spring-boot-docker
Container memory limit unset. Configuring JVM for 1G container.
Calculated JVM Memory Configuration: -XX:MaxDirectMemorySize=10M -XX:MaxMetaspaceSize=86381K -XX:ReservedCodeCacheSize=240M -Xss1M -Xmx450194K (Head Room: 0%, Loaded Class Count: 12837, Thread Count: 250, Total Memory: 1073741824)
....
2015-03-31 13:25:48.035 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-03-31 13:25:48.037 INFO 1 --- [ main] hello.Application : Started Application in 5.613 seconds (JVM running for 7.293)
| buildpack は実行時にメモリ計算機を使用して、コンテナーに適合するように JVM のサイズを決定します。 |
| boot2docker で Mac を使用する場合、通常、起動時に次のような画面が表示されます。 Docker client to the Docker daemon, please set:
export DOCKER_CERT_PATH=/Users/gturnquist/.boot2docker/certs/boot2docker-vm
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://192.168.59.103:2376
|
実行中は、次の例のようなコンテナーのリストが表示されます。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81c723d22865 springio/gs-spring-boot-docker:latest "java -Djava.secur..." 34 seconds ago Up 33 seconds 0.0.0.0:8080->8080/tcp goofy_brown
再度シャットダウンするには、前のリストのコンテナー ID を使用して docker stop
を実行します (実際の ID は異なります)。
docker stop goofy_brown
81c723d22865
必要に応じて、使用が終わったらコンテナーを削除することもできます (コンテナーはファイルシステムの /var/lib/docker
のどこかに保存されます)。
Spring プロファイルの使用
新しく作成した Docker イメージを Spring プロファイルで実行するのは、環境変数を Docker 実行コマンド (prod
プロファイルの場合) に渡すだけです。
docker run -e "SPRING_PROFILES_ACTIVE=prod" -p 8080:8080 -t springio/gs-spring-boot-docker
docker run -e "SPRING_PROFILES_ACTIVE=dev" -p 8080:8080 -t springio/gs-spring-boot-docker
Docker コンテナーでのアプリケーションのデバッグ
docker run -e "JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n" -p 8080:8080 -p 5005:5005 -t springio/gs-spring-boot-docker