ちょっと話題の記事

[Docker] JDKのイメージを新しくしたらSpring Bootアプリケーションが起動しなくなった話

Debianの古いバージョンがミラーサイトから削除された影響で、開発・運用しているSpring BootアプリケーションのDockerイメージがビルドできなくなりました。そのため、依存しているopenjdkのイメージを変更したところ、イメージのビルドはできるようになったものの、なぜかアプリケーションが起動しなくなりました。その原因について、後学のため記事として残しておきます。
2019.04.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

先日、Debianの古いバージョンが軒並み削除されたのは記憶に新しいところです。 この影響で、古いDebianのイメージから作られている各種Dockerイメージがビルドに失敗するようになりました。

2019年3月25日 Debian,Wheezyなど古いバージョンをミラーサイトから削除へ

自分の周りでも、開発・運用しているSpring Bootアプリケーションがこの影響を受け、イメージのビルドができなくなりました。そのため、依存しているopenjdkのイメージを変更したところ、イメージのビルドはできるようになったものの、なぜかアプリケーションが起動しなくなりました。その原因について、後学のため記事として残しておきます。

事象

開発・運用中のSpring BootアプリケーションのDockerイメージをビルドしてみたところ、なぜかビルドに失敗するようになりました。調査したところ、以下のようなことが判明しました。

  • Dockerfileは FROM java:openjdk-8-jdk となっていました。これは debian:jessie を元にしているため、Debianの古いバージョンが削除された影響を受けていました。
  • そこでopenjdkのイメージを変更し FROM openjdk:8-jdk-stretch としました。
  • すると、イメージのビルドは成功するようになったのですが、なぜか起動できなくなりました。

原因その1

調べると、環境変数で spring.profiles.active=foo,bar を指定しているはずが、なぜか反映されていないことがわかりました。

Spring Bootでは、環境変数からプロパティを指定する際は SPRING_PROFILES_ACTIVE=foo,bar の形式で指定することができます(というかこっちの方が正しい)。そこで設定を書き換えたところ、起動するようになりました。

これで一応起動するようにはなったのですが、なぜ環境変数が反映されなくなったのかが分からなかったため、さらに調べてみました。

原因その2

色々調べたところ、下記のissueが見つかりました。これが原因でした。

https://github.com/docker-library/openjdk/issues/135

詳しくは、以下の2点が原因です。

一部の環境で /bin/sh がドットを含む環境変数を無視する

下記の環境では、/bin/sh ではドットを含む環境変数が使えなくなっていました。 *1

今回使用した openjdk:8-jdk-stretchdebian:stretch を元にしているため、上記の影響を受け /bin/sh ではドットを含む環境変数が使用できなくなっています。 実際に試してみると、ドットを含む環境変数 jp.classmethod=test が、/bin/sh からは見えなくなっています。

# 単にenvとすると普通に出る
$ docker run -it --rm -e jp.classmethod=test openjdk:8-jdk-stretch env | grep jp.classmethod
jp.classmethod=test
 
# /bin/shを明示すると出ない
$ docker run -it --rm -e jp.classmethod=test openjdk:8-jdk-stretch /bin/sh -c "env" | grep jp.classmethod

DockerのCMDはシェル形式だと /bin/sh を使用する

ではどこで /bin/sh が出てくるのかというと、DockerfileのCMDの仕様です。 CMDでのコマンドの指定形式は複数ありますが、そのうち「シェル形式」で指定した場合は /bin/sh が使われます。

http://docs.docker.jp/engine/reference/builder.html#cmd

CMD には3つの形式があります。

  • CMD ["実行バイナリ", "パラメータ1", "パラメータ2"] ( exec 形式、推奨する形式)
  • CMD ["パラメータ1", "パラメータ2"] ( ENTRYPOINT のデフォルト・パラメータ)
  • CMD <コマンド> (シェル形式)

CMD を シェル 形式で使えば、 <コマンド> は /bin/sh -c で実行されます。

当方のSpring Bootアプリケーションでは、DockerfileのCMDは以下のような形になっていました。これはシェル形式なので /bin/sh から実行されることになり、前述のとおりドットを含む環境変数は無視されます。

CMD java -Dfoo.bar=baz ... -jar hoge.jar

まとめ

JDKのイメージを新しくしたらSpring Bootアプリケーションが起動しなくなったため、原因を調査しました。結果、以下の2つが重なって生じた現象でした。

  • 新しい環境では/bin/sh からはドットを含む環境変数が使えないことがある
  • DockerfileのCMDでシェル形式で指定したコマンドは /bin/sh で実行される

このような現象にハマらないようにするには、以下のような対策が考えられます。

  • ドットを含む環境変数を使わない(SPRING_PROFILES_ACTIVEのような形式を使う)
  • どうしても無理な場合は、CMDをexec形式(CMD ["foo", "bar"])で書く

以上です。同じことでハマった方がいれば、参考になれば幸いです。

脚注

  1. POSIXに準拠していないというのが理由のようです。つまりバグではなく仕様で、そもそもドットを含む変数名は使うべきではないということのようです。