てけノート

on the foot of giants

GKEでkubernetesのnodesをロードバランサーのバックエンドとして使いたいとき with terraform

   


これがなかなか面倒だったのでメモ。

やりたいこと

  1. terraformでhttp load balancer、gke container clusterを作成する
  2. http load balancerのバックエンドとしてclusterのnodeを登録する

つくりかた

1. http load balancerを作成する

googleのhttp load balancerは疎結合な部品の組み合わせで成り立っており、
http load balancer = global forwarding rule + target http proxy + url map
です。
terraformのdocを参考にまるっと作ります。

2. GKE clusterを作成する

これは特に意識せずにterraformで作成して問題ないですが、一点だけ注意しないといけないのは

 addons_config {
   http_load_balancing {
     disabled = false
   }
   horizontal_pod_autoscaling {
     disabled = true
   }
 }

のように、GKE上でさらにload balancerを作らないようにします。
ここでtrueにしてしまうと、ロードバランサーの下にさらにロードバランサーがぶら下がることになってしまうので。
terraformで作成した時、自動的にnodeとしてインスタンスがいくつか立ち上がります。

3. backend serviceにnodesを登録

ここが一番困ったところ。
なぜなら、google_container_cluster.cluster-sample.instance_group_urlsというreferenceが返してくれるのは
instance groupではなくinstance group managerだからです。
これはgoogleのapiの仕様の問題らしくてhashicorpの対応の外
apiのリファレンスを読んでいたところ、instanceGroupManager を instanceGroupに書き換えれば
instance groupのuriになることがわかったので、terraformのreplaceで”Manager”を削除しました。

resource "google_compute_backend_service" "backend-sample" {
 name        = "backend-sample"
 port_name   = "http"
 protocol    = "HTTP"
 timeout_sec = 5

 backend {
   group = "${replace(element(google_container_cluster.cluster-sample.instance_group_urls, 1), "Manager","")}"
 }
 health_checks = ["${google_compute_http_health_check.hc-sample.self_link}"]
}

4. ロードバランサーのヘルスチェックが通るように、firewallとhealthcheckを作成する

ここも面倒だったところ。
もちろんセキュリティ的にできるだけnodeにアクセスできるportは弾きたいところですが、
ロードバランサーのhttpだけを許可したいときにどうやるのか?
先ほど書いたように、nodeが自動的に作成される前にinstance templateが自動で作成されます。
nodeは一意のrandomなtagを付与されているので、このtagに対してfirewallルールを作成します。
タイミング的にclusterを作った後にしかrandomなtagがわからないので、terraformのnull resourceを使います。
terraformの意味が。。。

resource "null_resource" "cluster-attach-fw" {
 triggers {
   instance-group-url = "${google_container_cluster.cluster-sample.instance_group_urls.0}"
 }

 provisioner "local-exec" {
   command = <<EOT
     CLUSTER_TEMPLATE=$(echo $(gcloud compute instance-templates list | grep cluster-sample | cut -d ' ' -f1))
     TARGET_TAG=$(echo $(gcloud compute instance-templates describe $CLUSTER_TEMPLATE --format json | jq -r .properties.tags.items[0]))
     gcloud compute firewall-rules create fw-allow-http-lb-$TARGET_TAG \
       --source-ranges 130.211.0.0/22 \
       --target-tags $TARGET_TAG --allow tcp:${var.gke-http-port} --network ${var.network-sample}
EOT
 }
}

130.211.0.0/22については参照
${var.gke-http-port}については、load balancer用に好きなportを指定します。(80でも動作は問題ないですが)
このportは、nodes上で動かしているkubernetes内のServiceで用いるNodeportと一致する必要があります。
そのportを使って通信するので、backend serviceのnamed portも同じように設定します。

resource "null_resource" "set-cluster-nodeports" {
 triggers {
   be-svc-fingerprint = "${google_compute_backend_service.backend-sample.fingerprint}"
 }

 provisioner "local-exec" {
   command = <<EOT
     INSTANCE_GROUP=$(echo ${google_container_cluster.cluster-revprox.instance_group_urls.0} | sed -e 's/.*\(gke[a-z0-9\-]*\)$/\1/')
     gcloud compute backend-services update backend-sample --port=${var.gke-http-port}
     gcloud compute instance-groups managed set-named-ports $INSTANCE_GROUP \
       --named-ports=http:${var.gke-http-port} --zone=${var.zone}
EOT
 }
}

最後にヘルスチェックですが、指定したNodeport向けに作成します。

resource "google_compute_http_health_check" "hc-sample" {
  name         = "test"
  request_path = "/health_check"

  port = ${var.gke-http-port}
  timeout_sec        = 1
  check_interval_sec = 1
}

だいぶ面倒でしたが、これで目的は果たされました。

 - インフラ