HerokuでJavaのバージョンを固定しないと起こりうる問題


HerokuでWebアプリケーションを稼働させる時に気にしておくべきことを紹介します。

HerokuはPaaSなのでライブラリなどはHerokuがプラットホームとして用意し提供してくれます。セキュリティパッチや新バージョンへのメンテナンスも全てHerokuが実行してくれます。言い換えれば、自分でメンテナンスしなくても済むというメリットはありますが、いつもならやっていた互換性を確保するという事を自身で管理しなくてもいいというわけではありません。

例えば一般的なJavaのwebアプリケーションならこんな事を気にするでしょう。

  • JDKのバージョンを固定する
  • 使用しているライブラリのバージョンを固定する
  • 実行環境の条件を固定する

最近ですと開発環境とステージング環境と本番環境全て同じ環境にして、オーケストレーションツールを利用し構築・再現方法も同じにするという事を行うのが一般的です。

HerokuのTwelve-Factor Appでも述べられている通りです。

http://12factor.net/ja/dev-prod-parity

現時点ですがHerokuのJavaがdynoを再起動すると最新のJavaのバージョンを参照するようになっているので、マイナーバージョンアップした時に前提条件が全く同じでないので、タイミングによっては前提条件が崩れる事があります。

下記のような致命的なエラーが発生するケースもあったので、原因と回避方法をお伝えしておきます。

現象

JDK8u60を利用するようになってから、バージョンを固定して使っていたAWS SDKのS3へのアクセスでエラーが発生。

原因

JDK8u60で時刻関係の扱いに変更が入ったのと、JodaTimeのバグとAWS SDKの認証トークン・シグネチャの生成の仕組みの組み合わせが重なり、不正なトークンが生成される形となった。

詳細はaws-sdk-javaのIssueを参照

https://github.com/aws/aws-sdk-java/issues/444 https://github.com/aws/aws-sdk-java/issues/484

対策方法

  • joda-timeのバージョンを事情があって変更できない場合
    • Java8u51を固定して利用する
    • Java8u60を使う場合
      • aws-sdk-javaのバージョンを最新にする

Javaのバージョンを固定する

HerokuでJavaのバージョンを固定化して利用するにはherokuコマンドでパラメータで実行します。

$heroku config:set JDK_URL_1_8=http://lang-jvm.s3.amazonaws.com/jdk/openjdk1.8.0_51-cedar14.tar.gz

固定する事で万が一dynoのrebootが走っても前提条件を担保できます。

根本的な対応

JDKのバージョンを固定するというのは意図しない動作をしないためのものであって、長い運用の中では一定のサイクルで見直すべき事です。テスト実施後にリリースタイミングを見計らい最新バージョンでも動くようしましょう。

今回の例なら根本的な対応しとして、根本的に解決するにはJodaTimeを2.8.1を使用する必要があり、aws-sdk-javaを1.10.1以降のバージョンに更新することで問題を解消できます。

最後に

オーケストレーションツールを使ってImmutable Infrastructureを意識し構築していく事は対応速度・開発効率だけでなく運用トラブルを避ける事にも繋がります。 クラウドサービスは便利な反面、押さえておかないポイントも多々あるので意識して使っていきたいですね。