App Mesh + Cloud Map1年運用で痛感した「これなら不要」な瞬間
EKSでApp MeshとCloud Mapを本運用1年。ドキュメントと現実の乖離、サイドカー初期化の罠、本当に必要な場面を技術者目線で解説します。
App Mesh + Cloud Map本番1年で見えた「本当の使いどころ」と「限界」
うちのチームがEKSで本格的にマイクロサービスに移行するときに、App MeshとCloud Mapを導入した。最初は「これで通信管理が楽になる」なんて楽観的に考えてた。でも1年運用してみたら、思ってたのと全然違う現実が見えてきたんですよね。
正直なところ、ネットに載ってるドキュメントと実際のプロダクション環境ってかなり乖離がある。今日はその辺りを忖度なく語りたいと思います。
App MeshとCloud Mapの基本的な役割
最初にざっくり整理しておくと、App MeshはサービスメッシュでKubernetes間のトラフィック管理をするもので、Cloud Mapはサービスディスカバリー層として動作するAWSのマネージドサービスなんです。
理想的には、App Meshがトラフィック制御(リトライ・タイムアウト・リクエスト分散)を担当して、Cloud Mapがサービスの場所を教える、という分業。でも実際に運用してみると…話がそう単純じゃないんですよね。
実は僕らは最初、ECS Service ConnectとApp Meshのハイブリッド構成で試行錯誤してた時期があります。ECS Service Connect導入してからのレポートでも書きましたが、その時点では「App Meshはいつ使うんだろう」って感じでした。
EKSだったからこそ、App Meshを本気で運用してみることにしたんです。そこから見えた現実は、なかなか厳しかった。
導入初期の「期待値と現実のズレ」
最初の3ヶ月は本当に大変でした。App Meshのコントローラーをインストールして、マイクロサービス群にサイドカープロキシ(Envoy)を注入する。Cloud Mapではサービスを登録する。理論上これで通信が自動で管理される、はずだった。
ただね、現実はこんな感じでした:
1. サイドカーの初期化時間が思ったより長い
EnvoyサイドカーがPodに注入されて実際に通信できるようになるまで、10~15秒かかるんです。小さいPodなら気にならないけど、サービスの再起動が多い開発環境では、このタイミングでコネクションエラーが頻発する。
テストやデプロイパイプラインで何度も失敗しました。「なんで通信が一瞬失敗するんだ」って調査に1週間費やしたことも…。この時点ではApp Meshの動作さえよく理解していなかったので、アプリケーション側のバグだと思い込んでました。
2. Cloud Mapの”登録遅延”問題
KubernetesのPodが起動して、実際にCloud Mapに登録されるまでに遅延がある。その間、他のサービスからのリクエストがDNSキャッシュミスで迷子になるんですよね。特に高速でのオートスケーリングや、カナリアデプロイ時に顕著です。
実装初期、この遅延を無視してた結果、本番で5分間くらい一部のトラフィックが「未登録サービス」に飛んでいました。顧客から「決済画面に繋がらない」って報告受けて初めて気づいた…。結構冷や汗ものでした。
3. Envoyの設定ファイル生成が複雑
App Meshは、Kubernetes CustomResourceDefinition(CRD)で通信ルールを定義します。VirtualNode・VirtualRouter・VirtualServiceといった概念を理解する必要があるんです。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualService
metadata:
name: api-service
namespace: default
spec:
awsName: api-service.default
provider:
virtualNode:
virtualNodeRef:
name: api-service-v1
これだけだと見えないんですが、100個のマイクロサービスがあると、100個の関連CRDを管理することになる。GitOpsでやろうとすると、マニフェストの地獄です。誰が何を管理してるのか、依存関係がどうなってるのか、本当にわからなくなる。
本番1年で学んだ「本当に効く使い方」
半年くらいで諦めかけた時期がありました。「もうApp Mesh外そうぜ」って話も出てた。でも、そっからの半年で運用パターンが固まってきたんです。
①リトライロジック集約化での障害対応短縮
アプリケーション層でリトライしていたコードを全部削除して、App Meshのリトライポリシーに統一しました。
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualRoute
metadata:
name: api-service-route
spec:
meshName: production-mesh
virtualRouterName: api-service-router
routes:
- name: api-route
httpRoute:
match:
prefix: /
action:
weightedTargets:
- virtualNodeRef:
name: api-service-v1
weight: 90
- virtualNodeRef:
name: api-service-v2
weight: 10
retryPolicy:
maxRetries: 3
perRetryTimeout: 1s
httpRetryEvents:
- server-error
- gateway-error
これにしたら、「どこのマイクロサービスがリトライロジック持ってるのか曖昧」という状態がなくなった。本番障害で「リトライが足りない」「リトライが多すぎる」って判断も統一できるようになった。
結果として、月30件くらいあったタイムアウト関連の一時的な障害が月5~7件に減りました。これは地味に大きいんですよ、運用側の疲労度が全然違う。
②トラフィックシフト(カナリアデプロイ)の安定化
App Meshのweighted targetsで、段階的なデプロイができるんです。うちは新しいバージョンを5%から始めて、24時間かけて段階的に100%に上げるパターンを確立しました。
初期状態:新バージョン5%
v1: 95%, v2: 5%
6時間後:新バージョン25%
v1: 75%, v2: 25%
12時間後:新バージョン50%
v1: 50%, v2: 50%
24時間後:新バージョン100%
v1: 0%, v2: 100%
これで「新バージョンにバグがある」ことに気づく率が飛躍的に上がりました。以前は一気に100%切り替えて、その瞬間にアラートが鳴って焦るパターンが多かったんです。この段階的な手法だと、小さな異常も早期に検知できるので、ロールバック判断も素早くできる。
個人的には、このメリットだけでもApp Mesh導入の価値があると思ってます。
③タイムアウト・コネクション設定の可視化
App Meshを使う前は、各マイクロサービスのコードに散らばってました。httpclientのタイムアウト、コネクションプール設定、再接続ロジック…バラバラだった。あるサービスは5秒、あるサービスは30秒みたいなカオス状態です。
App Meshで一元管理したら、整合性がとれるようになった。
connectionSettings:
tcp:
idleTimeout: 30s
http:
idleTimeout: 15s
http2:
maxRequests: 100
これを実装してから、謎のコネクション切断エラーがほぼ消えました。実装されてることを知らないチームもいっぱいいたので、「あ、App Meshに設定してあります」って教えるだけで問題解決することもあった。
正直に語る「限界と落とし穴」
ここからが本音の話です。1年使ってきて、これはApp Meshじゃできないな、って思うことがあります。
App Meshは「トラフィック管理」であって「リソース管理」ではない
Pod間のレイテンシが高い、メモリ使用率が異常…こういう問題は、App Meshでは解決しません。理由は単純で、メッシュはネットワーク層とアプリケーション層の間に存在するだけだから。
Podリソースの問題なら、KarpenterやEKSコスト最適化で対応すべき。うちはこれを理解するまでに3ヶ月かかりました。その間、App Meshの設定をいじくり回してたんですよ。本当に無駄だった。
Cloud Mapの登録遅延を完全には解決できない
技術的には、Pod起動時にプリウォーミング的な仕組みを入れることで、登録を少し早くできます。でも「100%即座に利用可能」は無理ですね、これはAWSの制限というより物理的な限界かもしれない。
結果、僕らは重要なサービスに対しては、readinessProbeをより厳しい条件に設定しました。Cloud Mapに登録されるまで、トラフィック送信対象にならないようにね。
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
failureThreshold: 3
これでほぼ問題消えたけど、「App Meshが自動でやってくれる」わけじゃない。結局アプリケーション側での工夫が必要。メッシュだけあれば大丈夫って考えはナイーブです。
マニフェスト管理が地獄になる
50個のマイクロサービス × 複数の環境 = 数千行のYAML。GitOpsで管理してますが、変更の波及を追いきれないことがあります。
テンプレート化(KustomizeやHelm)を入れると、今度は「テンプレートが複雑すぎて誰も理解できない」という別の地獄が…。新しいエンジニアが入るたびに「このテンプレートって何やってるんですか」って質問されます。
うちは最終的に、Kustomizeの基本的な重ね合わせだけに限定して、複雑なロジックはPythonスクリプトで生成するという、ちょっと無理やりな方法に落ち着きました。完璧なソリューションじゃないですが、チーム全体が理解できるので運用性はいい。
AWS構成図:うちの本番環境
graph TB
subgraph VPC["VPC (us-east-1)"]
subgraph AZ1["Availability Zone 1a"]
EKS_Node1["EKS Node<br/>t3.large"]
subgraph Pod_Group1["Pods"]
Pod_API1["API Service<br/>Pod v1"]
Pod_API2["API Service<br/>Pod v2"]
end
end
subgraph AZ2["Availability Zone 1b"]
EKS_Node2["EKS Node<br/>t3.large"]
subgraph Pod_Group2["Pods"]
Pod_DB1["DB Service<br/>Pod"]
end
end
subgraph AppMesh["App Mesh (production-mesh)"]
VS1["VirtualService<br/>api-service"]
VR1["VirtualRouter<br/>api-router"]
VN1["VirtualNode<br/>api-v1"]
VN2["VirtualNode<br/>api-v2"]
end
subgraph CloudMap["AWS Cloud Map"]
SD["Service Registry<br/>api-service.local"]
end
ALB["ALB<br/>Ingress"]
end
subgraph Monitoring["Monitoring & Control"]
AppMesh_Controller["App Mesh Controller"]
CloudWatch["CloudWatch<br/>Metrics & Logs"]
end
Client["External Client"]
Client -->|HTTP/HTTPS| ALB
ALB -->|Route to Pods| EKS_Node1
ALB -->|Route to Pods| EKS_Node2
Pod_API1 -->|Service to Service<br/>via Envoy Sidecar| VS1
Pod_API2 -->|Service to Service<br/>via Envoy Sidecar| VS1
VS1 --> VR1
VR1 -->|Weighted<br/>90/10| VN1
VR1 -->|Weighted<br/>90/10| VN2
VN1 --> Pod_DB1
VN2 --> Pod_DB1
Pod_API1 -->|Register/Query| SD
Pod_API2 -->|Register/Query| SD
Pod_DB1 -->|Register| SD
AppMesh_Controller -.->|Manage CRDs| AppMesh
AppMesh_Controller -.->|Inject Sidecars| EKS_Node1
AppMesh_Controller -.->|Inject Sidecars| EKS_Node2
VS1 -.->|Export Metrics| CloudWatch
SD -.->|DNS Queries Logged| CloudWatch
運用で工夫してる3つのこと
①段階的なロールアウトパイプライン
新しいサービスやバージョンをApp Meshに登録する前に、スタンドアロン環境で1週間テストします。その時点では、App Mesh CRDを作らない。本当に必要な通信ルールだけが見えるようになってから、初めてメッシュに乗せるんです。
これで「メッシュ管理の負担」と「実際のメリット」のバランスが取れるようになりました。全部をメッシュに乗せようとするから複雑になるんですよ。
②Cloud Mapのヘルスチェック設定の厳格化
Cloud Mapがサービスを登録したけど、実際には応答していない…これを避けるために、ヘルスチェック間隔を3秒に短縮しました。
HealthCheck:
Type: HTTP
Path: /health
Port: 8080
Interval: 3000ms # デフォルトより厳しい
Failures: 2 # 2回失敗で登録削除
これでダッシュボード上に「生きてない」サービスが登録されているという悪夢がなくなった。
③App Mesh CRDのバージョニング管理
マニフェスト変更の影響範囲を追跡するために、各VirtualServiceにlastModifiedタグを付けてます。
metadata:
labels:
app: api-service
version: v1
last-modified: "2026-08-07T14:30:00Z"
modified-by: "deployment-pipeline"
これで「いつ誰がルールを変えたのか」が追える。本番障害の原因特定が早くなります。「あ、このVirtualRouterを昨日変更したから、そっちが原因かも」みたいに絞り込める。
App MeshとCloud Mapが「本当に活躍する場面」
正直に言うと、全てのマイクロサービスに必要ではないんです。うちの運用を見ると:
| 使い分け | 向いてる/向いてない |
|---|---|
| 複数バージョンの並行運用(カナリアデプロイ) | ✅ App Mesh最高 |
| リトライロジックが複雑なサービス間通信 | ✅ App Meshの出番 |
| サーキットブレーカーパターン的な制御が必要 | ✅ App Meshで実装可能 |
| 動的スケーリングが多いサービス | ✅ Cloud Mapの活躍 |
| オンプレミスとハイブリッドで接続 | ✅ Cloud Map便利 |
| DNSベースの負荷分散が必要 | ✅ Cloud Mapで実装 |
| シンプルなHTTPサービス間通信 | ❌ Kubernetes Serviceで十分 |
| エッジコンピューティングや低遅延が最優先 | ❌ オーバーヘッド大きい |
| 学習フェーズの新しいチーム | ❌ 複雑さが負担 |
今、同じ選択をするか?
正直に言うと、また同じ状況なら…App Meshは採用するけど、段階的に導入します。
最初の3ヶ月は、本当に必要なサービス間通信(支払い系・認証系)だけに絞る。そこで効果を実感してから、段階的に広げる。全サービスに一気に適用しない。この教訓は本当に大事です。
Cloud Mapは、EKS Cluster Autoscalerとセットで考えると、スケーリング時の名前解決が確実になるメリットが大きいんですよ。ただしスケーリングしていない環境なら、Kubernetes Service Discoveryだけで十分。無駄なコンポーネント増やすと、トラブル時の調査ポイントが増えるので。
イベント駆動アーキテクチャのような非同期通信が主流のシステムなら、App Meshの出番は限られます。むしろKafkaやSQSでの通信制御がメインになりますから、そこに注力すべき。
まとめ
1. App Mesh + Cloud Mapは「銀の弾」ではない — トラフィック管理は得意だが、リソース最適化やアプリケーション設計は別問題。混同するとハマります。
2. 導入は段階的に — 全サービスに一気に適用するのは失敗への最短路。本当に必要なところから始めて、効果を確認してから拡大する。焦らないこと。
3. マニフェスト管理が鍵 — 数十~数百のCRDを管理するときは、テンプレート化とバージョニングが必須。これがないと保守地獄です。
4. Cloud Mapの遅延は「仕様」として受け入れる — 完全な即座性は無理。readinessProbeなどアプリケーション側での工夫が必要。メッシュだけで完結すると思うな。
5. 本当に活躍する場面は限定的 — カナリアデプロイ、複雑なリトライロジック、複数バージョン管理。シンプルなサービス間通信なら過度な投資です。
次のステップとして、僕らはSLI/SLO設計との組み合わせで、App Meshのメトリクスをもっと活用する予定です。メッシュレベルでの可観測性を高めることで、運用負荷がさらに減るんじゃないかなと。
1年運用して、App Meshは「必ず必要」ではないけど、「使いこなせると本当に強い」ツールだという結論に至りました。完璧ではないけど、使い方次第で本当に価値あるものになる。それが正直な評価です。