ECS + Fargate + gRPCを使ったマイクロサービス構成
ECS + Fargate + gRPCを使ったマイクロサービス構成:
バックエンドサーバは機能毎にマイクロサービスとして分割し、サーバ間通信には gRPC を使ってモダンな感じにしたい。
まず Node.js アプリケーションをコンテナベースにして、Code Pipeline + CodeBuild + ECR + ECS + Fargate で継続的デプロイ&オートスケールする仕組みを作った。
でもバックエンドの gRPC でサーバ間通信を行う部分で
この ALB を使って ECS タスクに対して負荷分散させる構成は gRPC を使わずに REST API でならうまくいった。
gRPC は HTTP/2 が必須となり、ALB も HTTP/2 に対応しているが、それはフロントエンドのみ。ALB によってバックエンドに転送する場合は強制的に HTTP/1.1 になってしまい、gRPC としての通信が失敗してしまう。
じゃあ ALB を諦めて CLB にするしかないか…と変更しようとしたが、ECS + Fargate を使う場合、ECS のネットワークモードが
つまり gRPC の負荷分散のために CLB を使うなら Fargate は諦めて ECS + EC2 で運用するということになり、インスタンス管理コストの低下という大きなメリットを享受できなくなってしまう。
それだったら gRPC は諦めて Swagger(OpenAPI) で REST API にするしかないかなぁという雰囲気になりつつあった。
悩んでいたところ、ECS サービスディスカバリが使えるのではないかと教えてもらった。
例えば ECS で
Route 53
複数値回答レコードはドメイン
試しに同じ VPC に AWS Cloud9 (起動直後から EC2 にログインした状態になるので確認するのに便利)を立ち上げ、ターミナルから
なお、Route53 側で Hosted zones の
あとはクライアント実装次第となるが、通常はラウンドロビンされた値が使われることになる。
ECS のタスク定義で Docker コンテナの HEALTHCHECK を指定できる。
ヘルスチェックが失敗するとタスクは異常と判定し、ECS はタスクを終了してから新しいタスクを自動起動してくれる。
これが ECS サービスディスカバリとも連携してくれるので、タスクが終了すると同時に、タスクの内部 IP アドレスに割り当てられた
バックエンドサーバは機能毎にマイクロサービスとして分割し、サーバ間通信には gRPC を使ってモダンな感じにしたい。
最初のインフラ構想
まず Node.js アプリケーションをコンテナベースにして、Code Pipeline + CodeBuild + ECR + ECS + Fargate で継続的デプロイ&オートスケールする仕組みを作った。でもバックエンドの gRPC でサーバ間通信を行う部分で
Unavailable, transport is closing というエラーになってしまいうまくいかなかった。この ALB を使って ECS タスクに対して負荷分散させる構成は gRPC を使わずに REST API でならうまくいった。
HTTP/2 の gRPC は ALB と相性が悪い
gRPC は HTTP/2 が必須となり、ALB も HTTP/2 に対応しているが、それはフロントエンドのみ。ALB によってバックエンドに転送する場合は強制的に HTTP/1.1 になってしまい、gRPC としての通信が失敗してしまう。じゃあ ALB を諦めて CLB にするしかないか…と変更しようとしたが、ECS + Fargate を使う場合、ECS のネットワークモードが
awsvpc 固定となり、ロードバランサーには ALB 以外を選ぶことができない。つまり gRPC の負荷分散のために CLB を使うなら Fargate は諦めて ECS + EC2 で運用するということになり、インスタンス管理コストの低下という大きなメリットを享受できなくなってしまう。
それだったら gRPC は諦めて Swagger(OpenAPI) で REST API にするしかないかなぁという雰囲気になりつつあった。
ECS サービスディスカバリを使うことで解決
悩んでいたところ、ECS サービスディスカバリが使えるのではないかと教えてもらった。 - How to setup Service Discovery in Amazon Elastic Container Service
- ECSでgRPC+ServiceDiscoveryな構成を試してみました
例えば ECS で
game-server というサービス名にし、3 つのタスクを起動した場合は 3 つの内部 IP アドレスそれぞれに game-server.local というドメイン名が割り当てられ、同一 VPC 内であればアクセスできるようになる。Route 53
| Name | Type | Value |
|---|---|---|
| game-server.local. | A | 10.0.0.1 |
| game-server.local. | A | 10.0.0.2 |
| game-server.local. | A | 10.0.0.3 |
game-service.local にアクセスすると、最大 8 つの IP アドレスをランダムでクライアントに返す。上記の場合は 10.0.0.1, 10.0.0.2, 10.0.0.3 の 3 つのアドレスが返る。 試しに同じ VPC に AWS Cloud9 (起動直後から EC2 にログインした状態になるので確認するのに便利)を立ち上げ、ターミナルから
dig コマンドを試してみると次のような結果となる。$ dig +short game-service.local 10.0.0.1 10.0.0.2 10.0.0.3
local. にチェックをいれ、Associated VPC に対象の VPC が含まれていないと参照できないので注意。 あとはクライアント実装次第となるが、通常はラウンドロビンされた値が使われることになる。
curl や wget だと 1 番目の値しか使われなかったが HTTP Client ライブラリの axios はラウンドロビンされて使われていた。
ECS サービスディスカバリとヘルスチェック
ECS のタスク定義で Docker コンテナの HEALTHCHECK を指定できる。ヘルスチェックが失敗するとタスクは異常と判定し、ECS はタスクを終了してから新しいタスクを自動起動してくれる。
これが ECS サービスディスカバリとも連携してくれるので、タスクが終了すると同時に、タスクの内部 IP アドレスに割り当てられた
game-server.local も削除され、ルーティングされなくなるのでとても便利。
コメント
コメントを投稿