grasys blog
grasysブログ

マニュアルでLBを作成し、GKEとGCSをバックエンドとして指定した時のハマりポイント

どうも。しみちゃんでございます。
今回は、マニュアルで作成したLBのバックエンドにGKEやGCSを指定しよう、というパターンの紹介です。
Webコンソールや Ingress などを使用してLBを立てていると、普段なかなか意識しないリソースの設定などでハマったりするので、そういったポイントをお話したいと思います。

はじめに

GKE, k8s, またそれにまつわるリソースの説明については割愛します。
他の記事で、GKEについて紹介しているものもあるので、よかったらそちら(記事のハッシュタグ#gkeをクリック)を参照ください。

前提

今回の記事は下記の部分まで進んだ前提でお話を進めます。 また、構築には terraform を使用します。

  • 公開用GCSバケットを作成していること
  • GKEクラスタを作成していること(今回はリージョナルで立てています)
  • Terraform v0.11.14 + provider.google: version = “~> 3.7”

GKEでサービスを作成する

マニフェスト

クラスタ上に、合計2つのサービス(今回はshimizuapp, grasysappという名前)をデプロイします。
コンテナの中身は nginx をほぼそのままのせているだけです。
マニフェストは片方だけしか載せていませんが、nameやportなど一意にすべきものはのぞいて、同じ設定でデプロイしています。

1.Deployment (spec.templateを抜粋)

...
  template:
    metadata:
      name: shimizuapp
      labels:
        app: shimizuapp
    spec:
      containers:
      - name: nginx
        image: nginx:stable-alpine
        env:
        - name: NGINX_HOST
          value: "hogehoge.com"
        - name: NGINX_PORT
          value: "8080"
        ports:
        - containerPort: 8080
          protocol: TCP
        readinessProbe:
          httpGet:
            scheme: HTTP
            path: /check
            port: 8080
        volumeMounts:
        - name: conf-volume
          mountPath: /etc/nginx/conf.d
      ...

2.ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx
data:
  virtualHost.conf: |
    log_format ltsv   'time:$time_iso8601\t'
                      'remote_addr:$remote_addr\t'
                      'request_method:$request_method\t'
                      'request_uri:$request_uri\t'
                      'https:$http_x_forwarded_proto\t'
                      'uri:$uri\t'
                      'status:$status\t'
                      'referer:$http_referer\t'
                      'forwardedfor:$http_x_forwarded_for\t';
   
    server {
        listen       8080;
        server_name  _;
        access_log  /var/log/nginx/access.log ltsv;
   
        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
        location /check {
            return 200 ;
        }
    }
---

3.Service

apiVersion: v1
kind: Service
metadata:
  name: shimizuapp
  namespace: default
  labels:
    app: shimizuapp
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 8080
    protocol: TCP
    nodePort: 30080
  selector:
    app: shimizuapp

メモ

初めの注意点として、 Service では spec.ports.nodePort で port を指定してあげてください。あとで、 名前付きポート の設定を付与する時に必要です。
クラスタ内で同じ port は作成できないので、異なる port で設定します。

できたものがこちら。

# kubectl  get svc -o wide
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE     SELECTOR
grasysapp    NodePort    10.7.240.186   <none>        8080:30081/TCP   6m50s   app=grasysapp
shimizuapp   NodePort    10.7.243.77    <none>        8080:30080/TCP   18m     app=shimizuapp
# kubectl get po
NAME                         READY   STATUS    RESTARTS   AGE
grasysapp-869cc776ff-f45r9   1/1     Running   0          8m51s
grasysapp-869cc776ff-xgfv4   1/1     Running   0          8m51s
shimizuapp-c9889d84c-qz6h9   1/1     Running   0          10m
shimizuapp-c9889d84c-tlg7k   1/1     Running   0          10m

LBを作る前に

ここが普段あまり触らないリソースがゆえに、ハマりやすいポイントです。
GKEによってできたインスタンスグループに、先ほど作成した Service の nodePort を 名前付きPort として設定してあげます。

ここで、設定をしっかり行っておかないと ヘルスチェックは通っているのに、status:502 errorが返ってくる といった現象が発生したりします。

これは Internet -> LB -> Service(k8s) と接続する時に、どの(Serviceが待ち受けている) port を使用するかを認識させるために必要です。

まずは、GKEによって作成されたインスタンスグループを gcloud compute instance-groups managed list で確認します。

# gcloud compute  instance-groups managed list
gke-shimizu-webapp-dev-app-8c257106-grp         asia-northeast1-b  zone   gke-shimizu-webapp-dev-app-8c257106         1     1            gke-shimizu-webapp-dev-app-8c257106         no
gke-shimizu-webapp-dev-app-9521b1ca-grp         asia-northeast1-c  zone   gke-shimizu-webapp-dev-app-9521b1ca         1     1            gke-shimizu-webapp-dev-app-9521b1ca         no
gke-shimizu-webapp-dev-app-d177c78a-grp         asia-northeast1-a  zone   gke-shimizu-webapp-dev-app-d177c78a         1     1            gke-shimizu-webapp-dev-app-d177c78a         no

gcloud compute instance-groups managed set-named-ports を使用して、 named-port を設定します。
設定は gcloud compute instance-groups managed get-named-ports で確認できます。

#!/bin/bash
named_port='shimizuapp30080:30080,grasysapp30081:30081'

gcloud compute  instance-groups managed list | grep shimizu \
  | while read list
    do
      group=""
      zone=""

      group=$(echo $list | awk '{print $1}' || echo "null")
      zone=$(echo $list | awk '{print $2}' || echo "null")
      echo "group: $(echo $list | awk '{print $1}') zone: $(echo $list | awk '{print $2}')"

      gcloud compute instance-groups managed set-named-ports $group --named-ports=$named_port --zone=$zone
      gcloud compute instance-groups managed get-named-ports $group --zone=$zone
    done

exit
# ./upate_named-port.sh
group: gke-shimizu-webapp-dev-app-8c257106-grp zone: asia-northeast1-b
Updated [https://www.googleapis.com/compute/v1/projects/grasys-dev/zones/asia-northeast1-b/instanceGroups/gke-shimizu-webapp-dev-app-8c257106-grp].
NAME             PORT
shimizuapp30080  30080
grasysapp30081   30081
group: gke-shimizu-webapp-dev-app-9521b1ca-grp zone: asia-northeast1-c
Updated [https://www.googleapis.com/compute/v1/projects/grasys-dev/zones/asia-northeast1-c/instanceGroups/gke-shimizu-webapp-dev-app-9521b1ca-grp].
NAME             PORT
shimizuapp30080  30080
grasysapp30081   30081
group: gke-shimizu-webapp-dev-app-d177c78a-grp zone: asia-northeast1-a
Updated [https://www.googleapis.com/compute/v1/projects/grasys-dev/zones/asia-northeast1-a/instanceGroups/gke-shimizu-webapp-dev-app-d177c78a-grp].
NAME             PORT
shimizuapp30080  30080
grasysapp30081   30081

それでは、LB を作成していく!

まずは、 外部IPとヘルスチェックをを定義します
以下、 2つのサービス(shimizuapp, grasysapp) のうち、片方の tf ファイルしか記載していませんが、 もう1つのリソースを作成するには名前や変数をそれぞれ置き換えてください。

また、ヘルスチェックの port やパスを間違えていると、 LB から正常とみなされなくなるので、注意が必要です。

ここで、最後まで作成しても、ヘルスチェックが通らない、といった場合は

  • パスはあっているか
  • コンテナ port がちゃんと空いているか
  • nginx.conf などで、LBからのアクセスを拒否していないか

などを疑ってみてください。

####################
# External IP
resource "google_compute_global_address" "gke-lb-dev-shimizuapp" {
  name         = "gke-lb-dev-shimizuapp"
}

####################
# Health Check
resource "google_compute_health_check" "gke-lb-dev-shimizuapp" {
  name                = "gke-healthcheck-https-lb-dev-shimizuapp"
  description         = "health check for shimizuapp"

  http_health_check {
    port                = 30080
    request_path        = "/check"
  }
}

バックエンドサービスを設定します。

名前付きポートは先ほど設定した named-port の名前を、
backend.group についてはそれぞれのインスタンスグループのリンクを、
細かなパラメータについては ingress で LB を立てた時に import した設定をそのまま反映しま す。

GKEでオートスケーリングをする予定であれば、特に分散周りのパラメータについては注意が必要です。
どこか特定の場所にトラフィックが偏り続けると、不必要にスケールアウトしたり、ピークタイムをすぎてもスケール前の状態に戻りずらくなったりします。

負荷試験の実施前であれば、 Ingress で自動で設定されたものを import してそのまま使用するのが無難かと思います。

#####################
## Backend Service
resource "google_compute_backend_service" "gke-lb-dev-shimizuapp-backend-service" {
  name        = "gke-lb-dev-shimizuapp-backend-service"
  # 先ほど設定した named-port の名前を指定する
  port_name   = "shimizuapp30080"
  protocol    = "HTTP"
  timeout_sec = 30             # default
  enable_cdn  = false

  backend {
  
    # asia-northeast1-a
    description     = "GKE Backend for shimizuapp aisa-northeast1-a"
    # gcloud compute  instance-groups managed list --format=json でインスタンスグループを確認する
    group           = "https://www.googleapis.com/compute/v1/projects/grasys-dev/zones/asia-northeast1-a/instanceGroups/gke-shimizu-webapp-dev-app-d177c78a-grp"
    
    balancing_mode  = "RATE"  # UTILIZATION(default) or RATE # imported parameter gke backed generated by ingress
    capacity_scaler = 1            # # imported parameter gke backed generated by ingress
    max_rate                     = 1 # imported parameter gke backed generated by ingress
    max_connections              = 0 # imported parameter gke backed generated by ingress
    max_connections_per_endpoint = 0 # imported parameter gke backed generated by ingress
    max_connections_per_instance = 0 # imported parameter gke backed generated by ingress
    max_rate_per_endpoint        = 0 # imported parameter gke backed generated by ingress
    max_rate_per_instance        = 0 # imported parameter gke backed generated by ingress
    max_utilization              = 0 # imported parameter gke backed generated by ingress
  }
  backend {
    description     = "GKE Backend for shimizuapp aisa-northeast1-b"
    group           = "https://www.googleapis.com/compute/v1/projects/grasys-dev/zones/asia-northeast1-b/instanceGroups/gke-shimizu-webapp-dev-app-8c257106-grp"
    balancing_mode  = "RATE"  # UTILIZATION(default) or RATE
    capacity_scaler = 1            # # imported parameter gke backed generated by ingress
    max_rate                     = 1 # imported parameter gke backed generated by ingress
    max_connections              = 0 # imported parameter gke backed generated by ingress
    max_connections_per_endpoint = 0 # imported parameter gke backed generated by ingress
    max_connections_per_instance = 0 # imported parameter gke backed generated by ingress
    max_rate_per_endpoint        = 0 # imported parameter gke backed generated by ingress
    max_rate_per_instance        = 0 # imported parameter gke backed generated by ingress
    max_utilization              = 0 # default 0.8 # imported parameter gke backed generated by ingress
  }
  backend {
    description     = "GKE Backend for shimizuapp aisa-northeast1-c"
    group           = "https://www.googleapis.com/compute/v1/projects/grasys-dev/zones/asia-northeast1-c/instanceGroups/gke-shimizu-webapp-dev-app-9521b1ca-grp"
    balancing_mode  = "RATE"  # UTILIZATION(default) or RATE # imported parameter gke backed generated by ingress
    capacity_scaler = 1            # # imported parameter gke backed generated by ingress
    max_rate                     = 1 # imported parameter gke backed generated by ingress
    max_connections              = 0 # imported parameter gke backed generated by ingress
    max_connections_per_endpoint = 0 # imported parameter gke backed generated by ingress
    max_connections_per_instance = 0 # imported parameter gke backed generated by ingress
    max_rate_per_endpoint        = 0 # imported parameter gke backed generated by ingress
    max_rate_per_instance        = 0 # imported parameter gke backed generated by ingress
    max_utilization              = 0 # default 0.8 # imported parameter gke backed generated by ingress
  }

  health_checks = [ "${google_compute_health_check.gke-lb-dev-shimizuapp.self_link}" ]
}

GCS のバックエンド設定もしてあげます。

####################
# Backend Bucket
resource "google_compute_backend_bucket" "gke-lb-static-images" {
  name        = "gke-lb-dev-static-images"
  description = "GCS Backdend for GKE"
  #bucket_name = "${google_storage_bucket.static.name}"
  bucket_name = "shimizu-gcs-public"
  enable_cdn  = false
}

最後に、 target http proxy と forwarding rule を設定します。

####################
# HTTP Proxy
resource "google_compute_target_http_proxy" "gke-http-lb-dev-shimizuapp" {
  name        = "gke-lb-dev-shimizu-target-proxy-shimizuapp"
  url_map     = "${google_compute_url_map.gke-lb-dev-shimizuapp.self_link}"
}

####################
# Forwarding Rules
resource "google_compute_global_forwarding_rule" "gke-http-lb-dev-shimizuapp" {
    name        = "gke-http-lb-dev-shimizuapp"
    target      = "${google_compute_target_http_proxy.gke-http-lb-dev-shimizuapp.self_link}"
    ip_address  = "${google_compute_global_address.gke-lb-dev-shimizuapp.address}"
    ip_protocol = "TCP"
    port_range  = "80"
}

作成したものがこちら。
うまく設定できていれば、こんな感じでそれぞれのバックエンドが紐づいているはずです。

終わりに

どうだったでしょうか。うまく設定できたでしょうか。

今回のパターンはどうしても、 GKE と GKE以外のリソースを使いながらLBを運用したいといった場合など、使うケースはかなり限られています。
LB 周りは一通り動かすのに、ただでさえ操作するリソースが多いのに、GKEを絡めていくと更に設定が必要になるので、ちょっと面倒だなと感じてしまいますね。
( GCS なら、バケットをマウントする GCSFuse などがありますし。。。)

とは言え、マニュアルで細かく設定できるようになれば、
GCEなどでも併用して使えるようになるため、選択肢としては一応もっていて損はないと思います。

今回はここまで。ではでは。


株式会社grasys(グラシス)は、技術が好きで一緒に夢中になれる仲間を募集しています。

grasysは、大規模・高負荷・高集積・高密度なシステムを多く扱っているITインフラの会社です。Google Cloud、Amazon Web Services (AWS)、Microsoft Azureの最先端技術を活用してクラウドインフラやデータ分析基盤など、ITシステムの重要な基盤を設計・構築し、改善を続けながら運用しています。

お客様の課題解決をしながら技術を広げたい方、攻めのインフラ技術を習得したい方、とことん技術を追求したい方にとって素晴らしい環境が、grasysにはあります。
お気軽にご連絡ください。