App MeshからService Connectに移行して6ヶ月、やっと気づいた運用の本当の話

マイクロサービス間の通信管理、App Meshの複雑さに疲れて乗り換えたECS Service Connect。本番運用で分かった実装のコツと予想外の落とし穴を正直に語ります。

App Meshから逃げるきっかけになった話

去年の秋、チームの複数マイクロサービス間で通信が増えてきて、何かしらのサービスメッシュが必要だと感じてた。その時点ではApp Meshを使ってたんだけど、正直めちゃくちゃ複雑だったんですよね。VirtualNode、VirtualRouter、Route設定……それぞれを別途定義しなきゃいけなくて、ECSのタスク定義とは別にAWS管理コンソールで設定が増える。本番でデプロイミスが頻繁に起きて、「ECSは更新したのにApp Meshの設定がずれてる」みたいなアプリケーション層での問題じゃないトラブルが絶えなかった。

そんな時にService Connectが正式サポートになったのが2026年初頭。マネージドで簡潔だという噂を聞いて、小規模なサービスペアで試してみたら……これが想像以上に良かった。デプロイもシンプルで、App Meshみたいに外部で管理する複雑さがない。6ヶ月運用してみて、気づいたこと、落とし穴、それから実装のコツについて話そうと思う。

ECS Service Connectってそもそも何か

Service Connectは、ECS上で稼働するコンテナサービス同士を簡単につなぐためのAWS内蔵ツール。Service Discoveryの後継で、DNS + プロキシを使ってサービス間の通信をハンドリングしてくれる。

App Meshとの大きな違いは「ECSネイティブ」ってこと。ECSのタスク定義内で直接設定できるから、別途マニフェストを書く必要がない。デプロイパイプラインが単純化される。これが本当に大きい。

{
  "name": "myservice",
  "image": "myrepo/myservice:latest",
  "portMappings": [
    {
      "containerPort": 8080,
      "protocol": "tcp"
    }
  ],
  "serviceConnectConfiguration": {
    "enabled": true,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/service-connect",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "services": [
      {
        "portName": "http",
        "clientAliases": [
          {
            "port": 8080,
            "dnsName": "myservice.local"
          }
        ],
        "ingressPortOverride": 0
      }
    ]
  }
}

このくらいシンプル。ECSのネットワークモードawsvpcでほぼ自動で動く。

実装の流れ

うちのチームは段階的に移行した。

最初に、小規模な2つのサービスペア(APIサーバーと内部用キャッシュサービス)で試した。ここで基本的な動作確認。DNS名前解決、レイテンシーが許容範囲か、ログ出力がちゃんと動いてるか。

次に、既存App Meshで動いてた5つのサービスを一気に移行。この時のポイントをまとめると、以下の3つ。

段階実施内容所要期間
依存関係把握Xrayのトレースを見直して「どのサービスがどのサービスを呼ぶのか」を一覧化1週間
DNS確認テストmyservice.localみたいなローカルDNSがちゃんと解決されるか。コンテナ内でnslookupgetent hostsで確認3日
段階的ロールアウト本番で一気に切り替えず、canaryデプロイで1割→5割→全量のフェイズドロールアウト2週間

実際にコンテナ内でDNS確認するなら、こんな感じで検証する。

# コンテナ内でのDNS確認
$ getent hosts myservice.local
172.31.0.50  myservice.local

# ローカルポートで疎通確認
$ curl http://myservice.local:8080/health
{"status": "ok"}

落とし穴を2つ踏んだ話

1. クラスタごとに名前空間が独立している

ECS上で複数クラスタを運用してる場合、Service Connectはクラスタローカル。「staging環境のサービスが本番のサービスを呼びたい」みたいなクロスクラスタ通信はService Connectだけでは不可能。

これに気づいたのが本番環境で運用開始してから1ヶ月目。stagingからの負荷テスト実行時に「あれ、本番サービスが見つからない」って話になった。結構なショック。

解決策は、クロスクラスタ通信が必要な部分だけRoute 53プライベートホストゾーンとVPCピアリングで別途構成した。手間が増えたけど、この設計のおかげでセキュリティ境界もはっきりした。

2. ロードバランシングが限定的

App Meshだと細かく重み付けやトラフィック分散ルールが設定できたけど、Service Connectはシンプル。基本的にラウンドロビン。

うちは新しいバージョンのサービスへのカナリアデプロイで段階的トラフィック切り替えが必要だったんだけど、Service Connect単体では9割/1割みたいな細かい制御ができない。結局、ALBとTarget Groupの組み合わせで実現した。その代わり、App Meshの複雑な設定が不要になった分、全体としてはシンプルになった。

AWS構成図

graph TB
  subgraph VPC["VPC"]
    subgraph AZ1["AZ-1"]
      Task1["ECS Task<br/>Service A"]
      Task2["ECS Task<br/>Service B"]
      SC1["Service Connect<br/>Proxy"]
      SC2["Service Connect<br/>Proxy"]
    end
    subgraph AZ2["AZ-2"]
      Task3["ECS Task<br/>Service A"]
      Task4["ECS Task<br/>Service C"]
      SC3["Service Connect<br/>Proxy"]
      SC4["Service Connect<br/>Proxy"]
    end
    R53["Route 53<br/>Private Zone<br/>servicea.local<br/>serviceb.local<br/>servicec.local"]
  end
  
  ALB["ALB<br/>External Traffic"]
  CloudWatch["CloudWatch<br/>Logs & Metrics"]
  
  ALB -->|Port 80/443| Task1
  Task1 -->|serviceb.local:8080| SC2
  SC2 -->|Proxy| Task2
  Task2 -->|servicea.local:8080| SC1
  SC1 -->|Proxy| Task1
  Task3 -->|servicec.local:8080| SC4
  SC4 -->|Proxy| Task4
  
  SC1 -->|Query| R53
  SC2 -->|Query| R53
  SC3 -->|Query| R53
  SC4 -->|Query| R53
  
  SC1 -->|Logs| CloudWatch
  SC2 -->|Logs| CloudWatch
  SC3 -->|Logs| CloudWatch
  SC4 -->|Logs| CloudWatch

この構成で、各タスク内のサイドカープロキシがRoute 53のプライベートホストゾーンに問い合わせ、サービスディスカバリーが自動で行われます。タスクが起動・停止されるたびに自動的にDNSレコードが更新されるので、手動管理の手間がありません。

本番6ヶ月で得た実装のコツ

1. サイドカープロキシのメモリ設定

Service Connectのプロキシは別プロセスで動くので、タスク定義でメモリを余裕持たせる必要がある。

{
  "family": "myservice-task",
  "memory": "512",
  "cpu": "256",
  "containerDefinitions": [
    {
      "name": "application",
      "memory": "384",
      "cpu": "200"
    },
    {
      "name": "envoy",
      "image": "public.ecr.aws/appmesh/aws-for-fluent-bit:latest",
      "memory": "128",
      "cpu": "56"
    }
  ]
}

実際には自動でEnvoyプロキシが挿入されるので、それ分のリソース予約が必要。目安としてはメモリは総タスクメモリの20%程度。CPU使用率が高いサービスならもう少し多めにしておくといい。

2. ログとトレーシング

Service Connectのプロキシログを有効にすると、リクエスト/レスポンスのメタデータが見える。本番でのトラブル診断に超便利。

{
  "serviceConnectConfiguration": {
    "enabled": true,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/service-connect-proxy",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs",
        "awslogs-datetime-format": "%Y-%m-%d %H:%M:%S"
      }
    }
  }
}

これでサービス間通信のすべてがCloudWatch Logsに記録される。Xrayとの組み合わせで、分散トレーシングもスムーズ。

3. ヘルスチェックとタイムアウト

一番細い針に踏んだ話。Service Connect経由の呼び出しでタイムアウトが頻繁に起きるようになった。原因は、ヘルスチェック時のタイムアウト設定。

{
  "healthCheck": {
    "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
    "interval": 30,
    "timeout": 5,
    "retries": 3,
    "startPeriod": 60
  }
}

このタイムアウトが5秒なんだけど、Service Connectのプロキシが初期化される際に一時的にレスポンスが遅延することがある。本番で運用してたら、デプロイのたびにヘルスチェックが失敗してタスク置き換えが連鎖。設定を以下のように変更したら解決。

{
  "healthCheck": {
    "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"],
    "interval": 30,
    "timeout": 10,
    "retries": 2,
    "startPeriod": 120
  }
}

timeoutを10秒に延長、startPeriodを120秒に。これでプロキシの初期化待ちがちゃんと確保される。

パフォーマンス比較

xychart-beta
    title "Service Connect vs App Mesh vs Direct (レイテンシー比較)"
    x-axis [No Mesh, App Mesh, Service Connect]
    y-axis "Latency (ms)" 0 --> 50
    line [5, 12, 8]

このグラフは本番で3週間計測した結果。Service Connectのプロキシオーバーヘッドは無視できるレベル。ただし、App Meshとの比較だと5ms程度の差が出てる。実務的には許容範囲だけど、超低レイテンシーが必須なら気になるかもね。

移行後の運用で変わったこと

デプロイの簡潔性

App Meshを使ってた時は、ECSのタスク定義更新 → App Meshマニフェスト更新 → 疎通確認、みたいにステップが多かった。Service Connectなら、タスク定義だけ更新すればOK。デプロイ時間が平均35%短縮された。正直、こんなに短縮されるとは思わなかった。

オンコール対応

サービス間通信の問題が減った。App Meshだと「ECS側は正常だけどメッシュ設定がずれてる」みたいな本来不要なトラブルが定期的に発生してた。Service Connectはその種の問題がほぼゼロ。深夜の呼び出しも減ったし、チーム全体のストレスが目に見えて減ってる。

チームの学習コスト

App Meshを理解するのに、チーム全体で結構な時間がかかってた。VirtualNode、Route、VirtualRouter……概念が多い。Service Connectは「ECSのサービスディスカバリー」ぐらいの理解でOK。新入りのオンボーディングが楽になったし、経験者じゃなくても設定をいじれるようになった。

ここまでの課題と次の検討

正直、Service Connectで全部が解決したわけじゃない。

トラフィック管理の限定性 が未だに課題。複雑なカナリアデプロイやA/Bテストをしたい場合、結局ALBやAPI Gatewayと組み合わせないといけない。

マルチリージョン 対応もない。うちはアジア太平洋圏に3つの地域でクラスタを運用してるんだけど、リージョン間通信はService Connectの範囲外。Kafkaを使った非同期通信で補完してる。

セキュリティ もApp Meshほどのきめ細かさがない。mTLSの設定やポリシーベースのアクセス制御は、どうしても別途で用意する必要がある。

次は、Kafkaベースの非同期アーキテクチャとの組み合わせを検討中。Service Connectは同期通信に特化して、非同期系の複雑な処理はイベント駆動にする、という役割分担ですね。

まとめ

App Meshの複雑さから本当に解放される — タスク定義だけでほぼ完結。デプロイパイプラインが単純化されて、チーム全体の生産性が向上する。個人的には、この単純さが一番のメリットだと思ってる。

本番では小規模から始めることが大事 — 依存関係把握→ペアでテスト→段階的ロールアウト。DNS解決のテストだけは絶対に本番前に済ませておく。ここを手抜きするとあとで痛い目を見る。

プロキシのリソース設定とヘルスチェック設定が地味だけど重要 — ここを失敗するとデプロイのたびにタスク置き換えが連鎖する。本番運用の1ヶ月で気づくような事柄ではなく、事前検証で潰しておくべき。

トラフィック管理が必要なら、ALBやAPI Gatewayとの併用を想定 — Service Connectはシンプルなサービスディスカバリー。複雑な制御が必要なら、別途ツールを組み合わせる。

次のステップは非同期アーキテクチャとの組み合わせ — Service Connectで同期通信を簡潔にして、複雑な処理はイベント駆動にする。これが2026年のマイクロサービスの現実的な設計だと感じてる。

6ヶ月本番運用してみて、App Meshから移行する価値は十分ある。特にチーム規模が10名前後でマイクロサービスを運用してるなら、導入を本気で検討する価値があると思う。

U

Untanbaby

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

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

関連記事