SPECIALIST

多様な専門性を持つNRIデジタル社員のコラム、インタビューやインサイトをご紹介します。

BACK

App Mesh on EKS 機能調査

はじめまして、NRIデジタルの島です。
最近は主にクラウドネイティブ技術に関する調査・検証やR&Dの推進などのお仕事をしております。
今回はマイクロサービスアーキテクチャにおける「サービスメッシュ」として使用できる「AWS App Mesh(以下 App Mesh」について、機能調査をしてみたのでご紹介させていただきたいと思います。

サービスメッシュとは

近年、マイクロサービスアーキテクチャの採用が増えてきています。
マイクロサービスアーキテクチャは、1つのシステムに複数の小さなサービスを組み合わせて開発する手法であり、各サービス(チーム)ごとに独立して、個々に最適な言語やアーキテクチャ等を採用します。

※Martin Fowlerが定義する、マイクロサービスアーキテクチャが持つ特性の一つに「分散ガバナンス」がある
分散ガバナンス:サービスごとに言語やデータベースなどは統一されず、個別に適切なものが選択される

また、マイクロサービスでは、従来のモノリスなシステムとは異なり、各サービスの呼び出しはネットワークを介して行われることになります。

ネットワークは基本信頼できないものとして考えるべきであり、マイクロサービスにおいてはタイムアウト処理やリトライ処理等の通信制御が必要となります。
また、カスケード障害防止の為に障害発生サービスへのリクエストを遮断する機能や、通信の状況を可視化する為のトレーシング機能も必要になってきます。

※ マイクロサービス実現の為に必要な通信制御系機能
・ネットワーク遅延時のタイムアウト処理や通信エラー時のリトライ処理
・サービス間通信の暗号化や認証・認可
・ 障害サービスへのリクエストを遮断するサーキットブレーキングや流量制御
・ 通信の可視化(メトリック、ログ)や時間帯別の通信状況を把握する為のトレーシング

このような機能を各サービス(チーム)で個々に実装していくのはかなり大変です。
共通化し、共通ライブラリに組み込むとしても、各言語ごとに異なるライブラリに習熟する必要があり負担はかなり大きいと考えます。
また、そもそも言語によって適切なライブラリがなかったり、あってもメンテされてなかったり、共通部品として組み込んだ途端競合して不動作したりなんかも考えられますので、理想はアプリと完全に切り離された別プロセスで実現するのがベストではないでしょうか。

そこで登場したのがサービスメッシュです。
サービスメッシュはアプリケーションレベルの通信を、各サービス⾃⾝が制御するのではなく、インフラストラクチャーで制御できるようにする技術です。
具体的にはアプリケーション間の通信にプロキシを挟み、そのプロキシにてトラフィック入出力の通信制御を行うものです。

これにより、各サービスのアプリケーションにて個々に実装する必要はなくなり、共通的に全サービス同一の品質で通信制御処理を実現することができるようになります。
また、各サービス開発チームはビジネスロジックの開発に専念できるというメリットももたらします。

※CNCF TrailMapではStep5に定義されている
出典:CNCF TrailMap

AWS App Mesh

サービスメッシュのプロダクトは数多く存在していますが、k8sベースではIstio(データプレーンはEnvoy)がデファクトになりつつあるように思います。

※CNCF LandScape
出典:CNCF LandScape

一方、各クラウドベンダーもサービスメッシュのマネージドサービス化を進めており、AWSでは「App Mesh」、GCPでは「Anthos Service Mesh」というサービスがGAされています。

弊社では昨年、クラウドネイティブ技術を採用した新規システムを商用リリースしております。
Amazon EKS(以下 EKS)上でコンテナベースのマイクロサービスアーキテクチャを採用しており、サービスメッシュも導入していますが、App MeshではなくIstioを採用しています。
(App Meshを採用しなかった背景としては、当時(2019末頃)は機能不十分で採用は時期尚早と判断した為です)

Istioのコントロールプレーンである「Istiod」はPodであり、可用性担保やスケール設計、障害時のトラブルシュートなどは自前で実施しなければなりません。
※Istio Architecture

前述したシステムは、現時点では利用ユーザを絞っている為不都合はありませんが、今後対象ユーザが増えることによるトラフィック増加や機能追加によるPod数の増加などにより運用上困難なことが表面化してくる可能性は高いです。

一方、App Meshはマネージドなサービスメッシュサービスであり、コントロールプレーンはAWSが管理する為、自前で管理する必要がありません。

App Meshでは、AWS側の責務でコントロールプレーンに対して下記のような対応をしてくれます。

・設定データの暗号化 (Envoy配布時、保存時)
・コントロールプレーンの可用性の担保(マルチAZ構成)
・コントロールプレーンの自動バックアップ、障害復旧
・Envoyコンテナイメージの脆弱性スキャンとパッチ適用
 etc…

運用管理コストの観点から見ると大きなメリットですので積極的に使っていくべきだと思っていますが、Istioと比較して現時点の機能充実度はどうでしょうか?

機能調査

サービスメッシュに必要と思われる機能と、Istio(v1.8)及びApp Mesh(v1.2)における対応状況についてまとめました。
※APIリファレンス等を元に著者作成(一部誤りがあるかもしれませんがご了承ください)


※App Meshロードマップ

上記機能一覧より、一部ピックアップして検証を実施してみました。

機能検証

・構成

検証時に構築した環境は下記のようなイメージです。
App Meshの初期構築は下記WorkShopを参考に実施して、比較的楽に構築できました。
※App Mesh WorkShop

App Meshを導入すると、VirtualNodeやVirtualService等のカスタムリソース(CRD)が作成され、それらをYamlで適用することによりPod間の様々な通信制御を設定できます。
各Pod内は上記の通り、Envoyコンテナがサイドカーとして注入され、CRDの設定に基づきトラフィックの入出力を制御します。

App Meshの基本コンセプトや各カスタムリソースの説明、APIリファレンスなどは下記を参照してください。
※ユーザガイド
※APIリファレンス
※APIリファレンス(App Mesh Controller)

・Gateway機能

通常、メッシュ外のリソース(ELB等)から直接メッシュ内のリソースへのアクセス出来ない為、メッシュ内外をつなぐGateway機能が必要となります。

Envoy Podを作成し、VirtualGatewayと紐づけることでGateway機能を実現できました。

※検証時のYamlイメージ

上記設定により、NLBへのトラフィックがメッシュ内のServer1まで到達するようになりました。

・Traffic Splittingルーティング

パーセンテージ指定で、複数サービスにトラフィックを分割してルーティングする機能です。

VirtualRouterの設定で、VirtualNodeに振り分ける割合を定義することで実現することができました。

※検証時のYamlイメージ

・Traffic Steelingルーティング(header値でのルーティング)

ヘッダーの値によりトラフィックをルーティングするサービスを制御する機能です。
(カナリアデプロイなどに利用可能)

VirtualRouterの設定で、ヘッダー値のマッチングを定義することで実現できました。

※検証時のYamlイメージ

なお、Header以外にもHTTP MethodやScheme(http/https)などでもルーティングは可能です。
※APIリファレンス HttpRouteMatch

・タイムアウト/リトライ

アプリケーションのコードに埋め込むことなく、App Mesh側(Envoy側)でタイムアウト・リトライを制御できます。
VirtualRouterの設定で、各VirtualNodeに対してタイムアウト値やリトライポリシーを定義することで実現できました。

※検証時のYamlイメージ

なお、リトライ実行のトリガーとなるイベントには下記が設定できます。

server-error – HTTP status codes 500, 501, 502, 503, 504, 505, 506, 507, 508, 510, and 511
gateway-error – HTTP status codes 502, 503, and 504
client-error – HTTP status code 409
stream-error – Retry on refused stream

・サーキットブレーカ ※プレビュー版

通信先のサービスに対してエラーの閾値を設定し、閾値を超えた場合に指定した時間の間そのサービスへのリクエストを遮断する機能です。

リクエスト先のVirtualNodeのListner定義にサーキットブレークの設定を定義をすることで実現できました。

※検証時のYamlイメージ

指定回数(3回エラー)でブレーカが発動し、指定時間(30秒)の間、エラー発生のPodへはリクエストが送信されないことを確認しています。

・コネクション数制限

通信先サービスに対してHTTPのコネクション数を制限する機能です。
(通信先サービスへの流量制御として利用可能)
VirtualNodeにコネクションプールの設定を定義することで実現できました。

・トレーシング

トラフィックのトレース情報をX-Ray等へ転送し、トレーシング情報を可視化する機能です。
下記手順にてX-Rayでトレース情報を参照することができました。

#権限付与
AWSXRayDaemonWriteAccessポリシーをWorkerNodeへ付与
#X-Rayへのトレーシング機能有効化
> upgrade -i appmesh-controller eks/appmesh-controller --namespace appmesh-system --set tracing.enabled=true --set tracing.provider=x-ray
#Deploymentを再起動
> kubectl rollout restart deploy

上記により、Pod内コンテナにX-Ray Agentが注入され、3コンテナとなります。
(アプリコンテナ+Envoyコンテナ+X-RayAgentコンテナ)

NAME             READY   STATUS      RESTARTS  AGE
server1-6f584c5cf-hkmr6   3/3     Running       0 2d1h

X-Ray

 

・GUI操作

AWS管理コンソールにて各種リソースの設定、設定内容の可視化ができることを確認しました。

App Mesh管理コンソール

 

以上、一部ですが機能検証結果を共有させていただきました。
App Meshを導入する際の一助になれば幸いです。

一点、App MeshをEKS上で利用するにあたり注意点がありますので最後に共有しておきます。

・PodのGracefulShutdown

k8sの仕様として、PodのRollingUpdate時や再起動時にはコンテナに対してSIGTERMシグナルが送信され、仮にPodがトラフィック処理中であっても終了してしまいます。

これを回避する為にアプリコンテナにGracefulShutdownの対応をいれることになります。
※SpringBoot例

 

しかしながら、このような対応を入れても、App Meshを利用するとサイドカーコンテナとしてのEnvoyがアプリコンテナの終了を待たず、先に終了してしまいます。
IstioではSidecarInjectorにpreStop処理を挿入することができる為、下記のようにアプリコンテナの終了を監視する処理をいれることが可能でした。

# IstioSidecarInjectorにPreStop設定
> kubectl edit cm -n istio-system istio-sidecar-injector
-----
lifecycle:
preStop:
exec:
command: ["/bin/sh","-c","while [ $(netstat -plnt | grep tcp | egrep -v 'envoy|pilot-agent|pilot-discovery' | wc -l | xargs) -ne 0 ]; do sleep 1; done"] # アプリコンテナのプロセスが停止するまでループ
-----

現在App Meshでは上記のような対応は出来ない為、別の方式を考える必要があります。
現時点では下記のような方法があります。

①Envoyコンテナに「preStopDelay」を設定する (preStopDelay)
→この方法を採用する場合、下記が課題となります。
・停止時にトラフィックがない場合も設定秒数待ってしまう為、デプロイ完了が遅くなってしまう
・各アプリ毎に必要な秒数が異なるが、個々に最適値を設定できない

②VirtualRouterにリトライ処理を入れる(新Podへの切り替わりを待つ)
→この方法がAWSが奨励している方法のようです。(App Mesh ベストプラクティス(Instrument all routes with retries))
ただ、この方法でも下記のような課題があると思います。
・最適なリトライポリシーを決定するのが難しい
・結果整合性となる為、アプリ側で冪等性を担保する作りにしておかなければならない(→マイクロサービスなので当然担保されているとは思いますが…)

以上、今のところ最適な(理想的な)方式はなさそうですが、上記を考慮した設計が必要となります。

さいごに

App Meshについて軽く調査・検証してみましたが、1年前と比べ機能が充実してきたという印象です。
ロードマップ上機能開発も活発に行われている為、これからマイクロサービスの導入を検討されてるプロジェクトがあればサービスメッシュ層としてApp Meshの採用は良い選択肢ではないかと思います。

もちろんIstioと比較すると現時点ではまだ機能差はあるので、機能的な観点でもしIstioを選択するのであれば、前述した通り全リソースを自前で管理しないければならないことを覚悟する必要があります。
この辺はトレードオフとなるので、各プロジェクトの機能要件、運用要件にて選択していただければと思います。

今回は基本的な機能の検証にとどまりましたが、引き続き詳細な動きなどについて確認していきたいと思います。

以上