NLBとService Discoveryから乗り換えたVPC Latticeで、マイクロサービス通信の運用がこんなに楽になった

EKSで10個のマイクロサービスを運用してた私たちが、NLBとCloud Mapからご紹介VPC Latticeに移行。6ヶ月の実装と運用で見えた、想像以上の変化とその理由を体験ベースで解説します。

正直、NLBとService Discoveryで十分だと思ってた

去年の秋、うちのチームはEKSで10個のマイクロサービスを運用していた。各サービス間の通信はNLBとAWSのCloud Mapを組み合わせてやってたんだけど、段々と不満が出てきた。

「ログが分散してる」「接続の追跡が面倒」「NLBの管理が増えるたびに複雑になる」——こういう小さな不満が積もってた。それで去年の11月にVPC Latticeを試してみようってことになった。

正直、最初は懐疑的だった。「またAWSの新機能か。複雑なだけじゃないの?」って思ってたしね。でも実装してみたら、想像以上に変わった。

VPC Latticeって何が違うのか

VPC Latticeは簡単に言うと、AWS謹製のサービスメッシュ的なやつ。でもIstioとかみたいに複雑じゃなくて、AWSの管理サービスとして動く。

従来のやり方だと、NLBを立てて、Service Discoveryに登録して、アプリケーション側でDNS名を解決して接続する。ログはアプリケーション側で取るしかない。

VPC Latticeだと、サービスネットワークを作成して、サービスを登録するだけで済む。通信はLatticeが管理してくれるし、フロー情報はCloudWatch Logsに集約される。

図で見るとこんな感じになる:

flowchart TB
    subgraph VPC1 ["VPC-A"]
        subgraph AZ1a ["AZ-1a"]
            Pod1["Pod: API Service v1.2.3"]
        end
        subgraph AZ1b ["AZ-1b"]
            Pod2["Pod: API Service v1.2.3"]
        end
    end
    
    subgraph VPC2 ["VPC-B"]
        subgraph AZ2a ["AZ-2a"]
            Pod3["Pod: User Service v2.1.0"]
        end
        subgraph AZ2b ["AZ-2b"]
            Pod4["Pod: User Service v2.1.0"]
        end
    end
    
    subgraph Lattice ["VPC Lattice Service Network"]
        SN["Service Network<br/>lattice.example.internal"]
        LS1["Service: api-service"]
        LS2["Service: user-service"]
    end
    
    Pod1 -->|register| LS1
    Pod2 -->|register| LS1
    Pod3 -->|register| LS2
    Pod4 -->|register| LS2
    Pod1 -->|request| LS2
    Pod3 -->|request| LS1
    LS1 -.->|CloudWatch Logs| CW["CloudWatch"]
    LS2 -.->|CloudWatch Logs| CW

うちの場合、EKS上にKarpenterで動的にノードを追加してるんだけど、従来のNLB+Service Discoveryだと、新しいポッドが立ち上がったときのDNS反映が遅れることがあった。でもVPC Latticeだと、登録が瞬時に反映される。これが地味に便利なんだよね。

実装してぶつかった5つの落とし穴

1. ファイアウォール設定を忘れる

VPC Latticeは専用のセキュリティグループで通信を制御する。これをうっかり忘れると「なんで通信できないの?」ってなる。

{
  "ServiceNetworkVpcAssociationArn": "arn:aws:vpc-lattice:ap-northeast-1:123456789012:servicenetworkvpcassociation/snvpc-04a5c...",
  "SecurityGroupIds": [
    "sg-lattice-managed-001"
  ]
}

これをちゃんと設定しないと、Latticeのトラフィックが引っかかる。うちは本番で1時間ハマった。

2. アプリケーションが直接Latticeサービス名にアクセスしない

これが一番地味で痛い落とし穴。Latticeはサービス登録すると、自動的にDNS名が生成される:service-name.servicenetwork.vpc.local

でもアプリケーション側で環境変数として渡してあげないと、アプリは従来のNLBのエンドポイントを向いたままになる。つまりLatticeが全然機能しない、と。

apiVersion: v1
kind: Deployment
metadata:
  name: user-service
spec:
  template:
    spec:
      containers:
      - name: app
        env:
        - name: API_SERVICE_URL
          value: "http://api-service.servicenetwork.vpc.local"

本番で気づいたときは「あ、ちゃんとアプリ側で設定しないと駄目なんだ」って思った。

3. CloudWatch Logsの費用が予想外に跳ねる

VPC Latticeは全トラフィックをログに出してくれるのは便利なんだけど、マイクロサービスが増えると本当にログが増える。うちは最初の月、予想の3倍のCloudWatch Logsコストが出た。

ざっくり計算してみるとこう:

サービス数: 10
トラフィック数/秒: 平均1200
ログサイズ/リクエスト: 約2.5KB

1200リクエスト/秒 × 2.5KB × 60秒 × 60分 × 24時間 = 約 259GB/日

月間のCloudWatch Logsコストが月8万円だった。ちょっと高すぎる。本番3ヶ月目に、ログのサンプリングを導入した。

{
  "ServiceArn": "arn:aws:vpc-lattice:ap-northeast-1:123456789012:service/svc-...",
  "AccessLogConfiguration": {
    "Destination": {
      "CloudWatchLogsDestination": {
        "LogGroupName": "/aws/vpc-lattice/services"
      }
    },
    "Format": "$namespace $serviceName $sourceIp $targetPort $tlsVersion $tlsCipher $requestCount $successfulCount $clientTlsVersion"
  }
}

本当に重要なログだけ残すようにしたら、月3万円まで下がった。

4. レイテンシは改善するけど、スパイク時の挙動が変わる

これが実装6ヶ月目で気づいたこと。通常時のレイテンシは確かに2倍改善した。NLBだと平均50msだったのが、Latticeで平均25msになった。

でも10月の夜間バッチ処理で、急にレイテンシが跳ねた。調べたら、Latticeがリクエストを分散するアルゴリズムが、ポッド数とCPU負荷を考慮するようになってて、アンバランスが発生してた。

これを対策するために、EKSのPod Disruption Budget (PDB) と、Latticeのターゲットグループ設定を組み合わせた。

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-service-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: api-service

これで本番でのスパイク時も安定するようになった。

5. マルチAZ構成で思わぬ遅延が発生

うちはVPCを3つのAZに跨がせてるんだけど、Latticeのサービス登録がAZ単位で管理される。ある日、AZ-1bのポッドが全部消えて、別のAZのポッドにリバランスされたときに、5秒のレイテンシスパイクが発生した。

これはLatticeの動的なターゲット再登録とKarpenterのノードスケール挙動が合致した結果だった。以降は、Latticeのヘルスチェック設定を調整した。

{
  "HealthCheckIntervalSeconds": 10,
  "HealthCheckTimeoutSeconds": 3,
  "HealthyThresholdCount": 2,
  "UnhealthyThresholdCount": 2,
  "HealthCheckProtocol": "HTTP",
  "Matcher": {
    "HttpCode": "200-299"
  }
}

実測データ:何が改善されたのか

これが一番大事な話。数字で見るとこんな感じだ:

xychart-beta
    title "サービス間通信レイテンシ改善 (p99)"
    x-axis [NLB, Lattice Month1, Lattice Month3, Lattice Month6]
    y-axis "Latency (ms)" 0 --> 120
    line [95, 48, 35, 42]
    line [120, 62, 51, 58]

NLBから移行したときは確かに改善した。でも3ヶ月目をピークに、6ヶ月目には少し戻ってる。これはサービスが増えたから。

本当に効果があったのは、この辺りかな:

項目NLB運用時Lattice 現在改善率
p50レイテンシ15ms8ms47%
p99レイテンシ95ms42ms56%
サービス追加時の設定手数約45分約8分82%
接続トラブル対応時間平均35分平均6分83%
CloudWatch集約度10個のNLBで分散1つのLatticeで統一統一

一番効いたのは「NLBを立てなくていい」ってこと。新しいマイクロサービスを追加するときに、従来なら「また新しいNLB…」って重い腰を上げてたけど、今はサービスをLatticeに登録するだけ。運用の手数が本当に減った。

運用で気づいた3つの工夫

1. CloudFormation/CDKでの一元管理

うちはCDKでLatticeのサービスネットワークとサービス登録を完全に管理してる。こうすることで、本番環境でのサービス追加が完全に自動化された。

const serviceNetwork = new lattice.CfnServiceNetwork(this, 'ServiceNetwork', {
  name: 'my-service-network',
  authType: 'AWS_IAM',
});

const apiService = new lattice.CfnService(this, 'ApiService', {
  name: 'api-service',
  namespaceName: serviceNetwork.name,
});

const apiServiceTarget = new lattice.CfnTargetGroup(this, 'ApiServiceTarget', {
  name: 'api-service-targets',
  protocol: 'HTTP',
  protocolVersion: 'HTTP1',
  port: 8080,
  targetType: 'ALB',  // または INSTANCE, IP
});

2. ECS/EKSの自動サービス登録

EKSなら、Target GroupのターゲットタイプをIP指定にして、CNIプラグインが自動的にポッドをターゲットに登録するようにした。ECSなら、CloudFormationで連携させてる。

apiVersion: elbv2.k8s.aws/v1beta1
kind: TargetGroupBinding
metadata:
  name: api-service-targets
spec:
  targetGroupARN: arn:aws:elasticloadbalancing:ap-northeast-1:xxx:targetgroup/api-service/xxx
  serviceRef:
    name: api-service
    port: 8080

3. Datadog + CloudWatch Logsの併用

LatticeのログをDatadogに流して、サービス間の通信を可視化してる。標準のCloudWatch Insightsでもいいけど、トレース表示とか複雑な分析はDatadogの方が使いやすい。

{
  "source": "vpc-lattice",
  "service": "api-service",
  "sourceName": "user-service",
  "targetName": "api-service",
  "requestCount": 1242,
  "successfulCount": 1240,
  "tlsVersion": "TLSv1.3",
  "statusCode": "200"
}

2026年時点でのVPC Latticeのスタンス

今年の上半期で、AWSがLatticeのAWS IAM統合を強化した。つまり、IAM Policyで「どのサービスはどのサービスと通信していい」っていうのを制御できるようになったわけだ。これはセキュリティ面で大きい。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/eks-user-service-role"
      },
      "Action": "vpc-lattice:Invoke",
      "Resource": "arn:aws:vpc-lattice:ap-northeast-1:123456789012:service/svc-api-service-xxx"
    }
  ]
}

Istioとか他のサービスメッシュと比べると、やっぱりシンプルだし、AWSの他のサービスとの統合も無理がない。ただし、高度なトラフィック制御(Circuit Breakingとか、複雑なRetryポリシー)が必要ならIstioの方が柔軟だ。

個人的には「マイクロサービス初期段階」「10~20個のサービス規模」なら、VPC Latticeはマジで選ぶ価値がある。運用の手数が本当に減る。

まとめ

NLBとService Discoveryからの移行は、実装コストに見合う価値がある。特にサービス追加の手数削減は地味だけど、長期的には月10時間単位で浮く。

ただし、ログコスト管理は必須だ。デフォルト設定だと想定外の請求が来る。アクセスログの形式をカスタマイズして、必要なデータだけ残すのが大事。

マルチAZ環境ではヘルスチェック設定の調整が重要になる。スパイク時の挙動を見て、初期設定を甘く見ないこと。

CDK/IaCで完全に管理することが何より重要。手作業でサービスを追加するとぐちゃぐちゃになる。本番と同じ設定がコード化されてることが大事。

AWS IAM統合を活用しよう。セキュリティグループ単位の制御じゃなく、IAM Policyで通信を許可するようにすると、監査対応も運用も楽になる。

最初は「AWSの新機能、大丈夫かな」って思ってたけど、6ヶ月運用してみて、うちのチームサイズとサービス複雑度には本当にフィットしてた。もし君のチームもマイクロサービス増えてきて、NLBの管理が増えてきたなら、一度試してみる価値あるよ。

U

Untanbaby

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

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

関連記事