terraform を使って GCP にロードバランサを設置してみた ～その①～

こんにちわ @ktoshi です！

今回は [terraform](https://www.terraform.io/) でGCPロードバランサを設置するお話をします。

「GCPにロードバランサを設置したい！」「リソースは terraform で管理したい！」
そんなことを思った方もおられるでしょう。
しかし、terraform の google プロバイダにロードバランサといったリソースはありません。
どうやって設置すんだ……私はしばらく悩みました………同じく悩んでいる方々に届けば…！

# 概要
先述の通り、Terraform の [GCPプロバイダ](https://www.terraform.io/docs/providers/google/index.html) にはロードバランサといったリソースはありません。
そのため、Terraform でロードバランサを設置するには下記のリソースを組み合わせる必要があります。

* [google_compute_backend_service](https://www.terraform.io/docs/providers/google/r/compute_backend_service.html)
* [google_compute_health_check](https://www.terraform.io/docs/providers/google/r/compute_health_check.html)
* [google_compute_global_forwarding_rule](https://www.terraform.io/docs/providers/google/r/compute_global_forwarding_rule.html)
* [google_compute_target_http_proxy](https://www.terraform.io/docs/providers/google/r/compute_target_http_proxy.html)
* [google_compute_url_map](https://www.terraform.io/docs/providers/google/r/compute_url_map.html)

また、今回は下記のリソースも利用します。

* [google_compute_instance](https://www.terraform.io/docs/providers/google/r/compute_instance.html)
* [google_compute_instance_group](https://www.terraform.io/docs/providers/google/r/compute_instance_group.html)
* [google_compute_global_address](https://www.terraform.io/docs/providers/google/r/compute_global_address.html)

# 環境
* Terraform v0.12.20
  * [こちら](https://www.terraform.io/docs/providers/google/index.html) を参考にGCPのProviderの設定をしてください。
* [gcloud](https://cloud.google.com/sdk/gcloud) 280.0.0

# Let's Enjoy!!
まず、ささっと一通りの設定書いちゃいます。
[サンプルコード](https://github.com/toshekey/terraform-sample/tree/master/gcp-lb)

```lb-instance.tf
# インスタンスに使用するイメージ
data "google_compute_image" "image" {
  family  = "centos-8"
  project = "centos-cloud"
}

# インスタンスに紐づけるIPアドレス
resource "google_compute_address" "instance-address" {
  name = "instance-address"
}

# ロードバランサに紐づけるIPアドレス
resource "google_compute_global_address" "lb-address" {
  name = "lb-address"
}

# インスタンス
resource "google_compute_instance" "instance" {
  name         = "instance"
  machine_type = "g1-small"
  zone         = "asia-northeast2-a"

  tags = ["http-server"]

  boot_disk {
    initialize_params {
      image = data.google_compute_image.image.self_link
    }
  }

  network_interface {
    network = "default"
    access_config {
      nat_ip = google_compute_address.instance-address.address
    }
  }
}

# 非マネージドインスタンスグループ
resource "google_compute_instance_group" "instance-group" {
  name        = "instance-group"

  instances = [
    google_compute_instance.instance.self_link,
  ]

  zone = "asia-northeast2-a"
}

# バックエンドサービス用ヘルスチェック
resource "google_compute_health_check" "health-check" {
  name = "health-check"
  timeout_sec        = 1
  check_interval_sec = 1
  tcp_health_check {
    port = "80"
  }
}

# バックエンドサービス
resource "google_compute_backend_service" "backend-service" {
  name        = "backend-service"
  port_name   = "http"
  protocol    = "HTTP"
  timeout_sec = 3000

  backend {
    group = google_compute_instance_group.instance-group.self_link
  }

  health_checks = [google_compute_health_check.health-check.self_link]
}

# URLマッピング
resource "google_compute_url_map" "url-map" {
  name        = "url-map"
  default_service = google_compute_backend_service.backend-service.self_link
}

# HTTP転送ターゲット
resource "google_compute_target_http_proxy" "target-http-proxy" {
  name             = "target-http-proxy"
  url_map          = google_compute_url_map.url-map.self_link
}

# 転送ルールの作成
resource "google_compute_global_forwarding_rule" "global-forwarding-rule-http" {
  name       = "global-forwarding-rule-http"
  target     = google_compute_target_http_proxy.target-http-proxy.self_link
  port_range = "80"
  ip_address = google_compute_global_address.lb-address.address
}

# ネットワーク設定の情報取得
data "google_compute_network" "default" {
  name = "default"
}

# ファイアウォール設定
resource "google_compute_firewall" "firewall" {
  name    = "http"
  network = data.google_compute_network.default.name

  target_tags = ["http-server"]
  source_ranges = ["130.211.0.0/22", "35.191.0.0/16"]

  allow {
    protocol = "icmp"
  }

  allow {
    protocol = "tcp"
    ports    = ["80"]
  }
}

# ロードバランサに紐づいたIPアドレスの出力
output "lb-ipaddress" {
  value = google_compute_global_address.lb-address.address
}

# インスタンスに紐づいたIPアドレスの出力
output "instance-ipaddress" {
  value = google_compute_address.instance-address.address
}
```
上記の設定ファイルを作成後、おまじないの`terraform plan` -> `terraform apply` を実行すると環境の作成ができます。

なお、コマンド発行後下記の出力がありますので、インスタンスやLBのIPアドレスはそれらを参照してください。

```
instance-ipaddress = xxx.xxx.xxx.xxx
lb-ipaddress = yyy.yyy.yyy.yyy
```

後はWebサービスを起動させるだけです。
インスタンスのIPアドレスにSSHで接続しても良いですが、せっかくなので gcloud コマンドでSSHしましょう。

```shell
gcloud compute ssh <インスタンス名> --zone <インスタンスの稼働しているゾーン名> --project <インスタンスの所属プロジェクト>
ex) gcloud compute ssh instance --zone asia-northeast2-a --project sample-project
```
上記のようなコマンドでSSH接続ができるので、IPアドレスなどを知らなくても接続できるんです！便利ですねぇ。
そして、このコマンドを使うとグローバルIPアドレスを付与していないインスタンスにもSSH接続できるようになるのですが、それはまた後日…！

少し余談が入りましたが、インスタンスへ接続できれば後はWebサービスを起動させるだけです。

```
$ sudo su -
# setenforce permissive
# dnf install -y httpd
# systemctl start httpd
# echo -n "TEST" > /var/www/html/index.html
```
上記の後にロードバランサのIPアドレスをブラウザで開くと「TEST」と表示されれば成功です！！

これであなたも簡単にロードバランサーを作ることができますね！

# まとめ
一回作ってしまえば、どうってことないですが、この構成を把握するまでにはめちゃくちゃ時間を要しました。。。
同じく苦しんでいる方の助けになればと思います！
ただ、この構成ではHTTPSに対応していなかったりするので、これをベースにさらに改良したものをまた載せていこうと思います。
仕事もこうやって簡単に負荷分散できれば。。。

それではみなさんも素敵な分散ライフを…！