Dockerってなんですか? それ、JavaEEで分かるよ。

f:id:pascal256:20150404165419p:plain

はじめに

最近、Dockerが大人気ですね。GoogleMicrosoftも参入してることはもちろんですが、Oracle Weblogicのサポート対象に入ってたりミドルウェア側、それもエンタープライズ系製品が対応してきたのは面白い動きです。

とはいえDockerって何?っていう人もまだまだ結構多いと思います。

Dockerとはvmwareなどの技術の先にある軽量な仮想マシンである、と捉えてもあまり間違ってないと思いますが、個人的にはJavaEEコンテナの類似品というか、それと同じ文脈で考えたほうがしっくりきました。

なので、自分の理解を深めるためにも、JavaEEエンジニアはどのようにDocker及びその周辺技術を理解すればいいのか、という視点でまとめてみました。

そもそもDockerって?

Dockerとvmwareのような従来の仮想化で何が違うかですが、vmwareがOSの仮想化をしているのに対し、 DockerはOSの上でアプリケーションとミドルウェアを仮想化するコンテナ技術です。 まあ、chrootやjailの偉いやつです。Solarisでいうところのゾーンですね。

OSレイヤーを仮想化しないので、オーバーヘッドが非常に少なく高速に動作します。 それでいて、アプリケーションの動作環境をパッケージングしてるので、 ライブラリのバージョンや依存関係を含めて競合しない形で配布できるので 開発環境と本番環境が違う、みたいな問題も従来の仮想環境同様に解決できます。

むしろ、従来の仮想環境ではそうは言っても複数台立ち上げるのはコストが高いので、 似たようなロールは同じサーバに置く運用でしたが、Dockerの場合は軽量なのでロール毎に厳密に分けるのに向いていますね。その辺が人気の秘密です。

Dockerとwarとアプリケーションのパッケージング

さて、アプリケーションを依存ライブラリも含めてパッケージングと言われると、どこかで聞いたことがある気がしますね?

そう、「war(Web Application Archive)」です。 JavaEEの場合、依存するライブラリ(jar)を含めてパッケージングし、異なるwar間での依存を切り離します。 なので、同じサーバにデプロイしていても、あるアプリケーションはバージョン1系を使い、 別のアプリケーションはバージョン2系を使うというようなケースでも、問題が起こることはありません。

JavaEEJavaとして閉じることで、これを実現していましたが、DockerはLinuxアーキテクチャで動作するものであればパッケージングが可能です。 なので、例えば「Apache + ImageMagic + Rails + アプリコード」をまとめてパッケージングする、という形になります。 デプロイもDockerコンテナに配置するだけなので、ImageMagicが無いとか、Apacheの設定が違うとかに悩まされることもなく、すぐに動作します。 この辺も、GlassFishにwarを配置するときとイメージは同じです。

JavaEEのwarと一番の違いはミドルウェアもパッケージングしちゃうところですね。 なので、JavaEEアーキテクチャで作成した、アプリをDocker化する場合はTomcatやJetty, あるいはGlassFishWeblogicごとパッケージングする形になります

DockerfileとMavenと構成管理

warを作るためには通常はMavenかGradleを使いますね? この辺を使えば、手順をコード化出来るのはもちろんのこと、依存関係も自動解決してくれて便利ですよね。

Dockerの場合はDockerfileが概ねこれに当たります。 また、依存関係の解決事態はDockerの中で使うyumだとかaptだとかのパッケージマネージャに任せるのが基本となります。

より高度なビルドをしたいときにはPackerやChef/Ansibleなんかを組み合わせるケースもあります。

MavenMavenリポジトリがあり各ライブラリを自動で落とせるように、 DockerにもDockerHubがあり必要なイメージを自動でダウンロードして使うことが出来ます。

オーケストレーションクラスタリング

JavaEEの仕様では無いですが、GlassFishWildFlyWeblogicといった一般的なJavaEEコンテナはクラスタリング機能を持っています。

これは複数のサーバを束ね、グループとして扱うことで、設定の同期やデプロイの一元化あるいは負荷分散や高可用性を提供します。

Dockerは基本的にはパッケージングに特化した技術なので、同等の機能はありませんが、SerfやConsulのようなオーケストレーションツールを使うことで実現できます。

オーケストレーションツールが何を提供するかといえば、基本的にはグルーピング(クラスタリング)とサービスディスカバリ、トリガの管理です。

詳細は後述しますが、簡単に言えばGlassFishWeblogicクラスタ機能/管理サーバを「自作するための機能」です。

残念ながら現時点でGlasFishのクラスタ機能と同じものを完成品として提供するツールは無いと思います。(使ったことがないけどGoogleのk8sはそれに当たるかも?) まあ、もうちょっと標準化されたものは今後出てくるでしょうが、根本的にインフラ構成に依存するので、 Consulをベースとした「Java + Dockerのソリューション」とか「Rails + Dockerのソリューション」みたいな感じで出てくるんじゃないかと思います。

これだけ書くと微妙ですが、クラウド、特にDockerを使うケースだとインスタンスの追加と削除を非常に頻繁に行うことになるので、 既存のGlassFishなどのクラスタ機能では不向きな部分がありますが、この点によくマッチします。

個人的にはGlassFishWeblogicクラスタ機能が微妙なので、この辺りは気になるところです。

では、クラスタリング、サービスディスカバリ、トリガ管理に関して、Consulを例に記載します。

クラスタリング

Consulでのクラスタリングは単なる名前付けです。主に後述するトリガ管理で有用な機能ですが、 Consulの場合は各クラスタに関してDNS登録をするので、単純な負荷分散はこの時点で対応できます。

サービスディスカバリ

サービスディスカバリはサービス、今回の話で言えばDockerコンテナを見つけるてクラスタに登録する機能です。

と言っても、難しいことをしているわけではなく、各DockerコンテナにConsul Agentをインストールし、 Consul Serverに起動時にリクエストを投げることで発見をします。

発見だけではなく、ヘルスチェックも提供していて、落ちれば自動的にクラスタから外されます。

トリガ管理

トリガ管理はイベントを起点に何らかの処理をクラスタに対して実行する機能です。

イベントはサービスディスカバリによりクラスタに追加されたり、削除されることはもちろん、外部から任意に実行することも出来ます。

トリガ機能を使うことで、クラスタに対するデプロイやZabbixやSensuといった監視システムに対して自動で登録削除をすることが出来ます。

上記の機能を組み合わせてGlassFishクラスタ機能や管理ツールと同等の機能を作成していくことになります。JavaEEコンテナの提供するクラスタ機能だと、当然JavaEEで完結するものにしか提供されません。

結果として、同じシステムに対してGlassFishクラスタとZabbixのクラスタが二重管理されるような形になります。Consul等を使うことでこれを集約して管理できるのは大きな利点です。

リソース管理

各コンテナが使うリソースが適切に配分されるように多くのGlassFishWeblogicでは同時にさばけるリクエスト数はDBコネクション数などのリソース制御を行います。

これと同様の仕組みはDocker単独では提供していませんが、Mesosやk8s(Kubernetes)を組み合わせて使います。このあたりは正直まだ成熟してないので、今後に期待。

フェイルオーバー

Dockerにはフェイルオーバーに類する機能はありません。こちらはDockerコンテナ内の各ミドルウェアの機能に頼ることになります。

例えばWebシステムであればセッションさえフェイルオーバーできれば基本は問題ないので、 KVSで外部に出して、各APPサーバ、各KVSサーバ自体は状態を持たないかレプリを持つのが基本となります。

JavaEEであればGlassFish, WeblogicであればCohrerence、WildflyであればInfinispanを使うことになります。 もちろんクラスタを作ってインメモリレプリケーションをすることも出来ます。

個人的にはインメモリレプリケーションJavaEEコンテナ側でするよりも、外部に取りたいのでSpring-Sessin + RedisがJSF等でも使えないか検討中です。

Immutable InfrastructureとJavaEE

Dockerそのものとは関係ありませんが、Dockerを取り巻く概念の一つにImmutable Infrastructureがあります。

これは、その名の通り、サーバなどのインフラに対して一切設定変更を行わず、修正やアプリケーションのデプロイをしたい場合はサーバごと作りなおす、という手法です。

これにより、Chefなどのように冪等性を気にせずスクリプトが作れますし、ローカルにファイルを出力するなどの状態が無いのでサーバの増減が非情に簡単になります。

もちろん、手動オペレーション + 物理サーバでこんなことが出来るはずがなく、自動構成管理と仮想化が基本になるのですが、Dockerはこの運用に非常にマッチします。

一見新しい概念なのですが、実はJavaEEは元々これを強く意識したアーキテクチャーです。

最近は出来ますが、JavaEEではトランザクションとポータビリティを意識してEJBへのローカルファイルへのアクセスは禁止でした。今は利便性のため可能ですが、やはりDBなどを使うのが基本となります。

EJBがJNDI Lookupで引いた場合、クラスタ内のどのサーバで動くか分からないため、そのような作ります。warまたはearをデプロイすればどこのサーバでも全く同様に動くのがJavaEEの基本思想です。 これって、Immutable Infrastructureと左程変わらないですよね? 当時と違いDockerはJava以外のものもパッケージングできるため適用対象が増えただけで、概念としては同様です。

なので、JavaEEエンジニアであればImmutable Infrastructureは考え方としてしっくりくるので、さほど警戒する必要は無いと思います。

まとめ

今回はJavaEEエンジニアはどのようにDocker及びその周辺技術を理解すればいいのか、という視点でまとめてみました。

Dockerやオーケストレーション、Immutable Infrastructureは非情にホットな技術で新しい用語も多いですが、 基本的には、JavaEEでも考慮されていたユースケースをより汎用的に、現代的に解決するための手段なので、 今回みたいに対応づけて考えると分かりやすいんじゃないかなー、と思います。

逆に言えば被ってる部分も多いので、機能の使い分けが重要になってくると思います。特にクラスタ周り。 Docker時代のJavaEEインフラをどう構成するのが良いのかは、なかなか面白そうなので継続して考えていきたいです。

この辺を考えると、未来のJavaEEコンテナはクラスタとかデプロイとかの運用要件を外部システムに任せて軽量なアプリケーションコンテナとして生きる道もあると思います。

また、現時点では要素技術が良くも悪くも独立していますが、Google Compute EngineのようなPublic PaaSやOpenShift 3.0のようなPrivate PaaSがPaaSという形で統合する統合する形になるかと思います。

それではHappy Hacking!