内部 TCP/UDP 負荷分散

このページでは、Google Kubernetes Engine で Compute Engine 内部 TCP / UDP ロードバランサを作成する方法について説明します。

概要

内部 TCP / UDP 負荷分散を使用すると、同じ VPC ネットワークを使用し、同じ Google Cloud リージョンに配置されているクラスタ外のアプリケーションに、クラスタのサービスがアクセスできるようになります。たとえば、us-west1 リージョンにクラスタを配置している場合、そのクラスタのサービスの 1 つが、同じ VPC ネットワークを使用し、このリージョンで実行されている Compute Engine VM インスタンスにアクセスできるようになります。

内部 TCP / UDP ロードバランサを作成するには、cloud.google.com/load-balancer-type: "Internal" アノテーションを含む Service リソースと type: LoadBalancer 仕様を作成します。以下では、その例と作成方法について説明します。

内部 TCP / UDP ロードバランサを使用しない場合は、外部ロードバランサとファイアウォール ルールを設定して、クラスタの外部からアプリケーションにアクセスできるようにする必要があります。

内部 TCP / UDP ロードバランサでは、Service の内部 IP アドレスが作成されます。Service はこの IP アドレスで、同じ VPC ネットワークの同じコンピューティング リージョン内に配置されているクライアントからのトラフィックを受信します。グローバル アクセスを有効にすると、同じ VPC ネットワークの任意のリージョン内に配置されているクライアントが Service にアクセスできます。

料金

Compute Engine の料金モデルに従って課金されます。詳細については、ロードバランサと転送ルールの料金をご覧ください。また、Google Cloud 料金計算ツールに関する Compute Engine のページもご覧ください。

始める前に

作業を始める前に、次のことを確認してください。

次のいずれかの方法で gcloud のデフォルトの設定を指定します。

  • gcloud init。デフォルトの設定全般を確認する場合に使用します。
  • gcloud config。プロジェクト ID、ゾーン、リージョンを個別に設定する場合に使用します。

gcloud init の使用

  1. gcloud init を実行して、次の操作を行います。

    gcloud init

    リモート サーバーで SSH を使用している場合は、--console-only フラグを指定して、コマンドがブラウザを起動しないようにします。

    gcloud init --console-only
  2. 手順に従って gcloud を承認し、Google Cloud アカウントを使用します。
  3. 新しい構成を作成するか、既存の構成を選択します。
  4. Google Cloud プロジェクトを選択します。
  5. デフォルトの Compute Engine ゾーンを選択します。

gcloud config の使用

  • デフォルトのプロジェクト ID を設定します。
    gcloud config set project project-id
  • ゾーンクラスタを使用する場合は、デフォルトのコンピューティング ゾーンを設定します。
    gcloud config set compute/zone compute-zone
  • リージョン クラスタを使用する場合は、デフォルトのコンピューティング リージョンを設定します。
    gcloud config set compute/region compute-region
  • gcloud を最新バージョンに更新します。
    gcloud components update

Deployment の作成

以下のマニフェストは、Hello World アプリの 3 つのレプリカを実行する Deployment を記述しています。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-app
spec:
  selector:
    matchLabels:
      app: hello
  replicas: 3
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: "gcr.io/google-samples/hello-app:2.0"

このサンプルアプリのソースコードと Dockerfile は GitHub で入手できます。PORT 環境変数が指定されていないため、コンテナはデフォルト ポート 8080 でリッスンします。

Deployment を作成するには、マニフェストから my-deployment.yaml ファイルを作成し、シェルまたはターミナル ウィンドウで次のコマンドを実行します。

kubectl apply -f my-deployment.yaml

内部 TCP ロードバランサを作成する

以降のセクションでは、Service を使用して内部 TCP ロードバランサを作成する方法について説明します。

Service 構成ファイルの作成

内部 TCP ロードバランサを作成する Service の例を次に示します。

apiVersion: v1
kind: Service
metadata:
  name: ilb-service
  annotations:
    cloud.google.com/load-balancer-type: "Internal"
  labels:
    app: hello
spec:
  type: LoadBalancer
  selector:
    app: hello
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

Service の最小要件

マニフェストには次のものを含める必要があります。

  • Service の name。この場合 ilb-service です。
  • アノテーション: cloud.google.com/load-balancer-type: "Internal"。内部 TCP / UDP ロードバランサが構成されることを指定します。
  • type: LoadBalancer
  • spec: selector フィールド。Service が対象にするポッドを指定します。例: app: hello
  • port には、Service が公開されるポートを指定します。targetPort には、コンテナがリッスンするポートを指定します。

Service のデプロイ

内部 TCP ロードバランサを作成するには、マニフェストから my-service.yaml ファイルを作成し、シェルまたはターミナル ウィンドウで次のコマンドを実行します。

kubectl apply -f my-service.yaml

Service の検査

デプロイ後、Service を検査し、正常に構成されていることを確認します。

Service に関する詳細情報を取得します。

kubectl get service ilb-service --output yaml

出力の status.loadBalancer.ingress の下に、内部ロードバランサの IP アドレスが表示されます。これは、clusterIP の値と異なります。この例の場合、ロードバランサの IP アドレスは 10.128.15.193 です。

apiVersion: v1
kind: Service
metadata:
  ...
  labels:
    app: hello
  name: ilb-service
  ...
spec:
  clusterIP: 10.0.9.121
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 30835
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 10.128.15.193

app: hello というラベルを持つポッドは、この Service のメンバーです。これらは、内部ロードバランサに送信されたリクエストを最後に受信するポッドになります。

クライアントは、Service マニフェストの port フィールドに指定された loadBalancer IP アドレスと TCP ポートを使用して Service を呼び出します。リクエストは、targetPort フィールドに指定された TCP ポートのいずれかのメンバーポッドに転送されます。前の例では、クライアントは 10.128.15.193 の TCP ポート 80 で Service を呼び出します。リクエストは、いずれかのメンバーポッドの TCP ポート 8080 に転送されます。メンバーポッドには、ポート 8080 をリッスンするコンテナが必要です。

nodePort 値 30835 は無関係です。内部ロードバランサには関係ありません。

ロードバランサの転送ルールの表示

内部ロードバランサは転送ルールとして実装されています。転送ルールにはバックエンド Service が設定され、この Service にはインスタンス グループが設定されています。

内部ロードバランサのアドレス(前の例では 10.128.15.193)は転送ルールのアドレスと同じです。内部ロードバランサを実装する転送ルールを確認するには、プロジェクト内のすべての転送ルールのリストを取得します。

gcloud compute forwarding-rules list --filter="loadBalancingScheme=INTERNAL"

出力で、内部ロードバランサと同じアドレスの転送ルールを探します(この例では 10.128.15.193)。

NAME                          ... IP_ADDRESS  ... TARGET
...
aae3e263abe0911e9b32a42010a80008  10.128.15.193   us-central1/backendServices/aae3e263abe0911e9b32a42010a80008

出力には、関連するバックエンド サービスも表示されます(この例では ae3e263abe0911e9b32a42010a80008)。

バックエンド サービスの説明:

gcloud compute backend-services describe aae3e263abe0911e9b32a42010a80008 --region us-central1

出力には、関連するインスタンス グループが表示されます(この例では k8s-ig--2328fa39f4dc1b75)。

backends:
- balancingMode: CONNECTION
  group: .../us-central1-a/instanceGroups/k8s-ig--2328fa39f4dc1b75
...
kind: compute#backendService
loadBalancingScheme: INTERNAL
name: aae3e263abe0911e9b32a42010a80008
...

Service の抽象化の仕組み

パケットが転送ルールで処理されると、パケットはいずれかのクラスタノードに転送されます。パケットがクラスタノードに到着すると、アドレスとポートは次のようになります。

宛先 IP アドレス 転送ルール。この例では 10.128.15.193 です。
宛先 TCP ポート Service の port フィールド。この例では 80 です。

転送ルール(つまり内部ロードバランサ)は、宛先 IP アドレスまたは宛先ポートを変更しません。代わりに、クラスタノードの iptables ルールにより、パケットが適切なポッドにルーティングされます。iptables ルールは、宛先 IP アドレスをポッドの IP アドレスに変更し、宛先ポートを Service の targetPort 値(この例では 8080)に変更します。

内部 TCP ロードバランサの確認

VM インスタンスに SSH で接続し、次のコマンドを実行します。

curl load-balancer-ip

load-balancer-ip は自分の LoadBalancer Ingress IP アドレスです。

レスポンスには hello-app の出力が含まれます。

Hello, world!
Version: 2.0.0
Hostname: hello-app-77b45987f7-pw54n

同じ VPC ネットワークの外部または同じリージョンの外部からコマンドを実行すると、タイムアウト エラーが発生します。グローバル アクセスを構成すると、同じ VPC ネットワーク内の任意のリージョン内に配置されているクライアントがロードバランサにアクセスできます。

クリーンアップ

Deployment と Service を削除するには、kubectl delete または Cloud Console を使用します。

kubectl

Deployment を削除する

Deployment を削除するには、次のコマンドを実行します。

kubectl delete deployment hello-app

Service を削除する

Service を削除するには、次のコマンドを実行します。

kubectl delete service ilb-service

Console

Deployment を削除する

Deployment を削除するには、次の手順を行います。

  1. Cloud Console で Google Kubernetes Engine の [ワークロード] メニューに移動します。

    [ワークロード] メニューに移動

  2. メニューから、目的のワークロードを選択します。

  3. [削除] をクリックします。

  4. 確認ダイアログのメニューで、[削除] をクリックします。

Service を削除する

Service を削除するには、次の手順を行います。

  1. Cloud Console で Google Kubernetes Engine の [サービス] メニューに移動します。

    [サービス] メニューに移動

  2. メニューから目的の Service を選択します。

  3. [削除] をクリックします。

  4. 確認ダイアログのメニューで、[削除] をクリックします。

既存の Ingress に関する考慮事項

内部 TCP / UDP ロードバランサと分散モード UTILIZATION を使用する Ingress の両方を使用することはできません。Ingress と内部 TCP / UDP 負荷分散の両方を使用するには、Ingress で分散モード RATE を使用する必要があります。

クラスタに存在する Ingress リソースが Kubernetes バージョン 1.7.1 以前で作成されている場合、内部 TCP / UDP ロードバランサと互換性はありません。Kubernetes Ingress Resources オブジェクトによって作成された、以前の BackendService リソースは、分散モードを指定しないで作成されています。この API では、HTTP ロードバランサについてはデフォルトでバランシング モード UTILIZATION が使用されていました。しかし、内部 TCP / UDP ロードバランサでは、UTILIZATION を使って他のロードバランサを使用するインスタンス グループを指すことができません。

Ingress 分散モードの確認

Ingress 分散モードを確認するには、シェルまたはターミナル ウィンドウから次のコマンドを実行します。

GROUPNAME=`kubectl get configmaps ingress-uid -o jsonpath='k8s-ig--{.data.uid}' --namespace=kube-system`
gcloud compute backend-services list --format="table(name,backends[].balancingMode,backends[].group)" | grep $GROUPNAME

これらのコマンドによって、シェル変数 GROUPNAME がエクスポートされます。これによってクラスタのインスタンス グループ名が取得されます。次に、プロジェクトの Compute Engine backend service リソースがポーリングされ、$GROUPNAME の内容に従って結果が絞り込まれます。

出力は次のようになります。

k8s-be-31210--...  [u'RATE']       us-central1-b/instanceGroups/k8s-ig--...
k8s-be-32125--...  [u'RATE']       us-central1-b/instanceGroups/k8s-ig--...

出力で RATE エントリが返された場合またはエントリが返されなかった場合、内部ロードバランサは互換性があります。追加作業は必要ありません。

出力で UTILIZATION とマークされたエントリが返された場合、Ingress は互換性がありません。

Ingress リソースを更新して内部 TCP / UDP ロードバランサとの互換性を維持するには、Kubernetes バージョン 1.7.2 以降で新しいクラスタを作成し、サービスをそのクラスタに移行します。

Service のパラメータ

GKE 内部 LoadBalancer Service では、次のパラメータがサポートされています。

機能 概要 サービス フィールド GKE バージョンのサポート
ローカル外部トラフィック ポリシー 外部トラフィックを GKE ノード間でロードバランサするかどうかを構成します。 spec:externalTrafficPolicy:Local GKE 1.14 以降
ロードバランサのソース範囲 特定のソース範囲のみを許可するように、GKE と VPC でオプションのファイアウォール ルールを構成します。 spec:loadBalancerSourceRanges サポートされているすべてのバージョン
ロードバランサの IP ロードバランサの IP を指定します。 spec:loadBalancerIP サポートされているすべてのバージョン
ロードバランサのサブネット ロードバランサが IP を自動的にプロビジョニングするサブネットを指定します。 metadata:annotations: networking.gke.io/internal-load-balancer-subnet GKE 1.17 以降と 1.16.8-gke.10 以降のベータ版
グローバル アクセス GCP リージョン間でクライアントが TCP / UDP ロードバランサの VIP にアクセスできるようにします metadata:annotations: networking.gke.io/internal-load-balancer-allow-global-access GKE 1.16 以降のベータ版
All-ports TCP / UDP ロードバランサが特定のポートではなくすべてのポートを転送する機能です なし ネイティブ サポートなし

外部トラフィック ポリシー

externalTrafficPolicy は、GKE ノードに着信するトラフィックのロードバランサの方法と有無を定義する標準サービス オプションです。Cluster はデフォルトのポリシーですが、クラスタノードに着信するトラフィックのソース IP を保持するために Local がよく使用されます。Local は、クラスタノードのロードバランサを効果的に無効にして、ローカル Pod が受信するトラフィックに元の送信元アドレスが表示されるようにします。

externalTrafficPolicy は、内部の LoadBalancer Service(TCP / UDP ロードバランサ経由)でサポートされていますが、ロードバランサの動作はトラフィックの送信元と構成されたトラフィック ポリシーによって異なります。

クラスタ外から TCP / UDP ロードバランサに送信されるトラフィックの場合、クラスタ内に少なくとも 1 つの正常な Service の Pod がある場合、次のようになります。

  • Cluster ポリシー: トラフィックはクラスタ内の正常な GKE ノードにロードバランサされ、kube-proxy がそれを Pod を持つノードに送信します。
  • Local ポリシー: バックエンド Pod のないノードは、TCP / UDP ロードバランサに対して異常として表示されます。トラフィックは、Pod がある残りの正常なクラスタノードの 1 つにのみ送信されます。トラフィックは kube-proxy によって再度ルーティングされることはなく、その IP ヘッダー情報がそのままの状態でローカル Pod に直接送信されます。

特定の LoadBalancer Service IP へのトラフィックがクラスタ内の GKE ノードから送信された場合、トラフィックの動作は異なります。次の表は、LoadBalancer Service のメンバー Pod を宛先とするクラスタ内のノードまたは Pod をソースとするトラフィックの動作をまとめたものです。

externalTrafficPolicy トラフィックが発生した同一のノードで実行されているサービス メンバー Pod かどうか トラフィックの動作
クラスタ はい パケットは、同じノードまたは別のノードのメンバーポッドに配信されます。
クラスタ いいえ パケットは、別のノードにあるメンバーポッドに配信されます。
ローカル はい パケットは、同じノードのメンバーポッドに配信されます。
ローカル いいえ

Kubernetes 1.14 以前: パケットはドロップされます。

Kubernetes 1.15 以降: パケットは、別のノードのメンバー Pod に配信されます。

ロードバランサのソース範囲

spec: loadBalancerSourceRanges 配列は、1 つ以上の内部 IP アドレス範囲を指定します。loadBalancerSourceRanges は、ロードバランサ経由のトラフィックをこのフィールドに指定された IP に制限します。この構成では、kube-proxy が Kubernetes ノード内に対応する iptables ルールを作成します。GKE により、自動的に VPC ネットワーク内にファイアウォール ルールも作成されます。このフィールドを省略すると、Service は任意の IP アドレス(0.0.0.0/0)からのトラフィックを受け入れます。

loadBalancerSourceRanges を構成して内部 TCP / UDP ロードバランサへのアクセスを制限する方法については、クラウド プロバイダのファイアウォールの構成をご覧ください。Service の仕様の詳細については、Service API リファレンスをご覧ください。

ロードバランサの IP

spec: loadBalancerIP を使用すると、ロードバランサに特定の IP アドレスを選択できます。他の内部 TCP / UDP ロードバランサまたは Service で使用されていない IP アドレスを選択する必要があります。省略した場合、エフェメラル IP が割り当てられます。詳細については、静的内部 IP アドレスの予約をご覧ください。

ロードバランサのサブネット(ベータ版)

デフォルトでは、GKE はノードのサブネット範囲を使用して内部 TCP / UDP ロードバランサをデプロイします。サブネットは、networking.gke.io/internal-load-balancer-subnet アノテーションを使用して Service ごとに指定できます。これは、内部ロードバランサの IP をノード IP から個別にファイアウォールする場合や、複数の GKE クラスタ間で同じ Service のサブネットを共有する場合に便利です。このパラメータは、内部 TCP/UDP LoadBalancer Service にのみ関連します。

GKE はサブネット自体のライフサイクルを管理しないため、Servie のリソースによって参照される前にサブネットが存在する必要があります。サブネットは、GKE クラスタと同じ VPC とリージョンに存在する必要もあります。このステップでは、GKE からバンド外に作成されます。

gcloud compute networks subnets create gke-vip-subnet \
    --network=default \
    --range=10.23.0.0/24 \
    --region=us-central1

次の Service 定義では、internal-load-balancer-subnet を使用してサブネットを名前で参照します。デフォルトでは、サブネットから使用可能な IP が自動的に選択されます。loadBalancerIP を指定することもできますが、参照されるサブネットの一部である必要があります。

この内部ロードバランサのサブネットを共有して、さまざまなユースケースを実現する方法は複数あります。

  • 同じクラスタ内の Service のグループに対する複数のサブネット
  • クラスタ内のすべての Service の単一サブネット
  • 複数のクラスタと Service 間で共有される単一のサブネット
apiVersion: v1
kind: Service
metadata:
  name: ilb-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"
    networking.gke.io/internal-load-balancer-subnet: "gke-vip-subnet"
  labels:
    app: hello
spec:
  type: LoadBalancer
  loadBalancerIP: 10.23.0.15
  selector:
    app: hello
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

グローバル アクセス(ベータ版)

グローバル アクセスは、同じ VPC ネットワーク内の任意のリージョンに配置されているクライアントに対し、内部 TCP / UDP ロードバランサへのアクセスを許可するために使用する、内部 LoadBalancer Service のオプション パラメータです。グローバル アクセスを使用しない場合、トラフィックを送信するクライアントは、ロードバランサと同じ VPC ネットワーク内の同じリージョンに配置されていなければなりません。グローバル アクセスを使用すると、任意のリージョン内のクライアントがロードバランサにアクセスできます。ただし、バックエンド インスタンスはロードバランサと同じリージョン内に配置されている必要があります。

グローバル アクセスは、次のアノテーションを使用して Service ごとに有効にされます。networking.gke.io/internal-load-balancer-allow-global-access: "true"

従来のネットワークでは、グローバル アクセスはサポートされません。リージョン間でグローバル アクセスを使用する場合は、通常のリージョン間トラフィック費用が適用されます。リージョン間の外向きのネットワーク料金については、ネットワーク料金をご覧ください。グローバル アクセスは、GKE クラスタ 1.16 以降でベータ版として利用できます。

All-ports

アノテーション付きの Service を使用して TCP / UDP ロードバランサを作成する場合、すべてのポートを使用する転送ルールを設定する方法はありません。ただし、内部 TCP / UDP ロードバランサを手動で作成する場合は、バックエンドとして Google Kubernetes Engine ノードのインスタンス グループを選択できます。type: NodePort の Kubernetes Services は ILB 経由で使用できます。

内部 TCP / UDP ロードバランサの制限事項

  • Kubernetes バージョン 1.7.3 以前を実行しているクラスタの場合、自動モードのサブネットでのみ内部 TCP / UDP ロードバランサを使用できます。Kubernetes バージョン 1.7.4 以降の場合は、自動モードのサブネットだけでなく、カスタムモードのサブネットでも内部ロードバランサを使用できます。
  • Kubernetes 1.7.X 以降を実行しているクラスタの場合、clusterIP が変更されないため、内部 TCP / UDP ロードバランサは予約済みの IP アドレスを使用できません。spec.loadBalancerIP フィールドに未使用の IP アドレスを定義し、特定の内部 IP を割り当てることができます。ポート、プロトコル、セッション アフィニティを変更すると、IP アドレスが変更されることがあります。

内部 UDP ロードバランサの制限事項

  • 内部 UDP ロードバランサは、sessionAffinity: ClientIP の使用をサポートしていません。

上限

type: Loadbalancer の Kubernetes Service と cloud.google.com/load-balancer-type: Internal アノテーションを使用すると、Kubernetes Service を対象にする ILB が作成されます。このような Service の数は、VPC ネットワークで作成できる内部転送ルールの数によって制限されます。詳細については、ネットワークごとの制限をご覧ください。

GKE クラスタでは、内部転送ルールはクラスタ内のすべてのノードを参照します。クラスタの各ノードは ILB のバックエンド VM になります。VM がインスタンス グループに関連付けられる方法に関係なく、ILB のバックエンド VM の最大数は 250 です。したがって、ILB 内 GKE クラスタの最大ノード数は 250 になります。クラスタで自動スケーリングが有効になっている場合、自動スケーリングでクラスタのノード数が 250 を超えないようにする必要があります。

これらの制限事項の詳細については、VPC リソースの割り当てをご覧ください。

次のステップ