😺

パブリックIPのみのCloud SQLにプライベートIPを付与してみる

2022/12/07に公開

tl;dr

パブリックIPのみで構成されたCloud SQLにプライベートIPを構成する(付与する)際の挙動を検証してみました。

はじめに

Cloud SQLでは接続方法としてインターネットからアクセスが可能なパブリックIP、VPC内部からのアクセスが可能なプライベートIPを選択することが可能です。

Cloud SQLではパブリックIPのみの構成から、プライベートIPのみの構成に変更する機能が備わっています。(インスタンスの再作成は不要です)

皆様も様々な事情でパブリックIPからプライベートIPへ切り替えないといけないケースもあるかと思います。

今回はこの機能を実際に動かした結果をまとめています。

注意1
本記事ではプライベートIPを構成するときの動作についてまとめていますので、プライベートIPに切り替えた後の接続方法には触れていません。

注意2
画像中にCloud SQLの接続名等が表示されていますが、当該プロジェクト・NW・Cloud SQLは削除済みであるためアクセスはできません。

プライベート接続のアーキテクチャ

プライベートIPを持つCloud SQLはユーザーのVPCではなく、Googleが管理するプロジェクトの中にあるVPC内に作成されます。
Googleが管理するVPCとユーザーが作成したVPCをVPCピアリングで接続してCloud SQLを使用します。
このGoogle管理のVPCとVPCピアリングすることをプライベート サービス アクセスと呼びます。

実際にやってみる

早速やってみましょう。
ここでは以下の流れで進めます。

  • パブリックIPのみのCloud SQLを作成
  • VPCを作成する
  • プライベートサービスアクセスを構成する
  • Cloud SQLにプライベートIPを付与する

パブリックIPのみのCloud SQLを作成

プロジェクトIDを設定します。

export PROJECT_ID=<任意のプロジェクトIDに置き換えてください>

プライベートサービスアクセスを有効化するために以下を実行します。

gcloud services enable servicenetworking.googleapis.com \
    --project=${PROJECT_ID}

https://cloud.google.com/service-infrastructure/docs/service-networking/getting-started?authuser=2&hl=ja#enabling_the_service

パブリックIPを付与したCloud SQLを作成します。
パブリックIPを付与するオプションは--assign-ipです。

gcloud sql instances create db001 \
    --region us-central1 \
    --assign-ip \
    --root-password hogefuga

しばらく待つと作成が完了します。
念の為、以下のコマンドで作成したCloud SQLでパブリックIPが付与されていて、プライベートIPが付与されていないことを確認しましょう。

gcloud sql instances list
出力結果
NAME: db001
DATABASE_VERSION: MYSQL_8_0
LOCATION: us-central1-c
TIER: db-n1-standard-1
PRIMARY_ADDRESS: 34.134.68.92
PRIVATE_ADDRESS: -
STATUS: RUNNABLE

コンソールでも確認できます。(画像サイズが小さいのでクリックして拡大してみてください)

VPCを作成する

作成するリソースの名称やIPアドレスの範囲は上図のような名称・IPアドレスレンジにしています。

構成図のリソース名 今回作成するリソース名 IPアドレスレンジ
Customer VPC network customer-vpc -
subnet customer-subnet 10.1.0.0/24
Private connectionのAllocated range service-producer-subnet 10.240.0.0/24

VPCを作成します。

gcloud compute networks create customer-vpc \
    --subnet-mode=custom

サブネットを作成します。

gcloud compute networks subnets create customer-subnet \
    --network=customer-vpc \
    --range=10.1.0.0/24 \
    --region=us-central1

プライベートサービスアクセスを構成する

プライベートIPの予約を行います。
図ではIPアドレスのレンジは10.240.0.0/16となっています。
実は/16となっていることに意味があるのですが、それを確認するためにあえて/24で設定をして進めてみます。

gcloud compute addresses create service-producer-subnet \
    --global \
    --purpose=VPC_PEERING \
    --addresses=10.240.0.0 \
    --prefix-length=24 \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc

プライベート接続を作成します。

gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=service-producer-subnet \
    --network=customer-vpc \
    --project=${PROJECT_ID}

Cloud SQLにプライベートIPを付与する

準備が整ったのでプライベートIPの付与を実行します。

gcloud beta sql instances patch db001 \
    --project=${PROJECT_ID} \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc

プライベートIPが付与されていることを確認します。

gcloud sql instances list
出力結果
NAME: db001
DATABASE_VERSION: MYSQL_8_0
LOCATION: us-central1-c
TIER: db-n1-standard-1
PRIMARY_ADDRESS: 34.134.68.92
PRIVATE_ADDRESS: 10.240.0.3
STATUS: RUNNABLE

Cloud SQLにプライベートIPを付与することができました。

ここでプライベート IP に関するクイック リファレンスを参照しながら注意点について確認していきます。
なお、今後もこのクイックリファレンスのトピックを何度か参照するため、以降は[クイックリファレンス:トピック名]と記載します。

注意点1
[クイックリファレンス:既存の Cloud SQL インスタンス]にも記載があるようにプライベートIPを付与する際にインスタンスが再起動するため、ダウンタイムが発生します。

注意点2
オプション --no-assign-ipを指定することでパブリックIPを削除してプライベートIPのみを付与することもできます。
ただし、[クイックリファレンス:パブリック IP とプライベート IP]にあるように、どちらの接続方法も他方に影響しないため、両方のIPアドレスを付与しておき、徐々にCloud SQLに接続しているアプリケーションをパブリックIP接続からプライベートIP接続に切り替えていくことが可能です。
パブリックIPの削除は次の章で試します。

リードレプリカがある場合はどうなるのか?

Cloud SQLにリードレプリカがある場合はどうなるでしょうか?
実はリードレプリカに直接プライベートIPを付与することはできず、プライマリがプライベートIPを付与されたタイミングで自動的にリードレプリカにもプライベートIPが付与されます。([クイックリファレンス:レプリカ]にて言及されています)

ではリードレプリカありの場合も実際にやってみましょう。
ここでは以下の流れで進めます。

  • パブリックIPのみのCloud SQLを作成
  • リードレプリカをプライマリと同じリージョンと別リージョンに作成
  • Cloud SQLにプライベートIPを付与する
  • パブリックIPを削除する

VPCは以前作ったものを再利用します。

パブリックIPのみのCloud SQLを作成

Cloud SQLを作成します。
–enable-bin-logを有効にしてリードレプリカを作成するための前提条件を満たすように構成します。

gcloud sql instances create db002 \
    --region us-central1 \
    --assign-ip \
    --root-password hogefuga \
    --enable-bin-log

リードレプリカをプライマリと同じリージョンと別リージョンに作成

検証のために同一リージョン(us-central1)と別リージョン(asia-northeast1)にリードレプリカを作成します。

リードレプリカ(us-central1)を作成。

gcloud sql instances create db002-replica1 \
    --master-instance-name=db002  \
    --region us-central1

リードレプリカ(asia-northeast1)を作成。

gcloud sql instances create db002-replica2 \
    --master-instance-name=db002 \
    --region asia-northeast1

Cloud SQLにプライベートIPを付与する

準備が整ったのでプライマリにプライベートIPを付与することでリードレプリカにもプライベートIPが付与されることを確認しましょう。
(実はasia-northeast1のリードレプリカはプライベートIPの付与に失敗します。理由は後述)

gcloud beta sql instances patch db002 \
    --project=${PROJECT_ID} \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc

しばらくするとリードレプリカも含めて実行可能の状態になるのですが、asia-northeast1のリードレプリカにはプライベートIPが付与されていません。

オペレーションのログを見て、状況を確認しましょう。

gcloud sql operations list --instance=db002-replica2
出力結果
NAME: bfb203c4-31a5-402b-a502-dd8300000032
TYPE: UPDATE
START: 2022-11-30T12:43:59.988+00:00
END: 2022-11-30T12:45:52.130+00:00
ERROR: ERROR_SN_SUBNET_CREATION_FAILURE
STATUS: DONE

エラーが発生しています。
実はCloud SQL は、リージョンごとにプライベート サービス アクセス IP 範囲から /24 サブネットを割り振ります。
https://cloud.google.com/sql/docs/mysql/private-ip?hl=ja#network_issues

よって、現在割り当てている10.240.0.0/24のみでは足りないためエラーが発生しています。
asia-northeast1のリードレプリカにもプライベートIPを付与するため、プライベートサービスアクセスにサブネット(10.240.1.0/24)を追加します。

gcloud compute addresses create service-producer-subnet1 \
    --global \
    --purpose=VPC_PEERING \
    --addresses=10.240.1.0 \
    --prefix-length=24 \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc

アドレスレンジを更新します。

gcloud services vpc-peerings update --network=customer-vpc \
    --service=servicenetworking.googleapis.com \
    --ranges=service-producer-subnet,service-producer-subnet1

リードレプリカには直接プライベートIPを付与できないため、プライマリに対してもう一度プライベートIPを付与するコマンドを実行します。

gcloud beta sql instances patch db002 \
    --project=${PROJECT_ID} \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc


asia-northeast1のリードレプリカにもプライベートIPが付与されました。

パブリックIPを削除する
パブリックIPとプライベートIPの両方が付与された状態からパブリックIPのみを削除してみましょう。

まず、プライマリから削除してみます。

gcloud beta sql instances patch db002 \
    --no-assign-ip


プライマリのパブリックIPを消してもレプリカのパブリックIPは自動的には消えないことがわかります。

リードレプリカのIPも消しましょう。

gcloud beta sql instances patch db002-replica1 \
    --no-assign-ip
gcloud beta sql instances patch db002-replica2 \
    --no-assign-ip

共有VPCがある場合

以下の図のようにCustomer VPCとは別のVPCからプライベートIP構成のCloud SQLに接続したい場合はどうすればよいでしょうか?
この場合、VPCピアリングでは接続できません。
Google管理のVPCまで2ホップ必要となり、推移的ピアリングの制限に抵触します。

[クイックリファレンス:推移的ピアリング]でも触れられている通り、共有VPCを使って接続することを検討しましょう。

ここでは以下の流れで進めます。

  • 共有VPCを作成する
  • ホストプロジェクトのCloud SQLにプライベートIPを付与してみる
  • サービスプロジェクトのCloud SQLにプライベートIPを付与してみる
  • サービスプロジェクトでプライベートIPのCloud SQLを新規作成してみる

なぜホストプロジェクトとサービスプロジェクトの場合を分けて説明しているかの理由は後ほど説明します。

共有VPCのホストプロジェクト、サービスプロジェクトについて知りたい方は以下をご確認ください。
https://cloud.google.com/vpc/docs/shared-vpc?hl=ja

共有VPCを作成する

本筋ではないため、主要なコマンドのみ記載しています。
権限付与などを含めた詳しい手順は以下をご確認ください。
https://cloud.google.com/vpc/docs/provisioning-shared-vpc?hl=ja

ホストプロジェクトの共有VPCを有効化します。

gcloud compute shared-vpc enable ${PROJECT_ID}

サービスプロジェクトのプロジェクトIDを設定します。

export SERVICE_PROJECT_ID=<任意のプロジェクトIDに置き換えてください>

サービスプロジェクトをホストプロジェクトに接続します。

gcloud compute shared-vpc associated-projects add ${SERVICE_PROJECT_ID} \
    --host-project ${PROJECT_ID}

ホストプロジェクトのCloud SQLにプライベートIPを付与してみる

パブリックIPで共有VPCのホストプロジェクト側でCloud SQLを作成します。

gcloud sql instances create db003 \
    --region us-central1 \
    --assign-ip \
    --root-password hogefuga \
    --enable-bin-log

リードレプリカ(us-central1)を作成します。

gcloud sql instances create db003-replica1 \
    --master-instance-name=db003 \
    --region us-central1

リードレプリカ(asia-northeast1)を作成します。

gcloud sql instances create db003-replica2 \
    --master-instance-name=db003 \
    --region asia-northeast1

プライベートIPを付与します。

gcloud beta sql instances patch db003 \
    --project=${PROJECT_ID} \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc


問題なく付与できました。

サービスプロジェクトのCloud SQLにプライベートIPを付与してみる

必要に応じてプロジェクトを切り替えます

gcloud config set project ${SERVICE_PROJECT_ID}

プライベートサービスアクセスを有効化するために以下を実行します。

gcloud services enable servicenetworking.googleapis.com \
    --project=${SERVICE_PROJECT_ID}

サービスプロジェクト側でCloud SQLを作成します。

gcloud sql instances create db004 \
    --zone us-central1-a \
    --assign-ip \
    --root-password hogefuga \
    --enable-bin-log

リードレプリカはあえて作成しません。(実はレプリカがあってもなくてもプライベートIPの付与はできないためです)

ではプライベートIPを付与してみましょう。

gcloud beta sql instances patch db004 \
    --project=${SERVICE_PROJECT_ID} \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc
出力結果
ERROR: (gcloud.beta.sql.instances.patch) HTTPError 400: This operation is not valid for this instance.

エラーが発生しました。

GUIでも同じエラーが発生します。
サービスプロジェクトに存在するパブリックIPのみで構成されたCloud SQLの場合に後からにプライベートIPを付与することができないことがわかりました。

これらのことから[クイックリファレンス:共有 VPC ネットワーク]の記載はホストプロジェクトであれば可能であり、サービスプロジェクトであれば不可能であるということになります。

サービスプロジェクトでCloud SQLを新規作成してみる

[クイックリファレンス:共有 VPC ネットワーク]では「既存のCloud SQL」と記載されています。
つまり新規作成の場合はサービスプロジェクトであってもCloud SQLにプライベートIPを付与することは可能です。

gcloud beta sql instances create db005 \
    --project=${SERVICE_PROJECT_ID} \
    --network=projects/${PROJECT_ID}/global/networks/customer-vpc \
    --no-assign-ip


問題なく作成されていることがわかります。

まとめ

長々とCloud SQLにプライベートIPを付与する方法を公式ドキュメントを参照しながら検証しました。
もしみなさんも種々の事情でパブリックIPのみで構成されたCloud SQLにプライベートIPを付与する場合は本記事を参考にして、各種注意点に気をつけながら実施してください。

Google Cloud Japan

Discussion