ネットワークの概要

このページでは、Google Kubernetes Engine ネットワークの主な特徴について概説します。この情報は、Kubernetes を使い始めたばかりのユーザーだけでなく、アプリケーションを適切に設計するために、または Kubernetes ワークロードを適切に構成するために Kubernetes ネットワークに関する知識をさらに必要とする経験豊富なクラスタ オペレータやアプリケーション開発者にも役立ちます。

Kubernetes を使用すると、アプリケーションのデプロイ方法、アプリケーションの相互通信方法、アプリケーションと Kubernetes コントロール プレーンの通信方法、クライアントがアプリケーションにアクセスする方法を宣言型の方法で定義できます。また、このページでは、GKE が Google Cloud サービスをどのように構成するかについても説明し、これはネットワーキングとも関連します。

Kubernetes を使用してアプリケーションをオーケストレートする際は、アプリケーションとそのホストのネットワーク設計について考え方を変えることが重要です。Kubernetes では、ホストや VM がどのように接続されているかを考えるのではなく、ポッド、サービス、外部クライアントがどのように通信するかを考えます。

Kubernetes の高度なソフトウェア定義ネットワーキング(SDN)により、同じリージョン クラスタ内の複数のゾーン間でのポッド、サービス、ノードのパケットのルーティングや転送が可能になります。Kubernetes と Google Cloud は、Kubernetes デプロイの宣言モデルと Google Cloud 上のクラスタ構成に応じて、各ノードの IP フィルタリング ルール、ルーティング テーブル、ファイアウォール ルールの動的な構成も行います。

要件

このページでは、HTTPDNS など、インターネット プロトコル スイートのトランスポート層、インターネット層、アプリケーション層に関連する用語を使用していますが、こうした領域の専門家である必要はありません。

また、Linux ネットワーク管理のコンセプト、および iptables ルールやルーティングなどのユーティリティの基礎を理解している場合は、このコンテンツを把握しやすくなります。

Kubernetes ネットワーク モデルは、IP アドレスに大きく依存しています。サービス、ポッド、コンテナ、ノードは、IP アドレスとポートを使用して通信します。Kubernetes は各種の負荷分散機能を提供しており、トラフィックを適切なポッドに振り分けます。これらの全メカニズムについては、このトピックの中で後ほど詳しく説明します。読み進めるとき、次の用語に注意してください。

  • ClusterIP: サービスに割り当てられた IP アドレス。他のドキュメントでは、「クラスタ IP」と呼ばれることもあります。このトピックのサービス セクションで説明しているように、このアドレスはサービスの存続期間中は安定しています。
  • ポッド IP: 特定のポッドに割り当てられた IP アドレス。このトピックのポッド セクションで説明しているように、このアドレスは一時的です。
  • ノード IP: 特定のノードに割り当てられた IP アドレス。

クラスタ内のネットワーク

このセクションでは、Kubernetes クラスタ内のネットワーキングを、IP 割り当てポッドサービスとの関連に注目して説明します。

IP 割り当て

Kubernetes はさまざまな IP 範囲を使用して、ノード、ポッド、サービスに IP アドレスを割り当てます。

  • 各ノードには、クラスタの Virtual Private Cloud(VPC)ネットワークから割り当てられた IP アドレスがあります。このノード IP によって、kube-proxykubelet などの制御コンポーネントから Kubernetes API サーバーに接続できるようになります。この IP により、クラスタの残りの部分とノードが接続されます。
  • 各ノードには、IP アドレスのプールがあり、GKE がそのノードで実行中のポッドをこれらのアドレスに割り当てます(デフォルトは /24 CIDR ブロック)。必要に応じて、クラスタの作成時に IP の範囲を指定できます。Flexible Pod CIDR 範囲機能を使用すると、特定のノードプール内のノードのポッド IP の範囲サイズを小さくできます。

  • 各ポッドには、そのノードのポッド CIDR 範囲から IP アドレスがひとつだけ割り当てられます。この IP アドレスはポッド内で実行中のすべてのコンテナで共有され、クラスタ内で実行中の他のポッドにコンテナを接続します。

  • 各サービスには、クラスタの VPC ネットワークから割り当てられた、ClusterIP という IP アドレスがあります。オプションで、クラスタの作成時に VPC ネットワークをカスタマイズできます。

詳細については、エイリアス IP を使用した VPC ネイティブ クラスタの作成をご覧ください。

ポッド

Kubernetes では、ポッドは Kubernetes クラスタ内にデプロイできる最も基本的な単位です。1 つのポッドは 1 つ以上のコンテナを実行します。0 個以上のポッドがノードで実行されます。クラスタ内の各ノードはノードプールに属します。GKE では、これらのノードは仮想マシンであり、それぞれ Compute Engine でインスタンスとして実行されます。

ポッドは、外部ストレージ ボリュームやその他のカスタム リソースに接続することもできます。この図では、1 つのノードが 2 つのポッドを実行し、各ポッドに 2 つのボリュームが接続されています。

上の段落で説明されている、2 つのポッドを実行する 1 つのノードを示す図

Kubernetes がポッドをノードで実行するようにスケジュールすると、ノードの Linux カーネルでポッドのネットワーク名前空間が作成されます。このネットワーク名前空間により、仮想ネットワーク インターフェースを使用して、ノードの物理ネットワーク インターフェース(eth0 など)とポッドが接続され、ポッドとの間でパケットをやり取りできるようになります。ノードのルート ネットワーク名前空間内の関連する仮想ネットワーク インターフェースは、同じノード上のポッド間の通信を可能にする Linux ブリッジに接続されます。ポッドは、同じ仮想インターフェースを使用して、ノード外にパケットを送信することもできます。

Kubernetes は、ノード上のポッド用に予約されたアドレスの範囲から、ポッドのネットワーク名前空間内の仮想ネットワーク インターフェースに IP アドレス(ポッド IP)を割り当てます。このアドレス範囲は、クラスタ作成時に構成できる、ポッドのクラスタに割り当てられる IP アドレス範囲のサブセットです。

ポッドで動作しているコンテナは、ポッドのネットワーク名前空間を使用します。コンテナの視点からは、ポッドはネットワーク インターフェースを 1 つ備えた物理マシンのように見えます。ポッド内のすべてのコンテナは、この同じネットワーク インターフェースを参照します。各コンテナの localhost は、ポッドを介して、ノードの物理ネットワーク インターフェース(eth0 など)に接続されます。

この接続は、GKE のネイティブ CNI を使用するか、クラスタ作成時にネットワーク ポリシーを有効にして Calico の実装を使用するかによって、大きく異なります。

  • GKE の CNI を使用する場合、veth ペアの一方の端が名前空間内のポッドに接続され、もう一方の端が Linux ブリッジ デバイス cbr0 に接続されます。この例では、以下のコマンドによって、cbr0 に接続されているさまざまなポッドの MAC アドレスが表示されます。

    arp -n
    

    さらに、ツールボックス コンテナで次のコマンドを実行することによって、cbr0 に接続している各 veth ペアの端のルート名前空間が表示されます。

    brctl show cbr0
    
  • ネットワーク ポリシーが有効になっている場合、veth ペアの一方の端はポッドに接続され、もう一方の端は eth0 に接続されています。この場合、次のコマンドは異なる veth デバイスに接続されているさまざまなポッドの MAC アドレスを表示します。

    arp -n
    

    さらに、ツールボックス コンテナで次のコマンドを実行すると、cbr0 という名前の Linux ブリッジ デバイスがないことが表示されます。

    brctl show
    

クラスタ内での転送を容易にする iptables ルールは、シナリオごとに異なります。接続の問題の詳細なトラブルシューティングを行う際には、この違いを考慮することが重要です。

デフォルトでは、各ポッドはクラスタのすべてのノードで実行されている他のポッドに無制限にアクセスできます。しかし、ポッド間のアクセスを制限することは可能です。Kubernetes は定期的にポッドを破棄し、再作成します。この動作は、ノードプールがアップグレードされたとき、ポッドの宣言型の構成を変更したとき、コンテナのイメージを変更したとき、ノードが使用不可になったときに行われます。したがって、ポッドの IP アドレスは実装の詳細であるため、それらに依存しないようにしてください。Kubernetes は、サービスを使用して安定した IP アドレスを提供します。

サービス

Kubernetes では、任意の Kubernetes リソースにラベルと呼ばれる任意の Key-Value ペアを割り当てることができます。Kubernetes はラベルを使用し、関連する複数のポッドをサービスと呼ばれる論理単位にグループ化します。サービスには安定した IP アドレスとポートがあり、サービス作成時にラベルセレクタで定義したすべてのラベルと一致するラベルを持つ一連のポッド間で負荷分散を行います。

次の図は、2 つの個別サービスを示しています。各サービスは複数のポッドで構成されています。図の各ポッドには app=demo というラベルが存在しますが、それぞれにそれ以外のラベルも存在します。サービス「frontend」は、app=democomponent=frontend の両方を持つすべてのポッドと対応しますが、サービス「users」は、app=democomponent=users を持つすべてのポッドと対応します。Client Pod はいずれのサービス セレクタとも正確には一致しないため、いずれのサービスにも含まれません。ただし、Client Pod も同じクラスタ内で実行されるため、いずれのサービスとも通信できます。

前の段落で説明されている 2 つの個別サービスを示す図

Kubernetes は新しく作成された各サービス(ClusterIP)に、クラスタの使用可能なサービス IP アドレスのプールから、安定した、信頼できる IP アドレスを割り当てます。また、Kubernetes は DNS エントリを追加することによって、ClusterIP にホスト名を割り当てます。ClusterIP とホスト名はクラスタ内で一意であり、サービスのライフサイクル全体で変更されません。Kubernetes は、クラスタの構成からサービスが削除された場合にのみ、ClusterIP とホスト名を解放します。ClusterIP またはサービスのホスト名のどちらを使用しても、アプリケーションを実行している正常なポッドにアクセスできます。

一見すると、サービスはアプリケーションにとって単一の障害点のように見えます。ただし、Kubernetes は、多数のノードで実行されているすべてのポッドにできるだけ均等にトラフィックを分散させます。このため、クラスタは、1 つ以上のノード(すべてではない)に影響する停止に耐えることができます。

Kubernetes は、各ノードで実行される kube-proxy コンポーネントを使用してポッドとサービス間の接続を管理します。kube-proxy は、インライン プロキシではなく、下りベースの負荷分散コントローラです。これは、Kubernetes API サーバーを監視し、ノードの iptables サブシステムに対して宛先 NAT(DNAT)ルールを追加および削除することで、絶えず ClusterIP の正常なポッドへのマッピングを行います。ポッドで動作しているコンテナがサービスの ClusterIP にトラフィックを送信すると、ノードはランダムにポッドを選択し、トラフィックをそのポッドにルーティングします。

サービスを構成する場合、オプションで porttargetPort の値を定義することで、サービスのリスニング ポートを再マップできます。

  • port は、クライアントがアプリケーションにアクセスする場所です。
  • targetPort は、アプリケーションがポッド内のトラフィックを実際にリッスンするポートです。

kube-proxy は、ノードに対して iptables ルールを追加および削除することで、このポートの再マッピングを管理します。

次の図は、クライアント ポッドから別のノード上のサーバーポッドへのトラフィックの流れを示しています。クライアントは 172.16.12.100:80 でサービスに接続します。Kubernetes API サーバーは、アプリケーションを実行するポッドのリストを保持しています。各ノードの kube-proxy プロセスはこのリストを使用して iptables ルールを作成し、該当するポッド(10.255.255.202:8080など)にトラフィックを送信します。クライアント ポッドは、クラスタのトポロジや、個々のポッドまたはそれらに含まれているコンテナに関する詳細を認識する必要はありません。

前の段落で説明されている、サービスに接続し、ポッドにルーティングされるクライアントを示す図

クラスタ外のネットワーク

このセクションでは、クラスタ外からのトラフィックが Kubernetes クラスタ内で実行されているアプリケーションにどのように到達するかについて説明します。この情報は、クラスタのアプリケーションとワークロードを設計する際に重要です。

Kubernetes がどのようにサービスを使用して、ポッド内で実行されるアプリケーションに安定した IP アドレスを提供するかについては、すでにご紹介しました。デフォルトでは、各ノードのすべてのトラフィックが kube-proxy によって管理されるため、ポッドは外部 IP アドレスを公開しません。ポッドとそのコンテナは自由に通信できますが、クラスタ外の接続はサービスにアクセスできません。たとえば、前の図では、クラスタ外のクライアントは ClusterIP 経由でフロントエンド サービスにアクセスできません。

GKE には、アクセスを制御し、クラスタ全体に着信トラフィックをできるだけ均等に分散させるための 3 種類のロードバランサが用意されています。複数の種類のロードバランサを同時に使用するように 1 つのサービスを構成できます。

  • 外部ロードバランサは、クラスタ外、および Google Cloud Virtual Private Cloud(VPC)ネットワーク外から着信するトラフィックを管理します。Google Cloud ネットワークに関連付けられた転送ルールを使用して、Kubernetes ノードにトラフィックをルーティングします。
  • 内部ロードバランサは、同じ VPC ネットワーク内から着信するトラフィックを管理します。外部ロードバランサの場合と同様に、Google Cloud ネットワークに関連付けられた転送ルールを使用して、Kubernetes ノードにトラフィックをルーティングします。
  • HTTP(S) ロードバランサは、HTTP(S) トラフィックに使用される専用の外部ロードバランサです。このロードバランサは、転送ルールではなく Ingress リソースを使用して、Kubernetes ノードにトラフィックをルーティングします。

トラフィックが Kubernetes ノードに到達すると、ロードバランサのタイプに関係なく、同じ方法で処理されます。ロードバランサは、クラスタ内のどのノードがそのサービス用のポッドを実行しているか認識しません。代わりに、ノードが関連するポッドを実行していない場合でも、クラスタ内のすべてのノード間でトラフィックのバランスをとります。リージョン クラスタでは、クラスタのリージョンのすべてのゾーン内のすべてのノードに対して負荷が分散されます。トラフィックがノードにルーティングされると、ノードはそのトラフィックを同じノードまたは別のノードで実行されているポッドにルーティングします。ノードは、kube-proxy によってノード上で管理される iptables ルールを使用して、ランダムに選択されたポッドにトラフィックを転送します。

次の図では、ネットワーク ロードバランサはトラフィックを中央のノードに転送し、そのトラフィックは左側のノードのポッドにリダイレクトされます。

前の段落で説明されている、あるノードから別のノード上のポッドにルーティングされるトラフィックを示す図

ロードバランサがトラフィックをノードに送信した後、そのトラフィックは別のノード上のポッドに転送される可能性があります。その場合、ネットワーク ホップが余計に必要となります。この追加のホップを避けたい場合は、最初にトラフィックを受信したノードと同じノード上のポッドにトラフィックを送信するように指定できます。

同じノード上のポッドにトラフィックが送信されるように指定するには、サービス マニフェストで externalTrafficPolicyLocal に設定します。

apiVersion: v1
kind: Service
metadata:
  name: my-lb-service
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: demo
    component: users
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

externalTrafficPolicyLocal に設定すると、ロードバランサは、サービスに属し、正常なポッドがあるノードにのみトラフィックを送信します。ロードバランサは、ヘルスチェックを使用して、どのノードに適切なポッドがあるかを判断します。

外部ロードバランサ

クラスタ外および VPC ネットワーク外からサービスに到達できるようにする必要がある場合、サービスの定義時に、サービスの type フィールドを LoadBalancer に設定することで、サービスを LoadBalancer として構成できます。これにより、GKE は、サービスの前にネットワーク ロードバランサをプロビジョニングします。ネットワーク ロードバランサは、クラスタ内のすべてのノードを認識し、VPC ネットワークのファイアウォール ルールを構成することで、VPC ネットワーク外からサービスの外部 IP アドレスを使用してサービスに接続できるようにします。静的な外部 IP アドレスをサービスに割り当てることができます。詳細については、静的 IP アドレスを使用したドメイン名の構成をご覧ください。

技術的な詳細情報

外部ロードバランサを使用する場合、到着するトラフィックは最初に、Google Cloud ネットワークに関連付けられた転送ルールを使用してノードに転送されます。トラフィックがノードに到達すると、ノードはその iptables NAT テーブルを使用して、ポッドを選択します。ノードの iptables ルールは、kube-proxy によって管理されます。

内部ロードバランサ

同じ VPC ネットワーク内からクラスタに到達する必要のあるトラフィックについては、サービスを構成して内部ロードバランサをプロビジョニングできます。内部ロードバランサは、外部 IP アドレスを選択するのではなく、クラスタの VPC サブネットから IP アドレスを選択します。VPC ネットワーク内のアプリケーションまたはサービスは、この IP アドレスを使用してクラスタ内のサービスと通信できます。

技術的な詳細情報

内部負荷分散機能は、Google Cloud によって提供されます。トラフィックが所定のノードに到達すると、ノードはその iptables NAT テーブルを使用してポッドを選択します。ポッドが別のノードにある場合でも、該当するポッドが選択されます。ノードの iptables ルールは、kube-proxy によって管理されます。

内部ロードバランサの詳細については、内部ロードバランサのドキュメントをご覧ください。

HTTP(S) ロードバランサ

RESTful ウェブサービス API などの多くのアプリケーションは、HTTP(S) を使用して通信します。Kubernetes Ingress リソースを使用することで、VPC ネットワークの外部のクライアントからこの種類のアプリケーションへのアクセスを可能にできます。Ingress リソースを使用すると、ホスト名と URL パスをクラスタ内のサービスにマップできます。HTTP(S) ロードバランサを使用する場合は、NodePort と ClusterIP が使用されるようにサービスを構成する必要があります。トラフィックが NodePort でノードの IP のサービスにアクセスすると、GKE はそのトラフィックをサービスの正常なポッドにルーティングします。NodePort を指定することも、GKE による未使用ポートのランダムな割り当てを許可することもできます。

Ingress リソースを作成すると、GKE によって Google Cloud プロジェクトに HTTP(S) ロードバランサがプロビジョニングされます。ロードバランサは、NodePort でノードの IP アドレスにリクエストを送信します。リクエストがノードに到達すると、ノードはその iptables NAT テーブルを使用して、ポッドを選択します。ノードの iptables ルールは、kube-proxy によって管理されます。

この Ingress 定義では、demo.example.com へのトラフィックはポート 80 の frontend という名前のサービスにルーティングされ、demo-backend.example.com へのトラフィックはポート 8080 の users という名前のサービスにルーティングされます。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: demo
spec:
  rules:
  - host: demo.example.com
    http:
      paths:
      - backend:
          serviceName: frontend
          servicePort: 80
  - host: demo-backend.example.com
    http:
      paths:
      - backend:
          serviceName: users
          servicePort: 8080

詳細については、Ingress on Google Cloudをご覧ください。

技術的な詳細情報

Ingress オブジェクトを作成すると、Ingress マニフェストと関連するサービス マニフェストのルールに従い、GKE Ingress コントローラによって Google Cloud HTTP(S) ロードバランサが構成されます。クライアントは HTTP(S) ロードバランサにリクエストを送信します。ロードバランサは実際のプロキシです。つまり、ノードを選択して、そのノードの NodeIP:NodePort の組み合わせにリクエストを転送します。ノードはその iptables NAT テーブルを使用してポッドを選択します。ノードの iptables ルールは、kube-proxy によって管理されます。

ポッドとサービスへの接続の制限

デフォルトでは、同じクラスタ内で実行されているすべてのポッドは自由に通信できます。ただし、必要に応じて、クラスタ内の接続をさまざまな方法で制限できます。

ポッド間のアクセスを制限する

ネットワーク ポリシーを使用して、ポッド間のアクセスを制限できます。ネットワーク ポリシー定義を使用すると、ラベル、IP 範囲、ポート番号の任意の組み合わせに基づいて、ポッドの上り下りを制限できます。デフォルトではネットワーク ポリシーはないため、クラスタ内のポッド間のすべてのトラフィックが許可されます。名前空間に最初のネットワーク ポリシーを作成すると、他のトラフィックはすべて拒否されます。

ポリシー自体を指定する方法について詳しくは、ネットワーク ポリシーをご覧ください。

ネットワーク ポリシーを作成した後、クラスタに対してそのポリシーを明示的に有効にする必要があります。詳細については、アプリケーションのネットワーク ポリシーの構成をご覧ください。

外部ロードバランサへのアクセスを制限する

サービスで外部ロードバランサが使用されている場合、外部 IP アドレスからのトラフィックはデフォルトでサービスにアクセスできます。サービス構成時に loadBalancerSourceRanges オプションを構成することで、クラスタ内のエンドポイントにアクセスできる IP アドレスの範囲を制限できます。複数の範囲を指定でき、またいつでも実行中のサービスの構成を更新できます。各ノードで動作している kube-proxy インスタンスは、指定された loadBalancerSourceRanges に一致しないトラフィックはすべて拒否するように、そのノードの iptables ルールを構成します。VPC ファイアウォール ルールは作成されません。

HTTP(S) ロードバランサへのアクセスを制限する

サービスで HTTP(S) ロードバランサを使用する場合、Google Cloud Armor セキュリティ ポリシーを使用して、サービスにアクセスできる外部 IP アドレスや、セキュリティ ポリシーによってアクセスが拒否されたときに返されるレスポンスなどを限定できます。Stackdriver Logging を構成することで、これらの操作に関する情報を記録できます。

Google Cloud Armor セキュリティ ポリシーの詳細度が十分に高くない場合は、エンドポイントで Identity-Aware Proxy を有効にして、ユーザー単位の認証と承認をアプリケーションに実装できます。詳細については、IAP の構成に関する詳細なチュートリアルをご覧ください。

次のステップ