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オブジェクトの緩和された命名要件

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

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

ポート定義

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

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

---
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

これは、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に関連付けられます。

自身で作成する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接続です。

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

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つの帯域に分割されています。 動的なポート割り当てはデフォルトで上位の帯域を使用し、上位の帯域が使い尽くされると下位帯域を使用することがあります。 ユーザーは、下位の帯域から割り当てることでポート衝突のリスクを低減することができます。

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リリースとも互換性があります。)

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フィールドは無視されます。

ロードバランサートラフィックに対するノードの可用性の影響 

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

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

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

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

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

ロードバランサーによる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とポートを宛先として設定された状態でNodeに配信されることを意味します。 「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の定義は、prodNamespace内の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

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

ヘッドレス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

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")バックエンドにトラフィックをルーティングする方法を制御できます。

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

トラフィック分散

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

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

PreferClose
クライアントと同じゾーンにあるエンドポイントへのトラフィックルーティングの優先設定を示します。
FEATURE STATE: Kubernetes v1.34 [beta](enabled by default)

Kubernetes 1.34では、(PreferSameTrafficDistribution フィーチャーゲートが無効になっていない限り)2つの追加の値が利用可能です:

PreferSameZone
これはPreferCloseのエイリアスで、意図されたセマンティクスについてより明確です。
PreferSameNode
クライアントと同じノード上にあるエンドポイントへのトラフィックルーティングの優先設定を示します。

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

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

スティッキーセッション

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

ExternalIPs

もし、1つ以上のクラスターノードに転送するexternalIPが複数ある場合、Kubernetes ServiceはそれらのexternalIPsで公開できます。 externalIP(宛先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

APIオブジェクト

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

次の項目

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

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

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