はじめに
こんにちは、技術本部ML・データ部MLOpsブロックの鹿山(@Ash_Kayamin)です。MLOpsブロックではバッチ実行環境としてVertex AI Pipelinesを用いています。Vertex AI PipelinesはGCPマネージドなKubeflow Pipelinesを提供するサービスで、コンテナ化した処理に依存関係をもたせたパイプラインを定義し実行できます。この記事ではVertex AI Pipelinesで起動するノードからIPアドレス制限があるエンドポイントへ通信するために、NATを利用して通信元IPアドレスを固定した方法と実装のはまりどころについてご紹介します。
Vertex AI Pipelinesの利用例については過去の記事で紹介していますので、併せてご覧ください。

Vertex AI Pipelinesによる機械学習ワークフローの自動化 - ZOZO TECH BLOG
目次
- はじめに
- 目次
- 課題:Vertex AI Pipelinesで起動するノードからIPアドレス制限があるエンドポイントと通信したい
- 解決策:ピアリングしたVPCにNATインスタンスを作成し、NATインスタンス経由で外部通信させる
- 通信元IPアドレスを固定するための最小構成
- 手順1. プライベートサービスアクセスを用いてユーザー管理のVPCとGoogleの共有VPCをピアリングする
- 手順2. 外部通信用VPC、サブネットおよびNATインスタンスを作成する
- 手順3. カスタムルートを作成してGoogleの共有VPCにエクスポートする
- NATインスタンスを冗長化した構成
- 差分1. Managed Instance Groupを用いてNATインスタンスを2台作成する
- 差分2. ロードバランサーのバックエンドにManaged Instance Groupを設定し、ロードバランサー経由でNATインスタンスに接続する
- 差分3. Cloud NATを用いて、NATインスタンスからの外部通信の通信元IPアドレスを固定する
実際に冗長構成で通信する例
- 終わりに
課題:Vertex AI Pipelinesで起動するノードからIPアドレス制限があるエンドポイントと通信したい
先日、Vertex AI Pipelinesで実行するバッチの中で、IPアドレス制限を課しているエンドポイントと通信することが必要になりました。しかしながら、Vertex AI Pipelinesで起動するノードからの通信の通信元IPアドレスは固定されず、またパイプラインの実行パラメータ等で指定もできません。そのため、デフォルトではIPアドレス制限を課しているエンドポイントと通信できません。通信先のIPアドレス制限に対応するため、通信元IPアドレスを固定する方法を検討する必要がありました。
解決策:ピアリングしたVPCにNATインスタンスを作成し、NATインスタンス経由で外部通信させる
GCP公式ブログでVertex AI Pipelinesを様々なネットワーク構成で利用する方法が紹介されています。今回作成した構成はこちらを参考にさせていただきました。公式ブログで紹介されている構成のおおまかな流れは以下になります。
- ユーザーが管理するVPCとGoogleの共有VPCをピアリングする。
- ユーザーが管理するVPCにNATインスタンスを作成する。
- Googleの共有VPCで起動するノードからの特定の宛先への外部通信がNATインスタンスを経由するようにカスタムルートを作成する。
- Vertex AI pipelinesのパイプラインパラメータVPC networkに、ピアリングしたユーザー管理のVPCを指定してパイプラインを実行する。
パイプラインパラメータVPC networkにピアリングしたユーザー管理のVPCを指定することで、ピアリング先のGoogleの共有VPCでノードを起動できます。このGoogleの共有VPC内で起動したノードから特定の宛先へ外部通信する際にNATインスタンスを経由させることで、通信元IPアドレスをNATインスタンスに付与したIPアドレスに固定します。
ネットワーク構成の概略図は以下になります。こちらの詳細については後ほどご説明します。
公式ブログの構成例ではNATインスタンスの耐障害性が考慮されていません。そこで、Managed Insntance Group(MIG)とCloud NATを利用することでNATインスタンスが担う機能に冗長性を持たせました。MIGはインスタンスの設定を記載したテンプレートからインスタンスを起動した上で、インスタンスのヘルスチェック・再起動・負荷に応じたスケーリング等を自動的に行ってくれます。リージョンMIGを使用すると、インスタンスを複数のゾーンに配置できます。また、Cloud NATはGCPマネージドなNATを提供するサービスになります。まず、Vertex AI Pipelinesで起動したノードからの外部通信は、MIGで起動したNATインスタンスを経由するようにしました。加えて、NATインスタンスからの外部通信はCloud NATに付与したIPアドレスを通信元IPアドレスとするようにCloud NATを設定しました。MIGとCloud NATを合わせて利用することで、NATインスタンスに冗長性を持たせた通信元IPアドレスの固定を実現しました。
ネットワーク構成の概略図は以下になります。こちらの詳細についても後ほどご説明します。
通信元IPアドレスを固定するための最小構成
この章では、NATインスタンスを用いて通信元IPアドレス固定を実現する最小構成の構築手順と利用方法についてご説明します。
図の例では、Vertex AI Pipelinesで起動したノードから、カスタムルートを設定しているIPアドレスx.x.x.xへ通信した場合、最終的にNATインスタンスから宛先アドレスx.x.x.xへ通信元アドレスy.y.y.yで通信が行われます。ルーティングの例を以下に示します。
手順1. プライベートサービスアクセスを用いてユーザー管理のVPCとGoogleの共有VPCをピアリングする
まずVPC Aを作成し、公式ドキュメントの手順に従ってGoogleの共有VPCとピアリングします。ピアリングをすることで、VPC間で内部通信できるようになります。ピアリングにあたっては、CIDRを指定する必要があります。このCIDRはユーザー管理のVPC内の他のサブネットで利用しているCIDRと重複してはいけません。Vertex AI Pipelinesでパイプラインを実行する際のパラメータVPC networkにピアリング済みのVPC Aを指定すると、パイプラインで利用するノードのIPアドレスはここで指定するCIDRから割り当てられるようになります。
Googleの共有VPCとピアリングした際のGCPマネジメントコンソールでのVPC Peeringの表示は以下のようになります。
ここで指定するCIDRのレンジが小さいと、同じネットワークを指定して複数のパイプラインを実行した際に、IPアドレスが枯渇してパイプラインを実行できなくなる可能性があります。そのため十分な大きさのレンジを割り当てるように注意します。Vertex AI Pipelinesで実行するパイプラインに含まれる各コンポーネントの処理はVertex AI TrainingのCustom Jobとして実行されます。割り当てるレンジと、Custom Jobで起動できるノードの数の関係は公式ドキュメントに記載があります。例えば/16を割り当てると最大63ジョブ(1ジョブ当たり8ノードと仮定)を並列実行できます。つまり、並列で63個のコンポーネントを同時に実行できますが、これ以上のコンポーネントを実行しようとするとIPアドレス不足でエラーが発生します。また、公式ドキュメントの通り、同じGCPプロジェクト内で特定のネットワークを指定して実行しているパイプラインがある場合、そのパイプラインを実行中は別のネットワークを指定したパイプラインは実行できないことにも注意が必要です。
加えて、Vertex AI Pipelinesで起動するノードから、VPC A内のサブネットに作成するNATインスタンスへの通信を許可するため、ここで指定するCIDRからの通信を許可するファイアウォールルールをVPC Aに作成します。
手順2. 外部通信用VPC、サブネットおよびNATインスタンスを作成する
次にVPC AのサブネットA、VPC BならびにVPC BのサブネットBを作成します。そして、サブネットA、Bそれぞれに接続した2つのネットワークインタフェースを持つCompute Engineインスタンスを作成します。サブネットBに接続するネットワークインタフェースにはパブリックIPを割り当てます。Compute EngineのインスタンスをNATとして機能させるため、以下コマンドをインスタンスの起動時に実行されるスクリプトとして設定します。
# サブネットAに10.95.0.0/16, サブネットBに10.97.0.0/16を割り当てている前提で
# それぞれのサブネットに接続しているネットワークインタフェース名を取得する
private_interface=$(ifconfig | grep 10.95 -B 1 | head -n 1 | awk -F: {'print $1'})
public_interface=$(ifconfig | grep 10.97 -B 1 | head -n 1 | awk -F: {'print $1'})
# フォワーディングとマスカレードを有効化
sysctl -w net.ipv4.ip_forward=1
sudo iptables -t nat -A POSTROUTING -o "$public_interface" -j MASQUERADE
# サブネットBのデフォルトゲートウェイをネクストホップとするデフォルトルートを作成
sudo ip route add default via 10.97.0.1 dev "$public_interface"
# Googleの共有VPCとのピアリングで10.98.0.0/21を割り当てている前提で
# Vertex AI Pipelinesで起動したノードへの戻り通信(=宛先が10.98.0.0/21)の場合はサブネットAのデフォルトゲートウェイをネクストホップとする
sudo ip route add 10.98.0.0/21 via 10.95.0.1 dev "$private_interface"
# Cloud IAPの戻り通信(=宛先が35.235.240.0/20)の場合はサブネットAのデフォルトゲートウェイをネクストホップとする. インスタンスへIAPを用いてSSH接続したい場合に必要
sudo ip route add 35.235.240.0/20 via 10.95.0.1 dev "$private_interface"このスクリプトではサブネットAに属するネットワークインタフェースで受けた通信を、サブネットBに属するネットワークインタフェースからVPC Bのデフォルトゲートウェイへ送るデフォルトルートを作成します。IPマスカレードの設定とこのデフォルトルートにより、NATインスタンスのサブネットAのネットワークインタフェースで受けた通信は、サブネットBのネットワークインタフェースに割り当てたパブリックIPアドレスを通信元IPアドレスとして、サブネットBのデフォルトゲートウェイ経由で外部と通信します。
また、Vertex AI Pipelinesで起動したノードへの戻り通信を考慮したルートを追加していることに注意してください。戻り通信に対しては、行きの通信を受けたサブネットAのデフォルトゲートウェイをネクストホップとしています。このルートがないと、デフォルトルートによって戻り通信がサブネットBのデフォルトゲートウェイにルーティングされてしまい、Vertex AI Pipelinesで起動したノードと通信ができません。
手順3. カスタムルートを作成してGoogleの共有VPCにエクスポートする
最後にNATインスタンス経由で通信させたい外部IPアドレスを定め、このIPアドレスへの通信を手順2で作成したNATインタンスへルーティングするカスタムルートをVPC Aに作成します。そしてVPC Peeringの設定でカスタムルートのエクスポートを有効にし、このルートをVertex AI Pipelinesのノードが起動するGoogleの共有VPCへエクスポートします。
カスタムルートのエクスポートを有効にした際の、GCPマネジメントコンソールでのVPC Peeringの表示は以下のようになります。
続きはこちら