🐣

作って学ぶ Cloud Load Balancing 入門

2025/03/24に公開
1

概要

Cloud Load Balancing を構成する要素は複数あり、初見でそれらを全て把握して設定するのは困難です。
本稿では初めて Cloud Load Balancing を触る人に向けて、terraform でグローバル外部アプリケーションロードバランサを設定する方法について、ドキュメントをベースにまとめました。
手を動かしながら Cloud Load Balancing の概要を把握するのに役立てば幸いです。

全体構成

今回作成する構成は下図の通りです。

  • VPC Network/subnet:instance group を配置するためのネットワーク、サブネットを作成します。

  • instance template:instance group を作るのにインスタンステンプレートを作成します。

  • instance group:マネージドインスタンスグループ(MIG)のこと。instance template を元にリージョン MIG を2つ作成して、それぞれを異なるリージョンに配置します。

  • firewall rule:内向きアクセスを許可するファイアウォールルールを作成して、instance group にアクセスできるようにします。

  • Backend service:Cloud Load Balancing によるトラフィックの分散方法(1秒あたりの最大リクエスト数や、VM インスタンスの使用率など)を定義します。トラフィックを流す宛先(=バックエンド)に指定できるアプリケーションタイプは限られており、ここでは2つの instance group にトラフィックを分散します。

  • health check:Backend service では、ロードバランサからの接続を受信するバックエンドの稼働状況を定期的に監視するヘルスチェックを指定します。これにより、処理不能なバックエンドにリクエストが送信されるリスクを軽減できます。

  • URL map:ホストルールやパスマッチャーを構成してリクエストを異なる宛先に転送できます。ここではシンプルにするためホストルールやパスマッチャーを設定せず、Backend service の構成に従ってリクエストを instance group に送ります。

  • external IP address:インターネットの向こう側にいるクライアントが Load Balancer にアクセスする際に指定する IP アドレス。forwarding rule に紐づけます。

  • forwarding rule:外部アプリケーションロードバランサでは、forwarding rule は Target HTTP(S) proxy を参照します。ここでは、トラフィックを Target HTTP proxy に転送します。

  • Target HTTP proxy:forwarding rule で指定された IP アドレスとポートをリッスンし、リクエストを受信すると HTTP 接続を終端します。URL map と Backend service の構成に従って、適切なバックエンドと新しい接続を確立します。

VPC Network

VPC Network を作成するには google_compute_network を使用します。

resource "google_compute_network" "lb_sample" {
  name                    = "lb-sample-network"
  auto_create_subnetworks = false
}

VPCネットワークが作成されたことを次のコマンドで確認します。

$ gcloud compute networks list --filter="name:lb-sample-network"
NAME               SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
lb-sample-network  CUSTOM       REGIONAL

subnet

subnet を作成するには google_compute_subnetwork を使用します。
network には作成した VPC Network を指定します。

resource "google_compute_subnetwork" "lb_sample_subnet" {
  name                             = "lb-sample-subnet"
  ip_cidr_range                    = "10.0.0.0/24"
  network                          = google_compute_network.lb_sample.self_link
  region                           = "asia-northeast1"
  stack_type                       = "IPV4_ONLY"
}

resource "google_compute_subnetwork" "lb_sample_subnet_2" {
  name                             = "lb-sample-subnet-2"
  ip_cidr_range                    = "10.0.1.0/24"
  network                          = google_compute_network.lb_sample.self_link
  region                           = "asia-southeast1"
  stack_type                       = "IPV4_ONLY"
}

サブネットが作成されたことを次のコマンドで確認します。

$ gcloud compute networks subnets list --filter="network:lb-sample-network"
NAME                REGION           NETWORK            RANGE        STACK_TYPE
lb-sample-subnet    asia-northeast1  lb-sample-network  10.0.0.0/24  IPV4_ONLY
lb-sample-subnet-2  asia-southeast1  lb-sample-network  10.0.1.0/24  IPV4_ONLY

instance template

instance template を作成するには google_compute_instance_template を使用します。
network には作成した VPC Network、subnetwork には instance group を配置する subnet を設定します。
また、http-server というネットワークタグを設定して後述する firewall rule が適用されるようにします。
<project-id> は自身の Google Cloud Project のプロジェクト番号に置き換えてください。

resource "google_compute_instance_template" "lb_sample_template_asia_north" {
  name = "lb-sample-template-asia-northeast1"
  disk {
    auto_delete  = true
    boot         = true
    device_name  = "lb-sample-template-asia-northeast1"
    mode         = "READ_WRITE"
    source_image = "projects/debian-cloud/global/images/debian-12-bookworm-v20250212"
    type         = "PERSISTENT"
  }
  labels       = null
  machine_type = "e2-micro"
  metadata = {
    # The startup-script-url specifies a script that executes when instances are started. This script installs Apache and changes the welcome page to include the client IP and the name, region, and zone of the VM instance. Feel free to explore this script.
    # https://storage.googleapis.com/cloud-training/gcpnet/httplb/startup.sh
    startup-script-url = "gs://cloud-training/gcpnet/httplb/startup.sh"
  }
  network_interface {
    access_config {
      network_tier = "PREMIUM"
    }
    network                     = google_compute_network.lb_sample.self_link
    subnetwork                  = google_compute_subnetwork.lb_sample_subnet.self_link
  }
  region = "asia-northeast1"
  scheduling {
    automatic_restart           = true
    on_host_maintenance         = "MIGRATE"
    provisioning_model          = "STANDARD"
  }
  service_account {
    email = "<project-id>-compute@developer.gserviceaccount.com"
    scopes = [
      "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring.write", "https://www.googleapis.com/auth/service.management.readonly",
      "https://www.googleapis.com/auth/servicecontrol", "https://www.googleapis.com/auth/trace.append"
    ]
  }
  tags = ["http-server"]  # Network tags
}

resource "google_compute_instance_template" "lb_sample_template_asia_south" {
  name = "lb-sample-template-asia-southeast1"
  disk {
    auto_delete  = true
    boot         = true
    device_name  = "lb-sample-template-asia-southeast1"
    mode         = "READ_WRITE"
    source_image = "projects/debian-cloud/global/images/debian-12-bookworm-v20250212"
    type         = "PERSISTENT"
  }
  labels       = null
  machine_type = "e2-micro"
  metadata = {
    startup-script-url = "gs://cloud-training/gcpnet/httplb/startup.sh"
  }
  network_interface {
    access_config {
      network_tier = "PREMIUM"
    }
    network                     = google_compute_network.lb_sample.self_link
    subnetwork                  = google_compute_subnetwork.lb_sample_subnet_2.self_link
  }
  region = "asia-southeast1"
  scheduling {
    automatic_restart           = true
    on_host_maintenance         = "MIGRATE"
    provisioning_model          = "STANDARD"
  }
  service_account {
    email = "<project-id>-compute@developer.gserviceaccount.com"
    scopes = [
      "https://www.googleapis.com/auth/devstorage.read_only", "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring.write", "https://www.googleapis.com/auth/service.management.readonly",
      "https://www.googleapis.com/auth/servicecontrol", "https://www.googleapis.com/auth/trace.append"
    ]
  }
  tags = ["http-server"]  # Network tags
}

instance template が作成されたことを次のコマンドで確認します。

$ gcloud compute instance-templates list
NAME                                MACHINE_TYPE  PREEMPTIBLE  CREATION_TIMESTAMP
lb-sample-template-asia-northeast1  e2-micro                   2025-03-22T04:41:59.741-07:00
lb-sample-template-asia-southeast1  e2-micro                   2025-03-22T04:58:30.082-07:00

instance group

instance group を作成するには google_compute_region_instance_group_manager を使用します。
instance_template には作成した instance template を指定します。

resource "google_compute_region_instance_group_manager" "lb_mig_group_asia_north" {
  name                             = "lb-sample-asia-northeast1-mig"
  base_instance_name               = "lb-sample-asia-northeast1-mig"
  region                           = "asia-northeast1"
  distribution_policy_zones        = ["asia-northeast1-a", "asia-northeast1-b", "asia-northeast1-c"]
  wait_for_instances               = false
  wait_for_instances_status        = "STABLE"
  target_size                      = 1
  named_port {
    name = "http"
    port = 80
  }
  version {
    instance_template = google_compute_instance_template.lb_sample_template_asia_north.self_link
  }
}

resource "google_compute_region_instance_group_manager" "lb_mig_group_asia_south" {
  name                             = "lb-sample-asia-southeast1-mig"
  base_instance_name               = "lb-sample-asia-southeast1-mig"
  region                           = "asia-southeast1"
  distribution_policy_zones        = ["asia-southeast1-a", "asia-southeast1-b", "asia-southeast1-c"]
  wait_for_instances               = false
  wait_for_instances_status        = "STABLE"
  target_size                      = 1
  named_port {
    name = "http"
    port = 80
  }
  version {
    instance_template = google_compute_instance_template.lb_sample_template_asia_south.self_link
  }
}

instance group が作成されたことを次のコマンドで確認します。

$ gcloud compute instance-groups managed list
NAME                           LOCATION         SCOPE   BASE_INSTANCE_NAME             SIZE  TARGET_SIZE  INSTANCE_TEMPLATE                   AUTOSCALED
lb-sample-asia-northeast1-mig  asia-northeast1  region  lb-sample-asia-northeast1-mig  1     1            lb-sample-template-asia-northeast1  no
lb-sample-asia-southeast1-mig  asia-southeast1  region  lb-sample-asia-southeast1-mig  1     1            lb-sample-template-asia-southeast1  no

firewall rule

作成した instance group にアクセスするには、firewall rule が必要です。
ここでは全てのIPからのアクセスを許可する firewall rule を作成して、VPC ネットワーク内の http-server というネットワークタグを持つリソースに適用します。
firewall rule を作成するには google_compute_firewall を使用します。

resource "google_compute_firewall" "lb_allow_http" {
  name                    = "lb-sample-allow-http"
  direction               = "INGRESS"
  network                 = google_compute_network.lb_sample.self_link
  priority                = 1000
  source_ranges           = ["0.0.0.0/0"]
  target_tags             = ["http-server"]
  allow {
    ports    = ["80"]
    protocol = "tcp"
  }
  disabled                = false
}

firewall rule が作成されたことを次のコマンドで確認します。

$ gcloud compute firewall-rules list --filter network=lb-sample-network
NAME                  NETWORK            DIRECTION  PRIORITY  ALLOW   DENY  DISABLED
lb-sample-allow-http  lb-sample-network  INGRESS    1000      tcp:80        False

ここまでで全体像の赤枠部分が出来上がりました。

これまで作成したリソースが正しく設定されているか確認するため、instance group の VM インスタンスに設定された外部IPをブラウザから叩いてみます。
次のように HostnameServer Location が画面に表示されれば問題なく VM インスタンスにアクセスできています。

health check

health check を作成するには google_compute_health_check を使用します。

resource "google_compute_health_check" "lb_sample_health_check" {
  name                = "lb-sample-health-check"
  check_interval_sec  = 5
  healthy_threshold   = 2
  timeout_sec         = 5
  unhealthy_threshold = 2
  http_health_check {
    port               = 80
    port_specification = "USE_FIXED_PORT"
    proxy_header       = "NONE"
    request_path       = "/"
  }
}

health check が作成されたことを次のコマンドで確認します。

$ gcloud compute health-checks list
NAME                     REGION  PROTOCOL
lb-sample-health-check           HTTP

Backend service

Backend service を作成するには google_compute_backend_service を使用します。
health_checks に作成した health check を設定し、backend.group に作成した instance group を設定します。

resource "google_compute_backend_service" "lb_sample_backend" {
  name                            = "lb-sample-backend"
  connection_draining_timeout_sec = 300
  health_checks = [
    google_compute_health_check.lb_sample_health_check.id
  ]
  load_balancing_scheme           = "EXTERNAL_MANAGED"
  port_name                       = "http"
  protocol                        = "HTTP"
  session_affinity                = "NONE"
  timeout_sec                     = 30
  backend {
    group                        = google_compute_region_instance_group_manager.lb_mig_group_asia_north.instance_group
    balancing_mode               = "RATE"
    capacity_scaler              = 1
    max_rate_per_instance        = 1
  }
  backend {
    group                        = google_compute_region_instance_group_manager.lb_mig_group_asia_south.instance_group
    balancing_mode               = "UTILIZATION"
    capacity_scaler              = 1
    max_utilization              = 0.8
  }
}

asia-northeast1 の instance group には最大 1 rps(request per second)、asia-southeast1 の instance group には最大使用率80%までとして、トラフィックを流すように設定しました。

Backend service が作成されたことを次のコマンドで確認します。

$ gcloud beta compute backend-services list
NAME               BACKENDS                                                                                                                   PROTOCOL  LOAD_BALANCING_SCHEME  HEALTH_CHECKS
lb-sample-backend  asia-northeast1/instanceGroups/lb-sample-asia-northeast1-mig,asia-southeast1/instanceGroups/lb-sample-asia-southeast1-mig  HTTP      EXTERNAL_MANAGED       lb-sample-health-check

URL map

URL map を作成するには、google_compute_url_map を使用します。
default_service に作成した backend service を設定します。

resource "google_compute_url_map" "lb_sample_url_map" {
  name            = "lb-sample-url-map"
  default_service = google_compute_backend_service.lb_sample_backend.self_link
}

URL map が作成されたことを次のコマンドで確認します。

$ gcloud beta compute url-maps list
NAME                   DEFAULT_SERVICE
lb-sample-url-map      backendServices/lb-sample-backend

Target HTTP proxy

Target HTTP proxy を作成するには google_compute_target_http_proxy を使用します。
url_map に作成した url map を設定します。

resource "google_compute_target_http_proxy" "lb_sample_target_http_proxy" {
  name                        = "lb-sample-http-target-proxy"
  url_map                     = google_compute_url_map.lb_sample_url_map.self_link
}

Target HTTP proxy が作成されたことを次のコマンドで確認します。

$ gcloud beta compute target-http-proxies list
NAME                         URL_MAP
lb-sample-http-target-proxy  lb-sample-url-map

external IP address

外部 IP アドレスを予約するには google_compute_global_address を使用します。

resource "google_compute_global_address" "lb_sample_ip" {
  name       = "lb-sample-ipv4"
  ip_version = "IPV4"
}

予約された IPv4 アドレスを次のコマンドで確認します。

$ gcloud compute addresses describe lb-sample-ipv4 \
    --format="get(address)" \
    --global
<予約された IPv4 アドレス>

Forwarding rule

Forwarding rule を作成するには google_compute_global_forwarding_rule を使用します。
target に作成した Target HTTP proxy を指定し、ip_address に予約した IP アドレスを指定します。

resource "google_compute_global_forwarding_rule" "lb_sample_forwarding_rule" {
  name                  = "lb-sample-forwarding-rule"
  ip_protocol           = "TCP"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  port_range            = "80-80"
  target                = google_compute_target_http_proxy.lb_sample_target_http_proxy.self_link
  ip_address            = google_compute_global_address.lb_sample_ip.address
}

Forwarding rule が作成されたことを次のコマンドで確認します。

$ gcloud beta compute forwarding-rules list
NAME                           REGION  IP_ADDRESS          IP_PROTOCOL  TARGET
lb-sample-forwarding-rule              <予約された IPv4 アドレス>        TCP          lb-sample-http-target-proxy

ロードバランサの動作確認

Forwarding rule を作成してから反映されるまでに少し時間がかかるため数分程度待機します。
その後、ブラウザで http://<予約された IPv4 アドレス> にアクセスして、次のような画面が表示されればロードバランサが正常に動作しています。

次に、2つの instance group で負荷分散されているかを確認するため、以下のシェルスクリプトを実行します。

end=$((SECONDS+300)); while [ $SECONDS -lt $end ]; do seq 10 | xargs -n1 -P10 -I{} sh -c 'curl -s http://<予約された IPv4 アドレス> | sed -n "s/.*Server Hostname: \([^<]*\).*/\1/p"; echo'; done

これは <予約された IPv4 アドレス> にリクエストを送信しアクセス先の Hostname を出力する処理を10並列で5分間実行し続けるコマンドです。
これを実行すると最初は asia-northeast1 にある Hostname だけが出力され続け、時間が経つと asia-southeast1 の Hostname が多く出力されるようになります。

lb-sample-asia-northeast1-mig-4x3t
lb-sample-asia-northeast1-mig-4x3t
lb-sample-asia-northeast1-mig-4x3t
lb-sample-asia-northeast1-mig-4x3t
lb-sample-asia-northeast1-mig-4x3t
lb-sample-asia-northeast1-mig-4x3t
...
...
...
lb-sample-asia-southeast1-mig-pz8x
lb-sample-asia-southeast1-mig-pz8x
lb-sample-asia-southeast1-mig-pz8x
lb-sample-asia-northeast1-mig-4x3t
lb-sample-asia-southeast1-mig-pz8x
lb-sample-asia-southeast1-mig-pz8x
...

最初はクライアントに近い場所にある asia-northeast1 の instance group にリクエストが流されるものの、Backend service で最大 1 rps という制限を設定したため、それを超える頻度でリクエストが送られてきたことを検知すると asia-southeast1 の instance group に流されるようになったという動きになります。

参考

外部アプリケーションロードバランサの概要

1

Discussion