Java
lagom
ConductR

Java+microservice+DDD+CQRS+ESなフレームワーク lagom の勉強メモ 2(アプリケーションの実行環境、ConductR)

More than 3 years have passed since last update.

前回は、1つのサービスについてサービスの書き方とか、永続化周りについて書きました。

今回は、複数サービスをまとめて1つのアプリケーションとするにはという内容です。

lagom では ServiceLocator というものが、各サービスをとりまとめまる役割を持ちます。

マイクロサービスアーキテクチャでは、ServiceRegstory と呼ばるものにあたり、consulとかEureka とかが有名なツールですね。

これから書く内容を簡単にまとめると、以下の通りになります。

開発環境では、組み込みのServiceLocatorがあるのでそれを使う。

本番環境では、lightbend社による実行環境である ConductRがあるのでそれを使うか、自分で頑張るかという2択です。


ServiceLocator について

lagom ではおのおの作成したマイクロサービス同士がコミニュケーションするために、ServiceLocatorというオブジェクトが必要になる。

サンプルコードでは、hellostreamサービスがhelloworldサービスを呼び出したりしていたが、

その際もServiceLcatorを経由している。

cassandra とのやり取りも同様だ。

図示すると以下のようになる。

lagom-1-perspactive.png

図の破線はサービスの依存関係を示すもので、実際にはServiceLcatorを経由して依存先のサービスを呼び出している。

また、クライアントからのリクエストもServiceLocatorが受け付け、リクエストの内容に応じてサービスに振り分けている。

ServiceLocatorは、マイクローサービスアーキテクチャの、サービスレジストリやAPIゲートウェイに相当すると思われる。


開発環境・組み込みServiceLocator

開発環境では、組み込みのServiceLocatorが存在している。

sbt runAll でアプリケーションを起動すると、以下のプロセスが起動していることになる。


  1. 組み込みCassandraサーバーを起動。(4000番ポート)

  2. 組み込みSaviceLocatorを起動。(8000番ポートでServiceLocator、9000番ポートでServiceGateway)

  3. 各サービスを起動。(20000番台ポートが使用される)

各サービスには、ServiceDescriptor(APIプロジェクトのインターフェースのdefaultメソッド)に、エンドポイントの設定を記述してあると思うが、これらのエンドポイントは ServiceLocatorの9000番ポートにまとめて登録されるようになるので、外部からは9000番ポートでアクセスする。

また、localhost:8000/servicesにアクセスすると、サービス一覧が照会できる。

$ curl http://localhost:8000/services

[
{"name":"hellostream","url":"http://0.0.0.0:26230"},
{"name":"cas_native","url":"tcp://127.0.0.1:4000/cas_native"},
{"name":"helloservice","url":"http://0.0.0.0:24266"}
]

開発環境でのアプリケーション稼動は、結構簡単にできる。

割と面倒だったのは、本番環境での稼動。

また、開発環境では、ホットリロードが有効なため、ソースコードを変更すると、自動的にサービスがリロードされる。

ただし、ホットリロードすると、結構な頻度で CPU が100%で回り続けることがある。

Windowsだからだろうか。

開発環境でアプリケーションを動かすのはそんなに難しくありませんでした。

新しいことが多くて困ったのは本番向けでした。


本番環境・ConductR


本番環境へのデプロイの考慮

本番環境で、lagom を動かすのは、casandra, 各サービス, ServiceLocator などをakka-cluster 上にデプロイ必要がある。

そして、 ServiceLocator の実装は自分で書かなくてはいけないのだが、

現時点で ServiceLocator をどうやって書くかといったガイドはなく、ソースを見てねという状態。

そのため、ServiceLocator を自分で書くのは結構難易度が高めだと思う。


ConductR とは

ここで、lightbend社から提供される ConductR が出てくる。

ConductR は平たく言えば、 JVM とakkaプロダクト群で構成された、play や lagom アプリケーションの実行環境。

Linux や EC2 へのインストール手順もある。(https://conductr.lightbend.com/docs/1.1.x/Install) ※リンク先にあるように有償のようなので注意。

アプリケーションを管理するためのツール、クラスター構成、負荷分散、監視、ロギングといった機能もついていて、隙のない作りになっていると思う。

そして、ConductR 向けの ServiceLocator は標準で提供される。

じゃあ、ConductR でいいんじゃね? とは思うが、 プロダクション目的で使う場合は有償 とあるので注意が必要。

プロダクションの範囲がどこまでなのかは、正直わからないのですが、lagom をプロダクションモードで動かした場合は引っかかるんじゃないかと思われます。

(これを書いた目的の半分は、ConductRおいくらなの? を知りたいためだったりするので、知っている人がいたら教えてください。 )

ただし、開発環境向けに ConductR へのデプロイをテストできる Sandbox という機能が提供されています。 Sandbox の実態は、Dockerイメージです。

以降、この Sandbox を使って、アプリケーションのデプロイがどんな感じかをみていきます。


Sandbox, ConductR-CLI の導入

事前に以下のツールが必要になる。

また、Sandboxでクラスタ構成にしたり、ログ機能を有効にしたりすると、メモリが足りなくなるので、docker-machine のメモリを 2-3GB に上げておくといい。

以下は、default のdocker-machine を作り直す例。docker-machineを消すと、今まで pull したdockerイメージも消えるので、すでにdockerやっている人は注意。

docker-machine rm default

docker-machine create -d virtualbox --virtualbox-memory 3072 -virtualbox-cpu-count 2 default

また、conduct-cli は sbtプラグインを入れることで、sbtからも使えるようになる。

コマンド体系が微妙に異なるが、できることは同じなので、どちらでやってもいい。

build.sbt に、Sandboxのバージョンを追記し、

SandboxKeys.imageVersion in Global := "x.x.x" // 最新バージョンを記載

plugin.sbt に、Sandboxプラグインと、ConductRビルド用のプラグインを設定する。

addSbtPlugin("com.typesafe.conductr" % "sbt-conductr-sandbox" % "1.4.2")

addSbtPlugin("com.typesafe.sbt" % "sbt-lagom-bundle" % "1.0.3")


Sandboxでアプリケーションを動かしてみる

準備が整ったので、sbt 上でビルドと Sandbox へのデプロイを行ってみる。


バンドルのビルド

ConductR向けのビルド成果物を作るには、 bundle:dist を実行する。

> bundle:dist

lagomの各 impl プロジェクトがビルドされ、 hellowold-impl/taget/bundle/hellostream-impl-v1-52750eb339a4181ff4aacc2e24defebe8c0dfc341a5704398d230278fa1c75a3.zip みたいなハッシュ値付きのzipファイルができる。

ConudctR ではこのようなファイルをバンドルと呼び、1つのバンドルが管理対象のモジュールになる(Warみたいなもの)

ついでに、cassandraの設定ファイルもバンドルにできる。

cassandra-configuration:dist

これも、target/bundle-configuration/cassandra-configuraton-648048ed3fccd17937c7962fc0303a50a2a70854f4dbf5a3251ae7281c6b5300.zip みたいなファイルができる。


Sandbox の起動・バンドルのロード

sbt 上で、 sandbox run と入力すると、 1台構成で ConductR のDockerコンテナが起動する。

(dockerをラップしている。dokcer ps コマンドを打つと、Docker コンテナが動いているのがわかる)

オプション次第で、クラスタ構成や機能追加ができる。

以下は3台クラスタで、かつ、ログ機能と可視化機能を有効にするコマンド。

こうするとDockerコンテナが3つ起動する。

また、ログ機能を有効にしないと、ログが見れないので、ログ機能は必須といっていい。

> sandbox run --nr-of-containers 3 --with-features logging visualization

続いて、conduct コマンドでバンドルのロードを行う。

conduct コマンドは IP をオプションで指定して、操作を行うサーバを指定するのだが、省略した場合で、Sandboxが起動している場合は、Sandbox に対して操作を行う。

まずは、cassandra のロード

> conduct load cassandra target/bundle-configuration/cassandra-configuraton-648048ed3fccd17937c7962fc0303a50a2a70854f4dbf5a3251ae7281c6b5300.zip

次に、lagomサービスのロード

プロジェクト名を入れて、conduct load をいれて TAB を押すとバンドルファイル名が補完されるので、それをロードする。

> helloworld-impl/conduct load <TAB>


バンドルの実行

conduct コマンドを使って、実行状況の確認や実行を行う。

conduct info でロードされたバンドルや実行数がわかる。

> conduct info

ID NAME #REP #STR #RUN
a89605b helloworld-impl 3 0 0
52750eb hellostream-impl 3 0 0
1f869c5-648048e cassandra 3 0 0
3513ff5 conductr-kibana 3 0 1
0a1abfa visualizer 3 0 1
5f28a5f conductr-elasticsearch 3 0 1

conduct run <IDの先頭数文字 or NAME> でバンドルが実行される。

# cassandra(ID 1f869c5-648048e) の起動

> conduct run 1f89
# または
> couduct run cassandra

バンドルがどのクラスタで動くかは自動的に調整される。

また、複数のクラスタでバンドルを実行することもできる。

# 3クラスタで実行

> conduct --scale 3 hellostream-impl

この場合の負荷分散なども、conductRがやってくれる。

エンドポイントの一覧は、conduct services で見れる。

> conduct services

SERVICE BUNDLE ID BUNDLE NAME STATUS
http://127.0.0.1:9200 5f28a5f conductr-elasticsearch Running
http://:5601 3513ff5 conductr-kibana Running
http://:9000/api/hello?preservePath a89605b helloworld-impl Running
http://:9000/hellostream 52750eb hellostream-impl Running
http://:9000/hellostream 52750eb hellostream-impl Running
http://:9000/hellostream 52750eb hellostream-impl Running
http://:9000/hellostream?preservePath 52750eb hellostream-impl Running
http://:9000/hellostream?preservePath 52750eb hellostream-impl Running
http://:9000/hellostream?preservePath 52750eb hellostream-impl Running
http://:9000/path/hello?preservePath a89605b helloworld-impl Running
http://:9200/elastic-search 5f28a5f conductr-elasticsearch Running
http://:9999 0a1abfa visualizer Running
tcp://:9042/cas_native 1f869c5-648048e cassandra Running
tcp://:9300 5f28a5f conductr-elasticsearch Running

実行時のログなどは、

conduct logs <ID or NAME>conduct events <ID or NAME> で見ることができる。

(ただし、logging機能を有効にしている場合)


visualization

visualization を有効にしていると、 9999番ポートで、以下のスナップショットような、クラスタ(青丸)とクラスタにロードされたバンドル(小さい緑丸)、動いているバンドル(大きい緑丸)が可視化される機能が追加される。

未来的でかっこいい。

lagom-4-vlisualizer.png


logging

logging 機能を有効にしていると、 クラスタまたぎのログが、condcut logs で確認できるほか、elastic-searchとkibanaによる可視化も行われる。

(でも、kibana はやったことがないのでよくわからない)

lagom-3-kibana.png


監視(Monitoring)

lightbend社の製品、 Monitoring を使うと、ConductR のパフォーマンスなど詳細な監視が行えるようだ。

時間があれば、もう少し調べてみたい。


windows でビルドする場合の注意

バンドルの実行パスが、windows だとバックスラッシュになってしまって、ConductRで動かないとい件で困っていたけど、sbt-bundle の仕様を見て実行パスを上書き可能だとわかって解決しました。

BundleKeys.executableScriptPath の設定をbuild.sbtに追加します。

lazy val helloworldImpl = project("helloworld-impl")

.enablePlugins(LagomJava)
.settings(
version := "1.0-SNAPSHOT",
libraryDependencies ++= Seq(
lagomJavadslPersistence,
lagomJavadslTestKit,
lagomJavadslCluster
),
BundleKeys.executableScriptPath in Bundle := (normalizedName in Bundle).value +"/bin/"+ (normalizedName in Bundle).value
)
.settings(lagomForkedTestSettings: _*)
.dependsOn(helloworldApi)

これに気づくまでの間、実行パスを上書きするパッチを送ろうか、windows捨てようか悩んでました。 sbt 怖い。


感想

lagom をやってると思ったら、ConudctR の調査に時間を取られました。

むしろ akka に対する興味が強くなった。 akka すごいよ。

ConductR は普通にアプリケーション作るとなると、こういうの必要だよな、というものが最初から入っている。価格にもよるけど欲しくなる。クラスタ構成作るのも簡単だし。

ただ、ConductR 以外の道も用意して欲しいとは思う。lighbend社 のプロダクトにロックインされすぎという印象。

一応lagomのサービスは、他のサービスへの依存がなければ、スタンドアローンで動く。

けれど、cassandra と連携させるには、ServiceLocatorが必要になるので、スタンドアローンで動かすことはほとんど無いと思う。

とはいえ、lagomはまだバージョン1だし、githubのリポジトリにも色々と要望がでているので、実行環境に関しては今後の展開次第だと思う。

lagom 以外のサービスとの連携方法は何か考える必要がありそう。

lagom のサービスだけで作ったアプリケーションなら、サービス間の連携では、位置透過性が得られるのでリモート呼び出しを考える必要がなく、非常に楽。

マイクロサービスアーキテクチャは、サービス同士を疎結合にすることでサービスごとに色々な技術を使えることが売りなので、lagom と その他の技術のサービス(Spring-boot とか Railsとか)との連携も考えないといけない。

lagom 内は Akka で、lagomとその他のサービスは REST でみたいな感じにはなると思うが、その場合 lagomのサービス群を1つのサービスとみなして、ServiceRegistoryに登録するような方針にしたほうがいいんだろうな。

全体像が何となく見えてきたので、今度は何か作ってみようと思います。