3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ZOZOAdvent Calendar 2024

Day 15

PSCを用いたプロジェクト・VPCをまたいだCloudRun同士の内部通信

Last updated at Posted at 2024-12-14

概要

PSC(Private Service Connect)を利用することで、GCP上でプロジェクト・VPCをまたいだ内部通信をすることができます。今回は、プロジェクトをまたいだCloudRunからCloudRunへの通信を可能とする構成とそのTerraform定義を紹介します。

構成

今回は以下のような構成を作りたいと思います。ProjectAにAPIを提供するCloudRunを配置し、ProjectBにそれを参照するClientアプリのCloudRunを配置する構成となっています。

qiita-psc.drawio.png

CloudRunからVPCへ通信する場合はVPC Connectorを利用するか、Direct VPCを利用する方法があります。今回はVPC Connectorを利用する構成とします。また、PSCのクライアント側の構成としてはPSC Backendと PSC Endpointがありますが、今回はPSC Endpointを利用した構成とします。

CloudRunからVPCへの接続については以下をご参照ください。

またPSCについては以下をご参照ください。

構成についての補足

ProjectAにおけるサブネット

今回ProjectAにおいて、3つのSubnetを用意しています。1つはLoadBalancerを配置しているSubnetです。このSubnetは普通のSubnetなのですが、その他2つは特殊なサブネットとなります。Subnetを作成するときにpurposeを指定することで特殊なSubnetを作ることができます。どんな種類があるかは以下のTerraformプロバイダのページが参考になります。

今回はProjectBにおいて、2つの特殊なサブネットを用意しています。1つはPSC Attachment専用のSubnetです。これはAttachmentにつき1つ作る必要があります。purposeは PRIVATE_SERVICE_CONNECTを指定します。

もう1つはregional managed proxy用のサブネットになります。これは、EnvoyベースのLBを利用する場合に用意しておく必要があります。purposeは REGIONAL_MANAGED_PROXY を指定します。詳しくは以下をご参照ください。

プロジェクトBにおけるサブネット

また、ProjectBにおいても2つのSubnetを用意しています。1つはPSC Endpointを配置するためのサブネットです。このSubnetは普通のSubnetです。もう1つはVPC Connector専用のサブネットです。purposeは PRIVATE_SERVICE_CONNECT を指定する必要があり、CIDRレンジは/28に固定する必要があります。

実装

それでは、具体的な実装を紹介します。基本的にはすべてTerraformでリソースを作成しますが、Cloud Runはgcloudコマンドでリリースする構成としました。これは、Cloud Runはインフラとしての管理ではなくアプリケーション側での管理としたいためです。Terraformで定義をした場合、アプリケーション側でデプロイを行うとTerraformで定義した構成と差分が出てしまうため、Terraformでの定義自体をしないようにしています。

プロジェクトA

Terraform

# Networ
resource "google_compute_network" "project_a_network" {
  name                    = "project-a-network"
  project                 = "project-a"
  auto_create_subnetworks = false
}

# Subnet
resource "google_compute_subnetwork" "project_a_subnet" {
  name          = "project-a-subnet"
  project       = "project-a"
  region        = "asia-northeast1"
  ip_cidr_range = "x.x.x.x/xx"
  network       = google_compute_network.project_a_network.name
}

resource "google_compute_subnetwork" "project_a_subnet_for_psc" {
  name          = "project-a-subnet-for-psc"
  project       = "project-a"
  region        = "asia-northeast1"
  ip_cidr_range = "x.x.x.x/xx"
  network       = google_compute_network.project_a_network.name
  purpose       = "PRIVATE_SERVICE_CONNECT"
}

resource "google_compute_subnetwork" "project_a_subnet_for_regional_proxy" {
  name          = "project-a-subnet-for-regional-proxy'
  project       = "project-a"
  region        = "asia-northeast1"
  ip_cidr_range = "x.x.x.x/xx"
  network       = google_compute_network.project_a_network.name
  purpose       = "REGIONAL_MANAGED_PROXY"
  role          = "ACTIVE"
}

# load balancer
resource "google_compute_region_network_endpoint_group" "neg_cloud_run_sample_api" {
  name                  = "neg-cloudrun-sample-api"
  network_endpoint_type = "SERVERLESS"
  region                = "asia-northeast1"

  cloud_run {
    service = "sample-api"
  }
}

resource "google_compute_region_backend_service" "backend_to_service_sample_api_internal" {
  name                  = "backend-service-to-sample-api-internal"
  region                = "asia-northeast1"
  load_balancing_scheme = "INTERNAL_MANAGED"
  protocol              = "HTTP"

  backend {
    balancing_mode = "UTILIZATION"
    group          = google_compute_region_network_endpoint_group.neg_cloud_run_sample_api.self_link
  }
}

resource "google_compute_region_url_map" "url_map_to_sample_api" {
  name            = "url-map-to-sample-api-internal"
  region          = "asia-northeast1"
  default_service = google_compute_region_backend_service.backend_to_service_sample_api.self_link
}

resource "google_compute_region_target_http_proxy" "target_http_proxy_to_sample_app_internal" {
  name        = "target-http-proxy-to-sample-app-internal"
  description = "target-http-proxy-to-sample-app-internal"
  region      = "asia-northeast1"
  url_map     = google_compute_region_url_map.url_map_to_sample_api.self_link
}

resource "google_compute_address" "sample_app_internal_load_balancer_address" {
  name         = "sample-app-internal-balancer-address"
  subnetwork   = google_compute_subnetwork.project_a_subnet.id
  address_type = "INTERNAL"
  address      = "x.x.x.x"
  region       = "asia-northeast1"
}

resource "google_compute_forwarding_rule" "forwarding_rule_to_sample_app_internal" {
  provider = google-beta
  name     = "forwarding-rule-to-sample-app-internal"
  region   = "asia-northeast1"

  ip_protocol           = "TCP"
  load_balancing_scheme = "INTERNAL_MANAGED"
  port_range            = "80"
  network               = google_compute_network.project_a_network.id
  subnetwork            = google_compute_subnetwork.project_a_subnet.id
  target                = google_compute_region_target_http_proxy.target_http_proxy_to_sample_app_internal.self_link
  ip_address            = google_compute_address.sample_app_internal_load_balancer_address.address
  network_tier          = "PREMIUM"
}

# PSC Attachment
resource "google_compute_service_attachment" "service_attachment_to_ma_manager" {
  name   = "service-attachment-to-sample-api"
  region = "asia-northeast1"

  enable_proxy_protocol = false
  connection_preference = "ACCEPT_MANUAL"
  nat_subnets           = [google_compute_subnetwork.project_a_subnet_for_psc.id]
  target_service        = google_compute_forwarding_rule.forwarding_rule_to_sample_app_internal.id

  consumer_accept_lists {
    project_id_or_num = "project-b"
    connection_limit  = 1
  }
}

CloudRun

gcloud run deploy sample-api \
  --region asia-northeast1 \
  --image "image" \
  --service-account "sa@xxx.iam.gserviceaccount.com" \
  --ingress internal-and-cloud-load-balancing \
  --concurrency 10 \
  --min-instances 1 \
  --max-instances 3

補足

CloudRunのdeployについて

Cloud Runにおいて、--ingress オプションで internal-and-cloud-load-balancing を指定することで、内部またはLBからの通信のみを許可することができます。gcloud run deploy コマンドの詳細は以下のドキュメントをご参照ください。

プロジェクトB

Terraform

# Network
resource "google_compute_network" "project_b_network" {
  name                    = local.project
  project                 = local.project
  auto_create_subnetworks = false
}

# Subnet
resource "google_compute_subnetwork" "project_b_subnet" {
  name          = "project-b-subnet"
  project       = "project-b"
  region        = "asia-northeast1"
  ip_cidr_range = "x.x.x.x/xx"
  network       = google_compute_network.project_b_network.name
}

resource "google_compute_subnetwork" "project_b_subnet_for_sample_app_vpc_connector" {
  name          = "${local.project}-for-vpc-connector-subnet-asia-northeast1"
  project       = local.project
  region        = "asia-northeast1"
  ip_cidr_range = "x.x.x.x/28"
  network       = google_compute_network.project_b_network.id

  private_ip_google_access = true
}

# VPCConnector
resource "google_vpc_access_connector" "sample_client_app_vpc_connector" {
  name   = "sample-client-app-vpc-connector"
  region = "asia-northeast1"

  subnet {
    name       = google_compute_subnetwork.project_b_subnet_for_sample_app_vpc_connector.name
    project_id = "project-b"
  }
  machine_type  = "e2-micro"
  min_instances = 2
  max_instances = 3
}

# PSC Endpoint
resource "google_compute_address" "sample_api_psc_endpoint_address" {
  address_type = "INTERNAL"
  ip_version   = "IPV4"
  name         = "sample-api-psc-endpoint-address"
  project      = "project-b"
  purpose      = "GCE_ENDPOINT"
  region       = "asia-northeast1"
  subnetwork   = google_compute_subnetwork.project_b_network.id
  address      = "x.x.x.x" # project_b_subnetのレンジから指定
}

resource "google_compute_forwarding_rule" "sample_api_psc_endpoint" {
  load_balancing_scheme = ""
  ip_address            = google_compute_address.sample_api_psc_endpoint_address.id
  name                  = "sample-api-psc-endpoint"
  subnetwork            = google_compute_subnetwork.project_b_network.id
  project               = "project-b"
  region                = "asia-northeast1"
  target                = "https://www.googleapis.com/compute/v1/projects/project-b/regions/asia-northeast1/serviceAttachments/service-attachment-to-sample-api"
"

  service_directory_registrations {
    namespace = "goog-psc-default"
  }
}

CloudRun

gcloud run deploy sample-client-app \
  --region asia-northeast1 \
  --image "image" \
  --service-account "sa@xxx.iam.gserviceaccount.com" \
  --vpc-connector sample-client-app-vpc-connector \
  --vpc-egress private-ranges-only \
  --concurrency 10 \
  --min-instances 1 \
  --max-instances 1

補足

PSC Endpointのアドレスに付いて

PSC Endpointでは、指定したサブネット内のアドレスを紐づけることができます。そして、VPC内から紐づけたアドレスに対して通信をすることでPSC Endpointに紐づいたPSC Attachmentを経由し、Attachmentに紐づいたリソースへアクセスすることができます。つまり、自分たちで管理しているVPC内のアドレスにアクセスすることで、他のプロジェクト・VPC内のリソースのアクセスが可能となります。これにより、他のVPCのCIDRレンジがどうなっているかなどを気にする必要がありません。

PSC Endpointのtargetについて

PSC Endpointでは、ターゲットとなるPSC Attachmentをlink形式で指定する必要があります。基本的には以下の形式のリンクとなります。

https://www.googleapis.com/compute/v1/projects/アタッチメントのプロジェクト名/regions/アタッチメントのリージョン/serviceAttachments/アタッチメント名

CloudRunのdeployについて

Cloud Runにおいて、VPC Connectorを経由した通信をしたい場合は、deployコマンドでどのVPC Connectorを利用するかを --vpc-connector オプションで指定する必要があります。また、すべての通信をVPC Connector経由にするか、Private IPのみをVPC Connectorを経由させるようにするかを選択ができます。今回は --vpc-egress private-ranges-only とすることで、Private IPのみをVPC Connector経由で通信するように設定しています。こちらも以下のドキュメントをご参照ください。

まとめ

今回は、PSCを利用してプロジェクトをまたいだCloudRunからCloudRunへの通信を内部の経路で行う構成について紹介しました。PSCを利用することで、VPCピアリングを利用せずにVPCをまたいだ通信ができるため、CIDRかぶりの問題を考えたりといった頭が痛い問題から開放されてかなり便利だなと感じています。今回の構成を参考にしていただけたら幸いです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?