Internal Passthrough LBで固定IPを実現するFloating IPパターンを構築してみた

こんにちは、SCSKの齋藤です。
本記事では、Internal Passthrough Load BalancerのFloating IPパターンを使って、VMの生死に影響されない固定IPを実現する方法についてご紹介します。

はじめに

前回までの記事でPartner InterconnectとCloud Routerの構築について書きました。今回はいよいよ、固定IPを実現するための核心部分であるInternal Passthrough LBの構築に入ります。

第1回の全体設計で説明した通り、固定IPをVMに直接持たせるとRegional MIGでゾーンをまたいだ際にIPが変わってしまう問題がありました。この問題を解決するために、ILB(Internal Passthrough Load Balancer)のフロントエンドに予約IPを持たせ、VMの生死からIPを切り離す「Floating IPパターン」を採用しています。

この記事では、Floating IPパターンの仕組みと、Terraformで構築する際に理解しておくべきポイント、そして構築中にハマった点について書いていきます。特に all_ports = true の必要性とPassthrough型の動作理解については、知らないとハマりやすいポイントなので詳しく説明します。


Internal Passthrough Load Balancerとは

Internal Passthrough Load Balancer(内部パススルーネットワークロードバランサ)は、Google Cloudが提供するL4のロードバランサです。「Passthrough」という名前が示す通り、パケットの送信元IPや宛先IPを一切書き換えずに、そのままバックエンドVMに転送するのが最大の特徴です。

通常のロードバランサ(Application Load BalancerやProxy型のNetwork Load Balancer)は、クライアントからの接続をLBが終端し、LBからバックエンドに対して新しい接続を張ります。つまりバックエンドから見るとクライアントのIPは見えず、LBのIPが送信元になります。一方、Passthrough型はパケットをそのまま転送するので、バックエンドVMはクライアントのIPを直接見ることができます。

この特性は通常「クライアントIPの保持」のために使われますが、今回はむしろ逆の発想で活用しています。「パケットの宛先がLBのフロントエンドIPのままVMに届く」という特性を利用して、VM側でそのIPに対する処理(iptables SNAT等)を行う設計です。

Internal Passthrough LBは内部(VPC内部)トラフィック向けのサービスで、インターネット向けには使えません。フロントエンドIPとしてVPCサブネット内の内部IPアドレスを予約して使います。このIPは google_compute_address で予約する形になり、VMが再作成されても不変です。


resource "google_compute_address" "floating_ip" {
  name         = "proxy-floating-ip"
  address_type = "INTERNAL"
  subnetwork   = google_compute_subnetwork.proxy.id
  address      = "xx.xx.xx.xx"  # 予約IPを指定
}

ヘルスチェック機能も備えており、バックエンドVMの正常性を監視してトラフィックを正常なVMだけに流すことができます。TCPヘルスチェックとHTTPヘルスチェックが選択できます。

Floating IPパターンの仕組み

Floating IPパターンとは、ロードバランサのフロントエンドIPを「浮動IP(Floating IP)」として利用し、バックエンドVMの入れ替えに影響されない固定のエンドポイントを提供するパターンです。

通常、GCEインスタンスに内部静的IPを割り当てると、そのIPはインスタンスのNICに紐づきます。MIGでインスタンスが再作成されると新しいインスタンスには別のIPが割り当てられます。Floating IPパターンでは、IPをVMのNICではなくILBのフロントエンドに持たせることで、VMの生死に関わらずIPが固定されます。


resource "google_compute_forwarding_rule" "ilb" {
  name                  = "proxy-ilb"
  load_balancing_scheme = "INTERNAL"
  ip_address            = google_compute_address.floating_ip.address
  all_ports             = true
  backend_service       = google_compute_region_backend_service.proxy.id
}

バックエンドVMには動的IPのみが割り当てられ、ILBがFloating IPへのトラフィックを適切なVMにルーティングします。VMが再作成されてIPが変わっても、ILBのバックエンドとして新しいVMが登録されれば、Floating IPへの通信は引き続き新VMに届きます。

Passthrough型ならではの設計上の考慮点

Passthrough型はパケットを書き換えないため、通常のロードバランサとは異なる設計上の考慮が必要でした。具体的には以下の3点をVM側で対応する必要がありました。

これらの設計上の考慮点をまとめると以下の通りです。

設定 目的 設定しないとどうなるか
loopbackにFloating IP追加 VMカーネルがFloating IP宛パケットを受理 パケットが破棄される
can_ip_forward = true Floating IPを送信元としたパケット送出を許可 VPCレベルでパケットがドロップ
all_ports = true 戻りパケット(エフェメラルポート宛)を通過 TCP接続が成立しない

第一に、loopbackインターフェースへのFloating IP追加です。ILBがパケットの宛先IPを書き換えないため、VMに届くパケットの宛先はFloating IPのままです。Linuxカーネルは自分が持つIPアドレス宛てでないパケットをデフォルトで破棄するため、loopbackにFloating IPを追加して「このIPは自分宛て」と認識させる必要があります。


# startup-scriptでの設定
if ! ip addr show lo | grep -q "${FLOATING_IP}/32"; then
  ip addr add ${FLOATING_IP}/32 dev lo
fi

第二に、can_ip_forward = true の設定です。GCEのデフォルトでは、VMのNIC IPと異なる送信元のパケットはVPCレベルでドロップされます。SNATでFloating IPを送信元に書き換えたパケットを外部に送出するには、この設定をInstance Templateで有効にしておく必要があります。

第三に、all_ports = true の設定です。これはForwarding Ruleに対する設定で、外部サーバーからの戻りパケットを漏れなくVMに届けるために必須です。戻りパケットの宛先ポートはエフェメラルポート(32768-60999)のいずれかになるため、特定ポートに制限するとTCP接続が成立しません。

工夫したところ・ハマったところ

all_ports = true に気づくまでに時間がかかった

構築中、最初はForwarding Ruleに接続先のポート番号だけを指定していました。しかしTCP接続が全く成立しません。SYNは送れるのにSYN-ACKが返ってこない、という状態でした。

tcpdumpでパケットを追っていくと、外部サーバーからのSYN-ACKパケットがILBのFloating IP宛てに送られてきているはずなのに、VM側に届いていないことがわかりました。原因はSYN-ACKの宛先ポートがエフェメラルポートであり、Forwarding Ruleで許可したポート以外だったためILBがドロップしていたのです。

all_ports = true を設定した瞬間にTCP接続が成立し、問題が解消しました。Internal Passthrough LBのPassthrough特性を正しく理解していれば事前に気づけたはずですが、「Forwarding Ruleのポートは接続先ポートを指定するもの」という思い込みがあり、戻りパケットのことを考慮できていませんでした。

Shared VPCでのILB配置

Shared VPC構成では、ILBのForwarding Ruleを作成する際にHost Projectのネットワークとサブネットを参照する必要がありました。ILB自体はService Projectに属するリソースとして作成しますが、networksubnetwork にはHost Projectのものを指定します。

最初はService Project自身のVPCを指定してしまい、「指定したネットワークが存在しない」というエラーに悩みました。Shared VPC構成では、Service ProjectはHost Projectのネットワークを利用するため、Service Project自身にはVPCが存在しないのです。このエラーメッセージからすぐに原因に辿り着けず、しばらく時間を使いました。

まとめ

– Internal Passthrough LBはパケットを書き換えないL4ロードバランサで、Floating IPパターンの実現に最適
– Floating IPパターンでは、IPをVMではなくILBのフロントエンドに持たせることで、VMの生死からIPを切り離す
– Passthrough型ならではの設計考慮(loopback IP、can_ip_forward、all_ports)を理解しておかないとハマりやすい
all_ports = true は戻りパケットのために必須。エフェメラルポートの存在を忘れないようにしたいところです

次回はProxy VM + Regional MIG + iptables SNATの構築について書きます。

著者について

SCSK株式会社
ソリューション事業グループ
基盤ソリューション事業本部

齋藤雄太をフォローする

クラウドに強いによるエンジニアブログです。

SCSKクラウドサービス(Google Cloud)は、Google Cloudの多彩なAIや各種サービスを活用したワンストップソリューションを提供します。SCSKのノウハウや体制を有効活用し、業務課題の解決に必要な全体検討と組み合わせで、最適な業務実装まで支援します。

Google Cloudその他技術ナレッジ
シェアする
×
タイトルとURLをコピーしました