DockerでのJavaの利用
筆者はJava自体がある種のコンテナでもあると考えているため、Dockerというコンテナ上で動かし、二重のコンテナを作る必要性はあまりないと感じてきました。
しかし、今回のサポート変更にともない、OpenJDKベースのすばやいバージョンアップに追従していくことや、アプリケーションとともにJRE環境を含めて配布することを考慮すると、Docker環境下でのJavaの利用は、今後のJavaのバージョンアップ対応として有効な手段なのではないかと思っています。
そしてJava 10では、CPUやメモリなどDocker環境下で動作するための改善が行われています。そこで今回はJava 10での改善点だけではなく、Dockerを用いたJavaの利用も紹介します。
Dockerのインストール
今回はDocker自体の詳細な導入方法は紹介しませんが、DockerをWindowsもしくはMacにインストールするには、Docker Storeからインストーラーをダウンロードする必要があります。Dockerには有償版のDocker EEと無償版のDocker CEがありますが、今回は無償版のDocker CEを使って説明します。
インストールが終了したら、正常にインストールされたことを確認するために、「docker version」コマンドを実行して、リスト1に示すようにインストールされているバージョンが出力されることを確認してください。リスト1はMac環境で実行した結果ですが、WindowsのPowerShell環境下で実行してもほぼ同様の結果となります。
bash-3.2$ docker version Client: Version: 18.03.1-ce API version: 1.37 Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:13:02 2018 OS/Arch: darwin/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.1-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:22:38 2018 OS/Arch: linux/amd64 Experimental: true
Dockerを使ったJavaの実行
Dockerではあらかじめ定義された環境を簡単に作ることができ、OpenJDKのさまざまなバージョンがあらかじめ使えるようにすでに定義が用意されています。
したがって、Dockerを使えば「JDKのダウンロードページに遷移し、ダウンロードし、インストールする」といった手間を省けます。また例えば、JDK 9からJDK 10への乗り換えの際にJDK 10用のDockerイメージを使えば、すぐにバージョンアップが可能です。そして、ちょっとした利用確認をしたい場合にも、さまざまなバージョンがすぐに確認できるために非常に便利です。
例えば、OpenJDK版のJava 10を試したければ、リスト2のように実行するだけで、JDK 10版のJShellが起動された状態になります。
$docker run -it openjdk:10-jdk Jun 17, 2018 3:33:56 AM java.util.prefs.FileSystemPreferences$1 run INFO: Created user preferences directory. | Welcome to JShell -- Version 10.0.1 | For an introduction type: /help intro jshell>
また、JShellではなく、bashなどのコマンドを起動したい場合には、リスト3のように実行します。後は、そこからjavaコマンドやjavacコマンドなどが利用できます。
$docker run -it openjdk:10-jdk bash root@3e4f20be9148:/# java -version openjdk version "10.0.1" 2018-04-17 OpenJDK Runtime Environment (build 10.0.1+10-Debian-4) OpenJDK 64-Bit Server VM (build 10.0.1+10-Debian-4, mixed mode)
初めての起動の場合にはイメージをダウンロードする必要があるため、少々時間がかかります。しかし、2回目以降はすぐに実行されます。
すでにリリースされたJava 9や、まだ正式リリースされていないJava 11についても「openjdk:10-jdk」というタグの部分を書き換えればすぐに実行が可能です。
これ以外にもさまざまなタグが用意されいて、OpenJDK用のDocker Hubの情報を参照するとこれ以外にも利用できるバージョンがあることがわかります。
また、Oracle版JDKのDockerイメージはDocker Storeから取得可能です。ただし、こちらのイメージを利用する際にはDocker Storeのアカウントが必要になります。
Java 10で修正されたDockerへの対応
JavaをDocker環境で利用しようとすると、ライセンスなどの問題で一癖あったのですが、OpenJDKであればライセンスの問題もクリアされるので、Docker環境での利用もより進んでくるはずです。しかし、Java 10まではDocker環境で動作する際、Docker上でのリソース制限などに一部対応していませんでした。具体的には、メモリやCPUの制限に関してDocker上で指定した制限ではなく、Dockerホストの制限がそのまま使われてしまうケースがありました。
例えば、ヒープメモリに関して、Java 8の環境で確認したものがリスト4です。
$docker run -it -m1024m openjdk:8-jdk
#cat /proc/meminfo | grep MemTotal
MemTotal: 2046796 kB
#java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
uintx MaxHeapSize := 524288000 {product}
openjdk version "1.8.0_171"
OpenJDK Runtime Environment (build 1.8.0_171-8u171-b11-1~deb9u1-b11)
OpenJDK 64-Bit Server VM (build 25.171-b11, mixed mode)
2G(2046796 kB)というのはDockerに割り当てているメモリ量です。ただし、コンテナを起動する際に1G(1024M)のメモリ上限を設定して起動しています。
ただし、Java 10以前では512Mと認識されています。Javaはヒープサイズの最大にメモリ数の1/4か1Gの小さい方を選択しますので、512Mという値になります。
同じコマンドをJava 10の環境で実行したものが、リスト5です。
$docker run -it -m1024m openjdk:10-jdk bash
root@6314b958ec52:/# java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
size_t MaxHeapSize = 268435456 {product} {ergonomic}
openjdk version "10.0.1" 2018-04-17
OpenJDK Runtime Environment (build 10.0.1+10-Debian-4)
OpenJDK 64-Bit Server VM (build 10.0.1+10-Debian-4, mixed mode)
こちらは正しく256Mと認識されています。メモリ量の指定に関しては、Xmxオプションで指定可能です。こちらを指定すれば間違った認識をしてしまっていても手動で変更することができます。
また、CPU数に関してもDocker側の起動で、--cpusにて利用できるCPUの数を制限した場合に、そのCPU数を正しく取得できない問題がありました。
Java 9にて利用できるプロセッサ数の数を取得した結果がリスト6です。
bash-3.2$ docker run -it --cpus 2 --rm openjdk:9-jdk jshell> Runtime.getRuntime().availableProcessors(); $1 ==> 4
Docker全体で指定しているプロセッサ数が4つなのですが、そのままその数がJava内からも4つと認識されてしまっています。
そして、Java 10で同様のことを確認した結果がリスト7です。
bash-3.2$ docker run -it --cpus 2 --rm openjdk:10-jdk jshell> Runtime.getRuntime().availableProcessors() $1 ==> 2
こちらはCPUの数が正しく取得できています。
CPUの数が正しく取得できないと、JVM内で行っているガベージコレクションでの最適化に影響が出る場合があります。一部のプログラムには、CPUの数をスレッドプール数の決定に用いているものもあります。そのため、実際のCPU数とDocker上で許可されているCPU数に大きく違いがある場合などはその影響をより受けやすくなってしまいます。
こういったことが原因の問題は開発時にはあまり生じないと思いますので、もし、何らかの影響で問題が起きても原因まで特定することは難しいはずです。そんな中、Dockerなどのコンテナ上での動作も前提としていることは、利用する側の安心につながります。今回の対応改善は大きな安心材料の1つだと思います。
また、今回Docker自体については詳しく説明できませんでしたが、Dockerを使うと非常に簡単にJava環境を構築できることがわかったと思います。
最後に
Java 10は、大きく仕様が変更されたJava 9と今後の長期利用バージョンとなるであろうJava 11の中間のバージョンであることも影響してか、それほど大きな変更点はありませんでした。
一方で、今後のバージョンアップの方向性がJava 9で示されてから、今までのJavaの方向性と本当に変わるのだろうかと半信半疑の方もいたのではないかと思います。とはいえ、Java 10は予定通りリリースされ、Java 11に関してもその後の変更計画などもなく予定通りのリリースとなりそうです。
個人的にはJava 11でOpenJDKのLTS版ができるのか、できないのか注目したいですが、一方で6カ月ごとのJDKもしくはJREのリリースサイクルに対応できる体制や仕組みなどのノウハウが共有されることにも期待しています。
また、既存資産の保守の面に目を向ければ、今回の有償化の流れは新たなコストの発生というマイナス面が目立ってしまいますが、Java EE(Jakarta EE)でも述べた通り、今後クラウドとの連携が強くなるとJavaという実行環境も1つのクラウドリソースとして広く利用されるようになるかもしれません。そうなれば、その他のクラウドリソースと同様に、サポートに関しても稼働分にのみ課金するモデルになるかもしれません。
いずれにしても、今後のJavaに注目していくトピックスが増えたことには間違いないと思います。
