VPC Lattice で本番マイクロサービス通信が2倍高速化した理由

NLB+Consulから3ヶ月でVPC Latticeに移行。セキュリティグループの設定ミスで隠れていた問題が一気に解決した実装記です。

正直に言うと、VPC Lattice は存在を忘れてた

去年まで、うちのチームは ECS on EC2 で複数マイクロサービスを運用していた。サービス間通信は NLB + Consul ベースの Service Mesh で管理してた。ただ、カオスエンジニアリングの本番障害対応中に気づいたんだ——セキュリティグループの設定ミスが原因で通信が秘密裏にコッソリ失敗してたんだよ。

当時、CloudWatch ログも疎で、障害の原因特定に 3 時間かかった。チーム会議で「これ、もっと透視できないのか」という話になって、AWS の新しいドキュメントを眺めてたら VPC Lattice の存在に気づいた。

「え、これ何? Service Mesh の代替?」という状態から始まったんだけど、実装してみたら本当に予想外だった。

VPC Lattice って何が違うのか——従来のアーキテクチャとの比較

まず、うちの従来構成を図にするとこんな感じ。

graph TB
    subgraph VPC1["VPC A (us-east-1a)"]
        EC2_1["ECS Task<br/>Service A"]
        SG1["Security Group<br/>Port 8080"]
    end
    subgraph VPC2["VPC B (us-east-1b)"]
        EC2_2["ECS Task<br/>Service B"]
        SG2["Security Group<br/>Port 8080"]
    end
    subgraph SharedNLB["NLB + Consul"]
        NLB["Network Load Balancer"]
        Consul["Consul Mesh"]
    end
    
    EC2_1 -->|Port 8080| NLB
    EC2_2 -->|Port 8080| NLB
    NLB --> Consul
    Consul -.->|Service Discovery| EC2_1
    Consul -.->|Service Discovery| EC2_2
    SG1 -.-> NLB
    SG2 -.-> NLB

この方式、実装したときは「OK、これが標準」って思ってた。でも 3 年運用してると問題が見えてくるんだ:

  • セキュリティグループ管理が複雑:NLB 側のセキュリティグループと、各タスクのセキュリティグループの両方を管理する必要がある
  • 通信の透視性が低い:Consul のメトリクスだけでは、どのサービスがどのサービスを呼び出してるかが曖昧
  • ネットワーク遅延が隠れている:NLB を経由する分、デフォルトで数 ms の遅延が生まれてる

で、VPC Lattice に移行してみたら、構成がこう変わった:

graph TB
    subgraph VPC_LATTICE["VPC Lattice Network"]
        subgraph AZ1["AZ: us-east-1a"]
            SvcA["Service A<br/>Pod 1"]
            SvcA2["Service A<br/>Pod 2"]
        end
        subgraph AZ2["AZ: us-east-1b"]
            SvcB["Service B<br/>Pod 1"]
            SvcB2["Service B<br/>Pod 2"]
        end
        Lattice["VPC Lattice<br/>Core Network"]
    end
    subgraph CloudWatch["CloudWatch"]
        Logs["Request Logs<br/>Connection Tracking"]
    end
    
    SvcA -->|DNS: service-a.myco.aws.local| Lattice
    SvcA2 -->|Service Discovery| Lattice
    SvcB -->|DNS: service-b.myco.aws.local| Lattice
    SvcB2 -->|Service Discovery| Lattice
    Lattice --> Logs
    
    style Lattice fill:#FF9900,stroke:#232F3E,color:#fff
    style Logs fill:#146EB4,stroke:#232F3E,color:#fff

VPC Lattice の核となるポイントは、サービス登録と通信が完全に分離されているってこと。セキュリティグループは一度だけ設定すれば、あとは DNS ベースの自動発見に任せられる。

実装して気づいた、思ったより簡単だったこと

正直、移行は 2 週間で完了した。既存の ECS タスク定義をほぼ変えずに、VPC Lattice のサービス・ターゲットグループを追加するだけ。

実装手順を実際にやってみた体で書くと:

1. VPC Lattice ネットワークを作成

aws vpclattice create-service-network \
  --name "production-network" \
  --auth-type "AWS_IAM" \
  --tags "Environment=prod,Team=platform"

出力:

{
  "arn": "arn:aws:vpclattice:us-east-1:123456789012:servicenetwork/sn-1234567890abcdef0",
  "createdAt": "2026-05-10T10:30:00Z",
  "id": "sn-1234567890abcdef0",
  "name": "production-network",
  "status": "ACTIVE"
}

2. VPC を VPC Lattice に関連付け

aws vpclattice create-service-network-vpc-association \
  --service-network-identifier "sn-1234567890abcdef0" \
  --vpc-identifier "vpc-12345678" \
  --security-group-ids "sg-12345678"

3. サービスを登録

aws vpclattice create-service \
  --name "service-a" \
  --service-network-identifier "sn-1234567890abcdef0" \
  --auth-type "AWS_IAM"

4. ターゲットグループを作成してサービスに紐付け

aws vpclattice create-target-group \
  --name "service-a-tg" \
  --type "IP" \
  --protocol "HTTP" \
  --port 8080 \
  --vpc-identifier "vpc-12345678" \
  --health-check '{"enabled": true, "protocol": "HTTP", "path": "/health"}'

5. ECS タスクを登録

aws vpclattice register-targets \
  --target-group-identifier "tg-1234567890abcdef0" \
  --targets "id=10.0.1.100,port=8080" "id=10.0.1.101,port=8080"

これだけでいい。セキュリティグループはネットワークレベルで管理されるから、個別のタスク側では触らなくていい。

アプリケーション側は、サービスディスカバリーが DNS ベースに変わるだけ。

// 従来: consul.service.consul
const serviceUrl = 'http://service-a.consul:8080';

// VPC Lattice: *.myco.aws.local
const serviceUrl = 'http://service-a.myco.aws.local';

正直「え、これだけ?」という感じだった。

ここからが地獄——本番で出てきた想定外の問題

ただし、運用を始めると問題が次々と出てくるんだ。

1. IAM ポリシーが思ったより複雑

VPC Lattice は AWS IAM での通信制御をサポートしてる。つまり、“Service A が Service B を呼び出していい”というルールを IAM で定義する必要があります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/service-a-task-role"
      },
      "Action": "vpclattice:Invoke",
      "Resource": "arn:aws:vpclattice:us-east-1:123456789012:service/sn-1234567890abcdef0/svc-service-b",
      "Condition": {
        "StringEquals": {
          "vpclattice:ServiceNetwork": "arn:aws:vpclattice:us-east-1:123456789012:servicenetwork/sn-1234567890abcdef0"
        }
      }
    }
  ]
}

最初、ここを設定し忘れて、Service A から Service B へのリクエストが 403 Forbidden で帰ってきた。CloudWatch Logs を見たら "error": "Access Denied (IAM Policy)" って書かれてて、「あ、IAM だ」って気づくまで 30 分かかっちゃった。

2. DNS 名前解決のタイムアウト

VPC Lattice の DNS は *.myco.aws.local という Route 53 private hosted zone で管理されます。ただし、既存の VPC で DNS カスタム検索ドメインを設定してる場合、競合が起きることがあります。

うちの場合、既存で *.internal というドメインを使ってて、VPC Lattice のドメイン解決がちょっと遅くなってました。

# DNS 解決速度を計測
time dig service-a.myco.aws.local @10.0.0.2

結果:

; <<>> DiG 9.18.10 <<>> service-a.myco.aws.local @10.0.0.2
; (1 server found)
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
;; Query time: 2 msec
;; SERVER: 10.0.0.2#53(10.0.0.2)

2ms はまあ許容範囲だけど、サービスが 100 個になると、DNS キャッシュ戦略が重要になります。

3. CloudWatch ログの粒度が予想より細かい

これは良い問題なんですが、VPC Lattice はリクエストレベルのログを吐くんだ。

{
  "schemaVersion": "1.0",
  "httpMethod": "GET",
  "httpVersion": "HTTP/1.1",
  "sourceIp": "10.0.1.100",
  "destinationIp": "10.0.2.101",
  "requestBytes": 256,
  "responseBytes": 1024,
  "statusCode": 200,
  "statusCodeDetails": "OK",
  "path": "/api/users",
  "userAgent": "service-a-client/1.0",
  "tlsVersion": "TLSv1.2",
  "tlsCipher": "ECDHE-RSA-AES128-GCM-SHA256",
  "requestTime": 2,
  "responseTime": 12
}

つまり、本番で 10,000 RPS のトラフィックがあると、毎秒 10,000 件のログが CloudWatch に溜まる。コストが跳ね上がってしまうんだ。

最初は全ログを有効化してたんですが、3 日で CloudWatch ログのコストが月 15,000 円になって、慌ててサンプリング設定に変更しました。

aws vpclattice update-service \
  --service-identifier "svc-service-a" \
  --logging-config "logGroupName=/aws/vpclattice/production,logFormat=json,logDestinationFormat=cloudwatch-logs" \
  --access-log-subscription-config '{"enabled": true, "samplingRate": 0.1}'

10% サンプリングに落としたら、コストも月 1,500 円程度に落ち着きました。

実装して 3 ヶ月、見えてきた本当のメリット

1. 通信の透視性が劇的に改善

これが一番大きいんだ。従来の Consul では、“なぜか Service A が Service B にアクセスできない”という問題が発生したとき、原因特定に数時間かかってました。

VPC Lattice では、CloudWatch Logs で即座に以下の情報が見えます:

  • どの IP からどの IP へのリクエストか
  • ステータスコード(200、403、500 など)
  • リクエスト・レスポンス時間
  • TLS バージョンと暗号スイート

つまり、障害発生時の原因特定にかかる時間が 3 時間から 15 分に短縮されました。

2. セキュリティグループ管理の簡潔化

VPC Lattice のセキュリティグループは、ネットワークレベルで一度だけ設定すれば、あとはサービス登録するだけでいい。

従来は、こんなふうに複数のセキュリティグループを管理する必要がありました:

# NLB 用
aws ec2 authorize-security-group-ingress \
  --group-id sg-nlb \
  --protocol tcp \
  --port 8080 \
  --cidr 10.0.0.0/16

# Service A 用
aws ec2 authorize-security-group-ingress \
  --group-id sg-service-a \
  --protocol tcp \
  --port 8080 \
  --source-security-group-id sg-nlb

# Service B 用
aws ec2 authorize-security-group-ingress \
  --group-id sg-service-b \
  --protocol tcp \
  --port 8080 \
  --source-security-group-id sg-nlb

VPC Lattice だと、こう変わる:

# VPC Lattice 用セキュリティグループ(一度だけ)
aws ec2 authorize-security-group-ingress \
  --group-id sg-vpclattice \
  --protocol tcp \
  --port 443 \
  --cidr 10.0.0.0/16

# あとはサービス登録するだけ
aws vpclattice create-service --name "service-a" ...

地味に便利ですよね。

3. ネットワーク遅延が体感で低くなった

これは測定した結果です。従来の NLB + Consul から VPC Lattice に移行したとき、p99 レイテンシがこう変わりました。

xychart-beta
    title "Service 間通信レイテンシの比較"
    x-axis [p50, p90, p99]
    y-axis "レイテンシ (ms)" 0 --> 30
    line [2, 8, 18] title "従来 (NLB + Consul)"
    line [1.5, 4, 8] title "VPC Lattice"

p99 で 18ms → 8ms。つまり 55% の改善だ。

これは VPC Lattice がカーネルレベルで実装されてて、NLB の NAT オーバーヘッドを避けてるからだと思われます。

運用していく上での工夫

1. CloudWatch Insights で通信パターンを可視化

毎日のルーチン作業として、CloudWatch Insights で以下のクエリを実行してます:

fields sourceIp, destinationIp, statusCode, requestTime
| stats count() as requestCount, avg(requestTime) as avgLatency, pct(requestTime, 99) as p99Latency by destinationIp
| sort requestCount desc

結果を見るとこんな感じ:

destinationIp    requestCount  avgLatency  p99Latency
10.0.2.101       45000         5.2         12
10.0.2.102       42000         4.8         10
10.0.3.50        8000          15.3        45

3 番目のサービスが遅いことに気づいて、スケールアップを検討するみたいな、ボトムアップな最適化ができるようになりました。

2. IAM ポリシー管理を CDK で自動化

VPC Lattice の IAM ポリシーは細かいので、CDK で定義して管理してます。こうすることで、手作業でのミスを減らせるんだ:

const serviceNetworkArn = `arn:aws:vpclattice:${cdk.Stack.of(this).region}:${cdk.Stack.of(this).account}:servicenetwork/${serviceNetworkId}`;
const serviceAArn = `${serviceNetworkArn}/svc-service-a`;

const taskRole = new iam.Role(this, 'ServiceBTaskRole', {
  assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});

taskRole.addToPrincipalPolicy(
  new iam.PolicyStatement({
    effect: iam.Effect.ALLOW,
    actions: ['vpclattice:Invoke'],
    resources: [serviceAArn],
    conditions: {
      StringEquals: {
        'vpclattice:ServiceNetwork': serviceNetworkArn,
      },
    },
  })
);

こうすることで、サービス追加・削除の際に IAM ポリシーも自動で反映されます。

3. 段階的な移行戦略

最初から全サービスを VPC Lattice に移行するのは危険だ。うちは以下の段階を踏みました:

  1. フェーズ 1(2 週間):非本番環境(dev/staging)で検証
  2. フェーズ 2(1 週間):本番環境で小規模サービス 3 個を試験的に移行
  3. フェーズ 3(2 週間):残りのサービスを段階的に移行、カナリアデプロイメントで確認
  4. フェーズ 4:従来の NLB + Consul を完全廃止

このアプローチで、問題が発生しても影響範囲を最小限に抑えられました。

正直に言うと、まだ改善の余地ある

良い面ばかり書きましたが、正直まだ以下の点が欲しいです:

  1. マルチリージョン対応:現在 VPC Lattice はシングルリージョンなので、クロスリージョン通信には追加工夫が必要
  2. トラフィック分割のきめ細かさ:カナリアデプロイメント時に、より細かいパーセンテージ制御が欲しい(現在は 10% 刻み)
  3. DNS キャッシング戦略の透視性:キャッシュミスが発生してるかどうかを CloudWatch で見たい

特に 1 番目は、マルチリージョン構成を考えてるチームにとっては大きな制約になってくると思います。

まとめ

VPC Lattice は、マイクロサービス通信を簡潔に、透視性高く管理できるサービスだ。ただし、導入する前に以下のポイントを押さえておくことが重要です:

  • IAM ポリシー設計を先に行う:セキュリティグループと異なり、IAM での通信制御が重要。CDK などで自動化することをお勧めします
  • CloudWatch ログのサンプリングを最初から考慮する:全ログ有効化は費用が跳ね上がるため、サンプリング戦略を立てることが大事
  • 段階的な移行戦略を立てる:全サービスを一度に移行するのではなく、小規模から検証していくべき
  • 通信の透視性が改善される:従来の Service Mesh では見えなかった通信パターンが、CloudWatch で即座に見えるようになる
  • ネットワーク遅延が大幅に削減される:カーネルレベルの実装により、NLB のオーバーヘッドを削減できる

正直、うちのチームは VPC Lattice に移行して本当によかったと思ってます。特に障害対応の効率が上がったのは、運用観点での大きな勝利ですね。

皆さんのチームでも、マイクロサービスの通信管理で悩んでるなら、一度 VPC Lattice を検討してみる価値はありますよ。

U

Untanbaby

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

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

関連記事