はじめに:クラウド時代のネットワークは、パケットの「宛先」の概念が変わる
こんにちは。SREとして日々クラウドの海を泳いでいる私ですが、夜中にページャーが鳴り響く原因の多くは、やはり「ネットワーク」の解像度不足に起因しています。
特にGCP(Google Cloud)上でKubernetes(GKE)を運用したり、マイクロサービスが複雑に絡み合うWeb API基盤を構築したりしていると、避けて通れないのが「IPアドレスの枯渇とルーティング」の壁です。
「1つの仮想マシン(VM)に、複数のプライベートIPを持たせたい」
「コンテナごとに独自のIPをアサインして、従来のレガシーなネットワーク監視ツールやセキュリティポリシーをそのまま適用したい」
そんな現場の切実な要求に対して、GCPが用意している強力な回答が 「エイリアスIPレンジ(Alias IP ranges)」 です。今回は、このエイリアスIPの裏側の仕組みから、パケットがどうルーティングされているのか、そして実務でハマりがちなポイントまで、シニアエンジニアの視点で徹底的に解説していきます。
—
1. エイリアスIPレンジとは何か? なぜ今、これが必要なのか
従来のオンプレミスや、初期のクラウドにおける仮想マシン(VM)は、1つのインターフェース(NIC)に対して1つのプライベートIPアドレスが基本でした。例えば、GCPのデフォルトのVPCネットワークでは、VM作成時にプライマリの内部IPが1つ割り当てられます。
しかし、DockerやKubernetesなどのコンテナオーケストレーション技術が主流になると、1台のVM(GKEのワーカーノード)の内部で、数十から数百のコンテナが稼働し、それぞれが独立したIPアドレスを持つ必要が出てきました。
ここで、昔ながらの「NAT(Network Address Translation)」や「ブリッジネットワーク」でコンテナのIPを隠蔽するアプローチをとると、どうなるでしょうか。
- 外部から特定のコンテナへのダイレクトなルーティングが困難になる
- パケットの送信元IPがノードのIPに書き換わってしまうため、アプリケーション層やL7/L4ログで「どのコンテナからリクエストが飛んできたのか」を追跡できなくなる
- SNAT(Source NAT)のポート枯渇問題(いわゆる *Port Exhaustion*)に悩まされる
GCPのVPCが持つ「ルーティングの魔法」
GCPのVPCは、ソフトウェア定義ネットワーキング(SDN)である Andromeda(アンドロメダ) アーキテクチャによって駆動しています。GCPのネットワーク世界では、VMのハイパーバイザー(ホストOS側)がソフトウェア的にスイッチングを行っているわけではありません。Googleの物理ネットワークのハードウェア(オフロードチップ)が、直接VMの仮想NICに対してL3ルーティングを行っています。
エイリアスIPレンジを使うと、VMのプライマリIPとは別に、VPCのサブネットから切り出された「セカンダリのIPレンジ(または個別のIPアドレス)」を、そのVMの仮想NIC(nic0 など)に直接バインドできます。
これにより、NATを一切挟むことなく、GCPの物理ルーターが直接そのエイリアスIP宛てのパケットを該当VMへと届けてくれるのです。これが、コンテナネットワークのパフォーマンスと可観測性を劇的に向上させる理由です。
—
2. エイリアスIPの仕組みと通信フロー
では、エイリアスIPを持つVMに対して、外部(あるいは同じVPC内の別のVM)からパケットが到達するまでのフローを追ってみましょう。
[クライアントVM / 外部]
│
▼ (宛先IP: 10.128.0.50 はエイリアスIP)
[GCP VPC ネットワーク (Andromeda)]
│ (Googleの分散ルーターがL3ルーティングを直接解決)
▼
[ターゲットVMの仮想NIC (nic0)]
│ (Linuxカーネルのルーティングテーブル & iptables)
▼
[コンテナ / アプリケーション (またはローカルインターフェース)]
1. ルーティングの解決: 発信元から送信されたパケットの宛先IPが、あるサブネット内のエイリアスIPレンジに含まれている場合、GCPのVPCルーターは、そのIPがどのVMのどのNICに割り当てられているかをVPCのコントロールプレーンから即座に特定します。
2. パケットの転送: カプセル化等のオーバーヘッドを最小限に抑えつつ、パケットは直接ターゲットVMの仮想NICにデリバリーされます。
3. VM内部でのハンドリング: Linuxカーネルに到達したパケットは、通常のプライマリIP宛てと同様に処理されます。もしそのIPがコンテナ(例: KubernetesのPod)にバインドされている場合、CNI(Container Network Interface)が設定したルーティングや仮想インターフェース(vethペアなど)を経由して、該当するコンテナへと渡されます。
—
3. 実践:GCP環境での設定と構築手順
理論が分かったところで、実際にGoogle Cloud CLI(gcloud)を使って、エイリアスIPを持つインスタンスを作成・設定してみましょう。
シナリオ
- VPCサブネット:
10.128.0.0/20 - 作成するVM:
app-server-01 - プライマリIP: 自動割当
- エイリアスIPレンジ:
10.128.10.0/24(このレンジをこのVMのコンテナ用に専有させる)
1. gcloudコマンドによるVM作成とエイリアスIPの付与
以下のコマンドを実行することで、VMの作成と同時にサブネットからのエイリアスIPレンジの割り当てを行います。
# 変数の定義
INSTANCE_NAME="app-server-01"
ZONE="asia-northeast1-a"
NETWORK="default"
SUBNET="default"
ALIAS_RANGE_NAME="container-ip-range"
ALIAS_CIDR="10.128.10.0/24"
# エイリアスIPレンジを持ったVMインスタンスの作成
gcloud compute instances create ${INSTANCE_NAME} \
--zone=${ZONE} \
--machine-type=e2-medium \
--network=${NETWORK} \
--subnet=${SUBNET} \
--private-network-ip=10.128.0.10 \
--aliases=${ALIAS_RANGE_NAME}=${ALIAS_CIDR} \
--tags=web-backend
> SREの現場Tips:
> すでに存在するVMに対して後からエイリアスIPを追加・変更したい場合は、一度インスタンスを停止(stop)してから gcloud compute instances network-interfaces update コマンドを実行する必要があります。本番環境ではダウンタイムが発生するため、Terraform等のIaCツールを使って最初から適切に設計・適用することが鉄則です。
—
4. アプリケーション層からの利用と動作確認(コード例)
エイリアスIPが割り当てられたVM上で動作するアプリケーション(例えば、複数の仮想IPごとに異なるバインドアドレスでWeb APIを待ち受けるような構成)を想定します。
Pythonの socket ライブラリを使用して、特定のエイリアスIPにバインドしてリクエストを待ち受けるシンプルなHTTPサーバーのコード例を見てみましょう。
Pythonによるバインドテストスクリプト (alias_server.py)
import socket
from http.server import HTTPServer, BaseHTTPRequestHandler
# 待ち受けるエイリアスIPアドレス(例: 10.128.10.5 はエイリアスレンジ内のIP)
BIND_IP = "10.128.10.5"
PORT = 8080
class SimpleHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/plain; charset=utf-8")
self.end_headers()
# どのIPでリクエストを受け付けたかをレスポンスに含める
response_message = f"Hello from Alias IP: {BIND_IP}\nPath: {self.path}"
self.wfile.write(response_message.encode("utf-8"))
def run():
server_address = (BIND_IP, PORT)
try:
# 自作のバインド先を指定してHTTPServerを起動
httpd = HTTPServer(server_address, SimpleHandler)
print(f"[*] Starting HTTP server on {BIND_IP}:{PORT} ...")
httpd.serve_forever()
except OSError as e:
print(f"[!] Error: {BIND_IP} にバインドできませんでした。IPが正しくVMに設定されているか確認してください。")
print(f"詳細: {e}")
if __name__ == "__main__":
run()
動作確認(curlによるテスト)
別のVMや同一VPC内から、このエイリアスIPに対して実際にリクエストを飛ばしてみます。
# エイリアスIPに対して直接HTTPリクエストを送信
curl -X GET http://10.128.10.5:8080/api/v1/status
# 期待される出力:
# Hello from Alias IP: 10.128.10.5
# Path: /api/v1/status
この挙動こそが、エイリアスIPの真骨頂です。VMのプライマリIP(10.128.0.10)を経由せずとも、指定したエイリアスIP(10.128.10.5)へ直接パケットがルーティングされ、アプリケーションが正確に応答を返しています。
—
5. トラブルシューティング:現場でよくある「罠」とデバッグ手順
最後に、実務でエイリアスIPを運用する際によく遭遇するトラブルと、その切り分け手法を共有します。
トラブル1: エイリアスIP宛てのパケットがVMに届かない(タイムアウトする)
原因の切り分けフロー
1. ルーティングの確認:
送信元から traceroute や ping を実行し、VPCのルーティングテーブルが正しく機能しているか確認します。
2. OS内部のIPエイリアス設定(Linuxカーネル)の確認:
GCP側でエイリアスIPを割り当てても、LinuxのOS側(ネットワークインターフェース)がそのIPを認識していない場合があります(GKEやKubernetesのCNIが管理している場合は自動設定されますが、素のVMの場合は手動で設定が必要です)。
OS側でのIP確認コマンド
# VM内部で現在バインドされているIPアドレスを確認
ip addr show dev nic0
# もしエイリアスIPがインターフェースに見当たらない場合、手動で追加するテスト
sudo ip addr add 10.128.10.5/32 dev nic0
トラブル2: サブネットのIP枯渇 (IP Address Exhaustion)
GCPのサブネットはCIDRブロックのサイズがあらかじめ決まっています。エイリアスIPレンジは、そのサブネット全体のIPプールから消費されます。
そのため、Kubernetesクラスタのノード数やPod数を見積も誤ると、VPCサブネット全体のIPが枯渇し、新しいVMやポッドが起動できなくなる障害に直結します。
- 対策: GKEを使用する場合は、ネイティブVPCモード(VPC-native cluster)のPod用セカンダリレンジのサイジングを入念に行いましょう。「足りなくなったら広げればいいや」が最も通用しないのがクラウドのIP設計です。
—
おわりに:ネットワークの「仕組み」を知れば、クラウドはもっとシンプルになる
今回はGCPのエイリアスIPレンジに焦点を当て、その概念からパケットの挙動、Pythonを用いた実装例、そして実務でのトラブルシューティングまでを紐解きました。
クラウドの抽象化されたレイヤーの向こう側で、パケットはGCPのAndromedaルーターを経由し、極めて洗練されたルートを通ってVMの目的のエイリアスIPへと到達しています。この背後の仕組み(ルーティングの原理)を頭に描けるようになっておくと、複雑なマイクロサービスアーキテクチャの設計や、いざというときの障害切り分けにおいて、迷いのない強力な武器となります。
皆さんのインフラ設計・運用の一助となれば幸いです。それでは、快適なSREライフを!
コメント