GKE 上のアプリケーションと他サービス上のアプリケーションを同じロードバランサで公開する
こんにちは、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 で 公開する構成を作成してみます。
0. 下準備
下記を準備します。本題から外れるため、詳細は割愛します。
- VPC の作成
- GKE クラスタの作成
- インスタンステンプレート及び MIG の作成
- Apache2 をインストール、
/apache/
へのアクセスで応答があるように設定しておく
- Apache2 をインストール、
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 が自動作成されます。
東京リージョンは 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/
にアクセスすると、それぞれ下のようなレスポンスを得ることができました。
注意事項
実際に動かしてみて感じた注意点を記載します。
ロードバランシングモード
ゾーン 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 ノード追加前
asia-northeast1-b ゾーンへの GKE ノード追加後
参考公式ドキュメント
スタンドアロン NEG によるコンテナ ネイティブのロード バランシング
ゾーン ネットワーク エンドポイント グループの概要
おわりに
同一ドメインで GKE 上のサービスや GCE、GCS バケットなどを公開したい場合は、ぜひスタンドアロン NEG を利用してみてください。
Discussion