App Mesh + Cloud Mapを1年本番運用して見えてきた「使いどころ」と「限界」【EKS実録2026】

「サービスメッシュ入れれば全部解決」と思ってませんか?EKS上でApp Mesh + Cloud Mapを1年回して見えた本音——何が機能して、どこで詰まったか率直に書きます。

先日チームの振り返りで「App Mesh、本当に必要だったんだっけ?」という話になって、導入から1年以上経ってようやく正直に答えられるようになってきた。結論から言うと「使いどころは確実にある。ただし思ってたよりコストが高い」というのが本音だ。

うちのチームは2025年初頭にEKS上のマイクロサービス基盤を全面刷新するタイミングで、App Mesh + Cloud Mapのスタックを採用した。当時は「サービスメッシュを入れれば可観測性と通信制御が一気に解決する」という甘い期待があって、正直なところ設計よりもノリで決めた部分が大きかった。その後の1年でどんな痛みがあって、何が本当に機能したかを書いておきたい。

余談だが、EKSのコスト最適化に興味があればEKS コスト最適化2026|Spot/On-Demand戦略とKarpenter活用も読んでみてほしい。App Meshのオーバーヘッドとコストは密接に関係する話なので。

App Mesh + Cloud Mapの構成と実際に動かした基盤

実際に本番で動かしていた構成はこんな感じだ。

graph TB
  subgraph VPC["VPC (10.0.0.0/16)"]
    subgraph AZ1["AZ-a"]
      subgraph EKS_A["EKS Node Group (AZ-a)"]
        POD_API_A["api-service Pod\n+ Envoy Sidecar"]
        POD_ORDER_A["order-service Pod\n+ Envoy Sidecar"]
      end
    end

    subgraph AZ2["AZ-c"]
      subgraph EKS_B["EKS Node Group (AZ-c)"]
        POD_API_B["api-service Pod\n+ Envoy Sidecar"]
        POD_ORDER_B["order-service Pod\n+ Envoy Sidecar"]
      end
    end

    subgraph MESH["App Mesh Control Plane"]
      VIRTUAL_SVC_API["VirtualService\napi.internal"]
      VIRTUAL_SVC_ORDER["VirtualService\norder.internal"]
      VIRTUAL_NODE_API["VirtualNode\napi-node"]
      VIRTUAL_NODE_ORDER["VirtualNode\norder-node"]
      VIRTUAL_ROUTER["VirtualRouter\n(重み付きルーティング)"]
    end

    subgraph CLOUDMAP["Cloud Map"]
      NS["Private DNS Namespace\ninternal.example.com"]
      SVC_REG_API["Service Registry\napi-service"]
      SVC_REG_ORDER["Service Registry\norder-service"]
    end

    subgraph OBSERVABILITY["可観測性スタック"]
      XRAY["X-Ray"]
      CW["CloudWatch Container Insights"]
    end
  end

  subgraph EXTERNAL["External"]
    ALB["ALB (Ingress)"]
    CLIENT["クライアント"]
  end

  CLIENT --> ALB
  ALB --> POD_API_A
  ALB --> POD_API_B
  POD_API_A -->|Envoy経由| VIRTUAL_SVC_ORDER
  POD_API_B -->|Envoy経由| VIRTUAL_SVC_ORDER
  VIRTUAL_SVC_ORDER --> VIRTUAL_ROUTER
  VIRTUAL_ROUTER --> VIRTUAL_NODE_ORDER
  VIRTUAL_NODE_ORDER --> NS
  NS --> SVC_REG_ORDER
  SVC_REG_ORDER --> POD_ORDER_A
  SVC_REG_ORDER --> POD_ORDER_B
  POD_API_A -.->|トレース| XRAY
  POD_ORDER_A -.->|トレース| XRAY
  XRAY --> CW

Cloud MapのPrivate DNS Namespaceを使って、order-service.internal.example.com みたいな名前でサービスディスカバリするのが基本設計だ。App MeshのVirtualServiceがこの名前解決をラップして、Envoyサイドカーが実際のトラフィック制御を担う。

実際のCloud Map + App Mesh連携のマニフェストはこんな感じで書いていた。

# Cloud Map namespace (Terraform)
resource "aws_service_discovery_private_dns_namespace" "internal" {
  name        = "internal.example.com"
  description = "Private namespace for service discovery"
  vpc         = aws_vpc.main.id

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

resource "aws_service_discovery_service" "order_service" {
  name = "order-service"

  dns_config {
    namespace_id = aws_service_discovery_private_dns_namespace.internal.id

    dns_records {
      ttl  = 10
      type = "A"
    }

    routing_policy = "MULTIVALUE"
  }

  health_check_custom_config {
    failure_threshold = 1
  }
}
# App Mesh VirtualNode (Kubernetes CRD)
apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
  name: order-service
  namespace: production
spec:
  meshRef:
    name: main-mesh
  serviceDiscovery:
    awsCloudMap:
      namespaceName: internal.example.com
      serviceName: order-service
      attributes:
        - key: ECS_TASK_DEFINITION_FAMILY
          value: order-service
  listeners:
    - portMapping:
        port: 8080
        protocol: http
      healthCheck:
        protocol: http
        path: /health
        healthyThreshold: 2
        unhealthyThreshold: 3
        timeoutMillis: 2000
        intervalMillis: 5000
  logging:
    accessLog:
      file:
        path: /dev/stdout

ここまではドキュメント通りに動いた。問題はここからだった。

1年間で踏んだ地雷とEnvoyのやっかいな現実

最初の1ヶ月で気づいたのがEnvoyサイドカーのリソース消費だ。うちの環境ではサービス数が15を超えたあたりから、Envoyが各Podで常時100〜150MBのメモリを使うようになった。小さいように見えるが、マイクロサービスが細かく分割されていると相当なオーバーヘッドになる。数字で見ると一目瞭然で、導入前後でPodのメモリ使用量はどのサービスも軒並み1.5倍以上に膨らんだ。

xychart-beta
  title "Envoyサイドカー導入前後のメモリ使用量(Pod平均)"
  x-axis ["api-svc", "order-svc", "user-svc", "payment-svc", "notify-svc"]
  y-axis "メモリ使用量 (MB)" 0 --> 600
  bar [180, 210, 160, 240, 150]
  bar [310, 355, 285, 390, 275]

これはKarpenterのNodePoolを調整して何とか乗り越えたが、当時は原因特定に数日かかった。Karpenterノードプロビジョニング最適化|2026年EKS完全ガイドでも触れているが、Envoyが入ると前提のリソース計算がかなり変わる。インスタンスタイプの選定からやり直しになったのはさすがに想定外だった。

もう一つ地味にしんどかったのが、Cloud MapとKubernetes DNSの二重管理だ。KubernetesにはCoreDNSが既にあるのに、Cloud MapのDNSと両方管理することになる。TTLの設定ミスや、ヘルスチェック設定のズレで、デプロイ後しばらくトラフィックが古いインスタンスに流れ続けることが2回あった。

ハマった設定を晒しておく。

# Cloud Mapのヘルスチェック状態確認
aws servicediscovery list-instances \
  --service-id srv-xxxxxxxxxx \
  --query 'Instances[*].{Id:Id,Health:Attributes.AWS_INIT_HEALTH_STATUS}'

# 出力例(トラブル時)
# [
#   { "Id": "10.0.1.45", "Health": "HEALTHY" },
#   { "Id": "10.0.1.46", "Health": "HEALTHY" },
#   { "Id": "10.0.2.12", "Health": "UNHEALTHY" }  # ← Podは削除済みなのに残ってた
# ]

# 手動で古いインスタンスを削除するときの対処
aws servicediscovery deregister-instance \
  --service-id srv-xxxxxxxxxx \
  --instance-id 10.0.2.12

Cloud MapはKubernetesのEndpointの変化を自動追跡してくれるが、Pod終了時の登録解除が遅延することがある。TTLを短くするのが第一の対策だが、短すぎるとDNSクエリが増えてコストが上がる。うちでは10秒に落ち着いたが、正直まだ最適解かどうか確信はない。このあたりの「どちらに転んでもコストがかかる」感じが、二重管理のしんどさを象徴していると思う。

一方でVirtualRouterを使った重み付きルーティングは、カナリアリリースで実際に助かったケースがあった。

apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualRouter
metadata:
  name: order-service-router
  namespace: production
spec:
  meshRef:
    name: main-mesh
  listeners:
    - portMapping:
        port: 8080
        protocol: http
  routes:
    - name: canary-route
      httpRoute:
        match:
          prefix: /
        action:
          weightedTargets:
            - virtualNodeRef:
                name: order-service-v2  # 新バージョン 10%
              weight: 10
            - virtualNodeRef:
                name: order-service-v1  # 旧バージョン 90%
              weight: 90
        retryPolicy:
          httpRetryEvents:
            - server-error
            - gateway-error
          maxRetries: 3
          perRetryTimeout:
            unit: ms
            value: 2000

Kubernetes標準のCanary実装よりずっとシンプルで、X-RayとのトレースIDの連携も自動で入るのが地味に便利だった。ただしVirtualRouterの設定ミスをすると無音で全トラフィックが落ちることがあって、一度ヒヤリとした経験がある。設定変更後は必ずX-Rayでトレースを確認する習慣がついた。

Cloud Map単体での使い方とECS混在環境での真価

App Meshを外してCloud Mapだけで使うケースも実は半年くらい並行して試していた。うちはECSサービスとEKSサービスが混在していた時期があって、そこではCloud Mapだけで統一のサービスディスカバリを提供するのが正解だった。App Meshの複雑さを抜きにすると、Cloud Map自体はかなりシンプルで扱いやすい。

sequenceDiagram
  participant EKS_POD as EKS Pod
  participant CLOUDMAP as Cloud Map
  participant ECS_SVC as ECS Service

  EKS_POD->>CLOUDMAP: DNS解決 legacy-service.internal.example.com
  CLOUDMAP-->>EKS_POD: 10.0.3.45:8080
  EKS_POD->>ECS_SVC: HTTP リクエスト
  ECS_SVC-->>EKS_POD: レスポンス

  Note over CLOUDMAP: ECSはサービス定義で自動登録<br/>EKSはAWS Cloud Map Controller for K8sで登録

EKS側でCloud Map Controllerを使うと、KubernetesのServiceリソースの変化が自動でCloud Mapに反映される。これは思ったより安定していて、ECS側からEKSのサービスをDNSで引けるようになったのはかなり便利だった。ECS/EKS混在という一見面倒な状況で、逆にCloud Mapの価値がいちばん光ったと思っている。

# aws-cloud-map-controller-for-k8s の設定例
apiVersion: v1
kind: ConfigMap
metadata:
  name: cloud-map-controller-config
  namespace: aws-cloud-map-controller-system
data:
  controller-config.yaml: |
    clusterName: production-eks-cluster
    region: ap-northeast-1
    namespaceConfig:
      - namespaceName: internal.example.com
        namespaceId: ns-xxxxxxxxxxxx
        serviceFilter:
          labelSelector:
            cloudmap-register: "true"  # このラベルがついたServiceだけ登録

機能面とコストを各観点で整理するとこうなる。

観点App Mesh + Cloud MapCloud Map単体VPC Lattice
サービスディスカバリ
トラフィック制御△(L4のみ)◯(L7あり)
可観測性(トレース)◎(X-Ray自動)△(別途実装)◯(CloudWatch)
マルチクラスター対応
ECS/EKS混在
運用コスト(人的)高い低い中程度
インフラコスト高い(Envoy分)低い中程度
学習コスト高い低い中程度
2026年時点のAWS推奨度△(ECSはService Connectへ)

ECSについてはECS Service Connectに移行して6ヶ月、App Meshの複雑さから解放された話でも書いたが、正直ECS + App Meshの組み合わせはもう新規採用しなくていいと思っている。EKS専用として使うかどうかという判断になってきた。

2026年時点の現実的な判断基準

1年本番運用して出てきた問いは「App Meshを今から入れるべきか?」だ。2026年時点では、AWSの方向性的にもVPC LatticeやECS Service Connectに重心が移っていて、App Meshの新機能開発は正直足踏み感がある。個人的にはすでに「枯れたサービスとして使い続けるか、移行コストを払うか」の二択フェーズに入っていると感じている。

意思決定のフローを整理するとこうなる。

flowchart TD
  START(["新規サービスメッシュ検討"]) --> Q1{"EKS専用?<br/>ECS混在あり?"}
  Q1 -->|"EKS専用"| Q2{"サービス数は?"}
  Q1 -->|"ECS混在あり"| Q3{"ECSはService Connect<br/>移行可能?"}
  Q2 -->|"5サービス以下"| REC1["Kubernetes標準機能<br/>(Service/CoreDNS)で十分"]
  Q2 -->|"6〜20サービス"| Q4{"L7トラフィック制御<br/>必要?"}
  Q2 -->|"20サービス以上"| Q5{"Istio/Cilium<br/>検討可能?"}
  Q4 -->|"不要"| REC2["Cloud Map Controller<br/>+ Prometheus/Grafana"]
  Q4 -->|"必要"| Q6{"マルチクラスター<br/>要件あり?"}
  Q6 -->|"なし"| REC3["App Mesh<br/>(既存踏襲なら許容)"]
  Q6 -->|"あり"| REC4["VPC Lattice<br/>または Cilium Mesh"]
  Q5 -->|"可能"| REC5["Cilium Service Mesh<br/>(eBPF、Envoy不要)"]
  Q5 -->|"難しい"| REC3
  Q3 -->|"可能"| REC6["ECS: Service Connect<br/>EKS: Cloud Map Controller"]
  Q3 -->|"難しい"| REC7["Cloud Map統一<br/>App Meshは最終手段"]

  style REC1 fill:#d4edda
  style REC2 fill:#d4edda
  style REC4 fill:#d4edda
  style REC5 fill:#d4edda
  style REC6 fill:#d4edda
  style REC3 fill:#fff3cd
  style REC7 fill:#f8d7da

個人的に一番注目しているのはCilium Service Meshで、Envoyサイドカーを使わずeBPFでメッシュ機能を実現できる点が魅力だ。コンテナセキュリティ完全ガイド2026|eBPF・SBOM・サプライチェーン対策でeBPFの可能性について触れているが、サイドカーレスのアーキテクチャはリソース効率の観点から見逃せない選択肢になってきている。

実際のところ、うちのチームは今年後半にApp Meshをどうするか再検討フェーズに入っている。候補はCilium Mesh移行かVPC Lattice部分採用のどちらかで、まだ結論は出ていない。

パフォーマンス面でのデータも1年分取れたので貼っておく。

xychart-beta
  title "サービス間レイテンシ p99 (ms):App Mesh有無の比較(測定期間6ヶ月)"
  x-axis ["1月", "2月", "3月", "4月", "5月", "6月"]
  y-axis "レイテンシ p99 (ms)" 0 --> 80
  line [18, 19, 20, 21, 19, 20]
  line [28, 30, 29, 31, 28, 30]

上が素のKubernetes Service、下がApp Mesh有りだ。Envoyのオーバーヘッドは常時10〜12ms程度あった。これをどう評価するかは完全に要件次第で、トレーサビリティや細かいトラフィック制御が必要なら許容できる数字だし、純粋なスループット重視なら痛い数字だ。個人的には、このレイテンシ増加よりも運用上の複雑さのほうがチームへのインパクトは大きかったと感じている。

まとめ

1年以上App Mesh + Cloud Mapを本番で動かしてきて、正直に言えることをまとめる。

  • Cloud MapはシンプルなサービスディスカバリとしてECS/EKS混在環境で今でも価値がある。 DNSベースの設計は理解しやすく、ECS Service Connectとも共存できる
  • App MeshのEnvoyサイドカーは確実にリソースを食う。 Pod数 × 100〜150MBのメモリオーバーヘッドは事前に計算して予算に入れておくべきだった
  • 重み付きルーティングとX-Rayの自動連携は本当に便利。 カナリアリリースとトレースが統合されているのは、標準機能だけでは再現が難しい価値だった
  • 2026年時点では新規のECS + App Meshはおすすめしない。 ECSはService Connect、EKSは要件次第でVPC LatticeかCilium Meshを先に検討したほうがいい
  • 段階的なアーキテクチャ変更に備えて、Cloud MapのPrivate Namespaceだけでも早めに整備しておく価値はある。 移行先が何であれ、サービスディスカバリの基盤として使い回せる

次のアクションとしては、既存App Meshユーザーなら現行の移行コストを試算しつつVPC Latticeのプレビューを触ってみることをおすすめする。完全移行は大変でも、新規サービスだけVPC Latticeで立てて比較するくらいはすぐできる。皆さんのチームはサービスメッシュどうしてます?EKSで別のアプローチ取ってる方いれば話聞いてみたい。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事