Open22

TerraformにおけるPrivate Service Connect と Cloud SQL についてまとめ

nollnoll

実務で調べた際に、ふとTerraformで色んな書き方が散見されたので、深く調べてみた

nollnoll

まずは、目指すべき姿を説明

あるVPCでCloudSQLを公開IPを持たないように構築し、複数のVPCからCloudSQL Auth Proxyで接続をすることができることを目指す

接続するクライアントは、VMということもあれば、Cloud Runなど、PaaSサービスということもある。

nollnoll

ご丁寧に公式ドキュメントあったけど、日本語だとエンドポイントを手動やる方法しか書いてない

https://cloud.google.com/sql/docs/mysql/configure-private-service-connect?hl=ja#terraform_1

英語のドキュメントには、エンドポイントを自動で作る方法書いてあるけど、Terraformでは書かれてない

https://cloud.google.com/sql/docs/mysql/configure-private-service-connect#create-psc-endpoint-new

nollnoll

自動でエンドポイントを構成するのは、ベータ版とのこと

nollnoll

手動は公式に書いてあるので、自動でエンドポイントを作る方法をTerraform化してみる

nollnoll

そもそも sevice connection policyとは、

サービス接続ポリシーは、リージョンの Google Cloud リソースです。これにより、ネットワーク管理者は、サービス接続の自動化を通じてデプロイおよび接続できるプロデューサー サービスを指定できます。マネージド サービスにサービス接続ポリシーが存在する場合、コンシューマー サービス管理者はそのサービスをデプロイできます。

https://cloud.google.com/vpc/docs/about-service-connection-policies#service-policies

nollnoll

検証してみたけど、CloudSQLにPrivate IPが付与されてない

公式ドキュメントのgcloudコマンドを見ると、知らないオプションを指定してる

--psc-auto-connections

これは、Terraformのプロバイダーには、これに該当するものがないかな...

nollnoll

しょうがないので、現状手動で構成する

nollnoll

CloudSQLの例

resource "google_sql_database_instance" "default" {
  name             = "mysql-instance"
  region           = "us-central1"
  database_version = "MYSQL_8_0"
  settings {
    tier              = "db-f1-micro"
    availability_type = "REGIONAL"
    backup_configuration {
      enabled            = true
      binary_log_enabled = true
    }
    ip_configuration {
      psc_config {
        psc_enabled               = true
        allowed_consumer_projects = []
      }
      ipv4_enabled = false
    }
  }
  deletion_protection = false # Set to "true" to prevent destruction of the resource
}


nollnoll

エンドポイントで使う内部IPアドレスを予約して、NATで、CloudSQLのサービスアタッチメントに紐づける

resource "google_compute_address" "default" {
  name         = "psc-compute-address-${google_sql_database_instance.default.name}"
  region       = "us-central1"
  address_type = "INTERNAL"
  subnetwork   = "default"     # Replace value with the name of the subnet here.
  address      = "10.128.0.43" # Replace value with the IP address to reserve.
}

data "google_sql_database_instance" "default" {
  name = resource.google_sql_database_instance.default.name
}

resource "google_compute_forwarding_rule" "default" {
  name                  = "psc-forwarding-rule-${google_sql_database_instance.default.name}"
  region                = "us-central1"
  network               = "default"
  ip_address            = google_compute_address.default.self_link
  load_balancing_scheme = ""
  target                = data.google_sql_database_instance.default.psc_service_attachment_link
}


nollnoll

結構大事なのは、リージョンの指定である
この書き方だと、同じリージョンじゃないと、PSC接続できない

もしグローバルでやるなら、この書き方になりそう

resource "google_compute_global_address" "default" {
  provider      = google-beta
  name          = "global-psconnect-ip"
  address_type  = "INTERNAL"
  purpose       = "PRIVATE_SERVICE_CONNECT"
  network       = google_compute_network.network.id
  address       = "100.100.100.105"
}
data "google_sql_database_instance" "default" {
  name = resource.google_sql_database_instance.default.name
}
resource "google_compute_global_forwarding_rule" "default" {
  provider      = google-beta
  project       = google_compute_network.network.project
  name          = "globalrule"
  target        = data.google_sql_database_instance.default.psc_service_attachment_link
  network       = google_compute_network.network.id
  ip_address    = google_compute_global_address.default.id
  load_balancing_scheme = ""
}

nollnoll

正直dataは、冗長。普通に指定できる

nollnoll

コンシューマー(CloudSQLにアクセスするクライアント)のVPCに限定公開DNSゾーンを構築する必要がある
それをterraform化してみよう

nollnoll

CloudSQLのDNS名を取得
限定公開DNSゾーンを構築
レコードをセット

data "google_sql_database_instance" "default" {
  name = resource.google_sql_database_instance.default.name
}

resource "google_dns_managed_zone" "private-zone" {
  name        = "private-zone"
  dns_name    = "${var.region}.sql.goog."
  description = "CloudSQL private DNS zone"

  visibility = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.consumer.id
    }
  }
}

resource "google_dns_record_set" "a" {
  name         = data.google_sql_database_instance.default.dns_name
  managed_zone = google_dns_managed_zone.private_zone.name
  type         = "A"
  ttl          = 300

  rrdatas = [google_compute_address.address]
}