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は 任意の 受信portをtargetPortにマッピングできます。
ただし、デフォルトでは、利便性のためにtargetPortはportフィールドと同じ値に設定されます。Serviceオブジェクトの緩和された命名要件
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に関連付けられます。
備考:
エンドポイントの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
Kubernetes v1.21 [stable]
EndpointSliceは、Serviceの背後にあるネットワークエンドポイントのサブセット(スライス)を表すオブジェクトです。
Kubernetesクラスターは、各EndpointSliceが保持するエンドポイントの数を追跡します。 特定のServiceに紐づくエンドポイント数が多く、しきい値に達した場合、Kubernetesは新しい空のEndpointSliceを追加し、そこに新たなエンドポイント情報を保存します。 デフォルトでは、既存のすべてのEndpointSliceがそれぞれ、少なくとも100個のエンドポイントを含むと、Kubernetesは新しいEndpointSliceを作成します。 Kubernetesは、追加のエンドポイントを追加する必要性が生じるまで、新しいEndpointSliceを作成しません。
このAPIの詳細については、EndpointSliceを参照してください。
Endpoints(非推奨)
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個を超えるエンドポイントを持つようにすることはできません。
アプリケーションプロトコル
Kubernetes v1.20 [stable]
appProtocolフィールドは、各Serviceポートに対してアプリケーションプロトコルを指定する方法を提供します。
これは、実装が理解するプロトコルに対して、より豊かな動作を提供するためのヒントとして使用されます。
このフィールドの値は、対応するEndpointsおよびEndpointSliceオブジェクトによってミラーリングされます。
このフィールドは、標準のKubernetesラベル構文に従います。有効な値は次のいずれかです:
-
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-abcやwebという名前は有効ですが、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つの帯域に分割されています。 動的なポート割り当てはデフォルトで上位の帯域を使用し、上位の帯域が使い尽くされると下位帯域を使用することがあります。 ユーザーは、下位の帯域から割り当てることでポート衝突のリスクを低減することができます。
type: NodePort ServiceのカスタムIPアドレス設定
NodePortサービスを提供するために特定のIPアドレスを使用するよう、クラスター内のノードを設定できます。 各ノードが複数のネットワークに接続されている場合(例: アプリケーショントラフィック用のネットワークと、ノード間およびコントロールプレーン間のトラフィック用の別のネットワーク)に、この設定が必要になることがあります。
ポートをプロキシする特定のIPアドレスを指定する場合は、kube-proxyの--nodeport-addressesフラグ、またはkube-proxy設定ファイルの同等のnodePortAddressesフィールドを特定のIPブロックに設定できます。
このフラグは、カンマ区切りのIPブロックリスト(例: 10.0.0.0/8、192.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フィールドをサポートする文脈で広く使用されています。
混合プロトコルタイプのロードバランサー
Kubernetes v1.26 [stable](enabled by default)デフォルトでは、LoadBalancerタイプのServiceで複数のポートが定義されている場合、すべてのポートは同じプロトコルを持つ必要があり、そのプロトコルはクラウドプロバイダーがサポートするものでなければなりません。
フィーチャーゲートMixedProtocolLBService(v1.24以降のkube-apiserverではデフォルトで有効)により、複数のポートが定義されている場合、LoadBalancerタイプのServiceで異なるプロトコルを使用できます。
備考:
ロードバランサーServiceで使用できるプロトコルのセットは、クラウドプロバイダーによって定義されます。 クラウドプロバイダーは、Kubernetes APIが強制する制限を超えた追加の制限を課す場合があります。ロードバランサーによるNodePort割り当ての無効化
Kubernetes v1.24 [stable]
spec.allocateLoadBalancerNodePortsフィールドをfalseに設定することで、type: LoadBalancerのServiceに対するNodePort割り当てをオプションで無効にできます。
これは、NodePortを使用せずに、トラフィックをPodに直接ルーティングするロードバランサー実装に対してのみ使用してください。
デフォルトでは、spec.allocateLoadBalancerNodePortsはtrueであり、LoadBalancerタイプのServiceは引き続きNodePortを割り当てます。
既に割り当て済みのNodePortを持つ既存のServiceに対してspec.allocateLoadBalancerNodePortsをfalseに設定しても、それらのNodePortは自動的に割り当て解除されません。
NodePortの割り当てを解除するには、すべてのServiceポートのnodePortsエントリを明示的に削除する必要があります。
ロードバランサー実装のクラスの指定
Kubernetes v1.24 [stable]
typeがLoadBalancerに設定されたServiceの場合、.spec.loadBalancerClassフィールドを使用すると、クラウドプロバイダーのデフォルト以外のロードバランサー実装を使用できます。
デフォルトでは、.spec.loadBalancerClassは設定されておらず、クラスターが--cloud-providerコンポーネントフラグを使用してクラウドプロバイダーで設定されている場合、LoadBalancerタイプのServiceはクラウドプロバイダーのデフォルトロードバランサー実装を使用します。
.spec.loadBalancerClassを指定すると、指定されたクラスに一致するロードバランサー実装がServiceを監視していると想定されます。
デフォルトのロードバランサー実装(たとえば、クラウドプロバイダーが提供するもの)は、このフィールドが設定されたServiceを無視します。
spec.loadBalancerClassはLoadBalancerタイプのServiceにのみ設定できます。
一度設定すると、変更できません。
spec.loadBalancerClassの値はラベル形式の識別子である必要があり、"internal-vip"や"example.com/internal-vip"などのプレフィックスをオプションとして持つことができます。
プレフィックスのない名前はエンドユーザー向けに予約されています。
ロードバランサーのIPアドレスモード
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-serviceやcassandraのような典型的なセレクターではなく、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
備考:
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: ExternalNameServiceのDNS CNAMEレコードExternalName以外のすべてのServiceタイプについて、Serviceの準備完了エンドポイントのすべてのIPアドレスに対するDNS A/AAAAレコード- IPv4エンドポイントの場合、DNSシステムはAレコードを作成します。
- IPv6エンドポイントの場合、DNSシステムはAAAAレコードを作成します。
セレクターなしでヘッドレスServiceを定義する場合、portはtargetPortと一致する必要があります。
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-nsにmy-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")バックエンドにトラフィックをルーティングする方法を制御できます。
詳細については、トラフィックポリシーを参照してください。
トラフィック分散
Kubernetes v1.33 [stable](enabled by default).spec.trafficDistributionフィールドは、Kubernetes Service内のトラフィックルーティングに影響を与える別の方法を提供します。
トラフィックポリシーが厳密なセマンティック保証に焦点を当てているのに対し、トラフィック分散では 優先設定(トポロジ的に近いエンドポイントへのルーティングなど)を表現できます。
これにより、パフォーマンス、コスト、または信頼性の最適化に役立ちます。
Kubernetes 1.34では、次のフィールド値がサポートされています:
PreferClose- クライアントと同じゾーンにあるエンドポイントへのトラフィックルーティングの優先設定を示します。
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
備考:
KubernetesはexternalIPsの割り当てを管理しません。
これらはクラスター管理者の責任です。APIオブジェクト
ServiceはKubernetes REST APIにおいてトップレベルのリソースです。 詳細については、Service APIオブジェクトを参照してください。
次の項目
Serviceと、それがKubernetesにどのように適合するかについて、さらに学習してください:
- Connecting Applications with Servicesのチュートリアルに従ってください。
- Ingressを参照してください。Ingressは、クラスター外部からクラスター内のServiceへのHTTPおよびHTTPSルートを公開します。
- Gatewayを参照してください。GatewayはKubernetesの拡張機能で、Ingressよりも柔軟性を提供します。
さらなる背景情報については、以下を参照してください: