Kubernetesのネットワーキングは4つの懸念事項に対処します。
- Pod内のコンテナは、ネットワーキングを利用してループバック経由の通信を行います。
- クラスターネットワーキングは、異なるPod間の通信を提供します。
- Serviceリソースは、Pod内で動作しているアプリケーションへクラスターの外部から到達可能なように露出を許可します。
- Serviceを利用して、クラスター内部のみで使用するServiceの公開も可能です。
これは、このセクションの複数ページの印刷可能なビューです。 印刷するには、ここをクリックしてください.
Kubernetesのネットワーキングは4つの懸念事項に対処します。
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 APIは、ネットワーク上でPodのグループを公開するのに役立つ抽象化です。各Serviceオブジェクトは、論理的なエンドポイントのセット(通常、これらのエンドポイントはPodです)と、それらのPodにアクセスする方法についてのポリシーを定義します。
例えば、3つのレプリカで実行されているステートレスな画像処理バックエンドを考えてみましょう。これらのレプリカは代替可能です。フロントエンドはどのバックエンドを使用するかを気にしません。バックエンドセットを構成する実際のPodが変更される可能性がありますが、フロントエンドクライアントはそのことを認識する必要はなく、バックエンドのセット自体を追跡する必要もありません。
Serviceの抽象化により、この分離が可能になります。
ServiceによってターゲットとされるPodのセットは、通常、定義したセレクターによって決定されます。 Serviceエンドポイントを定義する他の方法については、セレクターなしのServiceを参照してください。
ワークロードがHTTPを話す場合、Webトラフィックがそのワークロードに到達する方法を制御するためにIngressを使用することを選択するかもしれません。 IngressはServiceタイプではありませんが、クラスターのエントリーポイントとして機能します。Ingressを使用すると、ルーティングルールを単一のリソースに統合できるため、クラスター内で別々に実行されているワークロードの複数のコンポーネントを単一のリスナーの背後に公開できます。
Kubernetes用のGateway APIは、IngressとServiceを超えた追加機能を提供します。Gatewayをクラスターに追加できます(これはCustomResourceDefinitionsを使用して実装された拡張APIのファミリーです)。そして、これらを使用して、クラスター内で実行されているネットワークサービスへのアクセスを構成できます。
アプリケーションでサービスディスカバリーにKubernetes APIを使用できる場合、一致するEndpointSlicesについてAPIサーバーにクエリを実行できます。Kubernetesは、Service内のPodのセットが変更されるたびに、ServiceのEndpointSlicesを更新します。
非ネイティブなアプリケーションのために、KubernetesはアプリケーションとバックエンドPodの間にネットワークポートまたはロードバランサーを配置する方法を提供します。
いずれにせよ、ワークロードはこれらのサービスディスカバリーメカニズムを使用して、接続したいターゲットを見つけることができます。
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アドレス(クラスターIP)を割り当てます。これは仮想IPアドレスメカニズムによって使用されます。そのメカニズムの詳細については、仮想IPとサービスプロキシを読んでください。
そのServiceのコントローラーは、そのセレクターに一致するPodを継続的にスキャンし、ServiceのEndpointSlicesのセットに必要な更新を行います。
Serviceオブジェクトの名前は、有効なRFC 1035ラベル名である必要があります。
portをtargetPortにマッピングできます。デフォルトでは、利便性のために、targetPortはportフィールドと同じ値に設定されます。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は、セレクターのおかげでKubernetes Podへのアクセスを抽象化するのが最も一般的ですが、対応するEndpointSlicesオブジェクトのセットとともにセレクターなしで使用される場合、Serviceはクラスター外で実行されるものを含む他の種類のバックエンドを抽象化できます。
例えば:
これらのシナリオのいずれでも、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 # 上記で定義されたサービスポートの名前と一致する必要があります
appProtocol: http
protocol: TCP
port: 9376
endpoints:
- addresses:
- "10.4.5.6"
- addresses:
- "10.1.2.3"
Service用のEndpointSliceオブジェクトを作成する場合、EndpointSliceには任意の名前を使用できます。名前空間内の各EndpointSliceには一意の名前が必要です。EndpointSliceをServiceにリンクするには、そのEndpointSliceにkubernetes.io/service-name ラベルを設定します。
エンドポイント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のクラスターIPにすることはできません。これは、kube-proxyが宛先として仮想IPをサポートしていないためです。
自分で、または独自のコードで作成するEndpointSliceの場合、ラベルendpointslice.kubernetes.io/managed-byに使用する値も選択する必要があります。EndpointSlicesを管理するための独自のコントローラーコードを作成する場合は、"my-domain.example/name-of-controller"のような値を使用することを検討してください。サードパーティのツールを使用している場合は、ツールの名前をすべて小文字で使用し、スペースやその他の句読点をダッシュ(-)に変更してください。
kubectlなどのツールを直接使用してEndpointSlicesを管理する場合は、"staff"や"cluster-admins"など、この手動管理を説明する名前を使用してください。Kubernetes独自のコントロールプレーンによって管理されるEndpointSlicesを識別する予約値"controller"の使用は避けてください。
セレクターなしでServiceにアクセスすることは、セレクターがある場合と同じように機能します。セレクターなしのServiceの例では、トラフィックはEndpointSliceマニフェストで定義された2つのエンドポイントのいずれか(ポート9376の10.1.2.3または10.4.5.6へのTCP接続)にルーティングされます。
kubectl port-forward service/<service-name> forwardedPort:servicePortなどのアクションは、この制約のために失敗します。これにより、Kubernetes APIサーバーが、呼び出し元がアクセスを許可されていないエンドポイントへのプロキシとして使用されるのを防ぎます。ExternalName Serviceは、セレクターを持たず、代わりにDNS名を使用するServiceの特殊なケースです。詳細については、ExternalNameセクションを参照してください。
Kubernetes v1.21 [stable]
EndpointSlicesは、Serviceのバッキングネットワークエンドポイントのサブセット(スライス)を表すオブジェクトです。
Kubernetesクラスターは、各EndpointSliceが表すエンドポイントの数を追跡します。Serviceのエンドポイントが多すぎてしきい値に達した場合、Kubernetesは別の空のEndpointSliceを追加し、そこに新しいエンドポイント情報を保存します。 デフォルトでは、既存のEndpointSlicesがすべて少なくとも100個のエンドポイントを含むと、Kubernetesは新しいEndpointSliceを作成します。Kubernetesは、追加のエンドポイントを追加する必要があるまで、新しいEndpointSliceを作成しません。
このAPIの詳細については、EndpointSlicesを参照してください。
Kubernetes v1.33 [deprecated]
EndpointSlice APIは、古いEndpoints APIの進化形です。非推奨のEndpoints APIには、EndpointSliceと比較していくつかの問題があります。
このため、すべてのクライアントはEndpointsではなくEndpointSlice APIを使用することをお勧めします。
Kubernetesは、単一のEndpointsオブジェクトに収まるエンドポイントの数を制限します。Serviceのバッキングエンドポイントが1000を超えると、KubernetesはEndpointsオブジェクトのデータを切り捨てます。Serviceは複数のEndpointSliceにリンクできるため、1000個のバッキングエンドポイント制限はレガシーEndpoints APIにのみ影響します。
その場合、Kubernetesは最大1000個の可能なバックエンドエンドポイントを選択してEndpointsオブジェクトに保存し、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において、ユーザーは1つ以上のポートを公開する必要があります。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は無効です。
アプリケーションのいくつかの部分(例えば、フロントエンドなど)において、クラスターの外部からアクセス可能な外部IPアドレス上にServiceを公開したい場合があります。
KubernetesのServiceタイプを使用すると、必要なServiceの種類を指定できます。
利用可能なtypeの値とその振る舞いは以下の通りです:
ClusterIPtypeを明示的に指定しない場合に使用されるデフォルトです。
IngressまたはGatewayを使用して、Serviceをパブリックインターネットに公開できます。NodePortNodePort)上でServiceを公開します。ノードポートを利用可能にするために、Kubernetesはtype: ClusterIPのServiceを要求したかのように、クラスターIPアドレスを設定します。LoadBalancerExternalNameexternalNameフィールドの内容(例えば、ホスト名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アドレスを割り当てません。詳細については、Headless Serviceを参照してください。
Service作成リクエストの一部として、独自のクラスターIPアドレスを指定できます。これを行うには、.spec.clusterIPフィールドを設定します。例えば、再利用したい既存のDNSエントリがある場合や、特定のIPアドレス用に構成されており再構成が難しいレガシーシステムがある場合などです。
選択するIPアドレスは、APIサーバー用に構成されたservice-cluster-ip-range CIDR範囲内の有効なIPv4またはIPv6アドレスである必要があります。
無効なclusterIPアドレス値でServiceを作成しようとすると、APIサーバーは問題があることを示すために422 HTTPステータスコードを返します。
2つの異なるServiceが同じIPアドレスを使用しようとするリスクと影響をKubernetesがどのように軽減するかについては、衝突の回避を参照してください。
type: NodePorttypeフィールドをNodePortに設定すると、Kubernetesコントロールプレーンは--service-node-port-rangeフラグで指定された範囲(デフォルト: 30000-32767)からポートを割り当てます。
各ノードは、そのポート(すべてのノードで同じポート番号)をServiceにプロキシします。
Serviceは、割り当てられたポートを.spec.ports[*].nodePortフィールドで報告します。
NodePortを使用すると、独自のロードバランシングソリューションをセットアップしたり、Kubernetesによって完全にサポートされていない環境を構成したり、1つ以上のノードのIPアドレスを直接公開したりする自由が得られます。
ノードポート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コントロールプレーンはある範囲から1つポートを割り当てます(デフォルト値の範囲:30000-32767)
nodePort: 30007
NodePort Serviceへのポート割り当てのポリシーは、自動割り当てと手動割り当ての両方のシナリオに適用されます。ユーザーが特定のポートを使用するNodePort Serviceを作成しようとすると、ターゲットポートがすでに割り当てられている別のポートと競合する可能性があります。
この問題を回避するために、NodePort Serviceのポート範囲は2つの帯域に分割されています。 動的ポート割り当てはデフォルトで上位帯域を使用し、上位帯域が使い果たされると下位帯域を使用する場合があります。ユーザーは、ポート衝突のリスクが低い下位帯域から割り当てることができます。
type: NodePort ServiceのカスタムIPアドレス構成クラスター内のノードを設定して、ノードポートServiceの提供に特定の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 Service用にループバックインターフェースのみを選択します。
--nodeport-addressesのデフォルトは空のリストです。
これは、kube-proxyがNodePortのすべての利用可能なネットワークインターフェースを考慮すべきであることを意味します。(これは以前のKubernetesリリースとも互換性があります。)
<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コンポーネントは、割り当てられたノードポートにトラフィックを転送するように外部ロードバランサーを構成します。
クラウドプロバイダーの実装がこれをサポートしている場合は、ノードポートの割り当てを省略するようにロードバランシングされたServiceを構成できます。
一部のクラウドプロバイダーでは、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に異なるプロトコルを使用できます。
Kubernetes v1.24 [stable]
フィールドspec.allocateLoadBalancerNodePortsをfalseに設定することで、type: LoadBalancerのServiceのノードポート割り当てをオプションで無効にできます。これは、ノードポートを使用するのではなく、Podに直接トラフィックをルーティングするロードバランサー実装にのみ使用する必要があります。デフォルトでは、spec.allocateLoadBalancerNodePortsはtrueであり、タイプLoadBalancerのServiceは引き続きノードポートを割り当てます。既存のServiceでspec.allocateLoadBalancerNodePortsがfalseに設定されている場合、割り当てられたノードポートは自動的に割り当て解除されません。それらのノードポートを割り当て解除するには、すべてのServiceポートのnodePortsエントリを明示的に削除する必要があります。
Kubernetes v1.24 [stable]
typeがLoadBalancerに設定されているServiceの場合、.spec.loadBalancerClassフィールドを使用すると、クラウドプロバイダーのデフォルト以外のロードバランサー実装を使用できます。
デフォルトでは、.spec.loadBalancerClassは設定されておらず、LoadBalancerタイプのServiceは、クラスターが--cloud-providerコンポーネントフラグを使用してクラウドプロバイダーで構成されている場合、クラウドプロバイダーのデフォルトのロードバランサー実装を使用します。
.spec.loadBalancerClassを指定すると、指定されたクラスに一致するロードバランサー実装がServiceを監視していると想定されます。
デフォルトのロードバランサー実装(例えば、クラウドプロバイダーによって提供されるもの)は、このフィールドが設定されているServiceを無視します。
spec.loadBalancerClassは、タイプLoadBalancerのServiceにのみ設定できます。
一度設定すると変更できません。
spec.loadBalancerClassの値は、"internal-vip"や"example.com/internal-vip"などのオプションのプレフィックスを持つラベルスタイルの識別子である必要があります。
プレフィックスのない名前はエンドユーザー用に予約されています。
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つのケースがあります。
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: ExternalNameExternalNameタイプのServiceは、my-serviceやcassandraのような一般的なセレクターではなく、DNS名にServiceをマッピングします。spec.externalNameパラメータでこれらのServiceを指定します。
例えば、このService定義は、prod名前空間内の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アドレスに直接マッピングしたい場合は、Headless Serviceの使用を検討してください。
ホストmy-service.prod.svc.cluster.localを検索すると、クラスターDNS Serviceは値my.database.example.comを持つCNAMEレコードを返します。
my-serviceへのアクセスは他のServiceと同じように機能しますが、リダイレクトがプロキシや転送ではなくDNSレベルで行われるという重要な違いがあります。
後でデータベースをクラスター内に移動することにした場合は、そのPodを開始し、適切なセレクターまたはエンドポイントを追加し、Serviceのtypeを変更できます。
HTTPやHTTPSなどの一般的なプロトコルでExternalNameを使用すると、問題が発生する場合があります。 ExternalNameを使用する場合、クラスター内のクライアントが使用するホスト名は、ExternalNameが参照する名前とは異なります。
ホスト名を使用するプロトコルの場合、この違いによりエラーや予期しない応答が発生する可能性があります。
HTTPリクエストには、オリジンサーバーが認識しないHost:ヘッダーが含まれます。
TLSサーバーは、クライアントが接続したホスト名に一致する証明書を提供できません。
ロードバランシングや単一のService IPが必要ない場合があります。
この場合、クラスターIPアドレス(.spec.clusterIP)に"None"を明示的に指定することで、_Headless Service_と呼ばれるものを作成できます。
Headless Serviceを使用すると、Kubernetesの実装に縛られることなく、他のサービスディスカバリーメカニズムと連携できます。
Headless Serviceの場合、クラスターIPは割り当てられず、kube-proxyはこれらのServiceを処理しません。また、プラットフォームによってロードバランシングやプロキシは行われません。
Headless Serviceを使用すると、クライアントは好みのPodに直接接続できます。HeadlessであるServiceは、仮想IPアドレスとプロキシを使用してルートとパケット転送を構成しません。代わりに、Headless Serviceは、クラスターのDNSサービスを通じて提供される内部DNSレコードを介して、個々のPodのエンドポイントIPアドレスを報告します。
Headless Serviceを定義するには、.spec.typeをClusterIP(これはtypeのデフォルトでもあります)に設定し、さらに.spec.clusterIPをNoneに設定したServiceを作成します。
文字列値Noneは特殊なケースであり、.spec.clusterIPフィールドを設定しないままにするのと同じではありません。
DNSが自動的に構成される方法は、Serviceにセレクターが定義されているかどうかによって異なります。
セレクターを定義するHeadless Serviceの場合、エンドポイントコントローラーはKubernetes APIにEndpointSlicesを作成し、ServiceをバックアップするPodを直接指すAまたはAAAAレコード(IPv4またはIPv6アドレス)を返すようにDNS構成を変更します。
セレクターを定義しないHeadless Serviceの場合、コントロールプレーンはEndpointSliceオブジェクトを作成しません。ただし、DNSシステムは以下を探して構成します。
type: ExternalName ServiceのDNS CNAMEレコード。ExternalName以外のすべてのServiceタイプの、Serviceの準備完了エンドポイントのすべてのIPアドレスのDNS A / AAAAレコード。
セレクターなしでHeadless Serviceを定義する場合、portはtargetPortと一致する必要があります。
クラスター内で実行されているクライアントの場合、KubernetesはServiceを見つけるための2つの主要なモード(環境変数とDNS)をサポートしています。
PodがNode上で実行されると、kubeletはアクティブな各Serviceに一連の環境変数を追加します。{SVCNAME}_SERVICE_HOSTおよび{SVCNAME}_SERVICE_PORT変数を追加します。ここで、Service名は大文字になり、ダッシュはアンダースコアに変換されます。
例えば、TCPポート6379を公開し、クラスターIPアドレス10.0.0.11が割り当てられたredis-primaryというServiceは、次の環境変数を生成します。
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
Serviceにアクセスする必要があるPodがあり、環境変数メソッドを使用してポートとクラスターIPをクライアントPodに公開している場合、クライアントPodが存在する前にServiceを作成する必要があります。 そうしないと、それらのクライアントPodには環境変数が設定されません。
DNSのみを使用してServiceのクラスターIPを検出する場合、この順序の問題について心配する必要はありません。
Kubernetesは、Docker Engineの「レガシーコンテナリンク」機能と互換性のある変数もサポートおよび提供しています。
Kubernetesでこれがどのように実装されているかを確認するには、makeLinkVariablesを読むことができます。
アドオンを使用して、Kubernetesクラスター用のDNSサービスをセットアップできます(そして、ほぼ常にそうすべきです)。
CoreDNSなどのクラスター対応DNSサーバーは、新しいServiceについてKubernetes APIを監視し、それぞれに一連のDNSレコードを作成します。クラスター全体でDNSが有効になっている場合、すべてのPodはDNS名によってServiceを自動的に解決できるはずです。
例えば、Kubernetes名前空間my-nsにmy-serviceというServiceがある場合、コントロールプレーンとDNSサービスが連携してmy-service.my-nsのDNSレコードを作成します。my-ns名前空間内のPodは、my-serviceの名前検索を行うことでServiceを見つけることができるはずです(my-service.my-nsも機能します)。
他の名前空間のPodは、名前をmy-service.my-nsとして修飾する必要があります。これらの名前は、Serviceに割り当てられたクラスターIPに解決されます。
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とサービスプロキシを読むと、Kubernetesが仮想IPアドレスを使用してServiceを公開するために提供するメカニズムが説明されています。
.spec.internalTrafficPolicyおよび.spec.externalTrafficPolicyフィールドを設定して、Kubernetesが健全な(「準備完了」)バックエンドにトラフィックをルーティングする方法を制御できます。
詳細については、トラフィックポリシーを参照してください。
Kubernetes v1.33 [stable](enabled by default).spec.trafficDistributionフィールドは、Kubernetes Service内のトラフィックルーティングに影響を与える別の方法を提供します。トラフィックポリシーは厳密な意味的保証に焦点を当てていますが、トラフィック分散を使用すると、好み(トポロジー的に近いエンドポイントへのルーティングなど)を表現できます。これにより、パフォーマンス、コスト、または信頼性を最適化できます。Kubernetes 1.34では、次のフィールド値がサポートされています。
PreferCloseKubernetes v1.34 [beta](enabled by default)Kubernetes 1.34では、2つの追加の値が利用可能です(PreferSameTrafficDistribution フィーチャーゲートが無効になっていない限り):
PreferSameZonePreferCloseのエイリアスであり、意図されたセマンティクスについてより明確です。PreferSameNodeフィールドが設定されていない場合、実装はデフォルトのルーティング戦略を適用します。
詳細については、トラフィック分散を参照してください。
特定のクライアントからの接続が毎回同じPodに渡されるようにしたい場合は、クライアントのIPアドレスに基づいてセッションアフィニティを構成できます。詳細については、セッションアフィニティを参照してください。
1つ以上のクラスターノードにルーティングする外部IPがある場合、Kubernetes ServiceはそれらのexternalIPsで公開できます。ネットワークトラフィックがクラスターに到着し、外部IP(宛先IPとして)とポートがそのServiceと一致すると、Kubernetesが構成したルールとルートにより、トラフィックがそのServiceのエンドポイントの1つにルーティングされることが保証されます。
Serviceを定義するとき、任意のServiceタイプに対してexternalIPsを指定できます。
以下の例では、"my-service"という名前のServiceは、クライアントがTCPを使用して"198.51.100.32:80"(.spec.externalIPs[]と.spec.ports[].portから計算)でアクセスできます。
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
externalIPsの割り当てを管理しません。これらはクラスター管理者の責任です。Serviceは、Kubernetes REST APIのトップレベルリソースです。Service APIオブジェクトの詳細を確認できます。
ServiceとそれらがKubernetesにどのように適合するかについて詳しく学びましょう:
その他のコンテキストについては、以下をお読みください:
Kubernetes v1.19 [stable]
クラスター内のServiceに対する外部からのアクセス(主にHTTP)を管理するAPIオブジェクトです。
Ingressは負荷分散、SSL終端、名前ベースの仮想ホスティングの機能を提供します。
簡単のために、このガイドでは次の用語を定義します。
Ingressはクラスター外からクラスター内ServiceへのHTTPとHTTPSのルートを公開します。トラフィックのルーティングはIngressリソース上で定義されるルールによって制御されます。
全てのトラフィックを単一のServiceに送る単純なIngressの例を示します。
図. Ingress
IngressはServiceに対して、外部疎通できるURL、負荷分散トラフィック、SSL/TLS終端の機能や、名前ベースの仮想ホスティングを提供するように設定できます。Ingressコントローラーは通常はロードバランサーを使用してIngressの機能を実現しますが、エッジルーターや、追加のフロントエンドを構成してトラフィックの処理を支援することもできます。
Ingressは任意のポートやプロトコルを公開しません。HTTPやHTTPS以外のServiceをインターネットに公開する場合、Service.Type=NodePortやService.Type=LoadBalancerのServiceタイプを一般的には使用します。
Ingressを提供するためにはIngressコントローラーが必要です。Ingressリソースを作成するのみでは何の効果もありません。
ingress-nginxのような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にはapiVersion、kind、metadataやspecフィールドが必要です。Ingressオブジェクトの名前は、有効なDNSサブドメイン名である必要があります。設定ファイルに関する一般的な情報は、アプリケーションのデプロイ、コンテナの設定、リソースの管理を参照してください。Ingressでは、Ingressコントローラーに依存しているいくつかのオプションの設定をするためにアノテーションを一般的に使用します。例としては、rewrite-targetアノテーションなどがあります。Ingressコントローラーの種類が異なれば、サポートするアノテーションも異なります。サポートされているアノテーションについて学ぶためには、使用するIngressコントローラーのドキュメントを確認してください。
Ingress Specは、ロードバランサーやプロキシサーバーを設定するために必要な全ての情報を持っています。最も重要なものとして、外部からくる全てのリクエストに対して一致したルールのリストを含みます。IngressリソースはHTTP(S)トラフィックに対してのルールのみサポートしています。
ingressClassNameが省略された場合、デフォルトのIngressClassを定義する必要があります。
デフォルトのIngressClassを定義しなくても動作するIngressコントローラーがいくつかあります。例えば、Ingress-NGINXコントローラーはフラグ
--watch-ingress-without-classで設定できます。ただし、下記のようにデフォルトのIngressClassを指定することを推奨します。
各HTTPルールは以下の情報を含みます。
/testpath)。各パスにはservice.nameとservice.port.nameまたはservice.port.numberで定義されるバックエンドが関連づけられます。ロードバランサーがトラフィックを関連づけられたServiceに転送するために、外部からくるリクエストのホスト名とパスが条件と一致させる必要があります。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と扱うことも、PrefixやExactと同一のパスタイプと扱うこともできます。
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ヘッダーのhostがhostフィールドと一致することが必要です。
ワイルドカードによる一致では、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は異なったコントローラーで実装されうるため、しばしば異なった設定を必要とします。 各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コントローラーに依存します。
Ingressコントローラーによっては、クラスター全体で設定したパラメーターを使用できる場合もあれば、1つのNamespaceに対してのみ設定したパラメーターを使用できる場合もあります。
IngressClassパラメーターのデフォルトのスコープは、クラスター全体です。
.spec.parametersフィールドを設定して.spec.parameters.scopeフィールドを設定しなかった場合、または.spec.parameters.scopeをClusterに設定した場合、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
Kubernetes v1.23 [stable]
.spec.parametersフィールドを設定して.spec.parameters.scopeフィールドをNamespaceに設定した場合、IngressClassはNamespaceスコープのリソースを参照します。また.spec.parameters内のnamespaceフィールドには、使用するパラメーターが含まれているNamespaceを設定する必要があります。
パラメーターのkind(およびapiGroup)はNamespaceスコープのAPI(例えば:ConfigMap)を指し、パラメーターのnameはnamespaceで指定したNamespace内の特定のリソースを特定します。
Namespaceスコープのパラメーターはクラスターオペレーターがワークロードに使用される設定(例えば:ロードバランサー設定、APIゲートウェイ定義)に対する制御を委譲するのに役立ちます。クラスタースコープパラメーターを使用した場合は以下のいずれかになります:
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.kubernetes.io/is-default-classアノテーションをtrueに設定すると、ingressClassNameフィールドが指定されないIngressにはこのデフォルト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
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を利用するためのものです。
<pending>となっているのを確認できます。ファンアウト設定では単一のIPアドレスのトラフィックを、リクエストされたHTTP URIに基づいて1つ以上のServiceに転送します。Ingressによってロードバランサーの数を少なくすることができます。例えば、以下のように設定します。
図. 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(service1、service2)が存在する限り、Ingressの条件を満たす実装固有のロードバランサーを構築します。
構築が完了すると、ADDRESSフィールドでロードバランサーのアドレスを確認できます。
名前ベースのバーチャルホストは、HTTPトラフィックを同一のIPアドレスの複数のホスト名に転送することをサポートしています。
図. 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の秘密鍵と証明書を含んだSecretを指定することにより、Ingressをセキュアにできます。Ingressは単一のTLSポートである443番ポートのみサポートし、IngressでTLS終端を行うことを想定しています。IngressからServiceやPodへのトラフィックは平文です。IngressのTLS設定のセクションで異なるホストを指定すると、それらのホストはSNI TLSエクステンション(IngressコントローラーがSNIをサポートしている場合)を介して指定されたホスト名に対し、同じポート上で多重化されます。TLSのSecretはtls.crtとtls.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セクションのhostsはrulesセクションの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
Ingressコントローラーは、負荷分散アルゴリズムやバックエンドの重みスキームなど、すべてのIngressに適用されるいくつかの負荷分散ポリシーの設定とともにブートストラップされます。発展した負荷分散のコンセプト(例: セッションの永続化、動的重み付けなど)はIngressによってサポートされていません。代わりに、それらの機能はService用のロードバランサーを介して利用できます。
ヘルスチェックの機能はIngressによって直接には公開されていませんが、Kubernetesにおいて、同等の機能を提供するReadiness Probeのようなコンセプトが存在することは注目に値します。コントローラーがどのようにヘルスチェックを行うかについては、コントローラーのドキュメントを参照してください(例えばnginx、またはGCE)。
リソースを編集することで、既存の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リソースを直接含まずにサービスを公開する方法は複数あります。
継続的に実行され、複製されたアプリケーションの準備ができたので、ネットワーク上で公開することが可能になります。 Kubernetesのネットワークのアプローチについて説明する前に、Dockerの「通常の」ネットワーク手法と比較することが重要です。
デフォルトでは、Dockerはホストプライベートネットワーキングを使用するため、コンテナは同じマシン上にある場合にのみ他のコンテナと通信できます。 Dockerコンテナがノード間で通信するには、マシンのIPアドレスにポートを割り当ててから、コンテナに転送またはプロキシする必要があります。 これは明らかに、コンテナが使用するポートを非常に慎重に調整するか、ポートを動的に割り当てる必要があることを意味します。
コンテナを提供する複数の開発者やチーム間でポートの割り当てを調整することは、規模的に大変困難であり、ユーザが制御できないクラスターレベルの問題にさらされます。 Kubernetesでは、どのホストで稼働するかに関わらず、Podが他のPodと通信できると想定しています。 すべてのPodに独自のクラスタープライベートIPアドレスを付与するため、Pod間のリンクを明示的に作成したり、コンテナポートをホストポートにマップしたりする必要はありません。 これは、Pod内のコンテナがすべてlocalhostの相互のポートに到達でき、クラスター内のすべてのPodがNATなしで相互に認識できることを意味します。 このドキュメントの残りの部分では、このようなネットワークモデルで信頼できるサービスを実行する方法について詳しく説明します。
このガイドでは、シンプルなnginxサーバーを使用して概念実証を示します。
前の例でネットワークモデルを紹介しましたが、再度ネットワークの観点に焦点を当てましょう。 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と同様に、ポートは引き続きホストノードのインターフェースに公開できますが、ネットワークモデルにより、この必要性は根本的に減少します。
興味があれば、これをどのように達成するかについて詳しく読むことができます。
そのため、フラットでクラスター全体のアドレス空間で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は完全に仮想的なもので、ホスト側のネットワークには接続できないことに注意してください。
この仕組みに興味がある場合は、サービスプロキシの詳細をお読みください。
Kubernetesは、環境変数とDNSの2つの主要なService検索モードをサポートしています。 前者はそのまま使用でき、後者はCoreDNSクラスターアドオンを必要とします。
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
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
これまでは、クラスター内からnginxサーバーにアクセスしただけでした。 サービスをインターネットに公開する前に、通信チャネルが安全であることを確認する必要があります。 これには、次のものが必要です:
これらはすべて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マニフェストに関する注目すべき点:
/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を外部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サービスのTypeをNodePortから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
...
Ingressリソースが動作するためには、クラスターでIngressコントローラーが実行されている必要があります。
kube-controller-managerバイナリの一部として実行される他のタイプのコントローラーとは異なり、Ingressコントローラーはクラスターで自動的に起動されません。このページを使用して、クラスターに最適なIngressコントローラーの実装を選択してください。
プロジェクトとしてのKubernetesは現在、AWS、GCE、およびnginxの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コントローラーはわずかに異なる動作をします。
拡張可能でロール指向な、プロトコルを意識した設定メカニズムを使用して、ネットワークサービスを利用可能にします。 Gateway APIは、動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類を含むアドオンです。
Gateway 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の種類の関係を示しています:
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はトラフィックを処理するインフラストラクチャのインスタンスを記述します。 これは、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の種類は、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に対するリクエストフローは次のようになります:
http://www.example.comに対するHTTPリクエストの準備を開始します。Gateway APIは幅広い機能をカバーし、広く実装されています。 この組み合わせは、APIがどこで使われても一貫した体験を提供することを保証するために、明確な適合性の定義とテストを必要とします。
リリースチャンネル、サポートレベル、そして適合テストの実行などの詳細を理解するためには、適合性のドキュメントを参照してください。
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仕様を参照してください。
Kubernetes v1.17 [beta]
EndpointSliceは、Kubernetesクラスター内にあるネットワークエンドポイントを追跡するための単純な手段を提供します。EndpointSliceは、よりスケーラブルでより拡張可能な、Endpointの代わりとなるものです。
Endpoint APIはKubernetes内のネットワークエンドポイントを追跡する単純で直観的な手段を提供してきました。 残念ながら、KubernetesクラスターやServiceが大規模になり、より多くのトラフィックを処理し、より多くのバックエンドPodに送信するようになるにしたがって、Endpoint APIの限界が明らかになってきました。 最も顕著な問題の1つに、ネットワークエンドポイントの数が大きくなったときのスケーリングの問題があります。
Serviceのすべてのネットワークエンドポイントが単一のEndpointリソースに格納されていたため、リソースのサイズが非常に大きくなる場合がありました。これがKubernetesのコンポーネント(特に、マスターコントロールプレーン)の性能に悪影響を与え、結果として、Endpointに変更があるたびに、大量のネットワークトラフィックと処理が発生するようになってしまいました。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種類のアドレスをサポートします。
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を簡単に検索できるようになっています。
場合によっては、アプリケーションはカスタムEndpointリソースを作成します。これらのアプリケーションがEndpointリソースとEndpointSliceリソースの両方に同時に書き込む必要がないようにするために、クラスターのコントロールプレーンは、ほとんどのEndpointリソースを対応するEndpointSliceにミラーリングします。
コントロールプレーンは、次の場合を除いて、Endpointリソースをミラーリングします。
endpointslice.kubernetes.io/skip-mirrorラベルがtrueに設定されています。control-plane.alpha.kubernetes.io/leaderアノテーションを持っています。個々のEndpointリソースは、複数のEndpointSliceに変換される場合があります。これは、Endpointリソースに複数のサブセットがある場合、または複数のIPファミリ(IPv4およびIPv6)を持つエンドポイントが含まれている場合に発生します。サブセットごとに最大1000個のアドレスがEndpointSliceにミラーリングされます。
それぞれのEndpointSliceにはポートの集合があり、リソース内のすべてのエンドポイントに適用されます。サービスが名前付きポートを使用した場合、Podが同じ名前のポートに対して、結果的に異なるターゲットポート番号が使用されて、異なるEndpointSliceが必要になる場合があります。これはサービスの部分集合がEndpointにグループ化される場合と同様です。
コントロールプレーンはEndpointSliceをできる限り充填しようとしますが、積極的にリバランスを行うことはありません。コントローラーのロジックは極めて単純で、以下のようになっています。
ここで重要なのは、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-proxyのEndpointSliceCache実装にあります。
IPアドレスまたはポートのレベル(OSI参照モデルのレイヤー3または4)でトラフィックフローを制御したい場合、クラスター内の特定のアプリケーションにKubernetesのネットワークポリシーを使用することを検討してください。ネットワークポリシーはアプリケーション中心の構造であり、Podがネットワークを介して多様な「エンティティ」(「Endpoint」や「Service」のようなKubernetesに含まれる特定の意味を持つ共通の用語との重複を避けるため、ここではエンティティという単語を使用します。)と通信する方法を指定できます。
Podが通信できるエンティティは以下の3つの識別子の組み合わせによって識別されます。
Podベースもしくは名前空間ベースのネットワークポリシーを定義する場合、セレクターを使用してセレクターに一致するPodとの間で許可されるトラフィックを指定します。
一方でIPベースのネットワークポリシーが作成されると、IPブロック(CIDRの範囲)に基づいてポリシーが定義されます。
ネットワークポリシーは、ネットワークプラグインにより実装されます。ネットワークポリシーを使用するには、NetworkPolicyをサポートするネットワークソリューションを使用しなければなりません。ネットワークポリシーを実装したコントローラーを使用せずにNetworkPolicyリソースを作成した場合は、何も効果はありません。
デフォルトでは、Podは分離されていない状態(non-isolated)となるため、すべてのソースからのトラフィックを受信します。
Podを選択するNetworkPolicyが存在すると、Podは分離されるようになります。名前空間内に特定のPodを選択するNetworkPolicyが1つでも存在すると、そのPodはいずれかのNetworkPolicyで許可されていないすべての接続を拒否するようになります。(同じ名前空間内のPodでも、どのNetworkPolicyにも選択されなかった他のPodは、引き続きすべてのトラフィックを許可します。)
ネットワークポリシーは追加式であるため、競合することはありません。複数のポリシーがPodを選択する場合、そのPodに許可されるトラフィックは、それらのポリシーのingress/egressルールの和集合で制限されます。したがって、評価の順序はポリシーの結果には影響がありません。
リソースの完全な定義については、リファレンスの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
必須フィールド: 他のKubernetesの設定と同様に、NetworkPolicyにもapiVersion、kind、metadataフィールドが必須です。設定ファイルの扱い方に関する一般的な情報については、ConfigMapを使用してコンテナを構成するとオブジェクト管理を参照してください。
spec: NetworkPolicyのspecを見ると、指定した名前空間内で特定のネットワークポリシーを定義するのに必要なすべての情報が確認できます。
podSelector: 各NetworkPolicyには、ポリシーを適用するPodのグループを選択するpodSelectorが含まれます。ポリシーの例では、ラベル"role=db"を持つPodを選択しています。podSelectorを空にすると、名前空間内のすべてのPodが選択されます。
policyTypes: 各NetworkPolicyには、policyTypesとして、Ingress、Egress、またはその両方からなるリストが含まれます。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の例では、次のようにネットワークポリシーを適用します。
追加の例については、ネットワークポリシーを宣言するの説明を参照してください。
toとfromのセレクターの振る舞いingressのfromセクションまたはegressのtoセクションに指定できるセレクターは4種類あります。
podSelector: NetworkPolicyと同じ名前空間内の特定のPodを選択して、ingressの送信元またはegressの送信先を許可します。
namespaceSelector: 特定の名前空間を選択して、その名前空間内のすべてのPodについて、ingressの送信元またはegressの送信先を許可します。
namespaceSelector および podSelector: 1つのtoまたはfromエントリーでnamespaceSelectorとpodSelectorの両方を指定して、特定の名前空間内の特定の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でも、内向きと外向きのトラフィックが許可されないことを保証できます。
Kubernetes v1.19 [beta]
ベータ版の機能として、これはデフォルトで有効化されます。
クラスターレベルでSCTPを無効化するために、クラスター管理者はAPIサーバーで--feature-gates=SCTPSupport=false,…と指定して、SCTPSupportフィーチャーゲートを無効にする必要があります。
Kubernetes1.20現在、ネットワークポリシーAPIに以下の機能は存在しません。 しかし、オペレーティングシステムのコンポーネント(SELinux、OpenVSwitch、IPTablesなど)、レイヤー7の技術(Ingressコントローラー、サービスメッシュ実装)、もしくはアドミッションコントローラーを使用して回避策を実装できる場合があります。 Kubernetesのネットワークセキュリティを初めて使用する場合は、ネットワークポリシーAPIを使用して以下のユーザーストーリーを(まだ)実装できないことに注意してください。これらのユーザーストーリーの一部(全てではありません)は、ネットワークポリシーAPIの将来のリリースで活発に議論されています。
このページではKubernetesによるDNSサポートについて概観します。
KubernetesのDNSはクラスター上でDNS PodとServiceをスケジュールし、DNSの名前解決をするために各コンテナに対してDNS ServiceのIPを使うようにKubeletを設定します。
クラスター内(DNSサーバーそれ自体も含む)で定義された全てのServiceはDNS名を割り当てられます。デフォルトでは、クライアントPodのDNSサーチリストはPod自身のネームスペースと、クラスターのデフォルトドメインを含みます。
下記の例でこの仕組みを説明します。
Kubernetesのbarというネームスペース内でfooという名前のServiceがあると仮定します。barネームスペース内で稼働しているPodは、fooに対してDNSクエリを実行するだけでこのServiceを探すことができます。barとは別のquuxネームスペース内で稼働しているPodは、foo.barに対してDNSクエリを実行するだけでこのServiceを探すことができます。
下記のセクションでは、サポートされているレコードタイプとレイアウトについて詳しくまとめています。
うまく機能する他のレイアウト、名前、またはクエリーは、実装の詳細を考慮し、警告なしに変更されることがあります。
最新の仕様に関する詳細は、KubernetesにおけるDNSベースのServiceディスカバリを参照ください。
"通常の"(Headlessでない)Serviceは、my-svc.my-namespace.svc.cluster.localという形式のDNS A(AAAA)レコードを、ServiceのIPバージョンに応じて割り当てられます。このAレコードはそのServiceのClusterIPへと名前解決されます。
"Headless"(ClusterIPなしの)Serviceもまたmy-svc.my-namespace.svc.cluster.localという形式のDNS A(AAAA)レコードを、ServiceのIPバージョンに応じて割り当てられます。通常のServiceとは異なり、このレコードはServiceによって選択されたPodのIPの一覧へと名前解決されます。クライアントはこの一覧のIPを使うか、その一覧から標準のラウンドロビン方式によって選択されたIPを使います。
SRVレコードは、通常のServiceもしくはHeadless
Servicesの一部である名前付きポート向けに作成されます。それぞれの名前付きポートに対して、そのSRVレコードは_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster.localという形式となります。
通常のServiceに対しては、このSRVレコードはmy-svc.my-namespace.svc.cluster.localという形式のドメイン名とポート番号へ名前解決します。
Headless Serviceに対しては、このSRVレコードは複数の結果を返します。それはServiceの背後にある各Podの1つを返すのと、auto-generated-name.my-svc.my-namespace.svc.cluster.localという形式のPodのドメイン名とポート番号を含んだ結果を返します。
一般的にPodは下記のDNS解決となります。
pod-ip-address.my-namespace.pod.cluster-domain.example
例えば、defaultネームスペースのpodのIPアドレスが172.17.0.3で、クラスターのドメイン名がcluster.localの場合、PodのDNS名は以下になります。
172-17-0-3.default.pod.cluster.local
DeploymentかDaemonSetに作成され、Serviceに公開されるどのPodも以下のDNS解決が利用できます。
pod-ip-address.deployment-name.my-namespace.svc.cluster-domain.example
現在、Podが作成されたとき、そのPodのホスト名はPodのmetadata.nameフィールドの値となります。
Pod Specは、オプションであるhostnameフィールドを持ち、Podのホスト名を指定するために使うことができます。hostnameが指定されたとき、hostnameはそのPodの名前よりも優先されます。例えば、hostnameフィールドが"my-host"にセットされたPodを考えると、Podはそのhostnameが"my-host"に設定されます。
Pod Specはまた、オプションであるsubdomainフィールドも持ち、Podのサブドメイン名を指定するために使うことができます。例えば、"my-namespace"というネームスペース内でhostnameがfooとセットされていて、subdomainがbarとセットされているPodの場合、そのPodは"foo.bar.my-namespace.svc.cluster.local"という名前の完全修飾ドメイン名(FQDN)を持つことになります。
例:
apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo # 実際は、portは必要ありません。
port: 1234
targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-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: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
もしそのPodと同じネームスペース内で、同じサブドメインを持ったHeadless Serviceが存在していた場合、クラスターのDNSサーバーもまた、そのPodの完全修飾ドメイン名(FQDN)に対するA(AAAA)レコードを返します。
例えば、"busybox-1"というホスト名で、"default-subdomain"というサブドメインを持ったPodと、そのPodと同じネームスペース内にある"default-subdomain"という名前のHeadless Serviceがあると考えると、そのPodは自身の完全修飾ドメイン名(FQDN)を"busybox-1.default-subdomain.my-namespace.svc.cluster.local"として扱います。DNSはそのPodのIPを指し示すA(AAAA)レコードを返します。"busybox1"と"busybox2"の両方のPodはそれぞれ独立したA(AAAA)レコードを持ちます。
そのエンドポイントオブジェクトはそのIPに加えてhostnameを任意のエンドポイントアドレスに対して指定できます。
hostnameはPodのA(AAAA)レコードが作成されるために必須となります。hostnameを持たないがsubdomainを持つようなPodは、そのPodのIPアドレスを指し示すHeadless Service(default-subdomain.my-namespace.svc.cluster.local)に対するA(AAAA)レコードのみ作成します。Kubernetes v1.19 [alpha]
前提条件: API Serverに対してSetHostnameAsFQDNフィーチャーゲートを有効にする必要があります。
Podが完全修飾ドメイン名(FQDN)を持つように構成されている場合、そのホスト名は短いホスト名です。
例えば、FQDNがbusybox-1.default-subdomain.my-namespace.svc.cluster-domain.exampleのPodがある場合、
デフォルトではそのPod内のhostnameコマンドはbusybox-1を返し、hostname --fqdnコマンドはFQDNを返します。
PodのspecでsetHostnameAsFQDN: trueを設定した場合、そのPodの名前空間に対してkubeletはPodのFQDNをホスト名に書き込みます。
この場合、hostnameとhostname --fqdnの両方がPodのFQDNを返します。
Linuxでは、カーネルのホスト名のフィールド(struct utsnameのnodenameフィールド)は64文字に制限されています。
Podがこの機能を有効にしていて、そのFQDNが64文字より長い場合、Podは起動に失敗します。
PodはPendingステータス(kubectlでみられるContainerCreating)のままになり、「Podのホスト名とクラスタードメインからFQDNを作成できなかった」や、「FQDNlong-FQDNが長すぎる(64文字が最大, 70文字が要求された)」などのエラーイベントが生成されます。
このシナリオのユーザー体験を向上させる1つの方法は、admission webhook controllerを作成して、ユーザーがDeploymentなどのトップレベルのオブジェクトを作成するときにFQDNのサイズを制御することです。
DNSポリシーはPod毎に設定できます。現在のKubernetesでは次のようなPod固有のDNSポリシーをサポートしています。これらのポリシーはPod SpecのdnsPolicyフィールドで指定されます。
Default": そのPodはPodが稼働しているNodeから名前解決の設定を継承します。詳細に関しては、関連する議論を参照してください。ClusterFirst": "www.kubernetes.io"のようなクラスタードメインのサフィックスにマッチしないようなDNSクエリーは、Nodeから継承された上流のネームサーバーにフォワーディングされます。クラスター管理者は、追加のstubドメインと上流のDNSサーバーを設定できます。このような場合におけるDNSクエリー処理の詳細に関しては、関連する議論を参照してください。ClusterFirstWithHostNet": hostNetworkによって稼働しているPodに対しては、ユーザーは明示的にDNSポリシーを"ClusterFirstWithHostNet"とセットするべきです。None": この設定では、Kubernetesの環境からDNS設定を無視することができます。全てのDNS設定は、Pod Spec内のdnsConfigフィールドを指定して提供することになっています。下記のセクションのPod's DNS configを参照ください。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設定は、ユーザーがPodに対してそのDNS設定上でさらに制御するための手段を提供します。
dnsConfigフィールドはオプションで、どのような設定のdnsPolicyでも共に機能することができます。しかし、PodのdnsPolicyが"None"にセットされていたとき、dnsConfigフィールドは必ず指定されなくてはなりません。
下記の項目は、ユーザーがdnsConfigフィールドに指定可能なプロパティーとなります。
nameservers: そのPodに対するDNSサーバーとして使われるIPアドレスのリストです。これは最大で3つのIPアドレスを指定することができます。PodのdnsPolicyが"None"に指定されていたとき、そのリストは最低1つのIPアドレスを指定しなければならず、もし指定されていなければ、それ以外のdnsPolicyの値の場合は、このプロパティーはオプションとなります。searches: Pod内のホスト名のルックアップのためのDNSサーチドメインのリストです。このプロパティーはオプションです。指定されていたとき、このリストは選択されたDNSポリシーから生成されたサーチドメイン名のベースとなるリストにマージされます。重複されているドメイン名は削除されます。Kubernetesでは最大6つのサーチドメインの設定を許可しています。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:
- 1.2.3.4
searches:
- ns1.svc.cluster.local
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
上記のPodが作成されたとき、testコンテナは、コンテナ内の/etc/resolv.confファイル内にある下記の内容を取得します。
nameserver 1.2.3.4
search ns1.svc.cluster.local my.dns.search.suffix
options ndots:2 edns0
IPv6用のセットアップのためには、サーチパスとname serverは下記のようにセットアップするべきです。
$ kubectl exec -it dns-example -- cat /etc/resolv.conf
nameserver 2001:db8:30::a
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
PodのDNS設定と"None"というDNSポリシーの利用可能なバージョンに関しては下記の通りです。
| k8s version | Feature support |
|---|---|
| 1.14 | ステーブル |
| 1.10 | β版 (デフォルトで有効) |
| 1.9 | α版 |
DNS設定の管理方法に関しては、DNS Serviceの設定 を確認してください。
Kubernetes v1.16 [alpha]
IPv4/IPv6デュアルスタックを利用すると、IPv4とIPv6のアドレスの両方をPodおよびServiceに指定できるようになります。
KubernetesクラスターでIPv4/IPv6デュアルスタックのネットワークを有効にすれば、クラスターはIPv4とIPv6のアドレスの両方を同時に割り当てることをサポートするようになります。
KubernetesクラスターでIPv4/IPv6デュアルスタックを有効にすると、以下の機能が提供されます。
IPv4/IPv6デュアルスタックのKubernetesクラスターを利用するには、以下の前提条件を満たす必要があります。
IPv4/IPv6デュアルスタックを有効にするには、クラスターの関連コンポーネントでIPv6DualStackフィーチャーゲートを有効にして、デュアルスタックのクラスターネットワークの割り当てを以下のように設定します。
--feature-gates="IPv6DualStack=true"--service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>--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です--feature-gates="IPv6DualStack=true"--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を参照してください)
クラスターでIPv4/IPv6デュアルスタックのネットワークを有効にした場合、IPv4またはIPv6のいずれかのアドレスを持つServiceを作成できます。Serviceのcluster IPのアドレスファミリーは、Service上に.spec.ipFamilyフィールドを設定することで選択できます。このフィールドを設定できるのは、新しいServiceの作成時のみです。.spec.ipFamilyフィールドの指定はオプションであり、ServiceとIngressでIPv4とIPv6を有効にする予定がある場合にのみ使用するべきです。このフィールドの設定は、外向きのトラフィックに対する要件には含まれません。
--service-cluster-ip-rangeフラグで設定した、最初のservice cluster IPの範囲のアドレスファミリーです。.spec.ipFamilyは、次のいずれかに設定できます。
IPv4: APIサーバーはipv4のservice-cluster-ip-rangeの範囲からIPアドレスを割り当てますIPv6: APIサーバーはipv6のservice-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: 9376IPv6が有効になった外部ロードバランサーをサポートしているクラウドプロバイダーでは、typeフィールドにLoadBalancerを指定し、ipFamilyフィールドにIPv6を指定することにより、クラウドロードバランサーをService向けにプロビジョニングできます。
パブリックおよび非パブリックでのルーティングが可能なIPv6アドレスのブロックを利用するためには、クラスターがベースにしているCNIプロバイダーがIPv6の転送を実装している必要があります。もし非パブリックでのルーティングが可能なIPv6アドレスを使用するPodがあり、そのPodをクラスター外の送信先(例:パブリックインターネット)に到達させたい場合、外向きのトラフィックと応答の受信のためにIPマスカレードを設定する必要があります。ip-masq-agentはデュアルスタックに対応しているため、デュアルスタックのクラスター上でのIPマスカレードにはip-masq-agentが利用できます。
Kubernetes v1.23 [beta]
Topology Aware Routingは、トラフィックを発信元のゾーンに維持するようにルーティング動作を調整します。場合によっては、コストを削減したり、ネットワークパフォーマンスを向上させたりすることができます。
Kubernetesクラスターは、マルチゾーン環境で展開されることが多くなっています。 Topology Aware Routingは、トラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。EndpointSliceコントローラーはServiceのendpointを計算する際に、各endpointのトポロジー(リージョンとゾーン)を考慮し、ゾーンに割り当てるためのヒントフィールドに値を入力します。kube-proxyのようなクラスターコンポーネントは、次にこれらのヒントを消費し、それらを使用してトラフィックがルーティングされる方法に影響を与えることが可能です(トポロジー的に近いendpointを優先します)。
service.kubernetes.io/topology-aware-hintsアノテーションを使用して制御されていました。service.kubernetes.io/topology-modeアノテーションをautoに設定すると、サービスに対してTopology Aware Routingを有効にすることができます。各ゾーンに十分なendpointがある場合、個々のendpointを特定のゾーンに割り当てるために、トポロジーヒントがEndpointSliceに入力され、その結果、トラフィックは発信元の近くにルーティングされます。
この機能は、次の場合に最も効果的に動作します。
トラフィックの大部分が単一のゾーンから発信されている場合、トラフィックはそのゾーンに割り当てられたendpointのサブセットに過負荷を与える可能性があります。受信トラフィックが単一のゾーンから発信されることが予想される場合、この機能は推奨されません。
3つのゾーンからなるクラスターでは、これは9つ以上のendpointがあることを意味します。ゾーン毎のendpointが3つ未満の場合、EndpointSliceコントローラーはendpointを均等に割り当てることができず、代わりにデフォルトのクラスター全体のルーティングアプローチに戻る可能性が高く(約50%)なります。
「Auto」ヒューリスティックは、各ゾーンに多数のendpointを比例的に割り当てようとします。このヒューリスティックは、非常に多くのendpointを持つサービスに最適です。
このヒューリスティックが有効な場合、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は、EndpointSliceコントローラーによって設定されたヒントに基づいて、ルーティング先のendpointをフィルター処理します。ほとんどの場合、これはkube-proxyが同じゾーン内のendpointにトラフィックをルーティングできることを意味します。コントローラーが別のゾーンからendpointを割り当てて、ゾーン間でendpointがより均等に分散されるようにする場合があります。これにより、一部のトラフィックが他のゾーンにルーティングされます。
各ノードのKubernetesコントロールプレーンとkube-proxyは、Topology Aware Hintを使用する前に、いくつかのセーフガードルールを適用します。これらがチェックアウトされない場合、kube-proxyは、ゾーンに関係なく、クラスター内のどこからでもendpointを選択します。
endpointの数が不十分です: クラスター内のゾーンよりもendpointが少ない場合、コントローラーはヒントを割り当てません。
バランスの取れた割り当てを実現できません: 場合によっては、ゾーン間でendpointのバランスの取れた割り当てを実現できないことがあります。たとえば、ゾーンaがゾーンbの2倍の大きさであるが、endpointが2つしかない場合、ゾーンaに割り当てられたendpointはゾーンbの2倍のトラフィックを受信する可能性があります。この「予想される過負荷」値が各ゾーンの許容しきい値を下回ることができない場合、コントローラーはヒントを割り当てません。重要なことに、これはリアルタイムのフィードバックに基づいていません。それでも、個々のendpointが過負荷になる可能性があります。
1つ以上のノードの情報が不十分です: ノードにtopology.kubernetes.io/zoneラベルがないか、割り当て可能なCPUの値を報告していない場合、コントロールプレーンはtopology-aware endpoint hintsを設定しないため、kube-proxyはendpointをゾーンでフィルタリングしません。
1つ以上のendpointにゾーンヒントが存在しません: これが発生すると、kube-proxyはTopology Aware Hintから、またはTopology Aware Hintへの移行が進行中であると見なします。この状態のサービスに対してendpointをフィルタリングすることは危険であるため、kube-proxyはすべてのendpointを使用するようにフォールバックします。
ゾーンはヒントで表されません: kube-proxyが、実行中のゾーンをターゲットとするヒントを持つendpointを1つも見つけることができない場合、すべてのゾーンのendpointを使用することになります。これは既存のクラスターに新しいゾーンを追加するときに発生する可能性が最も高くなります。
ServiceでexternalTrafficPolicyまたはinternalTrafficPolicyがLocalに設定されている場合、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リリースに含まれています。これは限定的な実装であり、関連する妥当と思われる状況をまだカバーしていない可能性があります。
Kubernetesでは、ServiceはPodの集合上で実行しているアプリケーションを抽象的に公開する方法です。Serviceはクラスター内で仮想IPアドレス(type: ClusterIPのServiceを使用)を持つことができます。クライアントはその仮想IPアドレスを使用してServiceに接続することができます。そしてKubernetesは、そのServiceへのトラフィックを異なる背後のPod間で負荷分散します。
KubernetesがServiceに仮想IPアドレスを割り当てる必要がある場合、2つの方法の内どちらかの方法で行われます:
type: ClusterIPのServiceのために設定されたIP範囲の中から未割り当てのIPアドレスを選びます。クラスター全体を通して、ServiceのClusterIPはユニークでなければいけません。割り当て済みのClusterIPを使用してServiceを作成しようとするとエラーが返ってきます。
時には、クラスター内の他のコンポーネントやユーザーが利用できるように、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を作成することができません。
Kubernetesで実装されているServiceへのClusterIPの割り当て戦略は、衝突リスクを軽減します。
ClusterIPの範囲は、min(max(16, cidrSize / 16), 256)という式に基づいて分割されます。最小で16、最大でも256で、その範囲内で段階的に変化する ように表されます。
動的IP割り当てはデフォルトで上位の帯域を使用し、それが使い切られると下位の範囲を使用します。これにより、ユーザーは下位の帯域を使用して静的な割り当てを行うことができ、衝突のリスクを抑えることができます。
この例では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
この例では、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
この例では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
Kubernetes v1.21 [alpha]
サービス内部トラフィックポリシーを使用すると、内部トラフィック制限により、トラフィックが発信されたノード内のエンドポイントにのみ内部トラフィックをルーティングできます。 ここでの「内部」トラフィックとは、現在のクラスターのPodから発信されたトラフィックを指します。これは、コストを削減し、パフォーマンスを向上させるのに役立ちます。
ServiceInternalTrafficPolicy フィーチャーゲートを有効にすると、.spec.internalTrafficPolicyをLocalに設定して、Service内部のみのトラフィックポリシーを有効にすることができます。
これにより、kube-proxyは、クラスター内部トラフィックにノードローカルエンドポイントのみを使用するようになります。
次の例は、.spec.internalTrafficPolicyをLocalに設定した場合の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.internalTrafficPolicyがLocalであれば、ノードのローカルエンドポイントにのみルーティングできるようにします。Clusterまたは未設定であればすべてのエンドポイントにルーティングできるようにします。
ServiceInternalTrafficPolicyフィーチャーゲートが有効な場合、spec.internalTrafficPolicyのデフォルトはClusterです。
externalTrafficPolicyがLocalに設定されている場合、サービス内部トラフィックポリシーは使用されません。同じServiceだけではなく、同じクラスター内の異なるServiceで両方の機能を使用することができます。