GoogleCloudでVPCを構築したらCloudNATのサイジングでハマった件

# はじめに
こんにちは！
私はディップ株式会社 DX事業本部でクラウド・インフラの担当しております。

この記事はディップ [Advent Calendar 2023](https://qiita.com/advent-calendar/2023/dip-dev) の17日目の投稿です。

AWSでNAT Gatewayのサイジングを考えたことありますか？
私の周りの仲間にも聞いてみたのですが、少なくとも私の周りではNATのサイジングを検討したことがある人はいませんでした。

今回はAWSの経験をもとにGCP（Google Cloud）も同じだろうと思ってCloud NATを構築したら、サイジングの問題が発生して再設計をすることになってしまった話をします。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/7b0b92e2-7d97-bc9b-6e71-cf731f2f5627.png">

# TL;DR
- GCP の Cloud NATは、ピーク時のインターネットへの同時アクセス数にあわせてサイジングが必要です。
  - VPCの視点
    - VPCで発生する最大同時アクセス数から、Cloud NATのIPアドレス数を確保する。
  - VM（GKE Node、GCE 等）の視点
    - VM 1台当たりで発生する最大同時アクセス数から、VMへの割り当てPort数を設定する。

# アジェンダ
1. GCPに挑戦するきっかけ
2. AWSとGCPのネットワーク構成の違い
3. GCPでVPC設計に初挑戦
4. ピンチ！Cloud NATでPort数が足らない問題が発生
5. Cloud NATのサイジングをしよう
6. まとめ

## 1. GCPに挑戦するきっかけ

DX事業本部では世の中の人手不足の課題を解決するために「コボット」というブランドで様々なSaaSを提供しています。
ひとつのSaaSサービスのバックエンド機能をGCPへ移行することになり、インフラの設計、構築を担当することになりました。

私自身はAWSでは経験があったものの、GCP上で大規模なVPC構築は今回が初めてだったので、試行錯誤しながら取り組みを始めることになりました。

- 要件
  - VPCをPrivateなネットワークで構成し、以下のリソースを構築する。
    - GKE （Standardモード）
    - CloudSQL
    - Memorystore for Redis
  - GKE上で稼働するアプリは外部サービスを呼出すため、VPCの内部からインターネットへアクセスできるようにする。
    - CloudNAT が必要
  - Terraform で構築する。
  - GCPへの移行は一度に全部を移行するのではなく、複数回に分けて実施する。

## 2. AWSとGCPのネットワーク構成の違い

GCPのネットワークは、AWSのと比較すると以下のような違いがあります。

- サブネットをわける必要性が低く、単一のサブネットでもよい。
  - ひとつのサブネットを複数のリージョンやゾーンにまたがって構成できる。
  - Publicサブネット、Privateサブネットに分ける必要性がない。
- CloudSQLやRedisなどの一部リソースは、直接VPC内に構築されない。
  - Google管理下の「サービスプロデューサーのネットワーク」内に構築され、ピアリングでVPCの中から接続できるようになる。

<img width="800" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/d45a5b77-311c-b703-929e-945b1172b5ef.png">

## 3. GCPのネットワーク設計に初挑戦

### 1. VPCのIPアドレスの設計
GKE、CloudSQL、Redisの台数を必要な設計を行いました。（基本AWSと同じだと思いますので、詳細な説明は省略します。）

### 2. Cloud NATの設計

さて、今回のポイントはここになります。

#### VMに割り当てるPort数の設計
既にGCPで稼働している別サブシステムの構築を担当したメンバーから、インターネットへの同時アクセスがVMに割当られたPort数を超える通信が発生した場合、通信が破棄されてしまうということを聞いていました。そこでVMに割り当てるPort数は多めに設定し、数を調整しながら移行することにしました。

- 最小ポート数（VMに割り当てるPort数）
  1024
- 動的ポートの割り当て
  無効（Port数は固定にした）

#### CloudNATに割り当てるIPアドレスの設計
AWSと同じ感覚でCloudNATに割り当てるIPアドレスを設計してしまったのが、しくじりの始まりでした。
NAT GatewayのIPアドレスは１個という経験から、GCPでも１個で十分だと考えました。

実は[Terraformのサンプル](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_router_nat#example-usage---router-nat-manual-ips)を見た時に複数のIPアドレスを割り当てられるように配列になっているのに気がついたのですが、この意味に気が付きませんでした。当時は配列にする必要はないと判断し、Terraformの定義ではひとつのIPアドレスしか持たない仕様で実装してしまいました。


## 4. ピンチ！Cloud NATでPort数が足らない問題が発生
GCPで環境を構築し、移行がスタートしました。
このシステムは分割して移行する必要があり、約２ヶ月をかけて毎週移行する方式で進めていきました。

移行が進むにつれ、GKE Node（GCEのVMとして構築される）の台数が徐々に増えていきました。
GKE Nodeで割り当てたCloudNAT用のPort数を外部への同時アクセス数が超えないように設定値を何度か数値を上げていきました。

いよいよ最後の移行だという直前に、監視からCloud NATでPortのアラートが発報されました。

#### Cloud NATのPortアラートの調査結果
アラートの内容はGKE Node数の増加したことにより、Nodeに割り当てるCloud NATのPortが枯渇してしまったため発報されていました。つまり、これ以上Nodeを追加できない状態になっていました。

そのため移行を中断し、CloudNATのPort不足を解決する方法の検討を開始しました。

## 5. Cloud NATのサイジングをしよう

### Cloud NATのPort枯渇がなぜ発生したのか？
Cloud NATの仕様で以下のことを見落としていることに気が付きました。

- CloudNATのPortは使用時に割当られるのではなく、VMの起動時に指定されたPort数を割り当てる
- VMに割り当てられるPort数の最大数はCloudNATのIPアドレス数によって決まる

Cloud NATのIPアドレス １個あたりの上限値は以下のようになっています。

#### GCP ClopudNAT 同時アクセス数の上限値
  ひとつのIPアドレスあたり 64,512 まで
  [GCPドキュメント 「Cloud NAT」](https://cloud.google.com/nat/docs/ports-and-addresses?hl=ja#ports)より引用
  > Cloud NAT ゲートウェイ（Public NAT と Private NAT の両方）の各 NAT IP アドレスは、64,512 個の TCP 送信元ポートと 64,512 個の UDP 送信元ポートをサポートします。


#### Port 枯渇発生時の状況
計算すると以下のようになります。
IPアドレス１個の最大Port数 64,512に対して、それを上回るPort数をVMが要求したので不足したと判断しました。 （数値はイメージです。わかりやすいように簡略化しています）
  
| Node１台の<br>割当Port数 | VM（GKE Node）数 | 全VMに<br>必要なPort数 | 割当可能な最大Port数<br>（IPアドレス 1個の場合） | Port 過不足 |
|-----------:|------------:|-------------:|-------------:|-------------:|
| 3,149      | 21          | 66,129       | 64,512       | -1,617       |

<br>
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/12c0c14c-d173-78bf-9f87-cc38d96d73c0.png">


#### AWS NAT Gateway との比較
ここで疑問がでてきました。
そういえば、AWS の NAT Gateway はどうなっているのだろうか？
気になったので、調べてみました。

- AWS の 同時アクセス数
  送信先毎に 最大 55,000 まで

  [AWSユーザーガイド 「NAT ゲートウェイ」](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-gateway.html#nat-gateway-basics)より引用
  > 各 IPv4 アドレスは、固有の送信先それぞれに対して最大 55,000 の同時接続をサポートできます。

NAT Gatewayは「送信先に対して55,000まで」と、よほどの大量アクセスでないと制限に該当しないことがわかりました。また可用性を考えると、サブネット毎にNAT Gatewayを構築する必要があります。つまり、NAT Gatewayを2つ以上で構成するため、理論上の上限値はさらに２倍（３つ以上で構成した場合は３倍）になります。
そのため、AWSでは「NAT Gatewayのサイジング」を気にする必要性が低いのか と気が付きました。

### Cloud NATの再設計のポイント

CloudNATで必要なPort数は、面積の計算と同じ考えで設計する必要があります。
- 計算式
  `VM（GKE Node）に割り当てるPort数` ✕ `VM(GKE Node）数` = `CloudNATに必要なPort数`

  具体的な例を挙げてみます。
  - VMに割り当てるPort数を増やすと、VMの最大台数が減ってしまう
    <img width="350" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/cce32fb1-b015-8e65-608e-2a2e0a955739.png">

  - VMの最大台数を増やすと、VMに割り当てるPort数が減ってしまう
    <img width="350" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/342ed048-2148-5c08-64ef-4ee842b43f35.png">

そこで、CloudNATのIPアドレス数だけでなく、VMに割り当てるPort数、GKEのNode数も見直しを行いました。
見直ししたポイントは以下の４点です。

1. CloudNATの割当可能なPort数を増やす。
  単純にCloudNAT IPアドレスを追加すれば増やせます。
  ただし、連携先のシステムではCloudNATのIPアドレスを指定してアクセス許可していることを考慮にいれる必要がありました。
   - 将来Node数が増えることを考慮して、IPアドレスを多めに確保しておく
   - 一方IPアドレスを増やしすぎると連携先側の設定で困る可能性があるので、多すぎてもいけない

2. GKE Nodeのスペックをあげ、必要なNodeの台数を減らす。
  Node数が増えると、全体で必要なPort数が増えてしまいます。
  必要なNode数を減らす為に、GKE Nodeのスペックを上げて1台のNode上で稼働できるPod数を増やしました。（集約度を上げた）

3. 外部のサービスを呼び出す処理にWaitをいれて、ピーク時の同時アクセス数を下げる。
   アプリケーションの送信処理にWaitをいれて送信ペースを落とすように変更してもらいました。これによりピーク時の数値を下げることが出来ました。

4. VM（GKE Node）に割り当てるPort数の設定を見直す。
  上記の変更を考慮にいれて、再度設定を見直しました。
   - 最小ポート数（VMに割り当てるPort数）
     2048
   - 動的ポートの割り当て
     無効 → 有効
  リスエスト数の増加にあわせて自動調整する機能を有効化しました。
  ただしアクセス数が急上昇する今回のケースではPortの追加が間にあわないことがわかったので、最小ポート数は多めに確保しました。

### Cloud NAT再設計版をリリースして、移行再開
複数IPアドレスを持たせた再設計版の Cloud NAT をリリースしました。
その後、移行を再開し、無事GCPへの移行を完了させることができました。


## 6. まとめ
移行後は、Cloud NATのPort枯渇が発生することなく稼働が出来ています。

今回のケースは幸いなことにPort不足発生によるお客様への影響がなく、またメンバーに協力や助言をもらうことでスムーズに解決することができました。

最後に今回のしくじりから学んだ教訓で終わりたいと思います。

- クラウドプラットフォームが違えば設計のポイントも変わってくる。”同じ”と思い込むのは危険なので、ちゃんと調べましょう。
- どんなに調べても、事前に気がつくのも難しいこともある。まわりの経験者にも相談してみましょう。

最後まで読んでいただき、ありがとうございました！