谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

Dapr Advent Calendar 12日目 - Daprをk8s以外の分散環境で使う

こんにちは Dapr Advent Calendar 12日目です。昨日は @tmak_tw さんの Daprの回復性を支える仕組み 現状と展望 でした。実際に僕もインフラ側の問題起きた時にDaprを使ったアプリが自動的に復旧しない*1とか、サーキットブレイカーパターンが使えないとか、その辺りに課題感を感じていたので興味があるエントリーでした。

さて、今回からは「分散環境編」です。

Daprの運用環境を考える

これまでのエントリーでは、ローカルの開発環境でDaprを使ってきました。これを運用環境や試験環境で、複数台のサーバでクラスタリングして動作させるにはどうすれば良いのでしょうか。

Daprのドキュメントなどでは、基本的にk8sを使うことが推奨されています。実際に僕もk8sAmazon EKS)上で運用しており、k8sを使ったのは初めてだったものの噂に聞いていたほどは難しくなく、意外と扱いやすいなと思いました。とは言え、k8sを使うことにまだまだ抵抗がある人も多いと思います。

分散環境編の1回目は、まずk8sを使わずにDaprのシステムを運用する方法を考えてみます。

f:id:cero-t:20211212084402p:plain
今回動かすアプリケーション

分散環境の何が問題になるのか?

ローカルPCでの開発から、分散環境(特にクラウド)になることで、何が困るのか整理してみましょう。

  1. Daprを運用するために、Dockerをインストールする必要がある
  2. Invoke APIで名前解決をする際に、他のサーバにあるDaprアプリケーションを見つけられない

この2つです。

この2つの問題以外はDaprの設定ファイルに従って外部のサーバやサービスを利用するため、特に問題にはならないはずです。もちろん設定ファイルやアプリケーションのデプロイが面倒だという問題はあるでしょうけど、それはDaprの運用特有の問題ではありません。

では、この問題の解決方法を1つずつ考えていきましょう。

DockerなしでDaprを利用する

まずは1つめ、Daprの運用にDockerが必要となる件なのですが dapr without dockerググると、あっさりこんなドキュメントが見つかりました。

How-To: Run Dapr in self-hosted mode without Docker | Dapr Docs

Daprの初期化を dapr init --slim というオプション付きで実行すれば、DockerなしでDaprをインストールできるようです。RedisやZipkinはインストールされませんが、必要であれば別に立てるでしょうから問題はありませんね。

何だかあっさり解決してしまいました。

名前解決をどうするか

そして2つめの名前解決についてです。これに関して、このドキュメントを見ると、

Service invocation overview - Round robin load balancing with mDNS | Dapr Docs

名前解決のためのmDNSに関して、こんな記述がありました。

These instance can be on the same machine or on different machines.

複数のマシンでも問題なく動くそうです。今回はこれを試してみましょう。

複数のPCでDaprを動作させる

それでは2台のPCで、それぞれDockerなしでDaprを動かしてみることにします。2台もPCを用意できない人は、ピザでも食べながらこの続きを読んでください。

Daprのアンインストール

これまでDaprを使っていた場合は、いったんDaprをアンインストールします。

dapr uninstall

また、残っているDockerコンテナも停止します。

docker stop `docker ps -q --filter name=dapr_` 
docker rm `docker ps -aq --filter name=dapr_` 

さらに ~/.dapr もフォルダごと削除しました。

これでアンインストールは完了です。

Daprをスタンドアロンモードで初期化

続いて2台のPCでDaprをセットアップします。

Dapr CLIが入っていない場合は、ドキュメントに従ってDapr CLIインストールします。

docs.dapr.io

続いてDaprをスタンドアロンモードで初期化します。

dapr init --slim

インストールが完了したら、Dockerコンテナの一覧を見てみます。

docker ps -a --filter name=dapr_

出力結果はこうなっているはずです。

CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

通常のインストールではDaprやRedis、Zipkinなどのコンテナが作られたのですが、--slim オプションつきの場合は作られませんでした。

またcomponentsディレクトリを見ると

ls ~/.dapr/components

どうやら空のようです。

設定ファイルも確認しておきましょう。

cat ~/.dapr/config.yaml

ファイルはできているのですが、中はこうなっています。

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec: {}

何の設定もありませんね。

Dockerコンテナもなく、設定も最小限となっているDapr環境ができました。これを2台分セットアップします。

2台のPCでそれぞれアプリを起動してアクセス

それでは2台のPCでそれぞれアプリを起動してみましょう。

f:id:cero-t:20211212084422p:plain
これから動かすアプリケーション

ソースコード

サンプルアプリケーションのソースコードGitHubに置いてあります。

https://github.com/cero-t/dapr-advent-2021

今回はこのうち、3日目のエントリーで説明したInvokeの機能を使います。

2台で起動

まず1台目のPCでHello Worldのアプリケーションを起動します。

cd (GitHubのディレクトリパス)/dapr-advent-2021/hello
dapr run --app-id hello-app --app-port 8080 ../mvnw spring-boot:run

そしてもう一台のPCで、Invokeアプリケーションを起動します。

cd (GitHubのディレクトリパス)/dapr-advent-2021/invoke
dapr run --app-id invoke-app --app-port 8081 -- ../mvnw spring-boot:run -Dspring-boot.run.profiles=dapr

そして、invokeを起動した方のPCで、次のコマンドを実行します。

curl localhost:8081/invokeHello

結果が取得できたはずです。

{
  "remoteMessage": {
    "message": "Hello, world!"
  },
  "baseUrl": "http://localhost:58418/v1.0/invoke/hello-app/method"
}

2台のPCで別々に動いていたアプリも問題なく通信ができました。

念のため、Hello WorldアプリケーションをCtrl + Cで停止させた後、もう一度アクセスしてみます。

curl localhost:8081/invokeHello

今度はエラーとなったはずです。

{
  "timestamp": "2021-12-12T00:00:00.000+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "path": "/invokeHello"
}

Invokeアプリのコンソールにもエラーが出ているはずです。

== APP == 2021-12-10 00:00:00.000 ERROR 80260 --- [nio-8081-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 Internal Server Error: "{"errorCode":"ERR_DIRECT_INVOKE","message":"fail to invoke, id: hello-app, err: failed to invoke target hello-app after 3 retries"}"] with root cause

3回リトライしても hello-app にアクセスできなかったというエラーです。つまり、別のPCで動いていたHello Worldにアクセスしようとしていたことが分かりますね。

別々のマシンであっても、DaprはmDNSを使ってアクセスができるようです。

AWSでも運用できるか?

Amazon EC2でDaprを動作させる?

ここまでで、2台のPCで通信できることは確認できましたが、同じことをAWSのEC2でもできるのでしょうか。

試しにやってみたのですが、残念ながら、EC2インスタンス上で動くDaprアプリケーションから別のEC2インスタンス上で動くDaprアプリケーションにはアクセスができませんでした。

mDNSはマルチキャストが使える環境でなければ使えないのですが、AWSのEC2ではマルチキャストが使えないため(たぶん)、mDNSで通信することはできないようです。Invoke API以外のAPIは使えるようなのですが、Invoke APIこそがDaprの要なので、それがなくては話にならないですよね。

また、mDNSの代わりにHashiCorp Consulを使えば良いようですが、僕がConsulに不慣れなので、ちょっと諦めてしまいました。

ちなみにAzureにはManaged HashiCorp Cosul Serviceがあったので、それで試してみようとしたのですが、画面に従ってインスタンスの作成を進めていただけなのに、Consulのインスタンス作成に失敗してしまい、カッとなって諦めてしまいました。無念。

Amazon ECSでDaprを動作させる?

EC2がダメなら、ECS(Elastic Container Service)ならどうか、と思って探していたところ、こういうIssueを見つけました。

AWS ECS name resolution component · Issue #1197 · dapr/components-contrib · GitHub

Issueを斜め読みした限りでは、ECSのサービスディスカバリを使うのではなく、外部DNSを利用できるようにすることで、ECSにも対応させようという試みのようです。この辺りがきちんと対応されれば、ECSで運用することもでそうです。

まとめ

  • Daprの運用にDockerは必須ではない
  • マルチキャストが使える環境であれば、mDNSを用いて複数のサーバ上でDaprを運用することができる
  • AWSのEC2はマルチキャストが使えないためmDNSが使えない。Consulを使えば運用できる可能性がある
  • AWSのECSにはいずれ対応されそう

という感じで、名前解決のところがもう少し強化されれば、k8sを使わずに運用することもできそうですね。SpringユーザーとしてはEurekaに対応してくれれば、サッとサーバを立てて運用できるのにな、と思うのですけどね。自分で実装するしかないですかね😇

そんなわけで、少し寄り道したような形になりましたが、次回からは王道のk8sを使った運用について説明していきます。

それでは、また!

2021-12-18 追記

アンインストール手順について、うらがみさんからコメントをもらいました。知らなかった!

rm -rf コマンドをブログに書きたくなかったので、手で削除するなどと言ってごまかしてましたが、dapr uninstall --all で削除するのが安全で良いですね。

*1:具体的にはk8sクラスタAWSのスポットインスタンスを使っていた時に、インスタンスの削除→再作成が行われたあと、Daprを使ったアプリが復旧しないことがありました