(GCP) CloudFunctionsからPrivateなCloudSQLへ接続する
はじめに
CloudSQLへの接続にPrivateIPを使用することには以下のようなメリットがあります。
セキュリティ面
- PrivateIPを使用するとCloudSQLインスタンスはインターネットに公開されません。これにより、インターネット経由の不正アクセスのリスクが軽減する。
パフォーマンス面
- VPC内のリソース間の通信が最適化されるため、レイテンシが低減するなどネットワークのパフォーマンスが向上する。
特にレイテンシに関してはPublicIP、PrivateIPそれぞれを比較した面白い記事もありました。
その他コスト面やネットワーク構成を柔軟に設定できるなど様々ありますが、VPC内に設置されたプライベートなSQLインタンスへはその外部に存在するサーバーレスコンピューティングからはどのように接続すればよいでしょうか。今回はその方法や設定方法についてCloudFunctionsを使用した例でご紹介できればと思います。
Terraform(1.4.0) を使用します。GitHubリポジトリがあるのでそちらを参考にして頂ければと思います!!🙇
全体構成
前提として、CloudSQLはサービスプロデューサーネットワークと呼ばれるGoogleの管理下にあるネットワークに作成されるため実際には自作したVPC内に設置することはできません。
PrivateIPを使用してCloudSQLへ接続したい場合、VPCピアリングを用いて自身のVPCとサービスプロデューサーネットワークとの接続をピアリングする必要があります。
さらにサーバレスコンピューティングから接続する場合Serverless VPC Access connectorを使用します。VPC内に作成したコネクタを使用してCloudSQLに接続するように構成することで公開ネットワークを経由せずGoogleCloudの内部ネットワークに閉じた接続を実現できます。
各モジュールと変数
構築に使用する各モジュールと変数(variables)を簡単に紹介します。
Network
ネットワークを構成するモジュール
resource "google_compute_network" "this" {
name = "${var.app_name}-vpc"
routing_mode = "REGIONAL"
auto_create_subnetworks = false
}
resource "google_compute_global_address" "this" {
name = "${var.app_name}-private-ip-block"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
ip_version = "IPV4"
address = var.peering_ip_range
prefix_length = 16
network = google_compute_network.this.self_link
}
resource "google_service_networking_connection" "this" {
network = google_compute_network.this.self_link
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.this.name]
}
resource "google_vpc_access_connector" "this" {
name = "${var.app_name}-connector"
network = google_compute_network.this.name
ip_cidr_range = "${var.connector_ip_range}/28"
}
google_compute_network
VPCを作成
今回はサブネットが不要なためauto_create_subnetworks
をfalseにする
google_compute_global_address
VPCピアリングに使うためのプライベートIPアドレスの範囲を確保
google_service_networking_connection
サービスプロデューサーネットワークとのピアリングを作成
(service = "servicenetworking.googleapis.com"
)
google_vpc_access_connector
Serverless VPC Access connectorを作成
※ip_cidr_range
はgoogle_compute_global_address
のIPrangeと重複しないように指定(後述)かつprefix長は28である必要があります。
CloudSQL
CloudSQLインスタンスを作成するモジュール(MySQL5.7を使用)
resource "google_sql_database_instance" "this" {
name = "${var.app_name}-db-instance"
database_version = "MYSQL_5_7"
settings {
tier = "db-f1-micro"
availability_type = "ZONAL"
disk_size = 10
ip_configuration {
ipv4_enabled = false
private_network = var.vpc_self_link
}
}
deletion_protection = true
}
resource "google_sql_database" "this" {
name = "${var.app_name}-db"
instance = google_sql_database_instance.this.name
}
resource "google_sql_user" "this" {
name = var.db_user
password = var.db_password
instance = google_sql_database_instance.this.name
}
特筆する箇所はありませんがip_configuration
ブロックにてネットワークに関する設定を行なっています。
-
ipv4_enabled = false
PublicIPの割り当てを行わない -
private_network = var.vpc_self_link
先ほどのVPCを指定
変数(variables)
各種変数を定義
variable "project_id" {
description = "project id"
type = string
}
variable "db_user" {
description = "db user name"
type = string
sensitive = true
}
variable "db_password" {
description = "db user name"
type = string
sensitive = true
}
variable "peering_ip_range" {
description = "ip range for peering"
type = string
sensitive = true
}
variable "connector_ip_range" {
description = "ip range for connector"
type = string
sensitive = true
}
variable "app_name" {
description = "app name which is used for prefix of resources"
type = string
default = "private-sql"
}
variable "region" {
description = "region"
type = string
default = "asia-northeast1"
}
default値が無いものは後述するterraform.tfvars
ファイルから値を渡します。
Terraformの実行
実際にTerraformを実行して構成を作成します。
main.tf
先述の二つのモジュールを配置したシンプルな内容になります。
module "network" {
source = "./modules/network"
app_name = var.app_name
peering_ip_range = var.peering_ip_range
connector_ip_range = var.connector_ip_range
}
module "cloud_sql" {
source = "./modules/cloud_sql"
app_name = var.app_name
db_user = var.db_user
db_password = var.db_password
vpc_self_link = module.network.vpc_self_link
}
terraform.tfvarsの作成
terraform.tfvars
ファイルをルートディレクトリに作成して各値を埋めます。
※peering_ip_range
とconnector_ip_range
は競合しないように指定してください。
(また、RFC1918
に沿ったプライベートIPrangeを指定する必要があります。)
project_id = "your-project-id" # 自身のプロジェクトID
db_user = "your-db-user-name" # 任意のDBユーザー名
db_password = "your-db-secure-password" # 任意のDBパスワード
peering_ip_range = "10.1.0.0" # connector_ip_rangeと競合しないよう任意指定
connector_ip_range = "10.2.0.0" # peering_ip_rangeと競合しないよう任意指定
必要なAPIサービスを有効化
構成を作成する前に必要なAPIサービスを有効化しておく必要があります。必要なものは以下の通りです。
- compute.googleapis.com
- servicenetworking.googleapis.com
- vpcaccess.googleapis.com
※下記はCloudFunctionsのデプロイに必要
- cloudfunctions.googleapis.com
- cloudbuild.googleapis.com
リポジトリに上記APIサービスを有効化するシェルがあるのでそちらを使用します。
$ sh enable_apis.sh
実行
init
→plan
→apply
を実行して構成を作成します。
※DBインスタンスの作成にはしばらく時間がかかります。
$ terraform init
$ terraform plan
$ terraform apply
確認
GoolgeCloudのSQLコンソールに移動し作成されたインスタンスにPrivateIPのみが割り当てられていることを確認します。(※PrivateIPはCloudFunctionsのデプロイ時に使用するので控えておきます。)
CloudFunctions
HTTPSトリガーの関数(Go)をデプロイしCloudSQLとの疎通を確認します。
関数
CloudSQLに接続し簡単なクエリを実行するシンプルな関数です。
package private_sql
:
(省略)
:
func ExecQueryHTTP(w http.ResponseWriter, r *http.Request) {
conn, err := connectTCPSocket()
if err != nil {
log.Fatal(err)
}
var result string
err = conn.QueryRow("SELECT 'Hello, World!'").Scan(&result)
if err != nil {
log.Fatal(err)
}
fmt.Fprint(w, result)
}
デプロイ
先述の関数をデプロイします。
$ gcloud functions deploy PrivateSqlFunc \
--entry-point ExecQueryHTTP \
--vpc-connector private-sql-connector \
--region asia-northeast1 \
--runtime go121 \
--trigger-http \
--allow-unauthenticated \
--source ./functions/private-sql \
--set-env-vars DB_USER=<your-db-user-name>,DB_PASS=<your-db-secure-password>,DB_NAME=private-sql-db,DB_PORT=3306,INSTANCE_HOST=<your-private-ip>
--vpc-connector
オプションへ先ほど作成したVPC Access connectorの名前を指定しています。
--set-env-vars
へは各値を埋めてDBへの接続情報を渡します。
CloudSQLとの接続を確認
CloudFunctionsのHTTPSトリガーURLへアクセスしクエリ結果が返ることを確認します。
URLはデプロイ実施後のターミナルもしくはコンソールから取得できます。
$ curl https://<your-function-url>
Hello, World!
おわりに
以上になります。CloudFunctionsからプライベートなCloudSQLインスタンスに接続する方法をご紹介しましたが、Serverless VPC Access connectorを使用したこの方法は同じくサーバーレスコンピューティングであるAppEngineやCloudRunでも応用できます。気になる方はぜひ試して頂けたら幸いです。🙇
参考
株式会社クロスビットでは、デスクレスワーカーのためのHR管理プラットフォームを開発しています。
一緒に開発を行ってくれる各ポジションのエンジニアを募集中です。
Discussion