ちょっと話題の記事

AWS App Mesh (with Fargate) 再入門

2021.09.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

おはようございます、もきゅりんです。

Shall we mesh ?

弊社コンサルティングには、今一度 AWS の各サービスを初心に返って、基本的な部分から見つめ直してみよう、解説してみようといったブログリレーという企画があるのですが、本稿はそれを模して、個人的な AWS App Mesh 再入門という体にしてみました。

どちらかというと、未来の自分のための備忘録と言えます。

これまで AWS App Mesh (以下 App Mesh) に入門されていなかった方も、すでに脱会されてしまった方も興味があれば再入門して下さい。

App Mesh とは何か

サービス(アプリケーション)間の通信制御と可視性を実現するサービスメッシュを提供する AWS マネージドサービス です。

ブラックベルトにならってまとめると以下のような機能があります。

  • HTTP通信のリトライやタイムアウト
  • 通信のトレーシングやログ、メトリクスの取得
  • TLSを使用した暗号化通信

なるほど。そうなんだ、それはすばらっっ!と理解できた貴方、ここまで読んで頂いてありがとうございました。

この先は不要でございます。

...そうなんだ。

何だかそれはありがたいような気がするけど、それが何なんですかね...?という方、多分自分も同じようなモヤモヤ感を最初抱いていました。

なぜ App Mesh (サービスメッシュ) が必要になってくるのかを抑えると、モヤモヤ感が薄れてくるはずです。

なお、この辺りは、ブラックベルトは勿論、 サービスメッシュは本当に必要なのか、何を解決するのか | AWS Summit Tokyo 2019 - YouTube で詳しく触れられていますので、できるだけ簡潔にまとめていきます。

なぜ App Mesh (サービスメッシュ)が必要になるのか

キーワードは「マイクロサービス」です。

マイクロサービスの概要 | AWS から、マイクロサービスとは、を引用します。

マイクロサービスは、小さな独立した複数のサービスでソフトウェアを構成する、ソフトウェア開発に対するアーキテクチャ的、組織的アプローチです。

(中略)

マイクロサービスアーキテクチャでは、アプリケーションは独立した複数のコンポーネントとして構築されます。
各コンポーネントは、1 つのサービスとして個別にアプリケーションプロセスを実行します。これらのサービスは、軽量の API を使用した、正確に定義されたインターフェイスを通じてやり取りします。

さまざまな機能がひとまとまりになっている一枚岩のモノリスアーキテクチャから、機能が一つ一つ独立されたマイクロサービス化されたアーキテクチャのイメージが下の図です。

micro_services

モノリスからマイクロサービスになることで必要になるものの例は以下になります。

マイクロサービスで必要になってくることの例

  1. サービス間通信の信頼性
    • サービス間はネットワーク経由での通信になる = 呼び出し失敗を前提とする
    • 呼び出し先サービスの位置は一定ではない = サービスディスカバリが必須
  2. サービス間通信の可観測性
    • マイクロサービス群から成す1つのシステムから問題や事象を抽出、可視化が必要
    • ログ・メトリクス・トレース情報などのフォーマットを共通にする必要
  3. 疎結合な実装方法、自律的チーム関係の維持
    • マイクロサービスの利点(俊敏性、柔軟性、デプロイの容易性など)を活かすために必須

以上のことより、冒頭に挙げたような機能が必要になってきます。

  • HTTP通信のリトライやタイムアウト
  • 通信のトレーシングやログ、メトリクスの取得
  • TLSを使用した暗号化通信

さらに、1,2の要件を満たしつつ、3の疎結合な実装方法、自律的チーム関係の維持の要件には、 サービス間において直接通信するのではなく、Envoyプロキシを介して相互通信することで実現します。

そして、この Envoyプロキシの管理を AWSマネージドサービスとして提供する のが、App Mesh です。

逆に言えば、これらの機能を満たすための Envoyプロキシの(手動対応しきれないような)動的な管理が必要ではない状況、 App Mesh を導入する必要はないかもしれません。

まずは、要不要を検討しましょう。

App Mesh の概要

App Meshの対象サービス

そんな App Mesh を利用できるサービスは現在(2021/9/12)、下記になります。本稿ではこの中から Fargate を中心として進めていきます。

  • AWS Fargate
  • Amazon ECS
  • Amazon EKS
  • Kubernetes on Amazon EC2
  • Amazon EC2 with Docker.

App Meshのコンポーネント

基本的なコンポーネントは、下図に描かれているものです。

それぞれ App Mesh の全体像と (Envoy) Proxy の図です。

appmesh_components

envoy_image

コンポーネント 機能
Mesh サービスメッシュ内に存在するサービス間のネットワークトラフィックの論理的な境界 (デフォルトでは、メッシュ外へのトラフィック転送しない)。他のアカウントとメッシュを共有することもできる。
Virtual services (図の左のサービスのように)直接的に仮想ノードによって提供されるサービス、または(図の右のサービスのように)間接的に仮想ルータを経由して仮想ノードによって提供される実際のサービスを抽象化する。
Virtual routers, routes 仮想サービスへのトラフィックを、ルートの宛先となる仮想ノードにトラフィックを分散するルータまたはロードバランサー
Virtual nodes ECS, Kubernetesサービスなどの検出可能なサービスへ充てる論理ポインター。 Envoy がプロキシとして介在する。リスナーポートやプロトコル、必要であればバックエンドを設定する。
Proxy メッシュ構成を読み取り、トラフィックを設定した通りに転送、制御する。仮想ノードや仮想ゲートウェイなどのApp Meshエンドポイントとして表される ECS タスクや k8s ポッド、EC2インスタンスにコンテナを追加する必要がある。
Virtual gateways 外部リソースのアクセスをメッシュ内リソースとをつなぐ。図の Client service からリクエストを受ける2つのProxy が1つの Proxy に統合されてルーティングするような機能。仮想ノードのようにサービスのサイドカーとしての Envoy ではなく、 Envoy 単体で gateways として機能する。

これらの詳細は、What Is AWS App Mesh? - AWS App MeshComponents of App Mesh やブラックベルトをご参照下さい。

ユースケース

リンクのGitHubにめっちゃ色々なパターンがあります。興味のあるものをご参照下さい。

まずは walkthroughs の howto-ecs-basics は internalALB -> Service Discovery -> App Mesh のように構成の変遷をしていくため、とっかかりとして理解しやすいのではないでしょうか。

弊社ブログでは以下のようなものを対応しています。

やってみる

Service Discovery から App Mesh Virtual gateways に切り替えてみる

過去このような構成で ServiceDiscovery を使った通信を行いましたが、この構成を App Mesh Virtual gateways に切り替えます。

この環境は構築済みの前提とします。

ecs_svd_archi

まず振り返りです。

ECS サービスで名前空間とサービス検出名が以下のように設定しました。(図の app_service.eg.com が app_sv.moqrin.com になります)

ecs_svd

Route53, CloudMap 上に名前空間とサービスレコードが作成されています。

route53_domain_c

cloudmap_records_c

route53_private_c

これから、以下の変更をできるだけコンソールを使って設定していきます。

  1. Mesh を作成
  2. Virtual nodeを作成
  3. Virtual serviceを作成
  4. Virtual gatewaysを作成
  5. イメージの更新
  6. タスク定義の作成
  7. サービスを作成
  8. Virtual router を作成

なお、AWSが用意してくれているApp Mesh Examples の方がケースもとても豊富で、数回スクリプトを叩くだけで環境がお手軽に構築されて、確認できます。

この節の意義としては、敢えて手動で設定していくことで何をやっているかを確認しやすくすることです。

ただし、(タスク定義の設定がしんどいので) AWS CLI は利用します。

ECS の場合、基本的な手順や設定は AWS AppMeshとAmazonECSの使用を開始する-AWSApp Mesh を参考にします。

構築する各コンポーネントと関連リソースの関係図

下図が構築していく概要図です。

appmesh-v1

作成していくと何が何やらこんがらがってくるので、図を振り返りながら作成していくと宜しいかと思います。

図の中に書かれている CloudMap については下記ブログに詳しく説明されています。

1. Mesh を作成

適当な名前で作成します。

mesh

2. Virtual nodeを作成

サービス検出(CloudMap)と仮想ノードが連携することが見て取れます。

v-node1_c

ヘルスチェックは設定しておきます。

v-node2

3. Virtual serviceを作成

先に Route53 で別のプライベートホストゾーンを作成します。

(ECS サービス検出で作成したプライベートホストゾーンを利用しても構いません)

仮想サービスで設定する予定のapp-sv.moqrin.local の名前解決さえできれば問題ないので、対応する適当なレコードを入力しています。

private_host_c

4. Virtual gatewaysを作成

メッシュ外側に位置する ALBとメッシュ内との窓口となる仮想ゲートウェイを作成します。

後ほど、この仮想ゲートウェイの実体となる Envoy を ECS サービスから起動させます。

ここでは、必要最小限の名前とプロトコル:ポートのみを選択して作成します。

App Mesh setup troubleshooting - AWS App Mesh にある通り、 Virtual gateway not accepting traffic on ports 1024 or less なので、仮想ゲートウェイのポートには注意です。(最初、80番ポートを充ててうまくいかない理由がわからず、困惑しました。。)

ALBのターゲットグループで開放するポートと合わせます。

v-gateway

続けて、ゲートウェイルートを作成します。

今回は宛先は1サービスですが、リクエストとの一致条件やリクエスト設定の書き換えなど、細かくトラフィックを複数の仮想サービスに分散させるルーティングも可能です。

HTTP / HTTP2 と grpc とでそれぞれ設定項目が異なります。

詳細は、Gateway routes - AWS App Mesh を参照下さい。

適当な名前を入力し、プロトコルタイプ、仮想サービス名を選択します。

一致条件は必ず1つ以上は入力しないといけないので、今回はパスの一致タイプだけ入力しました。

demo-app-gw-routes_c

5. アプリイメージの更新

X-Ray を導入したいので、こちらapp-mesh-gateway ブランチのイメージを ECR にプッシュします。(サービスメッシュを利用するにあたり、RESTful API に変更しています)

実運用での latest イメージ運用は非推奨なのですが、検証ということを言い訳にして雑にプッシュします。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com

docker build -t uwsgi-flask-restful-app .
docker tag uwsgi-flask-restful-app:latest xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/uwsgi-flask-restful-app:latest
docker push xxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/uwsgi-flask-restful-app:latest

6. タスク定義の作成

Virtual gatewayのタスク定義

Envoy コンテナのタスク定義については、手動で入力するには項目がかなり多いので、 AWS AppMeshとAmazonECSの使用を開始する-AWSApp Mesh のタスク定義の例を参考に json ファイルで対応します。

環境変数 APPMESH_RESOURCE_ARN には仮想ゲートウェイを識別する mesh/<メッシュ名>/virtualGateway/<仮想ゲートウェイ名> の値を設定する必要があります。

仮想ゲートウェイで設定したポート、ALBのターゲットグループで開放するポートにします。

ECS タスクロール、タスク実行ロールを更新します。

また ECS タスクロールには以下ポリシーを付与しておきます。

実環境で利用する際は、必要に応じて権限を限定しましょう。

  • CloudWatchFullAccess
  • AWSAppMeshEnvoyAccess
  • AWSXrayFullAccess

アプリのタスク定義を作成

同様にアプリのタスク定義の json ファイルを作成します。

APPMESH_RESOURCE_ARNmesh/<メッシュ名>/virtualNode/<仮想ノード名>を入力します。

ECR にプッシュされたイメージ、ECS タスクロール、タスク実行ロールを更新できたらタスク定義を作成します。

aws ecs register-task-definition --cli-input-json \
file://<path_to_json_file>/gw-envoy.json
aws ecs register-task-definition --cli-input-json \
file:/<path_to_json_file>/app-envoy.json

7. サービスを作成

サービスを実行する前にロググループを作成する必要があります。

aws logs create-log-group --log-group-name /ecs/demo-app-gateway
aws logs create-log-group --log-group-name /ecs/uwsgi-flask-restful-app

ロードバランサー直下となる仮想ゲートウェイのサービス検出名は、外部サービスがメッシュ内のリソースにアクセスするために使用する名前になります。

仮想ノードに関連付けられるアプリに関しては、既存のサービス検出名を選択すること、セキュリティグループは仮想ノードのリスナー:ポートを開放します。

X-Ray を確認すると以下のような図が出ます。

v1-xray

どこらへんで時間がかかっていることがあるのか、なども判別しやすいですね。

sub_segment

トレースリストから Segments を見るとリクエスト元やレスポンス結果、所要時間などの詳細も確認することもできます。

segment

8. Virtual router を作成

最後に仮想ルータを作って、複数の仮想ノードに転送するルートを作成してみます。

このような構成図になります。

appmesh-v2

まずは、もう1つ仮想ノードを作成します。

v-node-v2

仮想ノードのリスナーに合わせて、仮想ルータを作成します。

v-router_c

ルートを作成します。

App Mesh best practices - AWS App Mesh において、下記のように再試行ポリシーが推奨事項なので対応します。

再試行ポリシーの場合、maxRetries に少なくとも2の値を指定し、再試行イベントタイプをサポートする各ルートタイプの再試行イベントのタイプごとに、次のオプションを指定することをお勧めします。

  • TCP: connection-error
  • HTTPおよびHTTP / 2: stream-errorおよびgateway-error
  • gRPC: cancelled およびunavailable

v-router-route1_c

ゲートウェイとは異なり、書き換えはできないようですが、リクエストとの一致条件によって複数の仮想ノードにルーティングできます。

こちらは TCP 、 HTTP / HTTP2 と grpc とでそれぞれ設定項目が異なります。

(TCPでは一致条件が使えません)

詳細は、Routes - AWS App Meshを参照下さい。

特に条件指定はせず、2つの仮想ノードに振り分けるだけとします。

v-router-route2_c

適当に app.envoy.json の以下と APPMESH_RESOURCE_ARN とを更新して適当な名前でサービスを作成します。

      "environment": [
        {
          "name": "NAME",
          "value": "Fuga!"
        }
      ],

サービスが無事起動していることが確認できたら、最後に仮想サービスのプロバイダを仮想ノードから仮想ルータに変更します。

sv_provider_c

X-Rayの図は以下のようになりました。

v2-xray

さいごに

(今回メッシュ内でのTLS通信はしていませんが) 冒頭に挙げた以下のような機能の利用イメージをつかむことができたかと思います。

  • HTTP通信のリトライやタイムアウト
  • 通信のトレーシングやログ、メトリクスの取得
  • TLSを使用した暗号化通信

App Mesh をしっかりと活用するためには、それなりの規模 (各機能を分離してマイクロサービスのアーキテクチャにする必要性がある水準)が前提となり、気軽に導入するわけにもいかず、はっきとした課題解消イメージがなければ、とりあえず判断は先送りになると思います。そういった背景もあって、比較的に App Mesh の情報は少ないのだろうと考えます。

ただ今後、年と共に成長を重ねるサービスや AWS にリフトしてきたサービスは、(新しいサービスや技術が世に普及されたり、開発スピードを改善するために) モノリシックなアーキテクチャからマイクロサービスのアーキテクチャに移行せざるを得なくなる流れは強くなるのではないかと考えています。

そういったケースが増えてくる際、App Mesh をしっかり活かせるように気楽な再入門ができると助かるな、というのが今回ブログでまとめた動機になります。

必要に応じて、ちょくちょく追記をしていければ良いなーと考えています。

今回、手動でやって思いましたが、 App Mesh は設定項目が多くてコンソールだと厳しめですね...

以上です。

どなたかのお役に立てば幸いです。