これは、このセクションの複数ページの印刷可能なビューです。 印刷するには、ここをクリックしてください.

このページの通常のビューに戻る.

Service、負荷分散とネットワーク

Kubernetesにおけるネットワークの概念とリソース。

Kubernetesのネットワークモデル

Kubernetesのネットワークモデルは、いくつかの要素で構成されています:

  • クラスター内の各Podは、クラスター全体で一意のIPアドレスを取得します。

    • Podは独自のプライベートネットワーク名前空間を持ち、Pod内のすべてのコンテナで共有されます。 同じPod内の異なるコンテナで実行されているプロセスは、localhostで相互に通信できます。
  • Podネットワーク(クラスターネットワークとも呼ばれます)は、Pod間の通信を処理します。 これにより、以下が保証されます(意図的なネットワークセグメンテーションがない限り):

    • すべてのPodは、同じノード上にあるか、異なるノード上にあるかに関わらず、他のすべてのPodと通信できます。 Podは、プロキシやアドレス変換(NAT)を使用せずに直接通信できます。

      Windowsでは、このルールはホストネットワークPodには適用されません。

    • ノード上のエージェント(システムデーモンやkubeletなど)は、そのノード上のすべてのPodと通信できます。

  • Service APIは、複数のバックエンドPodによって実装されるサービスに対して、安定した(長期的な)IPアドレスまたはホスト名を提供します。 サービスを構成する個々のPodが時間とともに変化しても、このIPアドレスやホスト名は変わりません。

    • Kubernetesは、Serviceを構成するPodに関する情報を提供するため、EndpointSliceオブジェクトを自動的に管理します。

    • サービスプロキシ実装は、ServiceとEndpointSliceオブジェクトのセットを監視します。 そして、オペレーティングシステムまたはクラウドプロバイダーAPIを使用してパケットをインターセプトまたは書き換えることで、サービストラフィックをバックエンドにルーティングするようデータプレーンをプログラムします。

  • Gateway API(またはその前身であるIngress)を使用すると、クラスター外部のクライアントからServiceにアクセスできるようになります。

    • サポートされているクラウドプロバイダーを使用している場合、Service APIのtype: LoadBalancerを使用することで、シンプルではあるものの、設定の自由度が低いクラスターイングレスの仕組みを利用することができます。
  • NetworkPolicyは、Pod間、またはPodと外部との間のトラフィックを制御できる組み込みのKubernetes APIです。

古いコンテナシステムでは、異なるホスト上のコンテナ同士は自動的には通信できませんでした。 そのため、コンテナ間のリンクを明示的に作成したり、コンテナポートをホストポートにマッピングして他のホストから到達可能にしたりする必要がありました。 Kubernetesではこのような作業は不要です。 Kubernetesのネットワークモデルでは、ポート割り当て、名前解決、サービスディスカバリ、負荷分散、アプリケーション設定、マイグレーションといった観点から、Podは、VMや物理ホストと同じように扱えます。

Kubernetes本体で実装されているのは、このモデルの一部のみです。 その他の部分については、KubernetesがAPIを定義し、実際の機能は外部コンポーネントが提供します。これらのコンポーネントの一部はオプションです:

  • Podネットワーク名前空間のセットアップは、コンテナランタイムインターフェースを実装するシステムレベルのソフトウェアによって処理されます。

  • Podネットワーク自体は、Podネットワーク実装によって管理されます。 Linuxでは、ほとんどのコンテナランタイムがコンテナネットワークインターフェース(CNI)を使用してPodネットワーク実装と連携するため、これらの実装は CNIプラグイン と呼ばれることがよくあります。

  • Kubernetesは、kube-proxyと呼ばれるサービスプロキシのデフォルト実装を提供していますが、一部のPodネットワーク実装は、実装の他の部分とより密に統合された独自のサービスプロキシを使用します。

  • 一般的には、NetworkPolicyもPodネットワーク実装によって実装されます(一部のシンプルなPodネットワーク実装ではNetworkPolicyを実装していない場合や、管理者がNetworkPolicyサポートなしでPodネットワークを設定する場合があります。これらの場合、APIは引き続き存在しますが、効果はありません)。

  • Gateway APIの実装は多数存在し、特定のクラウド環境に特化したもの、「ベアメタル」環境に焦点を当てたもの、より汎用的なものなどがあります。

次の項目

アプリケーションをServiceに接続するチュートリアルでは、実践的な例を通じてServiceとKubernetesネットワークについて学ぶことができます。

クラスターのネットワークでは、クラスターのネットワークを設定する方法について説明し、関連する技術の概要も提供しています。

1 - Service

ワークロードが複数のバックエンドに分散している場合でも、クラスター内で実行されているアプリケーションを、単一の外部向けエンドポイントの背後に公開します。

Kubernetesにおいて、Serviceとは、 クラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開する方法です。

KubernetesにおけるServiceの主要な目的の一つは、既存のアプリケーションを改修することなく、サービスディスカバリの仕組みを利用できるようにすることです。 Pod内で実行するコードは、クラウドネイティブな環境向けに設計されたものでも、コンテナ化した古いアプリケーションでも構いません。 Serviceを使用することで、その複数のPodがネットワーク上でアクセス可能になり、クライアントから通信できるようになります。

Deploymentを使用してアプリケーションを実行する場合、DeploymentはPodを動的に作成、削除します。 常に、どれだけのPodが動作していて健全な状態であるかは分からず、健全なPodの名前すら分からない可能性もあります。 KubernetesのPodは、クラスターの望ましい状態に一致させるために作成、削除されます。 Podは一時的なリソースです(個々のPodに対して、信頼性が高く永続的であるとは期待すべきではありません)。

各Podは独自のIPアドレスを取得します(Kubernetesは、ネットワークプラグインがこれを保証することを期待しています)。 クラスター内の特定のDeploymentで、ある時点で実行されているPodの集合は、少し時が経過すると、異なるPodの集合になっている可能性があります。

これは、とある問題につながります。 Podの集合(「バックエンド」と呼びます)がクラスター内の他のPod(「フロントエンド」と呼びます)に機能を提供する場合、フロントエンドがワークロードのバックエンド部分を使用できるように、どのようにして接続対象のIPアドレスを発見し、追跡するのでしょうか?

ここで Service の登場です。

KubernetesにおけるService

Kubernetesの一部であるService APIは、Podのグループをネットワーク経由で公開するための抽象化です。 各Serviceオブジェクトは、エンドポイントの論理的な集合(通常、これらのエンドポイントはPodです)および、Podの公開方法についてのポリシーを定義します。

例として、3つのレプリカで実行されているステートレスな画像処理バックエンドについて考えます。 これらのレプリカは互いに代替可能です—フロントエンドはどのバックエンドを使用するかを気にしません。 バックエンドを構成する実際のPodは変化する可能性がありますが、フロントエンドのクライアントはそれを意識する必要はなく、バックエンドの集合を自身で追跡すべきでもありません。

Serviceという抽象化により、この分離が可能になります。

Serviceが対象とするPodは、通常、ユーザーが定義するセレクターによって決定されます。 Serviceのエンドポイントを定義する他の方法については、セレクター なし のServiceを参照してください。

ワークロードがHTTPを使用する場合、Ingressを使用して、Webトラフィックがワークロードに到達する方法を制御できます。 IngressはServiceタイプではありませんが、クラスターへのエントリポイントとして機能します。 Ingressを使用すると、ルーティングルールを単一のリソースに統合できるため、クラスター内で個別に実行されている複数のワークロードコンポーネントを、単一のリスナーの背後で公開できます。

KubernetesのGateway APIは、IngressやServiceを超える追加機能を提供します。 GatewayはCustomResourceDefinitionを使用して実装された拡張APIの一種であり、クラスターに追加することで、クラスター内で実行されているネットワークサービスへのアクセスを設定できます。

クラウドネイティブなサービスディスカバリ

アプリケーションでサービスディスカバリにKubernetes APIを使用できる場合、APIサーバーに問い合わせて一致するEndpointSliceを取得できます。 KubernetesはService内のPodの集合が変更されるたびに、そのServiceのEndpointSliceを更新します。

ネイティブではないアプリケーションの場合、KubernetesはアプリケーションとバックエンドのPodの間にネットワークポートまたはロードバランサーを配置する方法を提供します。

いずれの場合でも、ワークロードはこれらのサービスディスカバリの仕組みを使用して、接続先を見つけることができます。

Serviceの定義方法

Serviceはオブジェクトです(PodやConfigMapがオブジェクトであるのと同じです)。 Kubernetes APIを使用してServiceの定義を作成、表示、変更できます。 通常はkubectlのようなツールを使用して、これらのAPI呼び出しを行います。

例えば、TCPポート9376でリッスンし、app.kubernetes.io/name=MyAppというラベルが付けられたPodの集合があるとします。 そのTCPリスナーを公開するServiceを定義できます:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

このマニフェストを適用すると、デフォルトのClusterIP Serviceタイプで「my-service」という名前の新しいServiceが作成されます。 このServiceは、app.kubernetes.io/name: MyAppラベルを持つすべてのPodのTCPポート9376を対象とします。

KubernetesはこのServiceに対してIPアドレス(ClusterIP)を割り当てます。 このIPアドレスは仮想IPアドレスメカニズムによって使用されます。 このメカニズムの詳細については、仮想IPとServiceプロキシを参照してください。

このServiceのコントローラーは、セレクターに一致するPodを継続的にスキャンし、ServiceのEndpointSliceの集合に対して必要に応じて更新を行います。

Serviceオブジェクト名は、有効なRFC 1035ラベル名である必要があります。

備考:

Serviceは 任意の 受信porttargetPortにマッピングできます。 ただし、デフォルトでは、利便性のためにtargetPortportフィールドと同じ値に設定されます。

Serviceオブジェクトの緩和された命名要件

FEATURE STATE: Kubernetes v1.34 [alpha](disabled by default)

RelaxedServiceNameValidationフィーチャーゲートは、Serviceオブジェクト名が数字で始まることを許可します。 このフィーチャーゲートが有効化されている場合、Serviceオブジェクト名は有効なRFC 1123ラベル名である必要があります。

ポート定義

Pod内のポート定義には名前が含まれ、ServiceのtargetPort属性でこれらの名前を参照できます。 例えば、次のようにServiceのtargetPortをPodのポートにバインドできます:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

これは、Serviceに異なるポート番号で同じネットワークプロトコルを提供する複数のPodが混在している場合でも、単一の設定された名前を使用して機能します。 これにより、Serviceのデプロイと進化に大きな柔軟性がもたらされます。 たとえば、バックエンドソフトウェアの次のバージョンでPodが公開するポート番号を変更しても、クライアントを壊すことはありません。

ServiceのデフォルトプロトコルはTCPです。 他のサポートされているプロトコルも使用できます。

多くのServiceは複数のポートを公開する必要があるため、Kubernetesは単一のServiceに対して、複数のポート定義をサポートしています。 各ポート定義は、同じprotocolを持つことも、異なるものを持つことも可能です。

セレクター無しのService

Serviceは、セレクターを使ってKubernetes Podへのアクセスを抽象化するのが一般的です。 ただし、セレクター無しで対応するEndpointSliceオブジェクトを使用すれば、クラスター外のバックエンドなど、他の種類のバックエンドも抽象化できます。

例えば:

  • 本番環境では外部のデータベースクラスターを使用したいが、テスト環境では独自のデータベースを使用する場合。
  • Serviceを別のNamespaceまたは別のクラスター上のServiceに向けたい場合。
  • ワークロードをKubernetesに移行中の場合。 移行方法を評価している間は、バックエンドの一部のみをKubernetesで実行する場合。

これらのいずれのシナリオでも、Podに一致するセレクターを指定 せずに Serviceを定義できます。 例えば:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

このServiceにはセレクターが含まれないため、対応するEndpointSliceオブジェクトは自動的に作成されません。 EndpointSliceオブジェクトを手動で追加することで、実行されているネットワークアドレスとポートにServiceをマッピングできます。 例えば、次のように定義します:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # 慣習で、EndpointSliceの名前にプレフィックスとしてService名を使用します

  labels:
    # "kubernetes.io/service-name"ラベルを設定する必要があります。
    # その値はServiceの名前と一致させてください
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: http # 上記で定義したServiceポート名と一致させる必要があります
    appProtocol: http
    protocol: TCP
    port: 9376
endpoints:
  - addresses:
      - "10.4.5.6"
  - addresses:
      - "10.1.2.3"

カスタムEndpointSlice

Service用にEndpointSliceオブジェクトを作成する場合、EndpointSliceには任意の名前を使用できます。 ただし、名前空間内の各EndpointSliceは一意の名前である必要があります。 EndpointSliceにkubernetes.io/service-nameラベルを設定することで、EndpointSliceをServiceに関連付けられます。

備考:

エンドポイントのIPは次のものであっては いけません: ループバック(IPv4の場合は127.0.0.0/8、IPv6の場合は::1/128)、またはリンクローカル(IPv4の場合は169.254.0.0/16と224.0.0.0/24、IPv6の場合はfe80::/64)。

エンドポイントIPアドレスは、他のKubernetes ServiceのClusterIPにすることはできません。 これは、kube-proxyが宛先として仮想IPをサポートしていないためです。

自身で作成するEndpointSlice、または独自のコード内で作成するEndpointSliceの場合、endpointslice.kubernetes.io/managed-byラベルに使用する値も選択するべきです。 EndpointSliceを管理する独自のコントローラーコードを作成する場合は、"my-domain.example/name-of-controller"のような値の使用を検討してください。 サードパーティツールを使用している場合は、ツールの名前をすべて小文字で使用し、スペースやその他の句読点をダッシュ(-)に変更してください。 kubectlのようなツールを直接使用してEndpointSliceを管理する場合は、"staff""cluster-admins"のような、手動で管理していることを説明する名前を使用してください。 Kubernetes自身のコントロールプレーンによって管理されるEndpointSliceを識別する予約語である"controller"の使用は避けてください。

セレクターの無いServiceへのアクセス

セレクターの無いServiceへのアクセスは、セレクターがある場合と同じように機能します。 セレクターの無いServiceのでは、トラフィックはEndpointSliceマニフェストで定義された2つのエンドポイントのいずれかにルーティングされます: ポート9376上の10.1.2.3または10.4.5.6へのTCP接続です。

備考:

Kubernetes APIサーバーは、Podにマッピングされていないエンドポイントへのプロキシを許可しません。 kubectl port-forward service/<service-name> forwardedPort:servicePortのようなアクションは、Serviceにセレクターが無い場合、この制約により失敗します。 これにより、Kubernetes APIサーバーが、呼び出し元に未許可のエンドポイントへのプロキシとして使用されることを防ぐことができます。

ExternalName Serviceは、セレクターを持たず、代わりにDNS名を使用するServiceの特殊なケースです。 詳細については、ExternalNameセクションを参照してください。

EndpointSlice

FEATURE STATE: Kubernetes v1.21 [stable]

EndpointSliceは、Serviceの背後にあるネットワークエンドポイントのサブセット(スライス)を表すオブジェクトです。

Kubernetesクラスターは、各EndpointSliceが保持するエンドポイントの数を追跡します。 特定のServiceに紐づくエンドポイント数が多く、しきい値に達した場合、Kubernetesは新しい空のEndpointSliceを追加し、そこに新たなエンドポイント情報を保存します。 デフォルトでは、既存のすべてのEndpointSliceがそれぞれ、少なくとも100個のエンドポイントを含むと、Kubernetesは新しいEndpointSliceを作成します。 Kubernetesは、追加のエンドポイントを追加する必要性が生じるまで、新しいEndpointSliceを作成しません。

このAPIの詳細については、EndpointSliceを参照してください。

Endpoints(非推奨)

FEATURE STATE: Kubernetes v1.33 [deprecated]

EndpointSlice APIは、従来のEndpoints APIの後継です。 非推奨となったEndpoints APIには、EndpointSliceと比較していくつかの問題があります:

  • デュアルスタッククラスターをサポートしていません。
  • trafficDistributionのような新しい機能をサポートするために必要な情報が含まれていません。
  • エンドポイントのリストが単一のオブジェクトに収まらないほど長い場合、切り捨てられます。

このため、すべてのクライアントはEndpointsではなくEndpointSlice APIを使用することをお勧めします。

容量超過のEndpoints

Kubernetesでは、単一のEndpointsオブジェクトに格納できるエンドポイントの数は制限されています。 Serviceに1000個を超えるエンドポイントが存在する場合、KubernetesはEndpointsオブジェクト内のデータを切り捨てます。 Serviceは複数のEndpointSliceにリンクできるため、1000個という制限はレガシーのEndpoints APIにのみ影響します。

その場合、KubernetesはEndpointsオブジェクトに格納する最大1000個のバックエンドエンドポイントを選択し、Endpointsにendpoints.kubernetes.io/over-capacity: truncatedというアノテーションを設定します。 コントロールプレーンはまた、バックエンドのPod数が1000未満に減少した場合、このアノテーションを削除します。

トラフィックは引き続きバックエンドに送信されますが、レガシーのEndpoints APIに依存するロードバランシング機構は、利用可能なバックエンドのエンドポイントのうち最大1000個にのみトラフィックを送信します。

同様に、APIの制限によって、Endpointsを手動で更新して1000個を超えるエンドポイントを持つようにすることはできません。

アプリケーションプロトコル

FEATURE STATE: Kubernetes v1.20 [stable]

appProtocolフィールドは、各Serviceポートに対してアプリケーションプロトコルを指定する方法を提供します。 これは、実装が理解するプロトコルに対して、より高度な動作を提供するためのヒントとして使用されます。 このフィールドの値は、対応するEndpointsおよびEndpointSliceオブジェクトに反映されます。

このフィールドは、標準のKubernetesラベル構文に従います。 有効な値は次のいずれかです:

  • IANA標準サービス名

  • mycompany.com/my-custom-protocolのような実装定義のプレフィックス付きの名前。

  • Kubernetes定義のプレフィックス付きの名前:

プロトコル 説明
kubernetes.io/h2c RFC 7540で説明されている平文のHTTP/2
kubernetes.io/ws RFC 6455で説明されている平文のWebSocket
kubernetes.io/wss RFC 6455で説明されているTLS上のWebSocket

マルチポートService

一部のServiceでは、複数のポートを公開する必要があります。 Kubernetesでは、単一のServiceオブジェクトに複数のポート定義を設定できます。 Serviceに複数のポートを使用する場合、曖昧さを避けるために、すべてのポートに名前を付ける必要があります。 例えば、下記のように設定します:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

備考:

Kubernetesの名前全般と同様に、ポート名は小文字の英数字と-のみを含む必要があります。 ポート名は英数字で始まり、英数字で終わる必要もあります。

例えば、123-abcwebという名前は有効ですが、123_abc-webは無効です。

Serviceタイプ

アプリケーションの一部(例えば、フロントエンド)で、Serviceをクラスターの外部からアクセス可能な外部IPアドレスとして公開したい場合があります。

KubernetesのServiceタイプを使用すると、必要なServiceの種類を指定できます。

利用可能なtypeの値とその動作は次の通りです:

ClusterIP
クラスター内部のIPでServiceを公開します。 この値を選択すると、Serviceはクラスター内からのみ到達可能になります。 これは、Serviceに対して明示的にtypeを指定しない場合に使用されるデフォルトです。 IngressまたはGatewayを使用して、Serviceをパブリックインターネットに公開できます。
NodePort
各ノードのIPの静的ポート(NodePort)でServiceを公開します NodePortを利用可能にするために、Kubernetesは、type: ClusterIPのServiceを要求した場合と同じように、ClusterIPアドレスを設定します。
LoadBalancer
外部のロードバランサーを使用してServiceを外部に公開します。 Kubernetesはロードバランシングコンポーネントを直接提供しないため、独自に提供するか、Kubernetesクラスターをクラウドプロバイダーと統合する必要があります。
ExternalName
ServiceをexternalNameフィールドの内容(例えば、ホスト名api.foo.bar.example)にマッピングします。 このマッピングにより、クラスターのDNSサーバーがその外部ホスト名の値を持つCNAMEレコードを返すように設定されます。 いかなる種類のプロキシも設定されません。

Service APIのtypeフィールドは階層的な機能として設計されており、上位のタイプは下位のタイプの機能を含みます。 ただし、この階層的な設計には例外があります。 ロードバランサーのNodePort割り当てを無効にすることで、LoadBalancer Serviceを定義できます。

type: ClusterIP

このデフォルトのServiceタイプでは、クラスターが予約済みのIPアドレスプールからIPアドレスが割り当てられます。

他のいくつかのServiceタイプは、ClusterIPタイプを基盤として構築されています。

.spec.clusterIP"None"に設定したServiceを定義すると、KubernetesはIPアドレスを割り当てません。 詳細については、ヘッドレスServiceを参照してください。

独自のIPアドレスの選択

Service作成リクエストの一部として、独自のclusterIPアドレスを指定できます。 指定するためには、.spec.clusterIPフィールドを設定します。 例えば、再利用したい既存のDNSエントリがある場合や、特定のIPアドレス用に設定されていて再設定が困難なレガシーシステムがある場合などです。

選択するIPアドレスは、APIサーバーに設定されているservice-cluster-ip-range CIDR範囲内の有効なIPv4またはIPv6アドレスである必要があります。 無効なclusterIPアドレスの値でServiceを作成しようとすると、APIサーバーは問題が発生したことを示す422 HTTPステータスコードを返します。

Kubernetesがどのように、2つの異なるServiceが同じIPアドレスを使用しようとする際のリスクと影響を軽減するかについては、衝突の回避を参照してください。

type: NodePort

typeフィールドをNodePortに設定すると、Kubernetesコントロールプレーンは--service-node-port-rangeフラグで指定された範囲(デフォルト: 30000-32767)からポートを割り当てます。 各ノードはそのポート(すべてのノードで同じポート番号)をServiceにプロキシします。 割り当てられたポートは、Serviceの.spec.ports[*].nodePortフィールドで確認できます。

NodePortを使用すると、独自のロードバランシングソリューションのセットアップや、Kubernetesで完全にサポートされていない環境の構成、あるいはノードのIPアドレスの直接公開さえ可能になります。

NodePortタイプのServiceの場合、Kubernetesはさらにポート(Serviceのプロトコルに合わせてTCP、UDP、またはSCTP)を割り当てます。 クラスター内のすべてのノードは、割り当てられたポートをリッスンし、Serviceに関連付けられた準備完了状態のエンドポイントの1つにトラフィックを転送するように自身を構成します。 適切なプロトコル(例: TCP)と適切なポート(そのServiceに割り当てられたポート)を使用して任意のノードに接続することで、クラスター外からtype: NodePortのServiceにアクセスできます。

独自のポート選択

特定のポート番号が必要な場合は、nodePortフィールドに値を指定できます。 コントロールプレーンはそのポートを割り当てるか、APIのトランザクションが失敗したことを報告します。 つまり、ポートの衝突を自分で処理する必要があります。 また、NodePortによる使用のために設定された範囲内の有効なポート番号を使用する必要があります。

以下は、NodePort値(この例では、30007)を指定したtype: NodePortのServiceのマニフェストの例です:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      # デフォルトでは、利便性のために`targetPort`は
      # `port`フィールドと同じ値に設定されます。
      targetPort: 80
      # オプションのフィールド
      # デフォルトでは、利便性のためにKubernetesコントロールプレーンは
      # 範囲(デフォルト: 30000-32767)からポートを割り当てます
      nodePort: 30007

ポート衝突を避けるためのNodePort範囲の予約

NodePortサービスへのポート割り当てのポリシーは、自動割り当てと手動割り当ての両方のシナリオに適用されます。 ユーザーが特定のポートを使用するNodePortサービスを作成する場合、ターゲットポートはすでに割り当てられている別のポートと競合する可能性があります。

この問題を回避するため、NodePortサービスのポート範囲は2つの帯域に分割されています。 動的なポート割り当てはデフォルトで上位の帯域を使用し、上位の帯域が使い尽くされると下位帯域を使用することがあります。 ユーザーは、下位の帯域から割り当てることでポート衝突のリスクを低減することができます。

デフォルトのNodePortの範囲である30000-32767を使用する場合、各帯域は次のように分割されます:

  • 静的帯域: 30000-30085
  • 動的帯域: 30086-32767

静的帯域と動的帯域がどのように計算されるかの詳細は、NodePort Serviceへのポート割り当てにおける衝突の回避を参照してください。

type: NodePort ServiceのカスタムIPアドレス設定

NodePortサービスを提供するために特定のIPアドレスを使用するよう、クラスター内のノードを設定できます。 各ノードが複数のネットワークに接続されている場合(例: アプリケーショントラフィック用のネットワークと、ノード間およびコントロールプレーン間のトラフィック用の別のネットワーク)に、この設定が必要になることがあります。

ポートをプロキシする特定のIPアドレスを指定する場合は、kube-proxyの--nodeport-addressesフラグ、またはkube-proxy設定ファイルの同等のnodePortAddressesフィールドを特定のIPブロックに設定できます。

このフラグは、カンマ区切りのIPブロックリスト(例: 10.0.0.0/8192.0.2.0/25)を受け取り、kube-proxyがこのノードのローカルとみなすIPアドレス範囲を指定します。

例えば、--nodeport-addresses=127.0.0.0/8フラグでkube-proxyを起動すると、kube-proxyはNodePortサービスに対してループバックインターフェースのみを選択します。 --nodeport-addressesのデフォルトは空のリストです。 これは、kube-proxyがNodePortに対して利用可能なすべてのネットワークインターフェースを考慮する必要があることを意味します。(これは以前のKubernetesリリースとも互換性があります。)

備考:

このServiceは<NodeIP>:spec.ports[*].nodePortおよび.spec.clusterIP:spec.ports[*].portとして表示されます。 kube-proxyの--nodeport-addressesフラグまたはkube-proxy設定ファイルの同等のフィールドが設定されている場合、<NodeIP>はフィルタリングされたノードのIPアドレス(または複数のIPアドレスの可能性)になります。

type: LoadBalancer

外部ロードバランサーをサポートするクラウドプロバイダーでは、typeフィールドをLoadBalancerに設定すると、Service向けにロードバランサーがプロビジョニングされます。 実際のロードバランサーの作成は非同期で行われ、プロビジョニングされたバランサーに関する情報はServiceの.status.loadBalancerフィールドで公開されます。 たとえば、次のように設定します:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

外部ロードバランサーからのトラフィックはバックエンドのPodに転送されます。 負荷分散の方法は、クラウドプロバイダーが決定します。

type: LoadBalancerのServiceを実装するために、Kubernetesは通常、まずtype: NodePortのServiceをリクエストした場合と同等の変更を行います。 その後、cloud-controller-managerコンポーネントが外部ロードバランサーを設定し、割り当てられたNodePortにトラフィックを転送します。

クラウドプロバイダーの実装でサポートされている場合、LoadBalancerタイプのServiceを設定してNodePortの割り当てを省略できます。

一部のクラウドプロバイダーではloadBalancerIPを指定できます。 その場合、ユーザーが指定したloadBalancerIPでロードバランサーが作成されます。 loadBalancerIPフィールドが指定されていない場合、ロードバランサーは一時的なIPアドレスで設定されます。 loadBalancerIPを指定しても、クラウドプロバイダーがこの機能をサポートしていない場合、設定したloadbalancerIPフィールドは無視されます。

備考:

Serviceの.spec.loadBalancerIPフィールドはKubernetes v1.24で非推奨になりました。

このフィールドは仕様が不十分で、実装によって意味が異なっていました。 また、デュアルスタックネットワーキングをサポートできません。 このフィールドは将来のAPIバージョンで削除される可能性があります。

(プロバイダー固有の)アノテーションを介してServiceのロードバランサーIPアドレスの指定をサポートするプロバイダーと統合する場合は、その方法に切り替えてください。

Kubernetesとロードバランサーの統合コードを書いている場合は、このフィールドの使用を避けてください。 ServiceではなくGatewayと統合するか、同等の詳細を指定するService上の独自の(プロバイダー固有の)アノテーションを定義できます。

ノードの稼働状態がロードバランサーのトラフィックに与える影響

ロードバランサーによるヘルスチェックは現代のアプリケーションにとって重要です。 これらはロードバランサーがトラフィックをディスパッチするサーバー(仮想マシンまたはIPアドレス)を決定するために使用されます。 Kubernetes APIはKubernetesが管理するロードバランサーに対してヘルスチェックをどのように実装する必要があるかを定義していません。 代わりに、クラウドプロバイダー(および統合コードを実装する人々)によって動作が決定されます。 ロードバランサーによるヘルスチェックはServiceのexternalTrafficPolicyフィールドをサポートする文脈で広く使用されています。

混合プロトコルタイプのロードバランサー

FEATURE STATE: Kubernetes v1.26 [stable](enabled by default)

デフォルトでは、LoadBalancerタイプのServiceで複数のポートが定義されている場合、すべてのポートは同じプロトコルを持つ必要があり、そのプロトコルはクラウドプロバイダーがサポートするものでなければなりません。

フィーチャーゲートMixedProtocolLBService(v1.24以降のkube-apiserverではデフォルトで有効)により、複数のポートが定義されている場合、LoadBalancerタイプのServiceで異なるプロトコルを使用できます。

備考:

ロードバランサーServiceで使用できるプロトコルのセットは、クラウドプロバイダーによって定義されます。 クラウドプロバイダーは、Kubernetes APIが強制する制限を超えた追加の制限を課す場合があります。

ロードバランサーによるNodePort割り当ての無効化

FEATURE STATE: Kubernetes v1.24 [stable]

spec.allocateLoadBalancerNodePortsフィールドをfalseに設定することで、type: LoadBalancerのServiceに対するNodePort割り当てをオプションで無効にできます。 これは、NodePortを使用せずに、トラフィックをPodに直接ルーティングするロードバランサー実装に対してのみ使用してください。 デフォルトでは、spec.allocateLoadBalancerNodePortstrueであり、LoadBalancerタイプのServiceは引き続きNodePortを割り当てます。 既に割り当て済みのNodePortを持つ既存のServiceに対してspec.allocateLoadBalancerNodePortsfalseに設定しても、それらのNodePortは自動的に割り当て解除されません。 NodePortの割り当てを解除するには、すべてのServiceポートのnodePortsエントリを明示的に削除する必要があります。

ロードバランサー実装のクラスの指定

FEATURE STATE: Kubernetes v1.24 [stable]

typeLoadBalancerに設定されたServiceの場合、.spec.loadBalancerClassフィールドを使用すると、クラウドプロバイダーのデフォルト以外のロードバランサー実装を使用できます。

デフォルトでは、.spec.loadBalancerClassは設定されておらず、クラスターが--cloud-providerコンポーネントフラグを使用してクラウドプロバイダーで設定されている場合、LoadBalancerタイプのServiceはクラウドプロバイダーのデフォルトロードバランサー実装を使用します。

.spec.loadBalancerClassを指定すると、指定されたクラスに一致するロードバランサー実装がServiceを監視していると想定されます。 デフォルトのロードバランサー実装(たとえば、クラウドプロバイダーが提供するもの)は、このフィールドが設定されたServiceを無視します。 spec.loadBalancerClassLoadBalancerタイプのServiceにのみ設定できます。 一度設定すると、変更できません。 spec.loadBalancerClassの値はラベル形式の識別子である必要があり、"internal-vip"や"example.com/internal-vip"などのプレフィックスをオプションとして持つことができます。 プレフィックスのない名前はエンドユーザー向けに予約されています。

ロードバランサーのIPアドレスモード

FEATURE STATE: Kubernetes v1.32 [stable](enabled by default)

type: LoadBalancerのServiceの場合、コントローラーは.status.loadBalancer.ingress.ipModeを設定できます。 .status.loadBalancer.ingress.ipModeは、ロードバランサーIPの動作方法を指定します。 これは.status.loadBalancer.ingress.ipフィールドも指定されている場合にのみ指定できます。

.status.loadBalancer.ingress.ipModeには、「VIP」と「Proxy」の2つの値が設定できます。 デフォルト値は「VIP」で、これはトラフィックがロードバランサーのIPとポートを宛先として設定された状態でノードに配信されることを意味します。 「Proxy」に設定する場合、クラウドプロバイダーのロードバランサーがトラフィックを配信する方法に応じて、2つのケースがあります:

  • トラフィックがノードに配信されてからPodにDNATされる場合、宛先はノードのIPとNodePortに設定されます。
  • トラフィックがPodに直接配信される場合、宛先はPodのIPとポートに設定されます。

Service実装はこの情報を使用してトラフィックルーティングを調整できます。

内部ロードバランサー

混在環境では、同じ(仮想)ネットワークアドレスブロック内のServiceからトラフィックをルーティングする必要がある場合があります。

スプリットホライズンDNS環境では、エンドポイントへの外部トラフィックと内部トラフィックの両方をルーティングできるように、2つのServiceが必要です。

内部ロードバランサーを設定するには、使用しているクラウドサービスプロバイダーに応じて、次のいずれかのアノテーションをServiceに追加します:

いずれかのタブを選択してください。

metadata:
  name: my-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"

metadata:
  name: my-service
  annotations:
    service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: "true"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"

metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx

metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-internal: true

type: ExternalName

ExternalNameタイプのServiceは、my-servicecassandraのような典型的なセレクターではなく、ServiceをDNS名にマッピングします。 これらのServiceはspec.externalNameパラメーターで指定します。

たとえば、このServiceの定義は、prod Namespace内のmy-service Serviceをmy.database.example.comにマッピングします:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

備考:

type: ExternalNameのServiceはIPv4アドレスの文字列を受け入れますが、文字列をIPアドレスとしてではなく、数字で構成されたDNS名として扱います(ただし、インターネットでは、DNSにそのような名前を設定することは許可されていません)。 IPv4アドレスに似た外部名を持つServiceはDNSサーバーによって解決されません。

ServiceをIPアドレスに直接マッピングしたい場合は、ヘッドレスServiceの使用を検討してください。

ホストmy-service.prod.svc.cluster.localを検索すると、クラスターのDNSサービスはmy.database.example.comを持つCNAMEレコードを返します。 my-serviceへのアクセスは他のServiceと同じように機能しますが、重要な違いは、リダイレクトがプロキシや転送を介してではなく、DNSレベルで発生することです。 後でデータベースをクラスター内に移動することにした場合、Podを起動し、適切なセレクターまたはエンドポイントを追加し、Serviceのtypeを変更できます。

注意:

ExternalNameを使用すると、HTTPやHTTPSを含む一部の一般的なプロトコルで問題が発生する可能性があります。 ExternalNameを使用する場合、クラスター内のクライアントが使用するホスト名は、ExternalNameが参照する名前とは異なります。

ホスト名を使用するプロトコルの場合、この違いによりエラーや予期しないレスポンスが発生する可能性があります。 HTTPリクエストには、オリジンサーバーが認識しないHost:ヘッダーが含まれることになります。 また、TLSサーバーは、クライアントが接続したホスト名と一致する証明書を提供できなくなります。

ヘッドレスService

時には、負荷分散や単一のService IPが不要な場合があります。 この場合、ClusterIPアドレス(.spec.clusterIP)に明示的に"None"を指定することで、いわゆる ヘッドレスService を作成できます。

ヘッドレスServiceを使用すると、Kubernetesの実装に縛られることなく、他のサービスディスカバリ機構と連携できます。

ヘッドレスServiceの場合、ClusterIPは割り当てられず、kube-proxyはこれらのServiceを処理せず、プラットフォームによる負荷分散やプロキシは行われません。

ヘッドレスServiceにより、クライアントは好みのPodに直接接続できます。 ヘッドレスServiceは仮想IPアドレスとプロキシを使用してルートやパケット転送を設定しません。 その代わりに、ヘッドレスServiceはクラスターのDNSサービスを通じて提供される内部DNSレコードを介して、個々のPodのエンドポイントIPアドレスを報告します。 ヘッドレスServiceを定義するには、.spec.typeをClusterIPに設定し(typeのデフォルトでもあります)、さらに.spec.clusterIPをNoneに設定します。

文字列値のNoneは特殊なケースであり、.spec.clusterIPフィールドを未設定のままにすることとは異なります。

DNSが自動的に設定される方法は、Serviceにセレクターが定義されているかどうかによって異なります:

セレクターありの場合

セレクターを定義するヘッドレスServiceの場合、エンドポイントコントローラーはKubernetes APIでEndpointSliceを作成し、ServiceのPodを直接指すAまたはAAAAレコード(IPv4またはIPv6アドレス)を返すようにDNS設定を変更します。

セレクターなしの場合

セレクターを定義しないヘッドレスServiceの場合、コントロールプレーンはEndpointSliceオブジェクトを作成しません。 ただし、DNSシステムは次のいずれかを検索して設定します:

  • type: ExternalName ServiceのDNS CNAMEレコード
  • ExternalName以外のすべてのServiceタイプについて、Serviceの準備完了エンドポイントのすべてのIPアドレスに対するDNS A/AAAAレコード
    • IPv4エンドポイントの場合、DNSシステムはAレコードを作成します。
    • IPv6エンドポイントの場合、DNSシステムはAAAAレコードを作成します。

セレクターなしでヘッドレスServiceを定義する場合、porttargetPortと一致する必要があります。

Serviceの検出

クラスター内で実行されているクライアントに対して、Kubernetesは、環境変数とDNSという2つの主要なServiceの検出方法をサポートしています。

環境変数

Podがノード上で実行されると、kubeletはアクティブな各Serviceに対して環境変数のセットを追加します。 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT変数が追加されます。 ここで、Service名は大文字に変換され、ハイフンはアンダースコアに変換されます。

たとえば、TCPポートとして6379を公開し、ClusterIPアドレスとして10.0.0.11が割り当てられたService redis-primaryは、次の環境変数を生成します:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

備考:

PodがServiceにアクセスする必要があり、環境変数を使用してポートとClusterIPをクライアントPodに公開する場合、クライアントPodが作成される前にServiceを作成する必要があります。 そうしないと、クライアントPodに環境変数が設定されません。

DNSのみを使用してServiceのClusterIPを検出する場合は、この順序の問題を気にする必要はありません。

Kubernetesは、Docker Engineの「レガシーコンテナリンク」機能と互換性のある変数もサポートし、提供しています。 これが、Kubernetesにおいて、どのように実装されているかを確認するには、makeLinkVariablesを参照してください。

DNS

アドオンを使用して、KubernetesクラスターのDNSサービスを設定できます(そしてほぼ常に設定すべきです)。

CoreDNSなどのクラスター対応のDNSサーバーは、新しいServiceについてKubernetes APIを監視し、それぞれに対してDNSレコードのセットを作成します。 クラスター全体でDNSが有効になっている場合、すべてのPodはDNS名によってServiceを自動的に解決できるはずです。

たとえば、KubernetesのNamespace my-nsmy-serviceというServiceがある場合、コントロールプレーンとDNS Serviceが連携してmy-service.my-nsのDNSレコードを作成します。 my-ns Namespace内のPodは、my-serviceの名前解決を行うことでServiceを見つけることができるはずです(my-service.my-nsも機能します)。

他のNamespace内のPodは、名前をmy-service.my-nsとして修飾する必要があります。 これらの名前は、Serviceに割り当てられたClusterIPに解決されます。

Kubernetesは、名前付きポートに対するDNS SRV(Service)レコードもサポートしています。 my-service.my-ns ServiceにプロトコルがTCPに設定されたhttpという名前のポートがある場合、_http._tcp.my-service.my-nsに対してDNS SRVクエリを実行して、httpのポート番号とIPアドレスを検出できます。

Kubernetes DNSサーバーは、ExternalName Serviceにアクセスする唯一の方法です。 ExternalNameの解決に関する詳細情報は、ServiceとPodに対するDNSを参照してください。

仮想IPアドレッシング機構

仮想IPとServiceプロキシでは、Kubernetesが仮想IPアドレスを使用してServiceを公開するために提供する機構について詳しく説明しています。

トラフィックポリシー

.spec.internalTrafficPolicyおよび.spec.externalTrafficPolicyフィールドを設定して、Kubernetesが正常な("ready")バックエンドにトラフィックをルーティングする方法を制御できます。

詳細については、トラフィックポリシーを参照してください。

トラフィック分散制御

.spec.trafficDistributionフィールドは、Kubernetes Service内のトラフィックルーティングに影響を与える別の方法を提供します。 トラフィックポリシーが厳密なセマンティック保証に焦点を当てているのに対し、トラフィック分散では 優先設定(トポロジー的に近いエンドポイントへのルーティングなど)を表現できます。 これにより、パフォーマンス、コスト、または信頼性の最適化に役立ちます。 Kubernetes 1.35では、次のフィールド値がサポートされています:

PreferSameZone
クライアントと同じゾーンにあるエンドポイントへのトラフィックルーティングの優先設定を示します。
PreferSameNode
クライアントと同じノード上にあるエンドポイントへのトラフィックルーティングの優先設定を示します。
PreferClose(非推奨)
これはPreferSameZoneの旧称にあたるエイリアスであり、この名称が示す対象が明確ではないため推奨されません。

フィールドが設定されていない場合、実装はデフォルトのルーティング戦略を適用します。

詳細については、トラフィック分散を参照してください。

スティッキーセッション

特定のクライアントからの接続が毎回同じPodに渡されるようにしたい場合、クライアントのIPアドレスに基づいてセッションアフィニティを設定できます。 詳細については、セッションアフィニティを参照してください。

外部IP

もし、1つ以上のクラスターノードに転送する外部IPが複数ある場合、Kubernetes ServiceはそれらをexternalIPsで公開できます。 外部IP(宛先IPとして)とServiceに一致するポートでネットワークトラフィックがクラスターに到達すると、Kubernetesが設定したルールとルートにより、トラフィックがそのServiceのエンドポイントの1つにルーティングされることが保証されます。

Serviceを定義する際、任意のServiceタイプに対してexternalIPsを指定できます。 以下の例では、"my-service"という名前のServiceは、"198.51.100.32:80"(.spec.externalIPs[].spec.ports[].portから計算)でTCPを使用してクライアントからアクセスできます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 49152
  externalIPs:
    - 198.51.100.32

備考:

KubernetesはexternalIPsの割り当てを管理しません。 これらはクラスター管理者の責任です。

APIオブジェクト

ServiceはKubernetes REST APIにおいてトップレベルのリソースです。 詳細については、Service APIオブジェクトを参照してください。

次の項目

Serviceと、それがKubernetesにどのように適合するかについて、さらに学習してください:

  • Connecting Applications with Servicesのチュートリアルに従ってください。
  • Ingressを参照してください。 Ingressは、クラスター外部からクラスター内のServiceへのHTTPおよびHTTPSルートを公開します。
  • Gatewayを参照してください。 GatewayはKubernetesの拡張機能で、Ingressよりも柔軟性を提供します。

さらなる背景情報については、以下を参照してください:

2 - Ingress

FEATURE STATE: Kubernetes v1.19 [stable]

クラスター内のServiceに対する外部からのアクセス(主にHTTP)を管理するAPIオブジェクトです。

Ingressは負荷分散、SSL終端、名前ベースの仮想ホスティングの機能を提供します。

用語

簡単のために、このガイドでは次の用語を定義します。

  • ノード: Kubernetes内のワーカーマシンで、クラスターの一部です。
  • クラスター: Kubernetesによって管理されているコンテナ化されたアプリケーションを実行させるノードの集合です。この例や、多くのKubernetesによるデプロイでは、クラスター内のノードはインターネットに公開されていません。
  • エッジルーター: クラスターでファイアウォールのポリシーを強制するルーターです。クラウドプロバイダーが管理するゲートウェイや、物理的なハードウェアの一部である場合もあります。
  • クラスターネットワーク: 物理的または論理的な繋がりの集合で、Kubernetesのネットワークモデルによって、クラスター内でのコミュニケーションを司るものです。
  • Service: ラベルセレクターを使ったPodの集合を特定するKubernetes Serviceです。特に指定がない限り、Serviceはクラスターネットワーク内でのみ疎通可能な仮想IPを持つものとして扱われます。

Ingressとは何か

Ingressはクラスター外からクラスター内ServiceへのHTTPとHTTPSのルートを公開します。トラフィックのルーティングはIngressリソース上で定義されるルールによって制御されます。

全てのトラフィックを単一のServiceに送る単純なIngressの例を示します。

ingress-diagram

図. Ingress

IngressはServiceに対して、外部疎通できるURL、負荷分散トラフィック、SSL/TLS終端の機能や、名前ベースの仮想ホスティングを提供するように設定できます。Ingressコントローラーは通常はロードバランサーを使用してIngressの機能を実現しますが、エッジルーターや、追加のフロントエンドを構成してトラフィックの処理を支援することもできます。

Ingressは任意のポートやプロトコルを公開しません。HTTPやHTTPS以外のServiceをインターネットに公開する場合、Service.Type=NodePortService.Type=LoadBalancerのServiceタイプを一般的には使用します。

Ingressを使用する上での前提条件

Ingressを提供するためにはIngressコントローラーが必要です。Ingressリソースを作成するのみでは何の効果もありません。

ingress-nginxのようなIngressコントローラーのデプロイが必要な場合があります。いくつかのIngressコントローラーの中から選択してください。

理想的には、全てのIngressコントローラーはリファレンスの仕様を満たすはずです。しかし実際には、各Ingressコントローラーは微妙に異なる動作をします。

備考:

Ingressコントローラーのドキュメントを確認して、選択する際の注意点について理解してください。

Ingressリソース

Ingressリソースの最小構成の例は以下のとおりです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

IngressにはapiVersionkindmetadataspecフィールドが必要です。Ingressオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。設定ファイルに関する一般的な情報は、アプリケーションのデプロイコンテナの設定リソースの管理を参照してください。Ingressでは、Ingressコントローラーに依存しているいくつかのオプションの設定をするためにアノテーションを一般的に使用します。例としては、rewrite-targetアノテーションなどがあります。Ingressコントローラーの種類が異なれば、サポートするアノテーションも異なります。サポートされているアノテーションについて学ぶためには、使用するIngressコントローラーのドキュメントを確認してください。

Ingress Specは、ロードバランサーやプロキシサーバーを設定するために必要な全ての情報を持っています。最も重要なものとして、外部からくる全てのリクエストに対して一致したルールのリストを含みます。IngressリソースはHTTP(S)トラフィックに対してのルールのみサポートしています。

ingressClassNameが省略された場合、デフォルトのIngressClassを定義する必要があります。

デフォルトのIngressClassを定義しなくても動作するIngressコントローラーがいくつかあります。例えば、Ingress-NGINXコントローラーはフラグ --watch-ingress-without-classで設定できます。ただし、下記のようにデフォルトのIngressClassを指定することを推奨します

Ingressのルール

各HTTPルールは以下の情報を含みます。

  • オプションで設定可能なホスト名。上記のリソースの例では、ホスト名が指定されていないので、そのルールは指定されたIPアドレスを経由する全てのインバウンドHTTPトラフィックに適用されます。ホスト名が指定されていると(例: foo.bar.com)、そのルールは指定されたホストに対して適用されます。
  • パスのリスト(例: /testpath)。各パスにはservice.nameservice.port.nameまたはservice.port.numberで定義されるバックエンドが関連づけられます。ロードバランサーがトラフィックを関連づけられたServiceに転送するために、外部からくるリクエストのホスト名とパスが条件と一致させる必要があります。
  • バックエンドはServiceドキュメントに書かれているようなService名とポート名の組み合わせ、またはCRDによるカスタムリソースバックエンドです。Ingressで設定されたホスト名とパスのルールに一致するHTTP(とHTTPS)のリクエストは、リスト内のバックエンドに対して送信されます。

Ingressコントローラーでは、defaultBackendが設定されていることがあります。これはSpec内で指定されているパスに一致しないようなリクエストのためのバックエンドです。

デフォルトのバックエンド

ルールが設定されていないIngressは、全てのトラフィックを単一のデフォルトのバックエンドに転送します。.spec.defaultBackendはその場合にリクエストを処理するバックエンドになります。defaultBackendは、Ingressコントローラーのオプション設定であり、Ingressリソースでは指定されていません。.spec.rulesを設定しない場合、.spec.defaultBackendの設定は必須です。defaultBackendが設定されていない場合、どのルールにもマッチしないリクエストの処理は、Ingressコントローラーに任されます(このケースをどう処理するかは、お使いのIngressコントローラーのドキュメントを参照してください)。

HTTPリクエストがIngressオブジェクトのホスト名とパスの条件に1つも一致しない時、そのトラフィックはデフォルトのバックエンドに転送されます。

リソースバックエンド

ResourceバックエンドはIngressオブジェクトと同じnamespaceにある他のKubernetesリソースを指すObjectRefです。 ResourceはServiceの設定とは排他であるため、両方を指定するとバリデーションに失敗します。 Resourceバックエンドの一般的な用途は、静的なアセットが入ったオブジェクトストレージバックエンドにデータを導入することです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

上記のIngressを作成した後に、次のコマンドで参照することができます。

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Namespace:        default
Address:
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  <none>
Events:       <none>

パスのタイプ

Ingressのそれぞれのパスは対応するパスのタイプを持ちます。pathTypeが明示的に指定されていないパスはバリデーションに通りません。サポートされているパスのタイプは3種類あります。

  • ImplementationSpecific(実装に特有): このパスタイプでは、パスとの一致はIngressClassに依存します。Ingressの実装はこれを独立したpathTypeと扱うことも、PrefixExactと同一のパスタイプと扱うこともできます。

  • Exact: 大文字小文字を区別して完全に一致するURLパスと一致します。

  • Prefix: /で分割されたURLと前方一致で一致します。大文字小文字は区別され、パスの要素対要素で比較されます。パス要素は/で分割されたパスの中のラベルのリストを参照します。リクエストがパス p に一致するのは、Ingressのパス p がリクエストパス p と要素単位で前方一致する場合です。

    備考:

    パスの最後の要素がリクエストパスの最後の要素の部分文字列である場合、これは一致しません(例えば、/foo/bar/foo/bar/bazと一致しますが、/foo/barbazとは一致しません)。

タイプ パス リクエストパス 一致するか
Prefix / (全てのパス) はい
Exact /foo /foo はい
Exact /foo /bar いいえ
Exact /foo /foo/ いいえ
Exact /foo/ /foo いいえ
Prefix /foo /foo, /foo/ はい
Prefix /foo/ /foo, /foo/ はい
Prefix /aaa/bb /aaa/bbb いいえ
Prefix /aaa/bbb /aaa/bbb はい
Prefix /aaa/bbb/ /aaa/bbb はい、末尾のスラッシュは無視
Prefix /aaa/bbb /aaa/bbb/ はい、末尾のスラッシュと一致
Prefix /aaa/bbb /aaa/bbb/ccc はい、パスの一部と一致
Prefix /aaa/bbb /aaa/bbbxyz いいえ、接頭辞と一致しない
Prefix /, /aaa /aaa/ccc はい、接頭辞/aaaと一致
Prefix /, /aaa, /aaa/bbb /aaa/bbb はい、接頭辞/aaa/bbbと一致
Prefix /, /aaa, /aaa/bbb /ccc はい、接頭辞/と一致
Prefix /aaa /ccc いいえ、デフォルトバックエンドを使用
Mixed /foo (Prefix), /foo (Exact) /foo はい、Exactが優先

複数のパスとの一致

リクエストがIngressの複数のパスと一致することがあります。そのような場合は、最も長くパスが一致したものが優先されます。2つのパスが同等に一致した場合は、完全一致が前方一致よりも優先されます。

ホスト名のワイルドカード

ホストは正確に一致する(例えばfoo.bar.com)かワイルドカード(例えば*.foo.com)とすることができます。 正確な一致ではHTTPヘッダーのhosthostフィールドと一致することが必要です。 ワイルドカードによる一致では、HTTPヘッダーのhostがワイルドカードルールに沿って後方一致することが必要です。

Host Hostヘッダー 一致するか
*.foo.com bar.foo.com 共通の接尾辞により一致
*.foo.com baz.bar.foo.com 一致しない。ワイルドカードは単一のDNSラベルのみを対象とする
*.foo.com foo.com 一致しない。ワイルドカードは単一のDNSラベルのみを対象とする
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

Ingress Class

Ingressは異なったコントローラーで実装されうるため、しばしば異なった設定を必要とします。 各Ingressはクラス、つまりIngressClassリソースへの参照を指定する必要があります。IngressClassリソースには、このクラスを実装するコントローラーの名前などの追加設定が含まれています。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

IngressClassの.spec.parametersフィールドを使って、そのIngressClassに関連する設定を持っている別のリソースを参照することができます。

使用するパラメーターの種類は、IngressClassの.spec.controllerフィールドで指定したIngressコントローラーに依存します。

IngressClassスコープ

Ingressコントローラーによっては、クラスター全体で設定したパラメーターを使用できる場合もあれば、1つのNamespaceに対してのみ設定したパラメーターを使用できる場合もあります。

IngressClassパラメーターのデフォルトのスコープは、クラスター全体です。

.spec.parametersフィールドを設定して.spec.parameters.scopeフィールドを設定しなかった場合、または.spec.parameters.scopeClusterに設定した場合、IngressClassはクラスタースコープのリソースを参照します。 パラーメーターのkind(およびapiGroup)はクラスタースコープのAPI(カスタムリソースの場合もあり)を指し、パラメーターのnameはそのAPIの特定のクラスタースコープのリソースを特定します。

例えば:

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-1
spec:
  controller: example.com/ingress-controller
  parameters:
    # このIngressClassのパラメーターは「external-config-1」という名前の
    # ClusterIngressParameter(APIグループk8s.example.net)で指定されています。この定義は、Kubernetesに
    # クラスタースコープのパラメーターリソースを探すように指示しています。
    scope: Cluster
    apiGroup: k8s.example.net
    kind: ClusterIngressParameter
    name: external-config-1

FEATURE STATE: Kubernetes v1.23 [stable]

.spec.parametersフィールドを設定して.spec.parameters.scopeフィールドをNamespaceに設定した場合、IngressClassはNamespaceスコープのリソースを参照します。また.spec.parameters内のnamespaceフィールドには、使用するパラメーターが含まれているNamespaceを設定する必要があります。

パラメーターのkind(およびapiGroup)はNamespaceスコープのAPI(例えば:ConfigMap)を指し、パラメーターのnamenamespaceで指定したNamespace内の特定のリソースを特定します。

Namespaceスコープのパラメーターはクラスターオペレーターがワークロードに使用される設定(例えば:ロードバランサー設定、APIゲートウェイ定義)に対する制御を委譲するのに役立ちます。クラスタースコープパラメーターを使用した場合は以下のいずれかになります:

  • クラスターオペレーターチームは、新しい設定変更が適用されるたびに、別のチームの変更内容を承認する必要があります。
  • クラスターオペレーターは、アプリケーションチームがクラスタースコープのパラメーターリソースに変更を加えることができるように、RBACのRoleやRoleBindingといった、特定のアクセス制御を定義する必要があります。

IngressClass API自体は常にクラスタースコープです。

以下はNamespaceスコープのパラメーターを参照しているIngressClassの例です:

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-2
spec:
  controller: example.com/ingress-controller
  parameters:
    # このIngressClassのパラメーターは「external-config」という名前の
    # IngressParameter(APIグループk8s.example.com)で指定されています。
    # このリソースは「external-configuration」というNamespaceにあります。
    scope: Namespace
    apiGroup: k8s.example.com
    kind: IngressParameter
    namespace: external-configuration
    name: external-config

非推奨のアノテーション

Kubernetes 1.18でIngressClassリソースとingressClassNameフィールドが追加される前は、Ingressの種別はIngressのkubernetes.io/ingress.classアノテーションにより指定されていました。 このアノテーションは正式に定義されたことはありませんが、Ingressコントローラーに広くサポートされています。

Ingressの新しいingressClassNameフィールドはこのアノテーションを置き換えるものですが、完全に等価ではありません。 アノテーションは一般にIngressを実装すべきIngressのコントローラーの名称を示していましたが、フィールドはIngressClassリソースへの参照であり、Ingressのコントローラーの名称を含む追加のIngressの設定情報を含んでいます。

デフォルトのIngressClass

特定のIngressClassをクラスターのデフォルトとしてマークすることができます。 IngressClassリソースのingressclass.kubernetes.io/is-default-classアノテーションをtrueに設定すると、ingressClassNameフィールドが指定されないIngressにはこのデフォルトIngressClassが割り当てられるようになります。

注意:

複数のIngressClassをクラスターのデフォルトに設定すると、アドミッションコントローラーはingressClassNameが指定されていない新しいIngressオブジェクトを作成できないようにします。クラスターのデフォルトIngressClassを1つ以下にすることで、これを解消することができます。

Ingressコントローラーの中には、デフォルトのIngressClassを定義しなくても動作するものがあります。 例えば、Ingress-NGINXコントローラーはフラグ --watch-ingress-without-classで設定することができます。ただし、デフォルトIngressClassを指定することを推奨します:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Ingressのタイプ

単一ServiceのIngress

Kubernetesには、単一のServiceを公開できるようにする既存の概念があります(Ingressの代替案を参照してください)。ルールなしでデフォルトのバックエンド を指定することにより、Ingressでこれを実現することもできます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

kubectl apply -fを実行してIngressを作成すると、その作成したIngressの状態を確認することができます。

kubectl get ingress test-ingress
NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

203.0.113.123はIngressコントローラーによって割り当てられたIPで、作成したIngressを利用するためのものです。

備考:

IngressコントローラーとロードバランサーがIPアドレス割り当てるのに1、2分ほどかかります。この間、ADDRESSの情報は<pending>となっているのを確認できます。

リクエストのシンプルなルーティング

ファンアウト設定では単一のIPアドレスのトラフィックを、リクエストされたHTTP URIに基づいて1つ以上のServiceに転送します。Ingressによってロードバランサーの数を少なくすることができます。例えば、以下のように設定します。

ingress-fanout-diagram

図. Ingressファンアウト

Ingressを以下のように設定します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

Ingressをkubectl apply -fによって作成したとき:

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

IngressコントローラーはService(service1service2)が存在する限り、Ingressの条件を満たす実装固有のロードバランサーを構築します。 構築が完了すると、ADDRESSフィールドでロードバランサーのアドレスを確認できます。

備考:

使用するIngressコントローラーに依存しますが、default-http-backendServiceの作成が必要な場合があります。

名前ベースのバーチャルホスティング

名前ベースのバーチャルホストは、HTTPトラフィックを同一のIPアドレスの複数のホスト名に転送することをサポートしています。

ingress-namebase-diagram

図. Ingress名前ベースのバーチャルホスティング

以下のIngress設定は、ロードバランサーに対して、Hostヘッダーに基づいてリクエストを転送するように指示するものです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

rules項目でのホストの設定がないIngressを作成すると、IngressコントローラーのIPアドレスに対するwebトラフィックは、要求されている名前ベースのバーチャルホストなしにマッチさせることができます。

例えば、以下のIngressはfirst.bar.comに対するトラフィックをservice1へ、second.foo.comに対するトラフィックをservice2へ、リクエストにおいてホスト名が指定されていない(リクエストヘッダーがないことを意味します)トラフィックはservice3へ転送します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: second.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service3
            port:
              number: 80

TLS

TLSの秘密鍵と証明書を含んだSecretを指定することにより、Ingressをセキュアにできます。Ingressは単一のTLSポートである443番ポートのみサポートし、IngressでTLS終端を行うことを想定しています。IngressからServiceやPodへのトラフィックは平文です。IngressのTLS設定のセクションで異なるホストを指定すると、それらのホストはSNI TLSエクステンション(IngressコントローラーがSNIをサポートしている場合)を介して指定されたホスト名に対し、同じポート上で多重化されます。TLSのSecretはtls.crttls.keyというキーを含む必要があり、TLSを使用するための証明書と秘密鍵を含む値となります。以下がその例です。

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls

IngressでこのSecretを参照すると、クライアントとロードバランサー間の通信にTLSを使用するようIngressコントローラーに指示することになります。作成したTLS Secretは、https-example.foo.comの完全修飾ドメイン名(FQDN)とも呼ばれる共通名(CN)を含む証明書から作成したものであることを確認する必要があります。

備考:

デフォルトルールではTLSが機能しない可能性があることに注意してください。 これは取り得る全てのサブドメインに対する証明書を発行する必要があるからです。 そのため、tlsセクションのhostsrulesセクションのhostと明示的に一致する必要があります。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

備考:

サポートされるTLSの機能はIngressコントローラーによって違いがあります。利用する環境でTLSがどのように動作するかを理解するためには、nginxや、GCE、または他のプラットフォーム固有のIngressコントローラーのドキュメントを確認してください。

負荷分散

Ingressコントローラーは、負荷分散アルゴリズムやバックエンドの重みスキームなど、すべてのIngressに適用されるいくつかの負荷分散ポリシーの設定とともにブートストラップされます。発展した負荷分散のコンセプト(例: セッションの永続化、動的重み付けなど)はIngressによってサポートされていません。代わりに、それらの機能はService用のロードバランサーを介して利用できます。

ヘルスチェックの機能はIngressによって直接には公開されていませんが、Kubernetesにおいて、同等の機能を提供するReadiness Probeのようなコンセプトが存在することは注目に値します。コントローラーがどのようにヘルスチェックを行うかについては、コントローラーのドキュメントを参照してください(例えばnginx、またはGCE)。

Ingressの更新

リソースを編集することで、既存のIngressに対して新しいホストを追加することができます。

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

このコマンドを実行すると既存の設定をYAMLフォーマットで編集するエディターが表示されます。新しいホストを追加するには、リソースを修正してください。

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: service1
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: bar.baz.com
    http:
      paths:
      - backend:
          service:
            name: service2
            port:
              number: 80
        path: /foo
        pathType: Prefix
..

変更を保存した後、kubectlはAPIサーバー内のリソースを更新し、Ingressコントローラーに対してロードバランサーの再設定を指示します。

変更内容を確認してください。

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   service2:80 (10.8.0.91:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

修正されたIngressのYAMLファイルに対してkubectl replace -fを実行することで、同様の結果を得られます。

アベイラビリティーゾーンをまたいだ障害について

障害のあるドメインをまたいでトラフィックを分散する手法は、クラウドプロバイダーによって異なります。詳細に関して、Ingress コントローラーのドキュメントを参照してください。

Ingressの代替案

Ingressリソースを直接含まずにサービスを公開する方法は複数あります。

次の項目

3 - サービスとアプリケーションの接続

コンテナを接続するためのKubernetesモデル

継続的に実行され、複製されたアプリケーションの準備ができたので、ネットワーク上で公開することが可能になります。 Kubernetesのネットワークのアプローチについて説明する前に、Dockerの「通常の」ネットワーク手法と比較することが重要です。

デフォルトでは、Dockerはホストプライベートネットワーキングを使用するため、コンテナは同じマシン上にある場合にのみ他のコンテナと通信できます。 Dockerコンテナがノード間で通信するには、マシンのIPアドレスにポートを割り当ててから、コンテナに転送またはプロキシする必要があります。 これは明らかに、コンテナが使用するポートを非常に慎重に調整するか、ポートを動的に割り当てる必要があることを意味します。

コンテナを提供する複数の開発者やチーム間でポートの割り当てを調整することは、規模的に大変困難であり、ユーザが制御できないクラスターレベルの問題にさらされます。 Kubernetesでは、どのホストで稼働するかに関わらず、Podが他のPodと通信できると想定しています。 すべてのPodに独自のクラスタープライベートIPアドレスを付与するため、Pod間のリンクを明示的に作成したり、コンテナポートをホストポートにマップしたりする必要はありません。 これは、Pod内のコンテナがすべてlocalhostの相互のポートに到達でき、クラスター内のすべてのPodがNATなしで相互に認識できることを意味します。 このドキュメントの残りの部分では、このようなネットワークモデルで信頼できるサービスを実行する方法について詳しく説明します。

このガイドでは、シンプルなnginxサーバーを使用して概念実証を示します。

Podをクラスターに公開する

前の例でネットワークモデルを紹介しましたが、再度ネットワークの観点に焦点を当てましょう。 nginx Podを作成し、コンテナポートの仕様を指定していることに注意してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

これにより、クラスター内のどのノードからでもアクセスできるようになります。 Podが実行されているノードを確認します:

kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd

PodのIPを確認します:

kubectl get pods -l run=my-nginx -o yaml | grep podIP
    podIP: 10.244.3.4
    podIP: 10.244.2.5

クラスター内の任意のノードにSSH接続し、両方のIPにcurl接続できるはずです。 コンテナはノードでポート80を使用していないことに注意してください。 また、Podにトラフィックをルーティングする特別なNATルールもありません。 つまり、同じcontainerPortを使用して同じノードで複数のnginx Podを実行し、IPを使用してクラスター内の他のPodやノードからそれらにアクセスできます。 Dockerと同様に、ポートは引き続きホストノードのインターフェースに公開できますが、ネットワークモデルにより、この必要性は根本的に減少します。

興味があれば、これをどのように達成するかについて詳しく読むことができます。

Serviceを作成する

そのため、フラットでクラスター全体のアドレス空間でnginxを実行するPodがあります。 理論的には、これらのPodと直接通信することができますが、ノードが停止するとどうなりますか? Podはそれで死に、Deploymentは異なるIPを持つ新しいものを作成します。 これは、Serviceが解決する問題です。

Kubernetes Serviceは、クラスター内のどこかで実行されるPodの論理セットを定義する抽象化であり、すべて同じ機能を提供します。 作成されると、各Serviceには一意のIPアドレス(clusterIPとも呼ばれます)が割り当てられます。 このアドレスはServiceの有効期間に関連付けられており、Serviceが動作している間は変更されません。 Podは、Serviceと通信するように構成でき、Serviceへの通信は、ServiceのメンバーであるPodに自動的に負荷分散されることを認識できます。

2つのnginxレプリカのサービスをkubectl exposeで作成できます:

kubectl expose deployment/my-nginx
service/my-nginx exposed

これは次のyamlをkubectl apply -fすることと同等です:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

この仕様は、run:my-nginxラベルを持つ任意のPodのTCPポート80をターゲットとするサービスを作成し、抽象化されたサービスポートでPodを公開します(targetPort:はコンテナがトラフィックを受信するポート、port:は抽象化されたServiceのポートであり、他のPodがServiceへのアクセスに使用する任意のポートにすることができます)。 サービス定義でサポートされているフィールドのリストはService APIオブジェクトを参照してください。

Serviceを確認します:

kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

前述のように、ServiceはPodのグループによってサポートされています。 これらのPodはエンドポイントを通じて公開されます。 Serviceのセレクターは継続的に評価され、結果はmy-nginxという名前のEndpointsオブジェクトにPOSTされます。 Podが終了すると、エンドポイントから自動的に削除され、Serviceのセレクターに一致する新しいPodが自動的にエンドポイントに追加されます。 エンドポイントを確認し、IPが最初のステップで作成されたPodと同じであることを確認します:

kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP Family Policy:    SingleStack
IP Families:         IPv4
IP:                  10.0.162.149
IPs:                 10.0.162.149
Port:                <unset> 80/TCP
TargetPort:          80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get ep my-nginx
NAME       ENDPOINTS                     AGE
my-nginx   10.244.2.5:80,10.244.3.4:80   1m

クラスター内の任意のノードから、<CLUSTER-IP>:<PORT>でnginx Serviceにcurl接続できるようになりました。 Service IPは完全に仮想的なもので、ホスト側のネットワークには接続できないことに注意してください。 この仕組みに興味がある場合は、サービスプロキシの詳細をお読みください。

Serviceにアクセスする

Kubernetesは、環境変数とDNSの2つの主要なService検索モードをサポートしています。 前者はそのまま使用でき、後者はCoreDNSクラスターアドオンを必要とします。

備考:

サービス環境変数が望ましくない場合(予想されるプログラム変数と衝突する可能性がある、処理する変数が多すぎる、DNSのみを使用するなど)、Pod仕様enableServiceLinksフラグをfalseに設定することでこのモードを無効にできます。

環境変数

ノードでPodが実行されると、kubeletはアクティブな各サービスの環境変数のセットを追加します。 これにより、順序付けの問題が発生します。 理由を確認するには、実行中のnginx Podの環境を調べます(Pod名は環境によって異なります):

kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

サービスに言及がないことに注意してください。これは、サービスの前にレプリカを作成したためです。 これのもう1つの欠点は、スケジューラーが両方のPodを同じマシンに配置し、サービスが停止した場合にサービス全体がダウンする可能性があることです。 2つのPodを強制終了し、Deploymentがそれらを再作成するのを待つことで、これを正しい方法で実行できます。 今回は、サービスはレプリカの「前」に存在します。 これにより、スケジューラーレベルのサービスがPodに広がり(すべてのノードの容量が等しい場合)、適切な環境変数が提供されます:

kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;

kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m

Podは強制終了されて再作成されるため、異なる名前が付いていることに気付くでしょう。

kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

DNS

Kubernetesは、DNS名を他のServiceに自動的に割り当てるDNSクラスターアドオンサービスを提供します。 クラスターで実行されているかどうかを確認できます:

kubectl get services kube-dns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m

このセクションの残りの部分は、寿命の長いIP(my-nginx)を持つServiceと、そのIPに名前を割り当てたDNSサーバーがあることを前提にしています。ここではCoreDNSクラスターアドオン(アプリケーション名: kube-dns)を使用しているため、標準的なメソッド(gethostbyname()など) を使用してクラスター内の任意のPodからServiceに通信できます。CoreDNSが起動していない場合、CoreDNS READMEまたはInstalling CoreDNSを参照し、有効にする事ができます。curlアプリケーションを実行して、これをテストしてみましょう。

kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt

次に、Enterキーを押してnslookup my-nginxを実行します:

[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      my-nginx
Address 1: 10.0.162.149

Serviceを安全にする

これまでは、クラスター内からnginxサーバーにアクセスしただけでした。 サービスをインターネットに公開する前に、通信チャネルが安全であることを確認する必要があります。 これには、次のものが必要です:

  • https用の自己署名証明書(既にID証明書を持っている場合を除く)
  • 証明書を使用するように構成されたnginxサーバー
  • Podが証明書にアクセスできるようにするSecret

これらはすべてnginx httpsの例から取得できます。 これにはツールをインストールする必要があります。 これらをインストールしたくない場合は、後で手動の手順に従ってください。つまり:

make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           kubernetes.io/tls                     2         1m

configmapも作成します:

kubectl create configmap nginxconfigmap --from-file=default.conf
configmap/nginxconfigmap created
kubectl get configmaps
NAME             DATA   AGE
nginxconfigmap   1      114s

以下は、(Windows上など)makeの実行で問題が発生した場合に実行する手動の手順です:

# 公開秘密鍵ペアを作成します
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
# キーをbase64エンコードに変換します
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64

前のコマンドの出力を使用して、次のようにyamlファイルを作成します。 base64でエンコードされた値はすべて1行である必要があります。

apiVersion: "v1"
kind: "Secret"
metadata:
  name: "nginxsecret"
  namespace: "default"
  type: kubernetes.io/tls
data:
  # 注意: 以下の値はご自身で base64 エンコードした証明書と鍵に置き換えてください。
  nginx.crt: "REPLACE_WITH_BASE64_CERT"
  nginx.key: "REPLACE_WITH_BASE64_KEY"

ファイルを使用してSecretを作成します:

kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           kubernetes.io/tls                     2         1m

次に、nginxレプリカを変更して、シークレットの証明書とServiceを使用してhttpsサーバーを起動し、両方のポート(80と443)を公開します:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        - containerPort: 443
        - containerPort: 80
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume

nginx-secure-appマニフェストに関する注目すべき点:

  • 同じファイルにDeploymentとServiceの両方が含まれています。
  • nginxサーバーはポート80のHTTPトラフィックと443のHTTPSトラフィックを処理し、nginx Serviceは両方のポートを公開します。
  • 各コンテナは/etc/nginx/sslにマウントされたボリュームを介してキーにアクセスできます。 これは、nginxサーバーが起動する前にセットアップされます。
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml

この時点で、任意のノードからnginxサーバーに到達できます。

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

最後の手順でcurlに-kパラメーターを指定したことに注意してください。 これは、証明書の生成時にnginxを実行しているPodについて何も知らないためです。 CNameの不一致を無視するようcurlに指示する必要があります。 Serviceを作成することにより、証明書で使用されるCNameを、Service検索中にPodで使用される実際のDNS名にリンクしました。 これをPodからテストしましょう(簡単にするために同じシークレットを再利用しています。PodはServiceにアクセスするためにnginx.crtのみを必要とします):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-deployment
spec:
  selector:
    matchLabels:
      app: curlpod
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: curlpod
        command:
        - sh
        - -c
        - while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
...
<title>Welcome to nginx!</title>
...

Serviceを公開する

アプリケーションの一部では、Serviceを外部IPアドレスに公開したい場合があります。 Kubernetesは、NodePortとLoadBalancerの2つの方法をサポートしています。 前のセクションで作成したServiceはすでにNodePortを使用しているため、ノードにパブリックIPがあれば、nginx HTTPSレプリカはインターネット上のトラフィックを処理する準備ができています。

kubectl get svc my-nginx -o yaml | grep nodePort -C 5
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 31704
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: https
    nodePort: 32453
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>

クラウドロードバランサーを使用するようにサービスを再作成しましょう。 my-nginxサービスのTypeNodePortからLoadBalancerに変更するだけです:

kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   LoadBalancer   10.0.162.149     xx.xxx.xxx.xxx     8080:30163/TCP        21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>

EXTERNAL-IP列のIPアドレスは、パブリックインターネットで利用可能なものです。 CLUSTER-IPは、クラスター/プライベートクラウドネットワーク内でのみ使用できます。

AWSでは、type LoadBalancerはIPではなく(長い)ホスト名を使用するELBが作成されます。 実際、標準のkubectl get svcの出力に収まるには長すぎるので、それを確認するにはkubectl describe service my-nginxを実行する必要があります。 次のようなものが表示されます:

kubectl describe service my-nginx
...
LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...

次の項目

4 - Ingressコントローラー

クラスターでIngressを動作させるためには、ingress controller が動作している必要があります。 少なくとも1つのIngressコントローラーを選択し、クラスター内にセットアップされていることを確認する必要があります。 このページはデプロイ可能な一般的なIngressコントローラーをリストアップします。

Ingressリソースが動作するためには、クラスターでIngressコントローラーが実行されている必要があります。

kube-controller-managerバイナリの一部として実行される他のタイプのコントローラーとは異なり、Ingressコントローラーはクラスターで自動的に起動されません。このページを使用して、クラスターに最適なIngressコントローラーの実装を選択してください。

プロジェクトとしてのKubernetesは現在、AWSGCE、およびnginxのIngressコントローラーをサポート・保守しています。

追加のコントローラー

備考: このセクションでは、Kubernetesが必要とする機能を提供するサードパーティープロジェクトにリンクしています。これらのプロジェクトはアルファベット順に記載されていて、Kubernetesプロジェクトの作者は責任を持ちません。このリストにプロジェクトを追加するには、変更を提出する前にcontent guideをお読みください。詳細はこちら。

複数のIngressコントローラーの使用

Ingress Classを使用して、複数のIngressコントローラーをクラスターにデプロイすることができます。 Ingress Classリソースの.metadata.nameに注目してください。 Ingressを作成する際には、IngressオブジェクトでingressClassNameフィールドを指定するために、その名前が必要になります(IngressSpec v1 referenceを参照)。 ingressClassNameは古いannotation methodの代替品です。

Ingressに対してIngressClassを指定せず、クラスターにはデフォルトとして設定されたIngressClassが1つだけある場合、KubernetesはIngressにクラスターのデフォルトIngressClassを適用します。 IngressClassのingressclass.kubernetes.io/is-default-classアノテーションを文字列"true"に設定することで、デフォルトとしてIngressClassを設定します。

理想的には、すべてのIngressコントローラーはこの仕様を満たすべきですが、いくつかのIngressコントローラーはわずかに異なる動作をします。

備考:

Ingressコントローラーのドキュメントを確認して、選択する際の注意点を理解してください。

次の項目

5 - Gateway API

Gateway APIは動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類のファミリーです。

拡張可能でロール指向な、プロトコルを意識した設定メカニズムを使用して、ネットワークサービスを利用可能にします。 Gateway APIは、動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類を含むアドオンです。

デザイン原則

Gateway APIのデザインとアーキテクチャは次の原則から成ります:

  • ロール指向: Gateway APIの種類は、Kubernetesのサービスネットワークの管理に対して責任を持つ組織のロールをモデルとしています:
    • インフラストラクチャプロバイダー: 複数の独立したクラスターをクラウドプロバイダーなどの複数のテナントに提供できるよう、インフラストラクチャを管理します。
    • クラスターオペレーター: クラスターを管理し、通常はポリシー、ネットワークアクセス、アプリケーションのパーミッションなどに関わります。
    • アプリケーション開発者: クラスター上で実行されるアプリケーションを管理し、通常はアプリケーションレベルの設定やServiceの構成に関わります。
  • ポータビリティ: Gateway APIの仕様はカスタムリソースとして定義され、多くの実装によってサポートされています。
  • 豊富な機能: Gateway APIの種類は、ヘッダーベースのマッチング、トラフィックの重み付けといった、一般的なトラフィックルーティングのユースケースに対する機能をサポートしています。これは、Ingressではカスタムアノテーションを使用することでのみ実現可能でした。
  • 拡張可能: GatewayはカスタムリソースをAPIのさまざまなレイヤーでリンクさせることができます。これにより、APIの構造内の適切な場所で細かなカスタマイズが可能となります。

リソースモデル

Gateway APIには3つの安定版のAPIの種類があります:

  • GatewayClass: 共通の設定を持ち、クラスを実装するコントローラーによって管理されたゲートウェイの集合を定義します。

  • Gateway: クラウドロードバランサーなどのトラフィックを処理するインフラストラクチャのインスタンスを定義します。

  • HTTPRoute: Gatewayリスナーからバックエンドのネットワークエンドポイントへのトラフィックのマッピングに関する、HTTP固有のルールを定義します。 これらのエンドポイントは多くの場合、Serviceで表されます。

Gateway APIは、組織のロール指向の性質をサポートするために、相互に依存関係を持つ異なるAPIの種類によって構成されます。 Gatewayオブジェクトはただ一つのGatewayClassと関連づけられます。 GatewayClassは、このクラスのGatewayを管理する責任を持つGatewayコントローラーを記述します。 HTTPRouteのような1つ以上のルートの種類がGatewayに関連づけられます。 Gatewayは、そのリスナーにアタッチされる可能性のあるルートをフィルタリングすることができ、ルートとの双方向の信頼モデルを形成します。

次の図は、3つの安定版のGateway APIの種類の関係を示しています:

3つの安定版のGateway API種別の関係を示している図

GatewayClass

Gatewayは、通常異なる設定を持つ、異なるコントローラーによって実装されます。 Gatewayはクラスを実装したコントローラーの名前を含むGatewayClassを参照する必要があります。

最小のGatewayClassの例:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: example-class
spec:
  controllerName: example.com/gateway-controller

この例では、Gateway APIを実装したコントローラーは、example.com/gateway-controllerという名前のコントローラーを持つGatewayClassを管理するように構成されます。 このクラスのGatewayは実装のコントローラーによって管理されます。

このAPIの種類の完全な定義については、GatewayClassのリファレンスを参照してください。

Gateway

Gatewayはトラフィックを処理するインフラストラクチャのインスタンスを記述します。 これは、Serviceのようなバックエンドに対して、フィルタリング、分散、分割などのようなトラフィック処理のために使用されるネットワークエンドポイントを定義します。 例えばGatewayは、HTTPトラフィックを受け付けるために構成された、クラウドロードバランサーやクラスター内のプロキシサーバーを表す場合があります。

最小のGatewayリソースの例:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: example-gateway
spec:
  gatewayClassName: example-class
  listeners:
  - name: http
    protocol: HTTP
    port: 80

この例では、トラフィックを処理するインフラストラクチャのインスタンスは、80番ポートでHTTPトラフィックをリッスンするようにプログラムされています。 addressフィールドが指定されていないので、アドレスまたはホスト名はコントローラーの実装によってGatewayに割り当てられます。 このアドレスは、ルートで定義されたバックエンドのネットワークエンドポイントのトラフィックを処理するためのネットワークエンドポイントとして使用されます。

このAPIの種類の完全な定義については、Gatewayのリファレンスを参照してください。

HTTPRoute

HTTPRouteの種類は、Gatewayリスナーからバックエンドのネットワークエンドポイントに対するHTTPリクエストのルーティングの振る舞いを指定します。 Serviceバックエンドに対して、実装はバックエンドのネットワークエンドポイントをService IPまたはServiceの背後のエンドポイントとして表すことができます。 基盤となるGatewayの実装に適用される設定はHTTPRouteによって表されます。 例えば、新しいHTTPRouteを定義することにより、クラウドロードバランサーやクラスター内のプロキシサーバーの追加のトラフィックルートを構成する場合があります。

最小のHTTPRouteの例:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: example-httproute
spec:
  parentRefs:
  - name: example-gateway
  hostnames:
  - "www.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /login
    backendRefs:
    - name: example-svc
      port: 8080

この例では、Host:ヘッダーにwww.example.comが設定され、リクエストパスに/loginが指定されたHTTPトラフィックが、example-gatewayという名前のGatewayから、8080番ポート上のexample-svcという名前のServiceにルーティングされます。

このAPIの種類の完全な定義については、HTTPRouteのリファレンスを参照してください。

リクエストフロー

以下は、GatewayとHTTPRouteを使用してHTTPトラフィックをServiceにルーティングする簡単な例です:

GatewayとHTTPRouteを使用してServiceにHTTPトラフィックをルーティングする例の図

この例では、リバースプロキシとして実装されたGatewayに対するリクエストフローは次のようになります:

  1. クライアントはURL http://www.example.comに対するHTTPリクエストの準備を開始します。
  2. クライアントのDNSリゾルバは宛先の名前をクエリし、Gatewayに関連づけられた1つ以上のIPアドレスとのマッピングを学習します。
  3. クライアントはGatewayのIPアドレスにリクエストを送信します。リバースプロキシはHTTPリクエストを受信し、Host:ヘッダーを使用してGatewayとそれに関連づけられたHTTPRouteから導かれた構成にマッチさせます。
  4. オプションで、リバースプロキシはHTTPRouteのマッチングルールに基づいて、リクエストヘッダーもしくはパスのマッチングを実行することができます。
  5. オプションで、リバースプロキシはリクエストを変更することができます。例えば、HTTPRouteのフィルタールールに従ってヘッダーを追加または削除します。
  6. 最後に、リバースプロキシはリクエストを1つ以上のバックエンドにフォワードします。

適合性

Gateway APIは幅広い機能をカバーし、広く実装されています。 この組み合わせは、APIがどこで使われても一貫した体験を提供することを保証するために、明確な適合性の定義とテストを必要とします。

リリースチャンネル、サポートレベル、そして適合テストの実行などの詳細を理解するためには、適合性のドキュメントを参照してください。

Ingressからの移行

Gateway APIはIngress APIの後継です。 しかし、Ingressは含まれていません。 このため、既存のIngressリソースからGateway APIリソースへの変換を1度だけ行う必要があります。

IngressリソースからGateway APIリソースへの移行の詳細に関するガイドは、Ingressの移行を参照してください。

次の項目

Gateway APIリソースをKubernetesでネイティブに実装する代わりに、幅広い実装によってサポートされたカスタムリソースとして仕様が定義されています。 Gateway API CRDをインストールするか、選んだ実装のインストール手順に従ってください。 実装をインストールした後、Getting Startedガイドを使用してGateway APIをすぐに使い始めることができます。

備考:

選択した実装のドキュメントを必ず確認し、注意点を理解するようにしてください。

すべてのGateway API種別の追加の詳細についてはAPI仕様を参照してください。

6 - EndpointSlice

FEATURE STATE: Kubernetes v1.17 [beta]

EndpointSliceは、Kubernetesクラスター内にあるネットワークエンドポイントを追跡するための単純な手段を提供します。EndpointSliceは、よりスケーラブルでより拡張可能な、Endpointの代わりとなるものです。

動機

Endpoint APIはKubernetes内のネットワークエンドポイントを追跡する単純で直観的な手段を提供してきました。 残念ながら、KubernetesクラスターやServiceが大規模になり、より多くのトラフィックを処理し、より多くのバックエンドPodに送信するようになるにしたがって、Endpoint APIの限界が明らかになってきました。 最も顕著な問題の1つに、ネットワークエンドポイントの数が大きくなったときのスケーリングの問題があります。

Serviceのすべてのネットワークエンドポイントが単一のEndpointリソースに格納されていたため、リソースのサイズが非常に大きくなる場合がありました。これがKubernetesのコンポーネント(特に、マスターコントロールプレーン)の性能に悪影響を与え、結果として、Endpointに変更があるたびに、大量のネットワークトラフィックと処理が発生するようになってしまいました。EndpointSliceは、この問題を緩和するとともに、トポロジカルルーティングなどの追加機能のための拡張可能なプラットフォームを提供します。

EndpointSliceリソース

Kubernetes内ではEndpointSliceにはネットワークエンドポイントの集合へのリファレンスが含まれます。 コントロールプレーンは、セレクターが指定されているKubernetes ServiceのEndpointSliceを自動的に作成します。 これらのEndpointSliceには、Serviceセレクターに一致するすべてのPodへのリファレンスが含まれています。 EndpointSliceは、プロトコル、ポート番号、およびサービス名の一意の組み合わせによってネットワークエンドポイントをグループ化します。 EndpointSliceオブジェクトの名前は有効なDNSサブドメイン名である必要があります。

一例として、以下にexampleというKubernetes Serviceに対するサンプルのEndpointSliceリソースを示します。

apiVersion: discovery.k8s.io/v1beta1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    topology:
      kubernetes.io/hostname: node-1
      topology.kubernetes.io/zone: us-west2-a

デフォルトでは、コントロールプレーンはEndpointSliceを作成・管理し、それぞれのエンドポイント数が100以下になるようにします。--max-endpoints-per-slicekube-controller-managerフラグを設定することで、最大1000個まで設定可能です。

EndpointSliceは内部トラフィックのルーティング方法に関して、kube-proxyに対する唯一のソース(source of truth)として振る舞うことができます。EndpointSliceを有効にすれば、非常に多数のエンドポイントを持つServiceに対して性能向上が得られるはずです。

アドレスの種類

EndpointSliceは次の3種類のアドレスをサポートします。

  • IPv4
  • IPv6
  • FQDN (Fully Qualified Domain Name、完全修飾ドメイン名)

トポロジー

EndpointSliceに属する各エンドポイントは、関連するトポロジーの情報を持つことができます。この情報は、エンドポイントの場所を示すために使われ、対応するNode、ゾーン、リージョンに関する情報が含まれます。 値が利用できる場合には、コントロールプレーンはEndpointSliceコントローラーに次のようなTopologyラベルを設定します。

  • kubernetes.io/hostname - このエンドポイントが存在するNodeの名前。
  • topology.kubernetes.io/zone - このエンドポイントが存在するゾーン。
  • topology.kubernetes.io/region - このエンドポイントが存在するリージョン。

これらのラベルの値はスライス内の各エンドポイントと関連するリソースから継承したものです。hostnameラベルは対応するPod上のNodeNameフィールドの値を表します。zoneとregionラベルは対応するNode上の同じ名前のラベルの値を表します。

管理

ほとんどの場合、コントロールプレーン(具体的には、EndpointSlice コントローラー)は、EndpointSliceオブジェクトを作成および管理します。EndpointSliceには、サービスメッシュの実装など、他のさまざまなユースケースがあり、他のエンティティまたはコントローラーがEndpointSliceの追加セットを管理する可能性があります。

複数のエンティティが互いに干渉することなくEndpointSliceを管理できるようにするために、KubernetesはEndpointSliceを管理するエンティティを示すendpointslice.kubernetes.io/managed-byというラベルを定義します。 EndpointSliceを管理するその他のエンティティも同様に、このラベルにユニークな値を設定する必要があります。

所有権

ほとんどのユースケースでは、EndpointSliceはエンドポイントスライスオブジェクトがエンドポイントを追跡するServiceによって所有されます。 これは、各EndpointSlice上のownerリファレンスとkubernetes.io/service-nameラベルによって示されます。これにより、Serviceに属するすべてのEndpointSliceを簡単に検索できるようになっています。

EndpointSliceのミラーリング

場合によっては、アプリケーションはカスタムEndpointリソースを作成します。これらのアプリケーションがEndpointリソースとEndpointSliceリソースの両方に同時に書き込む必要がないようにするために、クラスターのコントロールプレーンは、ほとんどのEndpointリソースを対応するEndpointSliceにミラーリングします。

コントロールプレーンは、次の場合を除いて、Endpointリソースをミラーリングします。

  • Endpointリソースのendpointslice.kubernetes.io/skip-mirrorラベルがtrueに設定されています。
  • Endpointリソースがcontrol-plane.alpha.kubernetes.io/leaderアノテーションを持っています。
  • 対応するServiceリソースが存在しません。
  • 対応するServiceリソースには、nil以外のセレクターがあります。

個々のEndpointリソースは、複数のEndpointSliceに変換される場合があります。これは、Endpointリソースに複数のサブセットがある場合、または複数のIPファミリ(IPv4およびIPv6)を持つエンドポイントが含まれている場合に発生します。サブセットごとに最大1000個のアドレスがEndpointSliceにミラーリングされます。

EndpointSliceの分散

それぞれのEndpointSliceにはポートの集合があり、リソース内のすべてのエンドポイントに適用されます。サービスが名前付きポートを使用した場合、Podが同じ名前のポートに対して、結果的に異なるターゲットポート番号が使用されて、異なるEndpointSliceが必要になる場合があります。これはサービスの部分集合がEndpointにグループ化される場合と同様です。

コントロールプレーンはEndpointSliceをできる限り充填しようとしますが、積極的にリバランスを行うことはありません。コントローラーのロジックは極めて単純で、以下のようになっています。

  1. 既存のEndpointSliceをイテレートし、もう必要のないエンドポイントを削除し、変更があったエンドポイントを更新する。
  2. 前のステップで変更されたEndpointSliceをイテレートし、追加する必要がある新しいエンドポイントで充填する。
  3. まだ追加するべき新しいエンドポイントが残っていた場合、これまで変更されなかったスライスに追加を試み、その後、新しいスライスを作成する。

ここで重要なのは、3番目のステップでEndpointSliceを完全に分散させることよりも、EndpointSliceの更新を制限することを優先していることです。たとえば、もし新しい追加するべきエンドポイントが10個あり、2つのEndpointSliceにそれぞれ5個の空きがあった場合、このアプローチでは2つの既存のEndpointSliceを充填する代わりに、新しいEndpointSliceが作られます。言い換えれば、1つのEndpointSliceを作成する方が複数のEndpointSliceを更新するよりも好ましいということです。

各Node上で実行されているkube-proxyはEndpointSliceを監視しており、EndpointSliceに加えられた変更はクラスター内のすべてのNodeに送信されるため、比較的コストの高い処理になります。先ほどのアプローチは、たとえ複数のEndpointSliceが充填されない結果となるとしても、すべてのNodeへ送信しなければならない変更の数を抑制することを目的としています。

現実的には、こうしたあまり理想的ではない分散が発生することは稀です。EndpointSliceコントローラーによって処理されるほとんどの変更は、既存のEndpointSliceに収まるほど十分小さくなるためです。そうでなかったとしても、すぐに新しいEndpointSliceが必要になる可能性が高いです。また、Deploymentのローリングアップデートが行われれば、自然な再充填が行われます。Podとそれに対応するエンドポイントがすべて置換されるためです。

エンドポイントの重複

EndpointSliceの変更の性質上、エンドポイントは同時に複数のEndpointSliceで表される場合があります。 これは、さまざまなEndpointSliceオブジェクトへの変更が、さまざまな時間にKubernetesクライアントのウォッチ/キャッシュに到達する可能性があるために自然に発生します。 EndpointSliceを使用する実装では、エンドポイントを複数のスライスに表示できる必要があります。 エンドポイント重複排除を実行する方法のリファレンス実装は、kube-proxyEndpointSliceCache実装にあります。

次の項目

7 - ネットワークポリシー

IPアドレスまたはポートのレベル(OSI参照モデルのレイヤー3または4)でトラフィックフローを制御したい場合、クラスター内の特定のアプリケーションにKubernetesのネットワークポリシーを使用することを検討してください。ネットワークポリシーはアプリケーション中心の構造であり、Podがネットワークを介して多様な「エンティティ」(「Endpoint」や「Service」のようなKubernetesに含まれる特定の意味を持つ共通の用語との重複を避けるため、ここではエンティティという単語を使用します。)と通信する方法を指定できます。

Podが通信できるエンティティは以下の3つの識別子の組み合わせによって識別されます。

  1. 許可されている他のPod(例外: Podはそれ自体へのアクセスをブロックできません)
  2. 許可されている名前空間
  3. IPブロック(例外: PodまたはノードのIPアドレスに関係なく、Podが実行されているノードとの間のトラフィックは常に許可されます。)

Podベースもしくは名前空間ベースのネットワークポリシーを定義する場合、セレクターを使用してセレクターに一致するPodとの間で許可されるトラフィックを指定します。

一方でIPベースのネットワークポリシーが作成されると、IPブロック(CIDRの範囲)に基づいてポリシーが定義されます。

前提条件

ネットワークポリシーは、ネットワークプラグインにより実装されます。ネットワークポリシーを使用するには、NetworkPolicyをサポートするネットワークソリューションを使用しなければなりません。ネットワークポリシーを実装したコントローラーを使用せずにNetworkPolicyリソースを作成した場合は、何も効果はありません。

分離されたPodと分離されていないPod

デフォルトでは、Podは分離されていない状態(non-isolated)となるため、すべてのソースからのトラフィックを受信します。

Podを選択するNetworkPolicyが存在すると、Podは分離されるようになります。名前空間内に特定のPodを選択するNetworkPolicyが1つでも存在すると、そのPodはいずれかのNetworkPolicyで許可されていないすべての接続を拒否するようになります。(同じ名前空間内のPodでも、どのNetworkPolicyにも選択されなかった他のPodは、引き続きすべてのトラフィックを許可します。)

ネットワークポリシーは追加式であるため、競合することはありません。複数のポリシーがPodを選択する場合、そのPodに許可されるトラフィックは、それらのポリシーのingress/egressルールの和集合で制限されます。したがって、評価の順序はポリシーの結果には影響がありません。

NetworkPolicyリソース

リソースの完全な定義については、リファレンスのNetworkPolicyのセクションを参照してください。

以下は、NetworkPolicyの一例です。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

備考:

選択したネットワークソリューションがネットワークポリシーをサポートしていない場合には、これをクラスターのAPIサーバーにPOSTしても効果はありません。

必須フィールド: 他のKubernetesの設定と同様に、NetworkPolicyにもapiVersionkindmetadataフィールドが必須です。設定ファイルの扱い方に関する一般的な情報については、ConfigMapを使用してコンテナを構成するオブジェクト管理を参照してください。

spec: NetworkPolicyのspecを見ると、指定した名前空間内で特定のネットワークポリシーを定義するのに必要なすべての情報が確認できます。

podSelector: 各NetworkPolicyには、ポリシーを適用するPodのグループを選択するpodSelectorが含まれます。ポリシーの例では、ラベル"role=db"を持つPodを選択しています。podSelectorを空にすると、名前空間内のすべてのPodが選択されます。

policyTypes: 各NetworkPolicyには、policyTypesとして、IngressEgress、またはその両方からなるリストが含まれます。policyTypesフィールドでは、指定したポリシーがどの種類のトラフィックに適用されるかを定めます。トラフィックの種類としては、選択したPodへの内向きのトラフィック(Ingress)、選択したPodからの外向きのトラフィック(Egress)、またはその両方を指定します。policyTypesを指定しなかった場合、デフォルトで常に Ingressが指定され、NetworkPolicyにegressルールが1つでもあればEgressも設定されます。

ingress: 各NetworkPolicyには、許可するingressルールのリストを指定できます。各ルールは、fromおよびportsセクションの両方に一致するトラフィックを許可します。ポリシーの例には1つのルールが含まれ、このルールは、3つのソースのいずれかから送信された1つのポート上のトラフィックに一致します。1つ目のソースはipBlockで、2つ目のソースはnamespaceSelectorで、3つ目のソースはpodSelectorでそれぞれ定められます。

egress: 各NetworkPolicyには、許可するegressルールのリストを指定できます。各ルールは、toおよびportsセクションの両方に一致するトラフィックを許可します。ポリシーの例には1つのルールが含まれ、このルールは、1つのポート上で10.0.0.0/24の範囲内の任意の送信先へ送られるトラフィックに一致します。

したがって、上のNetworkPolicyの例では、次のようにネットワークポリシーを適用します。

  1. "default"名前空間内にある"role=db"のPodを、内向きと外向きのトラフィックに対して分離する(まだ分離されていない場合)
  2. (Ingressルール) "default"名前空間内の"role=db"ラベルが付いたすべてのPodのTCPの6379番ポートへの接続のうち、次の送信元からのものを許可する
    • "default"名前空間内のラベル"role=frontend"が付いたすべてのPod
    • ラベル"project=myproject"が付いた名前空間内のすべてのPod
    • 172.17.0.0–172.17.0.255および172.17.2.0–172.17.255.255(言い換えれば、172.17.1.0/24の範囲を除く172.17.0.0/16)の範囲内のすべてのIPアドレス
  3. (Egressルール) "role=db"というラベルが付いた"default"名前空間内のすべてのPodからの、TCPの5978番ポート上でのCIDR 10.0.0.0/24への接続を許可する

追加の例については、ネットワークポリシーを宣言するの説明を参照してください。

tofromのセレクターの振る舞い

ingressfromセクションまたはegresstoセクションに指定できるセレクターは4種類あります。

podSelector: NetworkPolicyと同じ名前空間内の特定のPodを選択して、ingressの送信元またはegressの送信先を許可します。

namespaceSelector: 特定の名前空間を選択して、その名前空間内のすべてのPodについて、ingressの送信元またはegressの送信先を許可します。

namespaceSelector および podSelector: 1つのtoまたはfromエントリーでnamespaceSelectorpodSelectorの両方を指定して、特定の名前空間内の特定のPodを選択します。正しいYAMLの構文を使うように気をつけてください。このポリシーの例を以下に示します。

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

このポリシーには、1つのfrom要素があり、ラベルuser=aliceの付いた名前空間内にある、ラベルrole=clientの付いたPodからの接続を許可します。しかし、以下のポリシーには注意が必要です。

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

このポリシーには、from配列の中に2つの要素があります。そのため、ラベルrole=clientの付いた名前空間内にあるすべてのPodからの接続、または、任意の名前空間内にあるラベルuser=aliceの付いたすべてのPodからの接続を許可します。

正しいルールになっているか自信がないときは、kubectl describeを使用すると、Kubernetesがどのようにポリシーを解釈したのかを確認できます。

ipBlock: 特定のIPのCIDRの範囲を選択して、ingressの送信元またはegressの送信先を許可します。PodのIPは一時的なもので予測できないため、ここにはクラスター外のIPを指定するべきです。

クラスターのingressとegressの仕組みはパケットの送信元IPや送信先IPの書き換えを必要とすることがよくあります。その場合、NetworkPolicyの処理がIPの書き換えの前後どちらで行われるのかは定義されていません。そのため、ネットワークプラグイン、クラウドプロバイダー、Serviceの実装などの組み合わせによっては、動作が異なる可能性があります。

内向きのトラフィックの場合は、実際のオリジナルの送信元IPに基づいてパケットをフィルタリングできる可能性もあれば、NetworkPolicyが対象とする「送信元IP」がLoadBalancerやPodのノードなどのIPになってしまっている可能性もあることになります。

外向きのトラフィックの場合は、クラスター外のIPに書き換えられたPodからServiceのIPへの接続は、ipBlockベースのポリシーの対象になる場合とならない場合があることになります。

デフォルトのポリシー

デフォルトでは、名前空間にポリシーが存在しない場合、その名前空間内のPodの内向きと外向きのトラフィックはすべて許可されます。以下の例を利用すると、その名前空間内でのデフォルトの振る舞いを変更できます。

デフォルトですべての内向きのトラフィックを拒否する

すべてのPodを選択して、そのPodへのすべての内向きのトラフィックを許可しないNetworkPolicyを作成すると、その名前空間に対する「デフォルト」の分離ポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

このポリシーを利用すれば、他のいかなるNetworkPolicyにも選択されなかったPodでも分離されることを保証できます。このポリシーは、デフォルトの外向きの分離の振る舞いを変更しません。

デフォルトで内向きのすべてのトラフィックを許可する

(たとえPodを「分離されたもの」として扱うポリシーが追加された場合でも)名前空間内のすべてのPodへのすべてのトラフィックを許可したい場合には、その名前空間内のすべてのトラフィックを明示的に許可するポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

デフォルトで外向きのすべてのトラフィックを拒否する

すべてのPodを選択して、そのPodからのすべての外向きのトラフィックを許可しないNetworkPolicyを作成すると、その名前空間に対する「デフォルト」の外向きの分離ポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

このポリシーを利用すれば、他のいかなるNetworkPolicyにも選択されなかったPodでも、外向きのトラフィックが許可されないことを保証できます。このポリシーは、デフォルトの内向きの分離の振る舞いを変更しません。

デフォルトで外向きのすべてのトラフィックを許可する

(たとえPodを「分離されたもの」として扱うポリシーが追加された場合でも)名前空間内のすべてのPodからのすべてのトラフィックを許可したい場合には、その名前空間内のすべての外向きのトラフィックを明示的に許可するポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

デフォルトで内向きと外向きのすべてのトラフィックを拒否する

名前空間内に以下のNetworkPolicyを作成すると、その名前空間で内向きと外向きのすべてのトラフィックを拒否する「デフォルト」のポリシーを作成できます。

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

このポリシーを利用すれば、他のいかなるNetworkPolicyにも選択されなかったPodでも、内向きと外向きのトラフィックが許可されないことを保証できます。

SCTPのサポート

FEATURE STATE: Kubernetes v1.19 [beta]

ベータ版の機能として、これはデフォルトで有効化されます。 クラスターレベルでSCTPを無効化するために、クラスター管理者はAPIサーバーで--feature-gates=SCTPSupport=false,…と指定して、SCTPSupportフィーチャーゲートを無効にする必要があります。

備考:

SCTPプロトコルのネットワークポリシーをサポートするCNIプラグインを使用している必要があります。

ネットワークポリシーでできないこと(少なくともまだ)

Kubernetes1.20現在、ネットワークポリシーAPIに以下の機能は存在しません。 しかし、オペレーティングシステムのコンポーネント(SELinux、OpenVSwitch、IPTablesなど)、レイヤー7の技術(Ingressコントローラー、サービスメッシュ実装)、もしくはアドミッションコントローラーを使用して回避策を実装できる場合があります。 Kubernetesのネットワークセキュリティを初めて使用する場合は、ネットワークポリシーAPIを使用して以下のユーザーストーリーを(まだ)実装できないことに注意してください。これらのユーザーストーリーの一部(全てではありません)は、ネットワークポリシーAPIの将来のリリースで活発に議論されています。

  • クラスター内トラフィックを強制的に共通ゲートウェイを通過させる(これは、サービスメッシュもしくは他のプロキシで提供するのが最適な場合があります)。
  • TLS関連のもの(これにはサービスメッシュまたはIngressコントローラーを使用します)。
  • ノードの固有のポリシー(これらにはCIDR表記を使用できますが、Kubernetesのアイデンティティでノードを指定することはできません)。
  • 名前空間またはサービスを名前で指定する(ただし、Podまたは名前空間をラベルで指定することができます。これは多くの場合で実行可能な回避策です)。
  • サードパーティによって実行される「ポリシー要求」の作成または管理
  • 全ての名前空間もしくはPodに適用されるデフォルトのポリシー(これを実現できるサードパーティのKubernetesディストリビューションとプロジェクトがいくつか存在します)。
  • 高度なポリシークエリと到達可能性ツール
  • 単一のポリシー宣言でポートの範囲を指定する機能
  • ネットワークセキュリティイベント(例えばブロックされた接続や受け入れられた接続)をログに記録する機能
  • ポリシーを明示的に拒否する機能(現在、ネットワークポリシーのモデルはデフォルトで拒否されており、許可ルールを追加する機能のみが存在します)。
  • ループバックまたは内向きのホストトラフィックを拒否する機能(Podは現在localhostのアクセスやそれらが配置されているノードからのアクセスをブロックすることはできません)。

次の項目

8 - ServiceとPodに対するDNS

ワークロードは、DNSを用いてクラスター内のServiceを探索できます。 本ページでは、その仕組みを解説します。

KubernetesはServiceとPodのDNSレコードを作成します。 IPアドレスの代わりに安定したDNS名を用いて、Serviceに接続できます。

Kubernetesは、DNSの設定に用いられるPodとServiceの情報を公開します。 kubeletがPodのDNSを設定することで、実行中のコンテナがIPアドレスではなくDNS名でServiceの名前解決を行えます。

クラスター内で定義されたServiceにはDNS名が割り当てられます。 デフォルトでは、クライアントであるPodのDNS検索リストには、そのPod自身の名前空間とクラスターのデフォルトドメインが含まれています。

サービスの名前空間

DNSクエリの結果は、クエリを発行したPodの名前空間によって異なる場合があります。 名前空間を指定しないDNSクエリは、Pod自身の名前空間に限定して解決されます。 他の名前空間にアクセスするには、DNSクエリ内で名前空間を指定する必要があります。

例えば、testという名前空間にPodが、prodという名前空間にdataというServiceがそれぞれ存在しているとします。

Podからdataというクエリを発行しても、DNS解決はPodの名前空間testに限定されるため、結果は返りません。

data.prodというクエリを発行すれば、Serviceの属する名前空間が指定されているため、Serviceを探索することができます。

DNSクエリはPodの/etc/resolv.confに基づいて展開される場合があります。 kubeletは、それぞれのPodにこのファイルを設定します。 例えば、dataというクエリは、data.test.svc.cluster.localに展開される場合があります。 クエリの展開にはsearchオプションの値が用いられます。 DNSクエリの詳細については、resolv.confのマニュアルをご覧ください。

nameserver 10.32.0.10
search <namespace>.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

以上のように、test という名前空間に存在するPodは、data.proddata.prod.svc.cluster.localというクエリを正常に名前解決できます。

DNSレコード

DNSレコードが作成されるオブジェクトは、以下の2つです。

  1. Service
  2. Pod

以降のセクションでは、サポートされているDNSレコードの種類および構成について説明します。 その他の構成、名前、クエリは実装に依存し、予告なく変更される可能性があります。 最新の仕様については、Kubernetes DNS-Based Service Discoveryをご覧ください。

Service

A/AAAAレコード

「通常の」(ヘッドレスでない)Serviceは、my-svc.my-namespace.svc.cluster-domain.exampleという形式のDNS A(AAAA)レコードが、ServiceのIPファミリーに応じて割り当てられます。 割り当てられたAレコードは、ServiceのClusterIPへと名前解決されます。

ClusterIPのないヘッドレスServiceにもmy-svc.my-namespace.svc.cluster-domain.exampleの形式のDNS A(AAAA)レコードが割り当てられます。 通常のServiceとは異なり、Serviceによって選択されたすべてのPodのIPアドレスに解決されます。 クライアントは、得られたIPアドレスの集合を扱うか、標準のラウンドロビン方式で集合からIPアドレスを選択します。

SRVレコード

SRVレコードは、通常のServiceもしくはヘッドレスServiceの一部である名前付きポート向けに作成されます。

  • それぞれの名前付きポートに対して、SRVレコードは_port-name._port-protocol.my-svc.my-namespace.svc.cluster-domain.exampleという形式です。
  • 通常のServiceに対しては、このSRVレコードはmy-svc.my-namespace.svc.cluster-domain.exampleという形式のドメイン名とポート番号へ名前解決します。
  • ヘッドレスServiceに対しては、このSRVレコードは複数の結果を返します。 Serviceの背後にある各Podに対し1つずつレコードが返され、それぞれのレコードはPodのポート番号とhostname.my-svc.my-namespace.svc.cluster-domain.exampleという形式のドメイン名を含んでいます。

Pod

A/AAAAレコード

DNSの仕様が実装される前のバージョンのKube-DNSでは、以下のような名前解決が行われていました。

<pod-IPv4-address>.<namespace>.pod.<cluster-domain>

例えば、IPアドレスが172.17.0.3のPodがdefaultという名前空間に存在しており、クラスターのドメイン名がcluster.localの場合、PodのDNS名は以下のとおりです。

172-17-0-3.default.pod.cluster.local

CoreDNSなどの一部のクラスター実装では、以下のようなAレコードも提供されます。

<pod-ipv4-address>.<service-name>.<my-namespace>.svc.<cluster-domain.example>

例えば、IPアドレスが172.17.0.3のPodがcafeという名前空間に存在しており、PodがbaristaというServiceのエンドポイントで、クラスターのドメイン名がcluster.localの場合、Podは次のようなServiceスコープのAレコードを持ちます。

172-17-0-3.barista.cafe.svc.cluster.local

Podのhostnameとsubdomainフィールド

現在、Podが作成されたとき、Pod内部から観測されるホスト名は、Podのmetadata.nameフィールドの値です。

Podのspecでは、オプションのhostnameフィールドで別のホスト名を指定できます。 hostnameを指定すると、Podの内部から観測されるホスト名として、Podの名前よりも優先されます。 例えば、spec.hostnameフィールドが"my-host"に設定されたPodのホスト名は"my-host"です。

Podのspecはまた、オプションのsubdomainフィールドで、Podの名前空間のサブグループを指定することができます。 例えば、"my-namespace"という名前空間において、spec.hostname"foo"spec.subdomain"bar"にそれぞれ設定されているPodがあるとします。 このとき、Podのホスト名は"foo"で、Pod内部から観測される完全修飾ドメイン名(FQDN)は"foo.bar.my-namespace.svc.cluster.local"です。

Podと同じ名前空間内に、Podのサブドメインと同じ名前のヘッドレスServiceが存在する場合、クラスターのDNSサーバーは、そのPodのFQDNに対するA(AAAA)レコードを返します。

例えば、以下の設定について考えてみましょう:

apiVersion: v1
kind: Service
metadata:
  name: busybox-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # 単一ポートのServiceには、nameは必須ではありません
    port: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: busybox-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

上記の設定のように、"busybox-subdomain"というServiceと、spec.subdomain"busybox-subdomain"を指定したPodが存在するとします。 このとき、1つ目のPodは、自身のFQDNを"busybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.example"と認識します。 DNSはその名前に対して、PodのIPを指し示すA(AAAA)レコードを返します。 "busybox1"と"busybox2"の両方のPodはそれぞれ自身のA(AAAA)レコードを持ちます。

EndpointSliceでは、IPアドレスとともに、任意のエンドポイントアドレスに対するDNSホスト名を指定できます。

備考:

Podにhostnameが設定されていない場合、そのPod名に対するhostnameベースのA(AAAA)レコードは作成されません。 hostnameを持たずにsubdomainのみを持つPodの場合、ヘッドレスService(busybox-subdomain.my-namespace.svc.cluster-domain.example)に対し、PodのIPアドレスを指すA(AAAA)レコードのみが作成されます。 なお、ServiceにpublishNotReadyAddresses=Trueが設定されている場合を除き、PodがDNSレコードに含まれるためには、Podの状態がReadyである必要があります。

PodのsetHostnameAsFQDNフィールド

FEATURE STATE: Kubernetes v1.22 [stable]

PodがFQDNを持つように構成されている場合、そのホスト名は短縮されたホスト名です。 例えば、FQDNがbusybox-1.busybox-subdomain.my-namespace.svc.cluster-domain.exampleのPodがあるとします。 この場合、デフォルトではそのPod内でhostnameコマンドを実行するとbusybox-1が返され、hostname --fqdnコマンドを実行するとFQDNが返されます。

PodのspecでsetHostnameAsFQDN: trueを設定した場合、kubeletはPodのFQDNを、そのPodの名前空間におけるホスト名として書き込みます。 この場合、hostnamehostname --fqdnの両方がPodのFQDNを返します。

備考:

Linuxでは、カーネルのホスト名のフィールド(struct utsnamenodenameフィールド)は64文字に制限されています。

Podがこの機能を有効にしていて、そのFQDNが64文字より長い場合、Podは起動に失敗します。 PodはPendingステータス(kubectlではContainerCreatingと表示)のままになり、「Failed to construct FQDN from Pod hostname and cluster domain, FQDN long-FQDN is too long (64 characters is the max, 70 characters requested)」といったエラーイベントが生成されます。 この場合のユーザー体験を向上させる1つの方法は、admission webhook controllerを作成して、ユーザーがDeploymentなどのトップレベルのオブジェクトを作成するときにFQDNのサイズを制御することです。

PodのDNSポリシー

DNSポリシーはPodごとに設定できます。 現在のKubernetesでは次のようなPod固有のDNSポリシーをサポートしています。 これらのポリシーはPodのspecのdnsPolicyフィールドで指定されます。

  • "Default": Podが実行されているノードから名前解決の設定を継承します。 詳細に関しては、関連する議論を参照してください。

  • "ClusterFirst": "www.kubernetes.io"のようなクラスターのドメインのサフィックスに一致しないDNSクエリは、DNSサーバーによって上流のネームサーバーに転送されます。 クラスター管理者が追加のスタブドメインと上流のDNSサーバーを設定している場合があります。 このような場合におけるDNSクエリ処理の詳細に関しては、関連する議論を参照してください。

  • "ClusterFirstWithHostNet": hostNetworkによって稼働しているPodに対しては、明示的にDNSポリシーを"ClusterFirstWithHostNet"に設定してください。 そうしない場合、hostNetworkによって稼働し、かつ"ClusterFirst"が設定されているPodは、"Default"ポリシーの挙動にフォールバックします。

    備考:

    本ポリシーはWindowsではサポートされていません。 詳細はWindowsノードにおけるDNSの名前解決をご確認ください。
  • "None": Kubernetes環境からDNS設定を無視することができます。 全てのDNS設定は、PodのspecのdnsConfigフィールドで指定する必要があります。 詳細は、以下のPodのDNS設定をご覧ください。

備考:

"Default"は、デフォルトのDNSポリシーではありません。 dnsPolicyが明示的に指定されていない場合、"ClusterFirst"が使用されます。

下記の例では、hostNetworkフィールドがtrueのためdnsPolicyに"ClusterFirstWithHostNet"を指定したPodを示しています。

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

PodのDNS設定

FEATURE STATE: Kubernetes v1.14 [stable]

PodのDNS設定では、Podに対するDNS設定をより細かく制御できます。

dnsConfigフィールドは任意で指定でき、どのdnsPolicyの設定でも併用できます。 ただし、PodのdnsPolicyが"None"の場合、dnsConfigフィールドの設定は必須です。

以下は、dnsConfigフィールドで指定可能なプロパティです。

  • nameservers: PodのDNSサーバーとして使用されるIPアドレスのリストです。 最大で3つのIPアドレスを指定できます。 PodのdnsPolicyが"None"の場合、少なくとも1つのIPアドレスを指定する必要があります。 それ以外の場合は、このプロパティは任意です。 ここで指定したサーバーは、指定されたDNSポリシーから生成されるベースのネームサーバーと結合され、重複するアドレスは削除されます。
  • searches: Pod内のホスト名の名前解決のためのDNS検索ドメインのリストです。 このプロパティは任意です。 このプロパティを指定した場合、このリストは、選択されたDNSポリシーから生成されるベースの検索ドメイン名にマージされます。 重複するドメイン名は削除されます。 最大で32個の検索ドメインを指定できます。
  • options: nameプロパティ(必須)とvalueプロパティ(任意)を持つオブジェクトのリストです。 このプロパティの内容は、指定されたDNSポリシーから生成されるオプションにマージされます。 重複するエントリは削除されます。

以下は、カスタムDNS設定を持つPodの例です。

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
    - name: test
      image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
      - 192.0.2.1 # このIPアドレスは例です
    searches:
      - ns1.svc.cluster-domain.example
      - my.dns.search.suffix
    options:
      - name: ndots
        value: "2"
      - name: edns0

上記のPodが作成されたとき、testコンテナの/etc/resolv.confファイルには以下の内容が設定されます。

nameserver 192.0.2.1
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

IPv6の設定では、検索パスとネームサーバーは次のように設定されます。

kubectl exec -it dns-example -- cat /etc/resolv.conf

このコマンドの出力は以下のようになります。

nameserver 2001:db8:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

DNS検索ドメインリストの制限

FEATURE STATE: Kubernetes 1.28 [stable]

Kubernetes自体は、DNS検索リストの要素数が32を超えたり、DNS検索ドメイン名の文字数の合計が2048を超えたりしない限り、DNS設定に制限を設けません。 この制限は、ノードのリゾルバー設定ファイル、PodのDNS設定、およびそれらがマージされたDNS設定に適用されます。

備考:

古いバージョンの一部のコンテナランタイムでは、DNS検索リストの要素数に制限がある場合があります。 コンテナランタイムによっては、DNS検索ドメインの要素数が多い場合、Podの状態がPendingのままとなることがあります。

この問題は、containerd v1.5.5以前、およびCRI-O v1.21以前で発生することが確認されています。

WindowsノードにおけるDNSの名前解決

  • Windowsノード上で実行されるPodでは、DNSポリシーのClusterFirstWithHostNetはサポートされていません。 Windowsでは、.を含む名前をFQDNとして扱い、FQDN解決はスキップされます。
  • Windowsにおいては、複数のDNSリゾルバーを利用できます。 それぞれのリゾルバーの挙動がわずかに異なるため、Resolve-DNSName PowerShellコマンドレットを使用することが推奨されます。
  • Linuxには完全修飾名としての名前解決が失敗した後に使用されるDNSサフィックスリストがあります。 一方で、WindowsにはDNSサフィックスを1つしか指定できず、それはPodの名前空間に対応するDNSサフィックスです(例: mydns.svc.cluster.local)。 Windowsでは、この単一のサフィックスを用いてFQDNやService、ネットワーク名の名前解決を行えます。 例えば、defaultという名前空間で起動されたPodは、DNSサフィックスとしてdefault.svc.cluster.localが設定されます。 このPodでは、kubernetes.default.svc.cluster.localkubernetesは名前解決できますが、部分的に修飾された名前(kubernetes.defaultkubernetes.default.svc)は名前解決できません。

次の項目

DNS設定の管理方法に関しては、DNS Serviceの設定を確認してください。

9 - IPv4/IPv6デュアルスタック

FEATURE STATE: Kubernetes v1.16 [alpha]

IPv4/IPv6デュアルスタックを利用すると、IPv4とIPv6のアドレスの両方をPodおよびServiceに指定できるようになります。

KubernetesクラスターでIPv4/IPv6デュアルスタックのネットワークを有効にすれば、クラスターはIPv4とIPv6のアドレスの両方を同時に割り当てることをサポートするようになります。

サポートされている機能

KubernetesクラスターでIPv4/IPv6デュアルスタックを有効にすると、以下の機能が提供されます。

  • デュアルスタックのPodネットワーク(PodごとにIPv4とIPv6のアドレスが1つずつ割り当てられます)
  • IPv4およびIPv6が有効化されたService(各Serviceは1つのアドレスファミリーでなければなりません)
  • IPv4およびIPv6インターフェースを経由したPodのクラスター外向きの(たとえば、インターネットへの)ルーティング

前提条件

IPv4/IPv6デュアルスタックのKubernetesクラスターを利用するには、以下の前提条件を満たす必要があります。

  • Kubernetesのバージョンが1.16以降である
  • プロバイダーがデュアルスタックのネットワークをサポートしている(クラウドプロバイダーなどが、ルーティング可能なIPv4/IPv6ネットワークインターフェースが搭載されたKubernetesを提供可能である)
  • ネットワークプラグインがデュアルスタックに対応している(KubenetやCalicoなど)

IPv4/IPv6デュアルスタックを有効にする

IPv4/IPv6デュアルスタックを有効にするには、クラスターの関連コンポーネントでIPv6DualStackフィーチャーゲートを有効にして、デュアルスタックのクラスターネットワークの割り当てを以下のように設定します。

  • kube-apiserver:
    • --feature-gates="IPv6DualStack=true"
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
  • kube-controller-manager:
    • --feature-gates="IPv6DualStack=true"
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 デフォルトのサイズは、IPv4では/24、IPv6では/64です
  • kubelet:
    • --feature-gates="IPv6DualStack=true"
  • kube-proxy:
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --feature-gates="IPv6DualStack=true"

備考:

IPv4 CIDRの例: 10.244.0.0/16 (自分のクラスターのアドレス範囲を指定してください)

IPv6 CIDRの例: fdXY:IJKL:MNOP:15::/64 (これはフォーマットを示すための例であり、有効なアドレスではありません。詳しくはRFC 4193を参照してください)

Service

クラスターでIPv4/IPv6デュアルスタックのネットワークを有効にした場合、IPv4またはIPv6のいずれかのアドレスを持つServiceを作成できます。Serviceのcluster IPのアドレスファミリーは、Service上に.spec.ipFamilyフィールドを設定することで選択できます。このフィールドを設定できるのは、新しいServiceの作成時のみです。.spec.ipFamilyフィールドの指定はオプションであり、ServiceIngressでIPv4とIPv6を有効にする予定がある場合にのみ使用するべきです。このフィールドの設定は、外向きのトラフィックに対する要件には含まれません。

備考:

クラスターのデフォルトのアドレスファミリーは、kube-controller-managerに--service-cluster-ip-rangeフラグで設定した、最初のservice cluster IPの範囲のアドレスファミリーです。

.spec.ipFamilyは、次のいずれかに設定できます。

  • IPv4: APIサーバーはipv4service-cluster-ip-rangeの範囲からIPアドレスを割り当てます
  • IPv6: APIサーバーはipv6service-cluster-ip-rangeの範囲からIPアドレスを割り当てます

次のServiceのspecにはipFamilyフィールドが含まれていません。Kubernetesは、最初に設定したservice-cluster-ip-rangeの範囲からこのServiceにIPアドレス(別名「cluster IP」)を割り当てます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    app: MyApp
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80

次のServiceのspecにはipFamilyフィールドが含まれています。Kubernetesは、最初に設定したservice-cluster-ip-rangeの範囲からこのServiceにIPv6のアドレス(別名「cluster IP」)を割り当てます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv6
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

比較として次のServiceのspecを見ると、このServiceには最初に設定したservice-cluster-ip-rangeの範囲からIPv4のアドレス(別名「cluster IP」)が割り当てられます。

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv4
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Type LoadBalancer

IPv6が有効になった外部ロードバランサーをサポートしているクラウドプロバイダーでは、typeフィールドにLoadBalancerを指定し、ipFamilyフィールドにIPv6を指定することにより、クラウドロードバランサーをService向けにプロビジョニングできます。

外向きのトラフィック

パブリックおよび非パブリックでのルーティングが可能なIPv6アドレスのブロックを利用するためには、クラスターがベースにしているCNIプロバイダーがIPv6の転送を実装している必要があります。もし非パブリックでのルーティングが可能なIPv6アドレスを使用するPodがあり、そのPodをクラスター外の送信先(例:パブリックインターネット)に到達させたい場合、外向きのトラフィックと応答の受信のためにIPマスカレードを設定する必要があります。ip-masq-agentはデュアルスタックに対応しているため、デュアルスタックのクラスター上でのIPマスカレードにはip-masq-agentが利用できます。

既知の問題

  • Kubenetは、IPv4,IPv6の順番にIPを報告することを強制します(--cluster-cidr)

次の項目

10 - トポロジーを意識したルーティング

Topology Aware Routingは、ネットワークトラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。クラスター内のPod間で同じゾーンのトラフィックを優先することで、信頼性、パフォーマンス(ネットワークの待ち時間やスループット)の向上、またはコストの削減に役立ちます。
FEATURE STATE: Kubernetes v1.23 [beta]

備考:

Kubernetes 1.27より前には、この機能は、Topology Aware Hintとして知られていました。

Topology Aware Routingは、トラフィックを発信元のゾーンに維持するようにルーティング動作を調整します。場合によっては、コストを削減したり、ネットワークパフォーマンスを向上させたりすることができます。

動機

Kubernetesクラスターは、マルチゾーン環境で展開されることが多くなっています。 Topology Aware Routingは、トラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。EndpointSliceコントローラーはServiceのendpointを計算する際に、各endpointのトポロジー(リージョンとゾーン)を考慮し、ゾーンに割り当てるためのヒントフィールドに値を入力します。kube-proxyのようなクラスターコンポーネントは、次にこれらのヒントを消費し、それらを使用してトラフィックがルーティングされる方法に影響を与えることが可能です(トポロジー的に近いendpointを優先します)。

Topology Aware Routingを有効にする

備考:

Kubernetes 1.27より前には、この動作はservice.kubernetes.io/topology-aware-hintsアノテーションを使用して制御されていました。

service.kubernetes.io/topology-modeアノテーションをautoに設定すると、サービスに対してTopology Aware Routingを有効にすることができます。各ゾーンに十分なendpointがある場合、個々のendpointを特定のゾーンに割り当てるために、トポロジーヒントがEndpointSliceに入力され、その結果、トラフィックは発信元の近くにルーティングされます。

最も効果的なとき

この機能は、次の場合に最も効果的に動作します。

1. 受信トラフィックが均等に分散されている

トラフィックの大部分が単一のゾーンから発信されている場合、トラフィックはそのゾーンに割り当てられたendpointのサブセットに過負荷を与える可能性があります。受信トラフィックが単一のゾーンから発信されることが予想される場合、この機能は推奨されません。

2. 1つのゾーンに3つ以上のendpointを持つサービス

3つのゾーンからなるクラスターでは、これは9つ以上のendpointがあることを意味します。ゾーン毎のendpointが3つ未満の場合、EndpointSliceコントローラーはendpointを均等に割り当てることができず、代わりにデフォルトのクラスター全体のルーティングアプローチに戻る可能性が高く(約50%)なります。

使い方

「Auto」ヒューリスティックは、各ゾーンに多数のendpointを比例的に割り当てようとします。このヒューリスティックは、非常に多くのendpointを持つサービスに最適です。

EndpointSliceコントローラー

このヒューリスティックが有効な場合、EndpointSliceコントローラーはEndpointSliceにヒントを設定する役割を担います。コントローラーは、各ゾーンに比例した量のendpointを割り当てます。この割合は、そのゾーンで実行されているノードの割り当て可能なCPUコアを基に決定されます。

たとえば、あるゾーンに2つのCPUコアがあり、別のゾーンに1つのCPUコアしかない場合、コントローラーは2つのCPUコアを持つゾーンに2倍のendpointを割り当てます。

次の例は、ヒントが入力されたときのEndpointSliceの様子を示しています。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-hints
  labels:
    kubernetes.io/service-name: example-svc
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    zone: zone-a
    hints:
      forZones:
        - name: "zone-a"

kube-proxy

kube-proxyは、EndpointSliceコントローラーによって設定されたヒントに基づいて、ルーティング先のendpointをフィルター処理します。ほとんどの場合、これはkube-proxyが同じゾーン内のendpointにトラフィックをルーティングできることを意味します。コントローラーが別のゾーンからendpointを割り当てて、ゾーン間でendpointがより均等に分散されるようにする場合があります。これにより、一部のトラフィックが他のゾーンにルーティングされます。

セーフガード

各ノードのKubernetesコントロールプレーンとkube-proxyは、Topology Aware Hintを使用する前に、いくつかのセーフガードルールを適用します。これらがチェックアウトされない場合、kube-proxyは、ゾーンに関係なく、クラスター内のどこからでもendpointを選択します。

  1. endpointの数が不十分です: クラスター内のゾーンよりもendpointが少ない場合、コントローラーはヒントを割り当てません。

  2. バランスの取れた割り当てを実現できません: 場合によっては、ゾーン間でendpointのバランスの取れた割り当てを実現できないことがあります。たとえば、ゾーンaがゾーンbの2倍の大きさであるが、endpointが2つしかない場合、ゾーンaに割り当てられたendpointはゾーンbの2倍のトラフィックを受信する可能性があります。この「予想される過負荷」値が各ゾーンの許容しきい値を下回ることができない場合、コントローラーはヒントを割り当てません。重要なことに、これはリアルタイムのフィードバックに基づいていません。それでも、個々のendpointが過負荷になる可能性があります。

  3. 1つ以上のノードの情報が不十分です: ノードにtopology.kubernetes.io/zoneラベルがないか、割り当て可能なCPUの値を報告していない場合、コントロールプレーンはtopology-aware endpoint hintsを設定しないため、kube-proxyはendpointをゾーンでフィルタリングしません。

  4. 1つ以上のendpointにゾーンヒントが存在しません: これが発生すると、kube-proxyはTopology Aware Hintから、またはTopology Aware Hintへの移行が進行中であると見なします。この状態のサービスに対してendpointをフィルタリングすることは危険であるため、kube-proxyはすべてのendpointを使用するようにフォールバックします。

  5. ゾーンはヒントで表されません: kube-proxyが、実行中のゾーンをターゲットとするヒントを持つendpointを1つも見つけることができない場合、すべてのゾーンのendpointを使用することになります。これは既存のクラスターに新しいゾーンを追加するときに発生する可能性が最も高くなります。

制約事項

  • ServiceでexternalTrafficPolicyまたはinternalTrafficPolicyLocalに設定されている場合、Topology Aware Hintは使用されません。同じServiceではなく、異なるServiceの同じクラスターで両方の機能を使用することができます。

  • このアプローチは、ゾーンのサブセットから発信されるトラフィックの割合が高いサービスではうまく機能しません。代わりに、これは着信トラフィックが各ゾーンのノードの容量にほぼ比例することを前提としています。

  • EndpointSliceコントローラーは、各ゾーンの比率を計算するときに、準備ができていないノードを無視します。ノードの大部分の準備ができていない場合、これは意図しない結果をもたらす可能性があります。

  • EndpointSliceコントローラーは、node-role.kubernetes.io/control-planeまたはnode-role.kubernetes.io/masterラベルが設定されたノードを無視します。それらのノードでワークロードが実行されている場合、これは問題になる可能性があります。

  • EndpointSliceコントローラーは、各ゾーンの比率を計算するデプロイ時にtolerationを考慮しません。サービスをバックアップするPodがクラスター内のノードのサブセットに制限されている場合、これは考慮されません。

  • これはオートスケーリングと相性が悪いかもしれません。例えば、多くのトラフィックが1つのゾーンから発信されている場合、そのゾーンに割り当てられたendpointのみがそのトラフィックを処理することになります。その結果、Horizontal Pod Autoscalerがこのイベントを拾えなくなったり、新しく追加されたPodが別のゾーンで開始されたりする可能性があります。

カスタムヒューリスティック

Kubernetesは様々な方法でデブロイされ、endpointをゾーンに割り当てるための単独のヒューリスティックは、すべてのユースケースに通用するわけではありません。 この機能の主な目的は、内蔵のヒューリスティックがユースケースに合わない場合に、カスタムヒューリスティックを開発できるようにすることです。カスタムヒューリスティックを有効にするための最初のステップは、1.27リリースに含まれています。これは限定的な実装であり、関連する妥当と思われる状況をまだカバーしていない可能性があります。

次の項目

11 - ServiceのClusterIPの割り当て

Kubernetesでは、ServiceはPodの集合上で実行しているアプリケーションを抽象的に公開する方法です。Serviceはクラスター内で仮想IPアドレス(type: ClusterIPのServiceを使用)を持つことができます。クライアントはその仮想IPアドレスを使用してServiceに接続することができます。そしてKubernetesは、そのServiceへのトラフィックを異なる背後のPod間で負荷分散します。

どのようにServiceのClusterIPが割り当てられるのか?

KubernetesがServiceに仮想IPアドレスを割り当てる必要がある場合、2つの方法の内どちらかの方法で行われます:

動的割り当て
クラスターのコントロールプレーンは自動的にtype: ClusterIPのServiceのために設定されたIP範囲の中から未割り当てのIPアドレスを選びます。
静的割り当て
Serviceのために設定されたIP範囲の中から自身でIPアドレスを選びます。

クラスター全体を通して、ServiceのClusterIPはユニークでなければいけません。割り当て済みのClusterIPを使用してServiceを作成しようとするとエラーが返ってきます。

なぜServiceのClusterIPを予約する必要があるのか?

時には、クラスター内の他のコンポーネントやユーザーが利用できるように、Serviceをよく知られたIPアドレスで実行したい場合があります。

その最たる例がクラスターのDNS Serviceです。慣習として、一部のKubernetesインストーラーはServiceのIP範囲の10番目のIPアドレスをDNS Serviceに割り当てます。ServiceのIP範囲を10.96.0.0/16とするクラスターを構成し、DNS ServiceのIPを10.96.0.10にするとします。この場合、下記のようなServiceを作成する必要があります。

apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    kubernetes.io/name: CoreDNS
  name: kube-dns
  namespace: kube-system
spec:
  clusterIP: 10.96.0.10
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  selector:
    k8s-app: kube-dns
  type: ClusterIP

しかし、前述したように10.96.0.10のIPアドレスは予約されていません。他のServiceが動的割り当てよりも前に、または同時に作成された場合、このIPアドレスがそのServiceに割り当てられる可能性があります。その場合、競合エラーで失敗しDNS Serviceを作成することができません。

どのようにServiceのClusterIPの競合を回避するのか?

Kubernetesで実装されているServiceへのClusterIPの割り当て戦略は、衝突リスクを軽減します。

ClusterIPの範囲は、min(max(16, cidrSize / 16), 256)という式に基づいて分割されます。最小で16、最大でも256で、その範囲内で段階的に変化する ように表されます。

動的IP割り当てはデフォルトで上位の帯域を使用し、それが使い切られると下位の範囲を使用します。これにより、ユーザーは下位の帯域を使用して静的な割り当てを行うことができ、衝突のリスクを抑えることができます。

例1

この例ではServiceのIPアドレスとして、10.96.0.0/24(CIDR表記法)のIPアドレスの範囲を使用します。

範囲の大きさ: 28 - 2 = 254 帯域のオフセット(開始位置): min(max(16, 256/16), 256) = min(16, 256) = 16 静的割り当ての帯域の開始: 10.96.0.1 静的割り当ての帯域の終了: 10.96.0.16 範囲の終了: 10.96.0.254

pie showData title 10.96.0.0/24 "静的割り当て" : 16 "動的割り当て" : 238

例2

この例では、ServiceのIPアドレスとして、10.96.0.0/20(CIDR表記法)のIPアドレスの範囲を使用します。

範囲の大きさ: 212 - 2 = 4094 帯域のオフセット(開始位置): min(max(16, 4096/16), 256) = min(256, 256) = 256 静的割り当ての帯域の開始: 10.96.0.1 静的割り当ての帯域の終了: 10.96.1.0 範囲の終了: 10.96.15.254

pie showData title 10.96.0.0/20 "静的割り当て" : 256 "動的割り当て" : 3838

例3

この例ではServiceのIPアドレスとして、10.96.0.0/16(CIDR表記法)のIPアドレスの範囲を使用します。

範囲の大きさ: 216 - 2 = 65534 帯域のオフセット(開始位置): min(max(16, 65536/16), 256) = min(4096, 256) = 256 静的割り当ての帯域の開始: 10.96.0.1 静的割り当ての帯域の終了: 10.96.1.0 範囲の終了: 10.96.255.254

pie showData title 10.96.0.0/16 "静的割り当て" : 256 "動的割り当て" : 65278

次の項目

12 - サービス内部トラフィックポリシー

FEATURE STATE: Kubernetes v1.21 [alpha]

サービス内部トラフィックポリシーを使用すると、内部トラフィック制限により、トラフィックが発信されたノード内のエンドポイントにのみ内部トラフィックをルーティングできます。 ここでの「内部」トラフィックとは、現在のクラスターのPodから発信されたトラフィックを指します。これは、コストを削減し、パフォーマンスを向上させるのに役立ちます。

ServiceInternalTrafficPolicyの使用

ServiceInternalTrafficPolicy フィーチャーゲートを有効にすると、.spec.internalTrafficPolicyLocalに設定して、Service内部のみのトラフィックポリシーを有効にすることができます。 これにより、kube-proxyは、クラスター内部トラフィックにノードローカルエンドポイントのみを使用するようになります。

備考:

特定のServiceのエンドポイントがないノード上のPodの場合、Serviceに他のノードのエンドポイントがある場合でも、Serviceは(このノード上のポッドの)エンドポイントがゼロであるかのように動作します。

次の例は、.spec.internalTrafficPolicyLocalに設定した場合のServiceの様子を示しています:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  internalTrafficPolicy: Local

使い方

kube-proxyは、spec.internalTrafficPolicyの設定に基づいて、ルーティング先のエンドポイントをフィルタリングします。 spec.internalTrafficPolicyLocalであれば、ノードのローカルエンドポイントにのみルーティングできるようにします。Clusterまたは未設定であればすべてのエンドポイントにルーティングできるようにします。 ServiceInternalTrafficPolicyフィーチャーゲートが有効な場合、spec.internalTrafficPolicyのデフォルトはClusterです。

制約

  • ServiceでexternalTrafficPolicyLocalに設定されている場合、サービス内部トラフィックポリシーは使用されません。同じServiceだけではなく、同じクラスター内の異なるServiceで両方の機能を使用することができます。

次の項目