🌊

GKE 上のアプリケーションと他サービス上のアプリケーションを同じロードバランサで公開する

2023/05/17に公開

こんにちは、SRE ディビジョンの松島です。
この記事ではスタンドアロン NEG という機能を用いて、GKE 上のアプリケーションを GCE / Cloud Run など他のサービス上で動くアプリケーションと同一のロードバランサで公開する方法を紹介を紹介します。

スタンドアロン NEG について

NEG とは

NEG(Network Endpoint Group)は、主にコンテナでデプロイされたアプリケーションのエンドポイントを束ねる役割を持つもので、LB のバックエンドサービスとして利用されます。
複数種類がありますが、GKE でのサービス公開で利用されるのは主にゾーン NEG となります。

Ingress を利用する場合とスタンドアロン NEG の比較

Ingress で Service を公開する場合、特定のアノテーションを付与することで、Google Cloud 側の LB でサービス公開するための NEG (ゾーン NEG)が自動作成され、自動的に GCLB <-> ゾーン NEG <-> Pod、の通信経路が確立されます。

一方で、特定のアノテーションを Service に付与することで、Ingress が紐づかないゾーン NEG だけを作成することもできます。
これをスタンドアロン(ゾーン)NEG と呼び、これを利用することで kubernetes 上のアプリケーションを Ingress / Gateway でない Google Cloud 側で作成した LB のバックエンドサービスに紐づけて公開することができます。

(公式ドキュメント掲載の図)
責任分解

これを利用することで、GKE 上で稼働するアプリケーションと GCE や Cloud Run、GCS バケットなど、他の Google Cloud のサービスで稼働するアプリケーションを一つの LB で公開することが可能となります。
同一ドメインで複数種類の Google Cloud サービスで稼働するアプリケーションを公開したい場合などに有用な機能ではないかと思います。

ハンズオン

下図のような GCE と GKE それぞれの上で動くサービスを一つの LB で 公開する構成を作成してみます。

architecture.png

0. 下準備

下記を準備します。本題から外れるため、詳細は割愛します。

  • VPC の作成
  • GKE クラスタの作成
  • インスタンステンプレート及び MIG の作成
    • Apache2 をインストール、/apache/へのアクセスで応答があるように設定しておく

1. kubernetes service 及び Deploymentの作成

まずは kubernetes 上のリソースを作成していきます。
下記のマニフェストを apply します。
service.yaml でスタンドアロン NEG の構成を指定している点がポイントです。

k8s マニフェスト
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      run: nginx
  template: 
    metadata:
      labels:
        run: nginx
    spec: 
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"80":{"name": "nginx-neg"}}}' # ここでスタンドアロン NEG 作成を指定
spec:
  type: ClusterIP
  selector:
    run: nginx
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80

apply が完了すると、下記のようにゾーン NEG が自動作成されます。
neg.png

東京リージョンは a - c までありますが、今回 a と c 分しか作成されていないのは、GKE のノードがゾーン a と c のみに存在する状態で作成したためです。

2. LB でサービス公開

続いて下準備で作成した MIG と、先ほど作成したスタンドアロン NEG からバックエンドサービスを作成し、LB で公開していきます。

下記の Terraform を apply します。
ポイントは、前の手順で作成した NEG を data で取得してバックエンドサービスを作成している点です。
(NEG はゾーンごとに作成されるため、data ブロックで複数取得している)

Terraform コード
locals {
  project_id = <プロジェクト ID>
}

resource "google_compute_global_forwarding_rule" "main" {
  project               = local.project_id
  name                  = "test"
  ip_protocol           = "TCP"
  port_range            = "80"
  load_balancing_scheme = "EXTERNAL"
  target                = google_compute_target_http_proxy.main.self_link
}

resource "google_compute_target_http_proxy" "main" {
  project = local.project_id
  name    = "main"
  url_map = google_compute_url_map.main.id
}

resource "google_compute_url_map" "main" {
  name            = "main"
  project         = local.project_id
  default_service = google_compute_backend_service.nginx.id

  host_rule {
    hosts        = ["*"]
    path_matcher = "main"
  }

  path_matcher {
    name = "main"
    default_service = google_compute_backend_service.nginx.id

    path_rule {
      paths   = ["/apache/*"]
      service = google_compute_backend_service.apache.id
    }
  }
}

data "google_compute_instance_group" "apache" {
  name    = "apache"
  zone = "asia-northeast1-b"
  project = local.project_id
}

resource "google_compute_backend_service" "apache" {
  project               = local.project_id
  name                  = "apache"
  protocol              = "HTTP"
  timeout_sec           = 60
  port_name             = "http"
  load_balancing_scheme = "EXTERNAL"
  health_checks         = [google_compute_health_check.main.id]
  backend {
    group = data.google_compute_instance_group.apache.id
    balancing_mode  = "UTILIZATION"
    capacity_scaler = 1.0
  }
}

# 先ほど作成したゾーン NEG を data で取得
data "google_compute_network_endpoint_group" "nginx_tokyo_a" {
  name    = "nginx-neg"
  zone    = "asia-northeast1-a"
  project = local.project_id
}

# 先ほど作成したゾーン NEG を data で取得
data "google_compute_network_endpoint_group" "nginx_tokyo_b" {
  name    = "nginx-neg"
  zone    = "asia-northeast1-c"
  project = local.project_id
}

resource "google_compute_backend_service" "nginx" {
  project               = local.project_id
  name                  = "nginx"
  health_checks         = [google_compute_health_check.main.id]
  backend {
    group           = data.google_compute_network_endpoint_group.nginx_tokyo_a.id
    balancing_mode  = "RATE"
    max_rate = 300
  }
  backend {
    group           = data.google_compute_network_endpoint_group.nginx_tokyo_b.id
    balancing_mode  = "RATE"
    max_rate = 300
  }
}

resource "google_compute_health_check" "main" {
  project             = local.project_id
  name                = "test"
  check_interval_sec  = 5
  timeout_sec         = 5
  unhealthy_threshold = 3
  http_health_check {
    request_path       = "/"
    port_specification = "USE_FIXED_PORT"
    port               = 80
  }
}

3. 動作確認

作成した LB の/, /apache/にアクセスすると、それぞれ下のようなレスポンスを得ることができました。

test_nginx.png
test_apache.png

注意事項

実際に動かしてみて感じた注意点を記載します。

ロードバランシングモード

ゾーン NEG の場合は LB のロードバランシングモードに RATE または CONNECTION のみを指定可能となっています(UTILIZATION は指定不可)。
コンソールで HTTPS LB を作成する場合はそもそも RATE しか選べないようになっているため混乱はないかと思いますが、Terraform で作成する際は RATE などを明示的に指定する必要があるので、少し注意が必要です。

kubernetes 側リソース削除時の注意点

GKE 上の Service 及びクラスタが削除されてもスタンドアロン NEG は残るため、手動で削除する必要があります。
検証などで作成した際はご注意ください。

ゾーン NEG が後から増えるケース

スタンドアロン NEG がすでに存在している状態で後から GKE ノードが配置されたゾーンが増えた場合、ゾーン NEG も自動で追加作成されます。
この場合は増えた分の NEG を LB のバックエンドサービスに追加しないと、追加されたノード上の Pod にトラフィックがルーティングされませんので、ご注意ください。
特に Autopilot の場合はノードがない状態から始まるため、Pod 1つでデプロイしてオートスケールなどさせた場合にこの状況が発生しそうです。

asia-northeast1-b ゾーンへの GKE ノード追加前
neg.png

asia-northeast1-b ゾーンへの GKE ノード追加後
neg2.png

参考公式ドキュメント

スタンドアロン NEG によるコンテナ ネイティブのロード バランシング
ゾーン ネットワーク エンドポイント グループの概要

おわりに

同一ドメインで GKE 上のサービスや GCE、GCS バケットなどを公開したい場合は、ぜひスタンドアロン NEG を利用してみてください。

Discussion