Service、負荷分散とネットワーキング
Kubernetesにおけるネットワーキングの概念とリソース。
Kubernetesのネットワーキングは4つの懸念事項に対処します。
Pod内のコンテナは、ネットワーキングを利用してループバック経由の通信を行います。
クラスターネットワーキングは、異なるPod間の通信を提供します。
Serviceリソースは、Pod内で動作しているアプリケーションへクラスターの外部から到達可能なように露出を許可します。
Serviceを利用して、クラスター内部のみで使用するServiceの公開も可能です。
1 - Service
KubernetesにおけるServiceとは、 クラスター内で1つ以上のPod として実行されているネットワークアプリケーションを公開する方法です。
Kubernetesでは、なじみのないサービスディスカバリーのメカニズムを使用するためにユーザーがアプリケーションの修正をする必要はありません。
KubernetesはPodにそれぞれのIPアドレス割り振りや、Podのセットに対する単一のDNS名を提供したり、それらのPodのセットに対する負荷分散が可能です。
Serviceを利用する動機
Kubernetes Pod はクラスターの状態に合わせて作成され削除されます。Podは揮発的なリソースです。
Deployment をアプリケーションを稼働させるために使用すると、Podを動的に作成・削除してくれます。
各Podはそれ自身のIPアドレスを持ちます。しかしDeploymentでは、ある時点において同時に稼働しているPodのセットは、その後のある時点において稼働しているPodのセットとは異なる場合があります。
この仕組みはある問題を引き起こします。もし、あるPodのセット(ここでは"バックエンド"と呼びます)がクラスター内で他のPodのセット(ここでは"フロントエンド"と呼びます)に対して機能を提供する場合、フロントエンドのPodがワークロードにおけるバックエンドを使用するために、バックエンドのPodのIPアドレスを探し出したり、記録し続けるためにはどうすればよいでしょうか?
ここで Service について説明します。
Serviceリソース
Kubernetesにおいて、ServiceはPodの論理的なセットや、そのPodのセットにアクセスするためのポリシーを定義します(このパターンはよくマイクロサービスと呼ばることがあります)。
ServiceによってターゲットとされたPodのセットは、たいてい セレクター によって定義されます。
その他の方法について知りたい場合はセレクターなしのService を参照してください。
例えば、3つのレプリカが稼働しているステートレスな画像処理用のバックエンドを考えます。これらのレプリカは代替可能です。— フロントエンドはバックエンドが何であろうと気にしません。バックエンドのセットを構成する実際のPodのセットが変更された際、フロントエンドクライアントはその変更を気にしたり、バックエンドのPodのセットの情報を記録しておく必要はありません。
Serviceによる抽象化は、クライアントからバックエンドのPodの管理する責務を分離することを可能にします。
クラウドネイティブのサービスディスカバリー
アプリケーション内でサービスディスカバリーのためにKubernetes APIが使える場合、ユーザーはエンドポイントをAPI Server に問い合わせることができ、またService内のPodのセットが変更された時はいつでも更新されたエンドポイントの情報を取得できます。
非ネイティブなアプリケーションのために、KubernetesはアプリケーションとバックエンドPodの間で、ネットワークポートやロードバランサーを配置する方法を提供します。
Serviceの定義
KubernetesのServiceはPodと同様にRESTのオブジェクトです。他のRESTオブジェクトと同様に、ユーザーはServiceの新しいインスタンスを作成するためにAPIサーバーに対してServiceの定義をPOST
できます。Serviceオブジェクトの名前は、有効なDNSラベル名 である必要があります。
例えば、TCPで9376番ポートで待ち受けていて、app=Myapp
というラベルをもつPodのセットがあるとします。
apiVersion : v1
kind : Service
metadata :
name : my-service
spec :
selector :
app : MyApp
ports :
- protocol : TCP
port : 80
targetPort : 9376
この定義では、"my-service"という名前のついた新しいServiceオブジェクトを作成します。これはapp=Myapp
ラベルのついた各Pod上でTCPの9376番ポートをターゲットとします。
Kubernetesは、このServiceに対してIPアドレス("clusterIP"とも呼ばれます)を割り当てます。これはServiceのプロキシによって使用されます(下記の仮想IPとServiceプロキシ を参照ください)。
Serviceセレクターのコントローラーはセレクターに一致するPodを継続的にスキャンし、“my-service”という名前のEndpointsオブジェクトに対して変更をPOSTします。
備考: Serviceはport
からtargetPort
へのマッピングを行います。デフォルトでは、利便性のためにtargetPort
フィールドはport
フィールドと同じ値で設定されます。
Pod内のポートの定義は名前を設定でき、ServiceのtargetPort
属性にてその名前を参照できます。これは単一の設定名をもつService内で、複数の種類のPodが混合していたとしても有効で、異なるポート番号を介することによって利用可能な、同一のネットワークプロトコルを利用します。
この仕組みはServiceをデプロイしたり、設定を追加する場合に多くの点でフレキシブルです。例えば、バックエンドソフトウェアにおいて、次のバージョンでPodが公開するポート番号を変更するときに、クライアントの変更なしに行えます。
ServiceのデフォルトプロトコルはTCPです。また、他のサポートされているプロトコル も利用可能です。
多くのServiceが、1つ以上のポートを公開する必要があるように、Kubernetesは1つのServiceオブジェクトに対して複数のポートの定義をサポートしています。
各ポート定義は同一のprotocol
または異なる値を設定できます。
セレクターなしのService
Serviceは多くの場合、KubernetesのPodに対するアクセスを抽象化しますが、他の種類のバックエンドも抽象化できます。
例えば:
プロダクション環境で外部のデータベースクラスターを利用したいが、テスト環境では、自身のクラスターが持つデータベースを利用したい場合
Serviceを、異なるNamespace のServiceや他のクラスターのServiceに向ける場合
ワークロードをKubernetesに移行するとき、アプリケーションに対する処理をしながら、バックエンドの一部をKubernetesで実行する場合
このような場合において、ユーザーはPodセレクターなしで Serviceを定義できます。
apiVersion : v1
kind : Service
metadata :
name : my-service
spec :
ports :
- protocol : TCP
port : 80
targetPort : 9376
このServiceはセレクターがないため、対応するEndpointsオブジェクトは自動的に作成されません。
ユーザーはEndpointsオブジェクトを手動で追加することにより、向き先のネットワークアドレスとポートを手動でマッピングできます。
apiVersion : v1
kind : Endpoints
metadata :
name : my-service
subsets :
- addresses :
- ip : 192.0.2.42
ports :
- port : 9376
Endpointsオブジェクトの名前は、有効なDNSサブドメイン名 である必要があります。
備考: Endpointsのipは、loopback (127.0.0.0/8 for IPv4, ::1/128 for IPv6), や
link-local (169.254.0.0/16 and 224.0.0.0/24 for IPv4, fe80::/64 for IPv6)に設定することができません。
kube-proxy が仮想IPを最終的な到達先に設定することをサポートしていないため、Endpointsのipアドレスは他のKubernetes ServiceのClusterIPにすることができません。
セレクターなしのServiceへのアクセスは、セレクターをもっているServiceと同じようにふるまいます。上記の例では、トラフィックはYAMLファイル内で192.0.2.42:9376
(TCP)で定義された単一のエンドポイントにルーティングされます。
ExternalName Serviceはセレクターの代わりにDNS名を使用する特殊なケースのServiceです。さらなる情報は、このドキュメントの後で紹介するExternalName を参照ください。
エンドポイントスライス
FEATURE STATE:
Kubernetes v1.17 [beta]
エンドポイントスライスは、Endpointsに対してよりスケーラブルな代替手段を提供できるAPIリソースです。概念的にはEndpointsに非常に似ていますが、エンドポイントスライスを使用すると、ネットワークエンドポイントを複数のリソースに分割できます。デフォルトでは、エンドポイントスライスは、100個のエンドポイントに到達すると「いっぱいである」と見なされ、その時点で追加のエンドポイントスライスが作成され、追加のエンドポイントが保存されます。
エンドポイントスライスは、エンドポイントスライスのドキュメント にて詳しく説明されている追加の属性と機能を提供します。
アプリケーションプロトコル
FEATURE STATE:
Kubernetes v1.19 [beta]
AppProtocol
フィールドによってServiceの各ポートに対して特定のアプリケーションプロトコルを指定することができます。
この値は、対応するEndpointsオブジェクトとEndpointSliceオブジェクトに反映されます。
仮想IPとサービスプロキシ
Kubernetesクラスターの各Nodeはkube-proxy
を稼働させています。kube-proxy
はExternalName
タイプ以外のService
用に仮想IPを実装する責務があります。
なぜ、DNSラウンドロビンを使わないのでしょうか。
ここで湧き上がる質問として、なぜKubernetesは内部のトラフィックをバックエンドへ転送するためにプロキシに頼るのでしょうか。
他のアプローチはどうなのでしょうか。例えば、複数のAバリュー(もしくはIPv6用にAAAAバリューなど)をもつDNSレコードを設定し、ラウンドロビン方式で名前を解決することは可能でしょうか。
Serviceにおいてプロキシを使う理由はいくつかあります。
DNSの実装がレコードのTTLをうまく扱わず、期限が切れた後も名前解決の結果をキャッシュするという長い歴史がある。
いくつかのアプリケーションではDNSルックアップを1度だけ行い、その結果を無期限にキャッシュする。
アプリケーションとライブラリーが適切なDNS名の再解決を行ったとしても、DNSレコード上の0もしくは低い値のTTLがDNSに負荷をかけることがあり、管理が難しい。
user-spaceプロキシモード
このモードでは、kube-proxyはServiceやEndpointsオブジェクトの追加・削除をチェックするために、Kubernetes Masterを監視します。
各Serviceは、ローカルのNode上でポート(ランダムに選ばれたもの)を公開します。この"プロキシポート"に対するどのようなリクエストも、そのServiceのバックエンドPodのどれか1つにプロキシされます(Endpointsを介して通知されたPodに対して)。
kube-proxyは、どのバックエンドPodを使うかを決める際にServiceのSessionAffinity
項目の設定を考慮に入れます。
最後に、user-spaceプロキシはServiceのclusterIP
(仮想IP)とport
に対するトラフィックをキャプチャするiptablesルールをインストールします。
そのルールは、トラフィックをバックエンドPodにプロキシするためのプロキシポートにリダイレクトします。
デフォルトでは、user-spaceモードにおけるkube-proxyはラウンドロビンアルゴリズムによってバックエンドPodを選択します。
iptables
プロキシモード
このモードでは、kube-proxyはServiceやEndpointsオブジェクトの追加・削除のチェックのためにKubernetesコントロールプレーンを監視します。
各Serviceでは、そのServiceのclusterIP
とport
に対するトラフィックをキャプチャするiptablesルールをインストールし、そのトラフィックをServiceのあるバックエンドのセットに対してリダイレクトします。
各Endpointsオブジェクトは、バックエンドのPodを選択するiptablesルールをインストールします。
デフォルトでは、iptablesモードにおけるkube-proxyはバックエンドPodをランダムで選択します。
トラフィックのハンドリングのためにiptablesを使用すると、システムのオーバーヘッドが少なくなります。これは、トラフィックがLinuxのnetfilterによってuser-spaceとkernel-spaceを切り替える必要がないためです。
このアプローチは、オーバーヘッドが少ないことに加えて、より信頼できる方法でもあります。
kube-proxyがiptablesモードで稼働し、最初に選択されたPodが応答しない場合、そのコネクションは失敗します。
これはuser-spaceモードでの挙動と異なります: user-spaceモードにおいては、kube-proxyは最初のPodに対するコネクションが失敗したら、自動的に他のバックエンドPodに対して再接続を試みます。
iptablesモードのkube-proxyが正常なバックエンドPodのみをリダイレクト対象とするために、PodのReadinessProbe を使用してバックエンドPodが正常に動作しているか確認できます。これは、ユーザーがkube-proxyを介して、コネクションに失敗したPodに対してトラフィックをリダイレクトするのを除外することを意味します。
IPVSプロキシモード
FEATURE STATE:
Kubernetes v1.11 [stable]
ipvs
モードにおいて、kube-proxyはServiceとEndpointsオブジェクトを監視し、IPVSルールを作成するためにnetlink
インターフェースを呼び出し、定期的にKubernetesのServiceとEndpointsとIPVSルールを同期させます。
このコントロールループはIPVSのステータスが理想的な状態になることを保証します。
Serviceにアクセスするとき、IPVSはトラフィックをバックエンドのPodに向けます。
IPVSプロキシモードはiptablesモードと同様に、netfilterのフック関数に基づいています。ただし、基礎となるデータ構造としてハッシュテーブルを使っているのと、kernel-spaceで動作します。
これは、IPVSモードにおけるkube-proxyはiptablesモードに比べてより低いレイテンシーでトラフィックをリダイレクトし、プロキシのルールを同期する際にはよりパフォーマンスがよいことを意味します。
他のプロキシモードと比較して、IPVSモードはより高いネットワークトラフィックのスループットをサポートしています。
IPVSはバックエンドPodに対するトラフィックのバランシングのために多くのオプションを下記のとおりに提供します。
rr
: ラウンドロビン
lc
: 最低コネクション数(オープンされているコネクション数がもっとも小さいもの)
dh
: 送信先IPによって割り当てられたハッシュ値をもとに割り当てる(Destination Hashing)
sh
: 送信元IPによって割り当てられたハッシュ値をもとに割り当てる(Source Hashing)
sed
: 見込み遅延が最小なもの
nq
: キューなしスケジューリング
備考: IPVSモードでkube-proxyを稼働させるためには、kube-proxyを稼働させる前にNode上でIPVSを有効にしなければなりません。
kube-proxyはIPVSモードで起動する場合、IPVSカーネルモジュールが利用可能かどうかを確認します。
もしIPVSカーネルモジュールが見つからなかった場合、kube-proxyはiptablesモードで稼働するようにフォールバックされます。
このダイアグラムのプロキシモデルにおいて、ServiceのIP:Portに対するトラフィックは、クライアントがKubernetesのServiceやPodについて何も知ることなく適切にバックエンドにプロキシされています。
特定のクライアントからのコネクションが、毎回同一のPodにリダイレクトされるようにするためには、service.spec.sessionAffinity
に"ClientIP"を設定することにより、クライアントのIPアドレスに基づいたSessionAffinityを選択することができます(デフォルトは"None")。
また、service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
を適切に設定することにより、セッションのタイムアウト時間を設定できます(デフォルトではこの値は18,000で、3時間となります)。
複数のポートを公開するService
いくつかのServiceにおいて、ユーザーは1つ以上のポートを公開する必要があります。Kubernetesは、Serviceオブジェクト上で複数のポートを定義するように設定できます。
Serviceで複数のポートを使用するとき、どのポートかを明確にするために、複数のポート全てに対して名前をつける必要があります。
例えば:
apiVersion : v1
kind : Service
metadata :
name : my-service
spec :
selector :
app : MyApp
ports :
- name : http
protocol : TCP
port : 80
targetPort : 9376
- name : https
protocol : TCP
port : 443
targetPort : 9377
備考: KubernetesのPod名と同様に、ポート名は小文字の英数字と-
のみ含める必要があります。また、ポート名の最初と最後の文字は英数字である必要があります。
例えば、123-abc
やweb
という名前は有効で、123_abc
や-web
は無効です。
ユーザー所有のIPアドレスを選択する
Service
を作成するリクエストの一部として、ユーザー所有のclusterIPアドレスを指定することができます。
これを行うためには.spec.clusterIP
フィールドにセットします。
使用例として、もしすでに再利用したいDNSエントリーが存在していた場合や、特定のIPアドレスを設定されたレガシーなシステムや、IPの再設定が難しい場合です。
ユーザーが指定したIPアドレスは、そのAPIサーバーのために設定されているservice-cluster-ip-range
というCIDRレンジ内の有効なIPv4またはIPv6アドレスである必要があります。
もし無効なclusterIPアドレスの値を設定してServiceを作成した場合、問題があることを示すためにAPIサーバーはHTTPステータスコード422を返します。
サービスディスカバリー
Kubernetesは、Serviceオブジェクトを見つけ出すために2つの主要なモードをサポートしています。 - それは環境変数とDNSです。
環境変数
PodがNode上で稼働するとき、kubeletはアクティブな各Serviceに対して、環境変数のセットを追加します。
これはDocker links互換性 のある変数(
makeLinkVariables関数 を確認してください)や、より簡単な{SVCNAME}_SERVICE_HOST
や、{SVCNAME}_SERVICE_PORT
変数をサポートします。この変数名で使われるService名は大文字に変換され、-
は_
に変換されます。
例えば、TCPポート6379番を公開していて、さらにclusterIPが10.0.0.11に割り当てられているredis-master
というServiceは、下記のような環境変数を生成します。
REDIS_MASTER_SERVICE_HOST = 10.0.0.11
REDIS_MASTER_SERVICE_PORT = 6379
REDIS_MASTER_PORT = tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP = tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO = tcp
REDIS_MASTER_PORT_6379_TCP_PORT = 6379
REDIS_MASTER_PORT_6379_TCP_ADDR = 10.0.0.11
備考: Serviceにアクセスする必要のあるPodがあり、クライアントであるそのPodに対して環境変数を使ってポートとclusterIPを公開する場合、クライアントのPodが存在する前に Serviceを作成しなくてはなりません。
そうでない場合、クライアントのPodはそれらの環境変数を作成しません。
ServiceのclusterIPを発見するためにDNSのみを使う場合、このような問題を心配する必要はありません。
DNS
ユーザーはアドオン を使ってKubernetesクラスターにDNS Serviceをセットアップできます(常にセットアップすべきです)。
CoreDNSなどのクラスター対応のDNSサーバーは新しいServiceや、各Service用のDNSレコードのセットのためにKubernetes APIを常に監視します。
もしクラスターを通してDNSが有効になっている場合、全てのPodはDNS名によって自動的にServiceに対する名前解決をするようにできるはずです。
例えば、Kubernetesのmy-ns
というNamespace内でmy-service
というServiceがある場合、KubernetesコントロールプレーンとDNS Serviceが協調して動作し、my-service.my-ns
というDNSレコードを作成します。
my-ns
というNamespace内のPodはmy-service
という名前で簡単に名前解決できるはずです(my-service.my-ns
でも動作します)。
他のNamespace内でのPodはmy-service.my-ns
といった形で指定しなくてはなりません。これらのDNS名は、そのServiceのclusterIPに名前解決されます。
Kubernetesは名前付きのポートに対するDNS SRV(Service)レコードもサポートしています。もしmy-service.my-ns
というServiceがhttp
という名前のTCPポートを持っていた場合、IPアドレスと同様に、http
のポート番号を探すために_http._tcp.my-service.my-ns
というDNS SRVクエリを実行できます。
KubernetesのDNSサーバーはExternalName
Serviceにアクセスする唯一の方法です。
DNS Pods と Service にてExternalName
による名前解決に関するさらなる情報を確認できます。
Headless Service
場合によっては、負荷分散と単一のService IPは不要です。このケースにおいて、clusterIP(.spec.clusterIP
)の値を"None"
に設定することにより、"Headless"とよばれるServiceを作成できます。
ユーザーは、Kubernetesの実装と紐づくことなく、他のサービスディスカバリーのメカニズムと連携するためにHeadless Serviceを使用できます。
例えば、ユーザーはこのAPI上でカスタムオペレーター を実装することができます。
このService
においては、clusterIPは割り当てられず、kube-proxyはこのServiceをハンドリングしないのと、プラットフォームによって行われるはずの
ロードバランシングやプロキシとしての処理は行われません。DNSがどのように自動で設定されるかは、定義されたServiceが定義されたラベルセレクターを持っているかどうかに依存します。
ラベルセレクターの利用
ラベルセレクターを定義したHeadless Serviceにおいて、EndpointsコントローラーはAPIにおいてEndpoints
レコードを作成し、Service
のバックエンドにあるPod
へのIPを直接指し示すためにDNS設定を修正します。
ラベルセレクターなしの場合
ラベルセレクターを定義しないHeadless Serviceにおいては、EndpointsコントローラーはEndpoints
レコードを作成しません。
しかしDNSのシステムは下記の2つ両方を探索し、設定します。
ExternalName
タイプのServiceに対するCNAMEレコード
他の全てのServiceタイプを含む、Service名を共有している全てのEndpoints
レコード
Serviceの公開 (Serviceのタイプ)
ユーザーのアプリケーションのいくつかの部分において(例えば、frontendsなど)、ユーザーのクラスターの外部にあるIPアドレス上でServiceを公開したい場合があります。
KubernetesのServiceTypes
によって、ユーザーがどのような種類のServiceを使いたいかを指定することが可能です。
デフォルトではClusterIP
となります。
Type
項目の値と、そのふるまいは以下のようになります。
また、Serviceを公開するためにIngress も利用可能です。IngressはServiceのタイプではありませんが、クラスターに対するエントリーポイントとして動作します。
Ingressは同一のIPアドレスにおいて、複数のServiceを公開するように、ユーザーの設定した転送ルールを1つのリソースにまとめることができます。
NodePort タイプ
もしtype
フィールドの値をNodePort
に設定すると、Kubernetesコントロールプレーンは--service-node-port-range
フラグによって指定されたレンジのポート(デフォルト: 30000-32767)を割り当てます。
各Nodeはそのポート(各Nodeで同じポート番号)への通信をServiceに転送します。
作成したServiceは、.spec.ports[*].nodePort
フィールド内に割り当てられたポートを記述します。
もしポートへの通信を転送する特定のIPを指定したい場合、特定のIPブロックをkube-proxyの--nodeport-address
フラグで指定できます。これはKubernetes v1.10からサポートされています。
このフラグは、コンマ区切りのIPブロックのリスト(例: 10.0.0./8, 192.0.2.0/25)を使用し、kube-proxyがこのNodeに対してローカルとみなすべきIPアドレスの範囲を指定します。
例えば、--nodeport-addresses=127.0.0.0/8
というフラグによってkube-proxyを起動した時、kube-proxyはNodePort Serviceのためにループバックインターフェースのみ選択します。--nodeport-addresses
のデフォルト値は空のリストになります。これはkube-proxyがNodePort Serviceに対して全てのネットワークインターフェースを利用可能とするべきということを意味します(これは以前のKubernetesのバージョンとの互換性があります)。
もしポート番号を指定したい場合、nodePort
フィールドに値を指定できます。コントロールプレーンは指定したポートを割り当てるか、APIトランザクションが失敗したことを知らせるかのどちらかになります。
これは、ユーザーが自分自身で、ポート番号の衝突に関して気をつける必要があることを意味します。
また、ユーザーは有効なポート番号を指定する必要があり、NodePortの使用において、設定された範囲のポートを指定する必要があります。
NodePortの使用は、Kubernetesによって完全にサポートされていないようなユーザー独自の負荷分散を設定をするための有効な方法や、1つ以上のNodeのIPを直接公開するための方法となりえます。
注意点として、このServiceは<NodeIP>:spec.ports[*].nodePort
と、.spec.clusterIP:spec.ports[*].port
として疎通可能です。
(もしkube-proxyにおいて--nodeport-addressses
が設定された場合、はフィルターされたNodeIPとなります。)
例えば:
apiVersion : v1
kind : Service
metadata :
name : my-service
spec :
type : NodePort
selector :
app : MyApp
ports :
# デフォルトでは利便性のため、 `targetPort` は `port` と同じ値にセットされます。
- port : 80
targetPort : 80
# 省略可能なフィールド
# デフォルトでは利便性のため、Kubernetesコントロールプレーンはある範囲から1つポートを割り当てます(デフォルト値の範囲:30000-32767)
nodePort : 30007
LoadBalancer タイプ
外部のロードバランサーをサポートするクラウドプロバイダー上で、type
フィールドにLoadBalancer
を設定すると、Service用にロードバランサーがプロビジョニングされます。
実際のロードバランサーの作成は非同期で行われ、プロビジョンされたバランサーの情報は、Serviceの.status.loadBalancer
フィールドに記述されます。
例えば:
apiVersion : v1
kind : Service
metadata :
name : my-service
spec :
selector :
app : MyApp
ports :
- protocol : TCP
port : 80
targetPort : 9376
clusterIP : 10.0.171.239
type : LoadBalancer
status :
loadBalancer :
ingress :
- ip : 192.0.2.127
外部のロードバランサーからのトラフィックはバックエンドのPodに直接転送されます。クラウドプロバイダーはどのようにそのリクエストをバランシングするかを決めます。
LoadBalancerタイプのサービスで複数のポートが定義されている場合、すべてのポートが同じプロトコルである必要があり、さらにそのプロトコルはTCP
、UDP
、SCTP
のいずれかである必要があります。
いくつかのクラウドプロバイダーにおいて、loadBalancerIP
の設定をすることができます。このようなケースでは、そのロードバランサーはユーザーが指定したloadBalancerIP
に対してロードバランサーを作成します。
もしloadBalancerIP
フィールドの値が指定されていない場合、そのロードバランサーはエフェメラルなIPアドレスに対して作成されます。もしユーザーがloadBalancerIP
を指定したが、使っているクラウドプロバイダーがその機能をサポートしていない場合、そのloadBalancerIP
フィールドに設定された値は無視されます。
備考: もしSCTPを使っている場合、
LoadBalancer
タイプのServiceに関する
使用上の警告 を参照してください。
内部のロードバランサー
複雑な環境において、同一の(仮想)ネットワークアドレスブロック内のServiceからのトラフィックを転送する必要がでてきます。
Split-HorizonなDNS環境において、ユーザーは2つのServiceを外部と内部の両方からのトラフィックをエンドポイントに転送させる必要がでてきます。
ユーザーは、Serviceに対して下記のアノテーションを1つ追加することでこれを実現できます。
追加するアノテーションは、ユーザーが使っているクラウドプロバイダーに依存しています。
[...]
metadata :
name : my-service
annotations :
networking.gke.io/load-balancer-type : "Internal"
[...]
[...]
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-internal : 0.0.0.0 /0
[...]
[...]
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
[...]
AWSにおけるTLSのサポート
AWS上で稼働しているクラスターにおいて、部分的なTLS/SSLのサポートをするには、LoadBalancer
Serviceに対して3つのアノテーションを追加できます。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-ssl-cert : arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012
1つ目は、使用する証明書のARNです。これはIAMにアップロードされたサードパーティーが発行した証明書か、AWS Certificate Managerで作成された証明書になります。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-backend-protocol : (https|http|ssl|tcp)
2つ目のアノテーションはPodが利用するプロトコルを指定するものです。HTTPSとSSLの場合、ELBはそのPodが証明書を使って暗号化されたコネクションを介して自分自身のPodを認証すると推測します。
HTTPとHTTPSでは、レイヤー7でのプロキシを選択します。ELBはユーザーとのコネクションを切断し、リクエストを転送するときにリクエストヘッダーをパースして、X-Forwarded-For
ヘッダーにユーザーのIPを追加します(Podは接続相手のELBのIPアドレスのみ確認可能です)。
TCPとSSLでは、レイヤー4でのプロキシを選択します。ELBはヘッダーの値を変更せずにトラフィックを転送します。
いくつかのポートがセキュアに保護され、他のポートではセキュアでないような混合した環境において、下記のようにアノテーションを使うことができます。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-backend-protocol : http
service.beta.kubernetes.io/aws-load-balancer-ssl-ports : "443,8443"
上記の例では、もしServiceが80
、443
、8443
と3つのポートを含んでいる場合、443
と8443
はSSL証明書を使いますが、80
では単純にHTTPでのプロキシとなります。
Kubernetes v1.9以降のバージョンからは、Serviceのリスナー用にHTTPSやSSLと事前定義されたAWS SSLポリシー を使用できます。
どのポリシーが使用できるかを確認するために、aws
コマンドラインツールを使用できます。
aws elb describe-load-balancer-policies --query 'PolicyDescriptions[].PolicyName'
ユーザーは"service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy
"というアノテーションを使用することにより、複数のポリシーの中からどれか1つを指定できます。
例えば:
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy : "ELBSecurityPolicy-TLS-1-2-2017-01"
AWS上でのPROXYプロトコルのサポート
AWS上で稼働するクラスターでPROXY protocol のサポートを有効にするために、下記のServiceのアノテーションを使用できます。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol : "*"
Kubernetesバージョン1.3.0からは、このアノテーションを使用するとELBによってプロキシされた全てのポートが対象になり、そしてそれ以外の場合は構成されません。
AWS上でのELBのアクセスログ
AWS上でのELB Service用のアクセスログを管理するためにはいくつかのアノテーションが使用できます。
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled
というアノテーションはアクセスログを有効にするかを設定できます。
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval
というアノテーションはアクセスログをパブリッシュするためのインターバル(分)を設定できます。
ユーザーはそのインターバルで5分もしくは60分で設定できます。
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name
というアノテーションはロードバランサーのアクセスログが保存されるAmazon S3のバケット名を設定できます。
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix
というアノテーションはユーザーが作成したAmazon S3バケットの論理的な階層を指定します。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled : "true"
# ロードバランサーのアクセスログが有効かどうか。
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval : "60"
# アクセスログをパブリッシュするためのインターバル(分)。ユーザーはそのインターバルで5分もしくは60分で設定できます。
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name : "my-bucket"
# ロードバランサーのアクセスログが保存されるAmazon S3のバケット名。
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix : "my-bucket-prefix/prod"
# ユーザーが作成したAmazon S3バケットの論理的な階層。例えば: `my-bucket-prefix/prod`
AWSでの接続の中断
古いタイプのELBでの接続の中断は、service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled
というアノテーションを"true"
に設定することで管理できます。
service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout
というアノテーションで、インスタンスを登録解除するまえに既存の接続をオープンにし続けるための最大時間(秒)を指定できます。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled : "true"
service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout : "60"
他のELBアノテーション
古いタイプのELBを管理するためのアノテーションは他にもあり、下記で紹介します。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout : "60"
# ロードバランサーによってクローズされる前にアイドル状態(コネクションでデータは送信されない)になれる秒数
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled : "true"
# ゾーンを跨いだロードバランシングが有効かどうか
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags : "environment=prod,owner=devops"
# ELBにおいて追加タグとして保存されるキー・バリューのペアのコンマ区切りのリスト
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold : ""
# バックエンドへのトラフィックが正常になったと判断するために必要なヘルスチェックの連続成功数
# デフォルトでは2 この値は2から10の間で設定可能
service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold : "3"
# バックエンドへのトラフィックが異常になったと判断するために必要なヘルスチェックの連続失敗数
# デフォルトでは6 この値は2から10の間で設定可能
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval : "20"
# 各インスタンスのヘルスチェックのおよそのインターバル(秒)
# デフォルトでは10 この値は5から300の間で設定可能
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout : "5"
# ヘルスチェックが失敗したと判断されるレスポンスタイムのリミット(秒)
# この値はservice.beta.kubernetes.io/aws-load-balancer-healthcheck-intervalの値以下である必要があります。
# デフォルトでは5 この値は2から60の間で設定可能
service.beta.kubernetes.io/aws-load-balancer-security-groups : "sg-53fae93f"
# ELBが作成される際に追加されるセキュリティグループのリスト
# service.beta.kubernetes.io/aws-load-balancer-extra-security-groupsアノテーションと異なり
# 元々ELBに付与されていたセキュリティグループを置き換えることになります。
service.beta.kubernetes.io/aws-load-balancer-extra-security-groups : "sg-53fae93f,sg-42efd82e"
# ELBに追加される予定のセキュリティーグループのリスト
service.beta.kubernetes.io/aws-load-balancer-target-node-labels : "ingress-gw,gw-name=public-api"
# ロードバランサーがターゲットノードを指定する際に利用するキーバリューのペアのコンマ区切りリストです。
AWSでのNetwork Load Balancerのサポート
FEATURE STATE:
Kubernetes v1.15 [beta]
AWSでNetwork Load Balancerを使用するには、値をnlb
に設定してアノテーションservice.beta.kubernetes.io/aws-load-balancer-type
を付与します。
metadata :
name : my-service
annotations :
service.beta.kubernetes.io/aws-load-balancer-type : "nlb"
備考: NLBは特定のインスタンスクラスでのみ稼働します。サポートされているインスタンスタイプを確認するためには、ELBに関する
AWS documentation を参照してください。
古いタイプのElastic Load Balancersとは異なり、Network Load Balancers (NLBs)はクライアントのIPアドレスをNodeに転送します。
もしServiceの.spec.externalTrafficPolicy
の値がCluster
に設定されていた場合、クライアントのIPアドレスは末端のPodに伝播しません。
.spec.externalTrafficPolicy
をLocal
に設定することにより、クライアントIPアドレスは末端のPodに伝播します。しかし、これにより、トラフィックの分配が不均等になります。
特定のLoadBalancer Serviceに紐づいたPodがないNodeでは、自動的に割り当てられた.spec.healthCheckNodePort
に対するNLBのターゲットグループのヘルスチェックが失敗し、トラフィックを全く受信しません。
均等なトラフィックの分配を実現するために、DaemonSetの使用や、同一のNodeに配備しないようにPodのanti-affinity を設定します。
また、内部のロードバランサー のアノテーションとNLB Serviceを使用できます。
NLBの背後にあるインスタンスに対してクライアントのトラフィックを転送するために、Nodeのセキュリティーグループは下記のようなIPルールに従って変更されます。
Rule
Protocol
Port(s)
IpRange(s)
IpRange Description
ヘルスチェック
TCP
NodePort(s) (.spec.healthCheckNodePort
for .spec.externalTrafficPolicy = Local
)
VPC CIDR
kubernetes.io/rule/nlb/health=<loadBalancerName>
クライアントのトラフィック
TCP
NodePort(s)
.spec.loadBalancerSourceRanges
(デフォルト: 0.0.0.0/0
)
kubernetes.io/rule/nlb/client=<loadBalancerName>
MTUによるサービスディスカバリー
ICMP
3,4
.spec.loadBalancerSourceRanges
(デフォルト: 0.0.0.0/0
)
kubernetes.io/rule/nlb/mtu=<loadBalancerName>
どのクライアントIPがNLBにアクセス可能かを制限するためには、loadBalancerSourceRanges
を指定してください。
spec :
loadBalancerSourceRanges :
- "143.231.0.0/16"
備考: もし.spec.loadBalancerSourceRanges
が設定されていない場合、KubernetesはNodeのセキュリティーグループに対して0.0.0.0/0
からのトラフィックを許可します。
もしNodeがパブリックなIPアドレスを持っていた場合、NLBでないトラフィックも修正されたセキュリティーグループ内の全てのインスタンスにアクセス可能になってしまうので注意が必要です。
Tencent Kubernetes Engine(TKE)におけるその他のCLBアノテーション
以下に示すように、TKEでCloud Load Balancerを管理するためのその他のアノテーションがあります。
metadata :
name : my-service
annotations :
# 指定したノードでロードバランサーをバインドします
service.kubernetes.io/qcloud-loadbalancer-backends-label : key in (value1, value2)
# 既存のロードバランサーのID
service.kubernetes.io/tke-existed-lbid : lb-6swtxxxx
# ロードバランサー(LB)のカスタムパラメーターは、LBタイプの変更をまだサポートしていません
service.kubernetes.io/service.extensiveParameters : ""
# LBリスナーのカスタムパラメーター
service.kubernetes.io/service.listenerParameters : ""
# ロードバランサーのタイプを指定します
# 有効な値: classic(Classic Cloud Load Balancer)またはapplication(Application Cloud Load Balancer)
service.kubernetes.io/loadbalance-type : xxxxx
# パブリックネットワーク帯域幅の課金方法を指定します
# 有効な値: TRAFFIC_POSTPAID_BY_HOUR(bill-by-traffic)およびBANDWIDTH_POSTPAID_BY_HOUR(bill-by-bandwidth)
service.kubernetes.io/qcloud-loadbalancer-internet-charge-type : xxxxxx
# 帯域幅の値を指定します(値の範囲:[1-2000] Mbps)。
service.kubernetes.io/qcloud-loadbalancer-internet-max-bandwidth-out : "10"
# この注釈が設定されている場合、ロードバランサーはポッドが実行されているノードのみを登録します
# そうでない場合、すべてのノードが登録されます
service.kubernetes.io/local-svc-only-bind-node-with-pod : true
ExternalName タイプ
ExternalNameタイプのServiceは、ServiceをDNS名とマッピングし、my-service
やcassandra
というような従来のラベルセレクターとはマッピングしません。
ユーザーはこれらのServiceにおいてspec.externalName
フィールドの値を指定します。
このServiceの定義では、例えばprod
というNamespace内のmy-service
というServiceをmy.database.example.com
にマッピングします。
apiVersion : v1
kind : Service
metadata :
name : my-service
namespace : prod
spec :
type : ExternalName
externalName : my.database.example.com
備考: ExternalNameはIpv4のアドレスの文字列のみ受け付けますが、IPアドレスではなく、数字で構成されるDNS名として受け入れます。
IPv4アドレスに似ているExternalNamesはCoreDNSもしくはIngress-Nginxによって名前解決されず、これはExternalNameは正規のDNS名を指定することを目的としているためです。
IPアドレスをハードコードする場合、
Headless Service の使用を検討してください。
my-service.prod.svc.cluster.local
というホストをルックアップするとき、クラスターのDNS Serviceはmy.database.example.com
という値をもつCNAME
レコードを返します。
my-service
へのアクセスは、他のServiceと同じ方法ですが、再接続する際はプロキシや転送を介して行うよりも、DNSレベルで行われることが決定的に異なる点となります。
後にユーザーが使用しているデータベースをクラスター内に移行することになった場合は、Podを起動させ、適切なラベルセレクターやEndpointsを追加し、Serviceのtype
を変更します。
警告: HTTPやHTTPSなどの一般的なプロトコルでExternalNameを使用する際に問題が発生する場合があります。ExternalNameを使用する場合、クラスター内のクライアントが使用するホスト名は、ExternalNameが参照する名前とは異なります。
ホスト名を使用するプロトコルの場合、この違いによりエラーまたは予期しない応答が発生する場合があります。HTTPリクエストがオリジンサーバーが認識しないHost:
ヘッダーを持っていたなら、TLSサーバーはクライアントが接続したホスト名に一致する証明書を提供できません。
External IPs
もし1つ以上のクラスターNodeに転送するexternalIPが複数ある場合、Kubernetes ServiceはexternalIPs
に指定したIPで公開されます。
そのexternalIP(到達先のIPとして扱われます)のServiceのポートからトラフィックがクラスターに入って来る場合、ServiceのEndpointsのどれか1つに対して転送されます。
externalIPs
はKubernetesによって管理されず、それを管理する責任はクラスターの管理者にあります。
Serviceのspecにおいて、externalIPs
は他のどのServiceTypes
と併用して設定できます。
下記の例では、"my-service
"は"198.51.100.32:80
" (externalIP:port
)のクライアントからアクセス可能です。
apiVersion : v1
kind : Service
metadata :
name : my-service
spec :
selector :
app : MyApp
ports :
- name : http
protocol : TCP
port : 80
targetPort : 9376
externalIPs :
- 80.11.12.10
Serviceの欠点
仮想IP用にuserspaceモードのプロキシを使用すると、小規模もしくは中規模のスケールでうまく稼働できますが、1000以上のServiceがあるようなとても大きなクラスターではうまくスケールしません。
これについては、Serviceのデザインプロポーザル にてさらなる詳細を確認できます。
userspaceモードのプロキシの使用は、Serviceにアクセスするパケットの送信元IPアドレスが不明瞭になります。
これは、いくつかの種類のネットワークフィルタリング(ファイアウォールによるフィルタリング)を不可能にします。
iptablesプロキシモードはクラスター内の送信元IPを不明瞭にはしませんが、依然としてロードバランサーやNodePortへ疎通するクライアントに影響があります。
Type
フィールドはネストされた機能としてデザインされています。 - 各レベルの値は前のレベルに対して追加します。
これは全てのクラウドプロバイダーにおいて厳密に要求されていません(例: Google Compute EngineはLoadBalancer
を動作させるためにNodePort
を割り当てる必要はありませんが、AWSではその必要があります)が、現在のAPIでは要求しています。
仮想IPの実装について
これより前の情報は、ただServiceを使いたいという多くのユーザーにとっては有益かもしれません。しかし、その裏側では多くのことが行われており、理解する価値があります。
衝突の回避
Kubernetesの主要な哲学のうちの一つは、ユーザーは、ユーザー自身のアクションによるミスでないものによって、ユーザーのアクションが失敗するような状況に晒されるべきでないことです。
Serviceリソースの設計において、これはユーザーの指定したポートが衝突する可能性がある場合はそのポートのServiceを作らないことを意味します。これは障害を分離することとなります。
Serviceのポート番号を選択できるようにするために、我々はどの2つのServiceでもポートが衝突しないことを保証します。
Kubernetesは各Serviceに、それ自身のIPアドレスを割り当てることで実現しています。
各Serviceが固有のIPを割り当てられるのを保証するために、内部のアロケーターは、Serviceを作成する前に、etcd内のグローバルの割り当てマップをアトミックに更新します。
そのマップオブジェクトはServiceのIPアドレスの割り当てのためにレジストリー内に存在しなくてはならず、そうでない場合は、Serviceの作成時にIPアドレスが割り当てられなかったことを示すエラーメッセージが表示されます。
コントロールプレーンにおいて、バックグラウンドのコントローラーはそのマップを作成する責務があります(インメモリーのロックが使われていた古いバージョンのKubernetesからのマイグレーションをサポートすることも必要です)。
また、Kubernetesは(例えば、管理者の介入によって)無効な割り当てがされているかをチェックすることと、現時点でどのServiceにも使用されていない割り当て済みIPアドレスのクリーンアップのためにコントローラーを使用します。
ServiceのIPアドレス
実際に固定された向き先であるPodのIPアドレスとは異なり、ServiceのIPは実際には単一のホストによって応答されません。
その代わり、kube-proxyは必要な時に透過的にリダイレクトされる仮想 IPアドレスを定義するため、iptables(Linuxのパケット処理ロジック)を使用します。
クライアントがVIPに接続する時、そのトラフィックは自動的に適切なEndpointsに転送されます。
Service用の環境変数とDNSは、Serviceの仮想IPアドレス(とポート)の面において、自動的に生成されます。
kube-proxyは3つの微妙に異なった動作をするプロキシモード— userspace、iptablesとIPVS — をサポートしています。
Userspace
例として、上記で記述されている画像処理のアプリケーションを考えます。
バックエンドのServiceが作成されたとき、KubernetesのMasterは仮想IPを割り当てます。例えば10.0.0.1などです。
そのServiceのポートが1234で、そのServiceはクラスター内の全てのkube-proxyインスタンスによって監視されていると仮定します。
kube-proxyが新しいServiceを見つけた時、kube-proxyは新しいランダムポートをオープンし、その仮想IPアドレスの新しいポートにリダイレクトするようにiptablesを更新し、そのポート上で新しい接続を待ち受けを開始します。
クライアントがServiceの仮想IPアドレスに接続したとき、iptablesルールが有効になり、そのパケットをプロキシ自身のポートにリダイレクトします。
その"Service プロキシ"はバックエンドPodの対象を選択し、クライアントのトラフィックをバックエンドPodに転送します。
これはServiceのオーナーは、衝突のリスクなしに、求めるどのようなポートも選択できることを意味します。
クライアントは単純にそのIPとポートに対して接続すればよく、実際にどのPodにアクセスしているかを意識しません。
iptables
また画像処理のアプリケーションについて考えます。バックエンドServiceが作成された時、そのKubernetesコントロールプレーンは仮想IPアドレスを割り当てます。例えば10.0.0.1などです。
Serviceのポートが1234で、そのServiceがクラスター内のすべてのkube-proxyインスタンスによって監視されていると仮定します。
kube-proxyが新しいServiceを見つけた時、kube-proxyは仮想IPから各Serviceのルールにリダイレクトされるような、iptablesルールのセットをインストールします。
Service毎のルールは、トラフィックをバックエンドにリダイレクト(Destination NATを使用)しているEndpoints毎のルールに対してリンクしています。
クライアントがServiceの仮想IPアドレスに対して接続しているとき、そのiptablesルールが有効になります。
バックエンドのPodが選択され(SessionAffinityに基づくか、もしくはランダムで選択される)、パケットはバックエンドにリダイレクトされます。
userspaceモードのプロキシとは異なり、パケットは決してuserspaceにコピーされず、kube-proxyは仮想IPのために稼働される必要はなく、またNodeでは変更されていないクライアントIPからトラフィックがきます。
このように同じ基本的なフローは、NodePortまたはLoadBalancerを介してトラフィックがきた場合に、実行され、ただクライアントIPは変更されます。
IPVS
iptablesの処理は、大規模なクラスターの場合劇的に遅くなります。例としてはServiceが10,000ほどある場合です。
IPVSは負荷分散のために設計され、カーネル内のハッシュテーブルに基づいています。そのためIPVSベースのkube-proxyによって、多数のServiceがある場合でも一貫して高パフォーマンスを実現できます。
次第に、IPVSベースのkube-proxyは負荷分散のアルゴリズムはさらに洗練されています(最小接続数、位置ベース、重み付け、永続性など)。
APIオブジェクト
ServiceはKubernetesのREST APIにおいてトップレベルのリソースです。ユーザーはそのAPIオブジェクトに関して、Service API object でさらなる情報を確認できます。
サポートされているプロトコル
TCP
ユーザーはどの種類のServiceにおいてもTCPを利用できます。これはデフォルトのネットワークプロトコルです。
UDP
ユーザーは多くのServiceにおいてUDPを利用できます。 type=LoadBalancerのServiceにおいては、UDPのサポートはこの機能を提供しているクラウドプロバイダーに依存しています。
HTTP
もしクラウドプロバイダーがサポートしている場合、ServiceのEndpointsに転送される外部のHTTP/HTTPSでのリバースプロキシをセットアップするために、LoadBalancerモードでServiceを作成可能です。
備考: ユーザーはまた、HTTP/HTTPS Serviceを公開するために、Serviceの代わりに
Ingress を利用することもできます。
PROXY プロトコル
もしクラウドプロバイダーがサポートしている場合、Kubernetesクラスターの外部のロードバランサーを設定するためにLoadBalancerモードでServiceを利用できます。これはPROXY protocol がついた接続を転送します。
ロードバランサーは、最初の一連のオクテットを送信します。
下記のような例となります。
PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n
クライアントからのデータのあとに追加されます。
SCTP
FEATURE STATE:
Kubernetes v1.19 [beta]
KubernetesはService、Endpoints、EndpointSlice、NetworkPolicyとPodの定義においてprotocol
フィールドの値でSCTPをサポートしています。ベータ版の機能のため、この機能はデフォルトで有効になっています。SCTPをクラスターレベルで無効にするには、クラスター管理者はAPI ServerにおいてSCTPSupport
フィーチャーゲート を--feature-gates=SCTPSupport=false,…
と設定して無効にする必要があります。
そのフィーチャーゲートが有効になった時、ユーザーはService、Endpoints、EndpointSlice、NetworkPolicy、またはPodのprotocol
フィールドにSCTP
を設定できます。
Kubernetesは、TCP接続と同様に、SCTPアソシエーションに応じてネットワークをセットアップします。
警告
マルチホームSCTPアソシエーションのサポート
警告: マルチホームSCTPアソシエーションのサポートは、複数のインターフェースとPodのIPアドレスの割り当てをサポートできるCNIプラグインを要求します。
マルチホームSCTPアソシエーションにおけるNATは、対応するカーネルモジュール内で特別なロジックを要求します。
type=LoadBalancer Service について
警告: クラウドプロバイダーのロードバランサーの実装がプロトコルとしてSCTPをサポートしている場合は、type
がLoadBalancerで protocol
がSCTPの場合でのみサービスを作成できます。
そうでない場合、Serviceの作成要求はリジェクトされます。現時点でのクラウドのロードバランサーのプロバイダー(Azure、AWS、CloudStack、GCE、OpenStack)は全てSCTPのサポートをしていません。
Windows
警告: SCTPはWindowsベースのNodeではサポートされていません。
Userspace kube-proxy
警告: kube-proxyはuserspaceモードにおいてSCTPアソシエーションの管理をサポートしません。
次の項目
2 - Ingress
FEATURE STATE:
Kubernetes v1.19 [stable]
クラスター内のServiceに対する外部からのアクセス(主にHTTP)を管理するAPIオブジェクトです。
Ingressは負荷分散、SSL終端、名前ベースの仮想ホスティングの機能を提供します。
用語
簡単のために、このガイドでは次の用語を定義します。
ノード: Kubernetes内のワーカーマシンで、クラスターの一部です。
クラスター: Kubernetesによって管理されているコンテナ化されたアプリケーションを実行させるノードの集合です。この例や、多くのKubernetesによるデプロイでは、クラスター内のノードはインターネットに公開されていません。
エッジルーター: クラスターでファイアウォールのポリシーを強制するルーターです。クラウドプロバイダーが管理するゲートウェイや、物理的なハードウェアの一部である場合もあります。
クラスターネットワーク: 物理的または論理的な繋がりの集合で、Kubernetesのネットワークモデル によって、クラスター内でのコミュニケーションを司るものです。
Service: ラベル セレクターを使ったPodの集合を特定するKubernetes Service です。特に指定がない限り、Serviceはクラスターネットワーク内でのみ疎通可能な仮想IPを持つものとして扱われます。
Ingressとは何か
Ingress はクラスター外からクラスター内Service へのHTTPとHTTPSのルートを公開します。トラフィックのルーティングはIngressリソース上で定義されるルールによって制御されます。
全てのトラフィックを単一のServiceに送る単純なIngressの例を示します。
図. Ingress
IngressはServiceに対して、外部疎通できるURL、負荷分散トラフィック、SSL/TLS終端の機能や、名前ベースの仮想ホスティングを提供するように設定できます。Ingressコントローラー は通常はロードバランサーを使用してIngressの機能を実現しますが、エッジルーターや、追加のフロントエンドを構成してトラフィックの処理を支援することもできます。
Ingressは任意のポートやプロトコルを公開しません。HTTPやHTTPS以外のServiceをインターネットに公開する場合、Service.Type=NodePort やService.Type=LoadBalancer のServiceタイプを一般的には使用します。
Ingressを使用する上での前提条件
Ingressを提供するためにはIngressコントローラー が必要です。Ingressリソースを作成するのみでは何の効果もありません。
ingress-nginx のようなIngressコントローラーのデプロイが必要な場合があります。いくつかのIngressコントローラー の中から選択してください。
理想的には、全てのIngressコントローラーはリファレンスの仕様を満たすはずです。しかし実際には、各Ingressコントローラーは微妙に異なる動作をします。
備考: Ingressコントローラーのドキュメントを確認して、選択する際の注意点について理解してください。
Ingressリソース
Ingressリソースの最小構成の例は以下のとおりです。
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : minimal-ingress
annotations :
nginx.ingress.kubernetes.io/rewrite-target : /
spec :
ingressClassName : nginx-example
rules :
- http :
paths :
- path : /testpath
pathType : Prefix
backend :
service :
name : test
port :
number : 80
Ingressには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
を指定することを推奨します 。
Ingressのルール
各HTTPルールは以下の情報を含みます。
オプションで設定可能なホスト名。上記のリソースの例では、ホスト名が指定されていないので、そのルールは指定されたIPアドレスを経由する全てのインバウンドHTTPトラフィックに適用されます。ホスト名が指定されていると(例: foo.bar.com)、そのルールは指定されたホストに対して適用されます。
パスのリスト(例: /testpath
)。各パスにはservice.name
とservice.port.name
またはservice.port.number
で定義されるバックエンドが関連づけられます。ロードバランサーがトラフィックを関連づけられたServiceに転送するために、外部からくるリクエストのホスト名とパスが条件と一致させる必要があります。
バックエンドはServiceドキュメント に書かれているようなService名とポート名の組み合わせ、またはCRD によるカスタムリソースバックエンド です。Ingressで設定されたホスト名とパスのルールに一致するHTTP(とHTTPS)のリクエストは、リスト内のバックエンドに対して送信されます。
Ingressコントローラーでは、defaultBackend
が設定されていることがあります。これはSpec内で指定されているパスに一致しないようなリクエストのためのバックエンドです。
デフォルトのバックエンド
ルールが設定されていないIngressは、全てのトラフィックを単一のデフォルトのバックエンドに転送します。.spec.defaultBackend
はその場合にリクエストを処理するバックエンドになります。defaultBackend
は、Ingressコントローラー のオプション設定であり、Ingressリソースでは指定されていません。.spec.rules
を設定しない場合、.spec.defaultBackend
の設定は必須です。defaultBackend
が設定されていない場合、どのルールにもマッチしないリクエストの処理は、Ingressコントローラーに任されます(このケースをどう処理するかは、お使いのIngressコントローラーのドキュメントを参照してください)。
HTTPリクエストがIngressオブジェクトのホスト名とパスの条件に1つも一致しない時、そのトラフィックはデフォルトのバックエンドに転送されます。
リソースバックエンド
Resource
バックエンドはIngressオブジェクトと同じnamespaceにある他のKubernetesリソースを指すObjectRefです。
Resource
はServiceの設定とは排他であるため、両方を指定するとバリデーションに失敗します。
Resource
バックエンドの一般的な用途は、静的なアセットが入ったオブジェクトストレージバックエンドにデータを導入することです。
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : ingress-resource-backend
spec :
defaultBackend :
resource :
apiGroup : k8s.example.com
kind : StorageBucket
name : static-assets
rules :
- http :
paths :
- path : /icons
pathType : ImplementationSpecific
backend :
resource :
apiGroup : k8s.example.com
kind : StorageBucket
name : icon-assets
上記のIngressを作成した後に、次のコマンドで参照することができます。
kubectl describe ingress ingress-resource-backend
Name: ingress-resource-backend
Namespace: default
Address:
Default backend: APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
Host Path Backends
---- ---- --------
*
/icons APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations: <none>
Events: <none>
パスのタイプ
Ingressのそれぞれのパスは対応するパスのタイプを持ちます。pathType
が明示的に指定されていないパスはバリデーションに通りません。サポートされているパスのタイプは3種類あります。
ImplementationSpecific
(実装に特有): このパスタイプでは、パスとの一致はIngressClassに依存します。Ingressの実装はこれを独立したpathType
と扱うことも、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 Class
Ingressは異なったコントローラーで実装されうるため、しばしば異なった設定を必要とします。
各Ingressはクラス、つまりIngressClassリソースへの参照を指定する必要があります。IngressClassリソースには、このクラスを実装するコントローラーの名前などの追加設定が含まれています。
apiVersion : networking.k8s.io/v1
kind : IngressClass
metadata :
name : external-lb
spec :
controller : example.com/ingress-controller
parameters :
apiGroup : k8s.example.com
kind : IngressParameters
name : external-lb
IngressClassの.spec.parameters
フィールドを使って、そのIngressClassに関連する設定を持っている別のリソースを参照することができます。
使用するパラメーターの種類は、IngressClassの.spec.controller
フィールドで指定したIngressコントローラーに依存します。
IngressClassスコープ
Ingressコントローラーによっては、クラスター全体で設定したパラメーターを使用できる場合もあれば、1つのNamespaceに対してのみ設定したパラメーターを使用できる場合もあります。
IngressClassパラメーターのデフォルトのスコープは、クラスター全体です。
.spec.parameters
フィールドを設定して.spec.parameters.scope
フィールドを設定しなかった場合、または.spec.parameters.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
FEATURE STATE:
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ゲートウェイ定義)に対する制御を委譲するのに役立ちます。クラスタースコープパラメーターを使用した場合は以下のいずれかになります:
クラスターオペレーターチームは、新しい設定変更が適用されるたびに、別のチームの変更内容を承認する必要があります。
クラスターオペレーターは、アプリケーションチームがクラスタースコープのパラメーターリソースに変更を加えることができるように、RBAC のRoleやRoleBindingといった、特定のアクセス制御を定義する必要があります。
IngressClass API自体は常にクラスタースコープです。
以下はNamespaceスコープのパラメーターを参照しているIngressClassの例です:
---
apiVersion : networking.k8s.io/v1
kind : IngressClass
metadata :
name : external-lb-2
spec :
controller : example.com/ingress-controller
parameters :
# このIngressClassのパラメーターは「external-config」という名前の
# IngressParameter(APIグループk8s.example.com)で指定されています。
# このリソースは「external-configuration」というNamespaceにあります。
scope : Namespace
apiGroup : k8s.example.com
kind : IngressParameter
namespace : external-configuration
name : external-config
非推奨のアノテーション
Kubernetes 1.18でIngressClassリソースとingressClassName
フィールドが追加される前は、Ingressの種別はIngressのkubernetes.io/ingress.class
アノテーションにより指定されていました。
このアノテーションは正式に定義されたことはありませんが、Ingressコントローラーに広くサポートされています。
Ingressの新しいingressClassName
フィールドはこのアノテーションを置き換えるものですが、完全に等価ではありません。
アノテーションは一般にIngressを実装すべきIngressのコントローラーの名称を示していましたが、フィールドはIngressClassリソースへの参照であり、Ingressのコントローラーの名称を含む追加のIngressの設定情報を含んでいます。
デフォルトのIngressClass
特定のIngressClassをクラスターのデフォルトとしてマークすることができます。
IngressClassリソースのingressclass.kubernetes.io/is-default-class
アノテーションをtrue
に設定すると、ingressClassName
フィールドが指定されないIngressにはこのデフォルトIngressClassが割り当てられるようになります。
注意: 複数のIngressClassをクラスターのデフォルトに設定すると、アドミッションコントローラーはingressClassName
が指定されていない新しいIngressオブジェクトを作成できないようにします。クラスターのデフォルトIngressClassを1つ以下にすることで、これを解消することができます。
Ingressコントローラーの中には、デフォルトのIngressClass
を定義しなくても動作するものがあります。 例えば、Ingress-NGINXコントローラーはフラグ
--watch-ingress-without-class
で設定することができます。ただし、デフォルトIngressClass
を指定することを推奨します :
apiVersion : networking.k8s.io/v1
kind : IngressClass
metadata :
labels :
app.kubernetes.io/component : controller
name : nginx-example
annotations :
ingressclass.kubernetes.io/is-default-class : "true"
spec :
controller : k8s.io/ingress-nginx
Ingressのタイプ
単一ServiceのIngress
Kubernetesには、単一のServiceを公開できるようにする既存の概念があります(Ingressの代替案 を参照してください)。ルールなしでデフォルトのバックエンド を指定することにより、Ingressでこれを実現することもできます。
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : test-ingress
spec :
defaultBackend :
service :
name : test
port :
number : 80
kubectl apply -f
を実行してIngressを作成すると、その作成したIngressの状態を確認することができます。
kubectl get ingress test-ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
test-ingress external-lb * 203.0.113.123 80 59s
203.0.113.123
はIngressコントローラーによって割り当てられたIPで、作成したIngressを利用するためのものです。
備考: IngressコントローラーとロードバランサーがIPアドレス割り当てるのに1、2分ほどかかります。この間、ADDRESSの情報は<pending>
となっているのを確認できます。
リクエストのシンプルなルーティング
ファンアウト設定では単一のIPアドレスのトラフィックを、リクエストされたHTTP URIに基づいて1つ以上のServiceに転送します。Ingressによってロードバランサーの数を少なくすることができます。例えば、以下のように設定します。
図. Ingressファンアウト
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
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が機能しない可能性があることに注意してください。
これは取り得る全てのサブドメインに対する証明書を発行する必要があるからです。
そのため、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
備考: サポートされるTLSの機能はIngressコントローラーによって違いがあります。利用する環境でTLSがどのように動作するかを理解するためには、
nginx や、
GCE 、または他のプラットフォーム固有のIngressコントローラーのドキュメントを確認してください。
負荷分散
Ingressコントローラーは、負荷分散アルゴリズムやバックエンドの重みスキームなど、すべてのIngressに適用されるいくつかの負荷分散ポリシーの設定とともにブートストラップされます。発展した負荷分散のコンセプト(例: セッションの永続化、動的重み付けなど)はIngressによってサポートされていません。代わりに、それらの機能はService用のロードバランサーを介して利用できます。
ヘルスチェックの機能はIngressによって直接には公開されていませんが、Kubernetesにおいて、同等の機能を提供するReadiness Probe のようなコンセプトが存在することは注目に値します。コントローラーがどのようにヘルスチェックを行うかについては、コントローラーのドキュメントを参照してください(例えばnginx 、またはGCE )。
Ingressの更新
リソースを編集することで、既存のIngressに対して新しいホストを追加することができます。
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:80 (10.8.0.90:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 35s loadbalancer-controller default/test
kubectl edit ingress test
このコマンドを実行すると既存の設定をYAMLフォーマットで編集するエディターが表示されます。新しいホストを追加するには、リソースを修正してください。
spec :
rules :
- host : foo.bar.com
http :
paths :
- backend :
service :
name : service1
port :
number : 80
path : /foo
pathType : Prefix
- host : bar.baz.com
http :
paths :
- backend :
service :
name : service2
port :
number : 80
path : /foo
pathType : Prefix
..
変更を保存した後、kubectlはAPIサーバー内のリソースを更新し、Ingressコントローラーに対してロードバランサーの再設定を指示します。
変更内容を確認してください。
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:80 (10.8.0.90:80)
bar.baz.com
/foo service2:80 (10.8.0.91:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 45s loadbalancer-controller default/test
修正されたIngressのYAMLファイルに対してkubectl replace -f
を実行することで、同様の結果を得られます。
アベイラビリティーゾーンをまたいだ障害について
障害のあるドメインをまたいでトラフィックを分散する手法は、クラウドプロバイダーによって異なります。詳細に関して、Ingress コントローラー のドキュメントを参照してください。
Ingressの代替案
Ingressリソースを直接含まずにサービスを公開する方法は複数あります。
次の項目
3 - サービスとアプリケーションの接続
コンテナを接続するためのKubernetesモデル
継続的に実行され、複製されたアプリケーションの準備ができたので、ネットワーク上で公開することが可能になります。
Kubernetesのネットワークのアプローチについて説明する前に、Dockerの「通常の」ネットワーク手法と比較することが重要です。
デフォルトでは、Dockerはホストプライベートネットワーキングを使用するため、コンテナは同じマシン上にある場合にのみ他のコンテナと通信できます。
Dockerコンテナがノード間で通信するには、マシンのIPアドレスにポートを割り当ててから、コンテナに転送またはプロキシする必要があります。
これは明らかに、コンテナが使用するポートを非常に慎重に調整するか、ポートを動的に割り当てる必要があることを意味します。
コンテナを提供する複数の開発者やチーム間でポートの割り当てを調整することは、規模的に大変困難であり、ユーザが制御できないクラスターレベルの問題にさらされます。
Kubernetesでは、どのホストで稼働するかに関わらず、Podが他のPodと通信できると想定しています。
すべてのPodに独自のクラスタープライベートIPアドレスを付与するため、Pod間のリンクを明示的に作成したり、コンテナポートをホストポートにマップしたりする必要はありません。
これは、Pod内のコンテナがすべてlocalhostの相互のポートに到達でき、クラスター内のすべてのPodがNATなしで相互に認識できることを意味します。
このドキュメントの残りの部分では、このようなネットワークモデルで信頼できるサービスを実行する方法について詳しく説明します。
このガイドでは、シンプルなnginxサーバーを使用して概念実証を示します。
Podをクラスターに公開する
前の例でネットワークモデルを紹介しましたが、再度ネットワークの観点に焦点を当てましょう。
nginx Podを作成し、コンテナポートの仕様を指定していることに注意してください。
apiVersion : apps/v1
kind : Deployment
metadata :
name : my-nginx
spec :
selector :
matchLabels :
run : my-nginx
replicas : 2
template :
metadata :
labels :
run : my-nginx
spec :
containers :
- name : my-nginx
image : nginx
ports :
- containerPort : 80
これにより、クラスター内のどのノードからでもアクセスできるようになります。
Podが実行されているノードを確認します:
kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run = my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-3800858182-jr4a2 1/1 Running 0 13s 10.244.3.4 kubernetes-minion-905m
my-nginx-3800858182-kna2y 1/1 Running 0 13s 10.244.2.5 kubernetes-minion-ljyd
PodのIPを確認します:
kubectl get pods -l run = my-nginx -o yaml | grep podIP
podIP: 10.244.3.4
podIP: 10.244.2.5
クラスター内の任意のノードにSSH接続し、両方のIPにcurl接続できるはずです。
コンテナはノードでポート80を使用していない ことに注意してください。
また、Podにトラフィックをルーティングする特別なNATルールもありません。
つまり、同じcontainerPortを使用して同じノードで複数のnginx Podを実行し、IPを使用してクラスター内の他のPodやノードからそれらにアクセスできます。
Dockerと同様に、ポートは引き続きホストノードのインターフェースに公開できますが、ネットワークモデルにより、この必要性は根本的に減少します。
興味があれば、これをどのように達成するか について詳しく読むことができます。
Serviceを作成する
そのため、フラットでクラスター全体のアドレス空間でnginxを実行するPodがあります。
理論的には、これらのPodと直接通信することができますが、ノードが停止するとどうなりますか?
Podはそれで死に、Deploymentは異なるIPを持つ新しいものを作成します。
これは、Serviceが解決する問題です。
Kubernetes Serviceは、クラスター内のどこかで実行されるPodの論理セットを定義する抽象化であり、すべて同じ機能を提供します。
作成されると、各Serviceには一意のIPアドレス(clusterIPとも呼ばれます)が割り当てられます。
このアドレスはServiceの有効期間に関連付けられており、Serviceが動作している間は変更されません。
Podは、Serviceと通信するように構成でき、Serviceへの通信は、ServiceのメンバーであるPodに自動的に負荷分散されることを認識できます。
2つのnginxレプリカのサービスをkubectl expose
で作成できます:
kubectl expose deployment/my-nginx
service/my-nginx exposed
これは次のyamlをkubectl apply -f
することと同等です:
apiVersion : v1
kind : Service
metadata :
name : my-nginx
labels :
run : my-nginx
spec :
ports :
- port : 80
protocol : TCP
selector :
run : my-nginx
この仕様は、run:my-nginx
ラベルを持つ任意のPodのTCPポート80をターゲットとするサービスを作成し、抽象化されたサービスポートでPodを公開します(targetPort
:はコンテナがトラフィックを受信するポート、port
:は抽象化されたServiceのポートであり、他のPodがServiceへのアクセスに使用する任意のポートにすることができます)。
サービス定義でサポートされているフィールドのリストはService APIオブジェクトを参照してください。
Serviceを確認します:
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>
NAME ENDPOINTS AGE
my-nginx 10.244.2.5:80,10.244.3.4:80 1m
クラスター内の任意のノードから、<CLUSTER-IP>:<PORT>
でnginx Serviceにcurl接続できるようになりました。
Service IPは完全に仮想的なもので、ホスト側のネットワークには接続できないことに注意してください。
この仕組みに興味がある場合は、サービスプロキシ の詳細をお読みください。
Serviceにアクセスする
Kubernetesは、環境変数とDNSの2つの主要なService検索モードをサポートしています。
前者はそのまま使用でき、後者はCoreDNSクラスターアドオン を必要とします。
備考: サービス環境変数が望ましくない場合(予想されるプログラム変数と衝突する可能性がある、処理する変数が多すぎる、DNSのみを使用するなど)、
Pod仕様 で
enableServiceLinks
フラグを
false
に設定することでこのモードを無効にできます。
環境変数
ノードでPodが実行されると、kubeletはアクティブな各サービスの環境変数のセットを追加します。
これにより、順序付けの問題が発生します。
理由を確認するには、実行中のnginx Podの環境を調べます(Pod名は環境によって異なります):
kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
サービスに言及がないことに注意してください。これは、サービスの前にレプリカを作成したためです。
これのもう1つの欠点は、スケジューラーが両方のPodを同じマシンに配置し、サービスが停止した場合にサービス全体がダウンする可能性があることです。
2つのPodを強制終了し、Deploymentがそれらを再作成するのを待つことで、これを正しい方法で実行できます。
今回は、サービスはレプリカの「前」に存在します。
これにより、スケジューラーレベルのサービスがPodに広がり(すべてのノードの容量が等しい場合)、適切な環境変数が提供されます:
kubectl scale deployment my-nginx --replicas= 0; kubectl scale deployment my-nginx --replicas= 2;
kubectl get pods -l run = my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-3800858182-e9ihh 1/1 Running 0 5s 10.244.2.7 kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4 1/1 Running 0 5s 10.244.3.8 kubernetes-minion-905m
Podは強制終了されて再作成されるため、異なる名前が付いていることに気付くでしょう。
kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443
DNS
Kubernetesは、DNS名を他のServiceに自動的に割り当てるDNSクラスターアドオンサービスを提供します。
クラスターで実行されているかどうかを確認できます:
kubectl get services kube-dns --namespace= kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.0.0.10 <none> 53/UDP,53/TCP 8m
このセクションの残りの部分は、寿命の長いIP(my-nginx)を持つServiceと、そのIPに名前を割り当てたDNSサーバーがあることを前提にしています。ここではCoreDNSクラスターアドオン(アプリケーション名: kube-dns
)を使用しているため、標準的なメソッド(gethostbyname()
など) を使用してクラスター内の任意のPodからServiceに通信できます。CoreDNSが起動していない場合、CoreDNS README またはInstalling CoreDNS を参照し、有効にする事ができます。curlアプリケーションを実行して、これをテストしてみましょう。
kubectl run curl --image= radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt
次に、Enterキーを押してnslookup my-nginx
を実行します:
[ root@curl-131556218-9fnch:/ ] $ nslookup my-nginx
Server: 10.0.0.10
Address 1: 10.0.0.10
Name: my-nginx
Address 1: 10.0.162.149
Serviceを安全にする
これまでは、クラスター内からnginxサーバーにアクセスしただけでした。
サービスをインターネットに公開する前に、通信チャネルが安全であることを確認する必要があります。
これには、次のものが必要です:
https用の自己署名証明書(既にID証明書を持っている場合を除く)
証明書を使用するように構成されたnginxサーバー
Podが証明書にアクセスできるようにするSecret
これらはすべてnginx httpsの例 から取得できます。
これにはツールをインストールする必要があります。
これらをインストールしたくない場合は、後で手動の手順に従ってください。つまり:
make keys KEY = /tmp/nginx.key CERT = /tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
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
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 :
nginx.crt : "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
nginx.key : "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
ファイルを使用してSecretを作成します:
kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME TYPE DATA AGE
default-token-il9rc kubernetes.io/service-account-token 1 1d
nginxsecret kubernetes.io/tls 2 1m
次に、nginxレプリカを変更して、シークレットの証明書とServiceを使用してhttpsサーバーを起動し、両方のポート(80と443)を公開します:
apiVersion : v1
kind : Service
metadata :
name : my-nginx
labels :
run : my-nginx
spec :
type : NodePort
ports :
- port : 8080
targetPort : 80
protocol : TCP
name : http
- port : 443
protocol : TCP
name : https
selector :
run : my-nginx
---
apiVersion : apps/v1
kind : Deployment
metadata :
name : my-nginx
spec :
selector :
matchLabels :
run : my-nginx
replicas : 1
template :
metadata :
labels :
run : my-nginx
spec :
volumes :
- name : secret-volume
secret :
secretName : nginxsecret
containers :
- name : nginxhttps
image : bprashanth/nginxhttps:1.0
ports :
- containerPort : 443
- containerPort : 80
volumeMounts :
- mountPath : /etc/nginx/ssl
name : secret-volume
nginx-secure-appマニフェストに関する注目すべき点:
同じファイルにDeploymentとServiceの両方が含まれています。
nginxサーバー はポート80のHTTPトラフィックと443のHTTPSトラフィックを処理し、nginx Serviceは両方のポートを公開します。
各コンテナは/etc/nginx/ssl
にマウントされたボリュームを介してキーにアクセスできます。
これは、nginxサーバーが起動する前に セットアップされます。
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml
この時点で、任意のノードからnginxサーバーに到達できます。
kubectl get pods -l run = my-nginx -o custom-columns= POD_IP:.status.podIPs
POD_IP
[ map[ ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>
最後の手順でcurlに-k
パラメーターを指定したことに注意してください。
これは、証明書の生成時にnginxを実行しているPodについて何も知らないためです。
CNameの不一致を無視するようcurlに指示する必要があります。
Serviceを作成することにより、証明書で使用されるCNameを、Service検索中にPodで使用される実際のDNS名にリンクしました。
これをPodからテストしましょう(簡単にするために同じシークレットを再利用しています。PodはServiceにアクセスするためにnginx.crtのみを必要とします):
apiVersion : apps/v1
kind : Deployment
metadata :
name : curl-deployment
spec :
selector :
matchLabels :
app : curlpod
replicas : 1
template :
metadata :
labels :
app : curlpod
spec :
volumes :
- name : secret-volume
secret :
secretName : nginxsecret
containers :
- name : curlpod
command :
- sh
- -c
- while true; do sleep 1; done
image : radial/busyboxplus:curl
volumeMounts :
- mountPath : /etc/nginx/ssl
name : secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app = curlpod
NAME READY STATUS RESTARTS AGE
curl-deployment-1515033274-1410r 1/1 Running 0 1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/tls.crt
...
<title>Welcome to nginx!</title>
...
Serviceを公開する
アプリケーションの一部では、Serviceを外部IPアドレスに公開したい場合があります。
Kubernetesは、NodePortとLoadBalancerの2つの方法をサポートしています。
前のセクションで作成したServiceはすでにNodePort
を使用しているため、ノードにパブリックIPがあれば、nginx HTTPSレプリカはインターネット上のトラフィックを処理する準備ができています。
kubectl get svc my-nginx -o yaml | grep nodePort -C 5
uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
clusterIP: 10.0.162.149
ports:
- name: http
nodePort: 31704
port: 8080
protocol: TCP
targetPort: 80
- name: https
nodePort: 32453
port: 443
protocol: TCP
targetPort: 443
selector:
run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
- address: 104.197.41.11
type: ExternalIP
allocatable:
--
- address: 23.251.152.56
type: ExternalIP
allocatable:
...
$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>
クラウドロードバランサーを使用するようにサービスを再作成しましょう。
my-nginx
サービスの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
...
次の項目
4 - Ingressコントローラー
クラスターで
Ingress を動作させるためには、
ingress controller が動作している必要があります。 少なくとも1つのIngressコントローラーを選択し、クラスター内にセットアップされていることを確認する必要があります。 このページはデプロイ可能な一般的なIngressコントローラーをリストアップします。
Ingressリソースが動作するためには、クラスターでIngressコントローラーが実行されている必要があります。
kube-controller-manager
バイナリの一部として実行される他のタイプのコントローラーとは異なり、Ingressコントローラーはクラスターで自動的に起動されません。このページを使用して、クラスターに最適なIngressコントローラーの実装を選択してください。
プロジェクトとしてのKubernetesは現在、AWS 、GCE 、およびnginx のIngressコントローラーをサポート・保守しています。
追加のコントローラー
備考: このセクションでは、Kubernetesが必要とする機能を提供するサードパーティープロジェクトにリンクしています。これらのプロジェクトはアルファベット順に記載されていて、Kubernetesプロジェクトの作者は責任を持ちません。このリストにプロジェクトを追加するには、変更を提出する前に
content guide をお読みください。
詳細はこちら。
複数のIngressコントローラーの使用
Ingress Class を使用して、複数のIngressコントローラーをクラスターにデプロイすることができます。
Ingress Classリソースの.metadata.name
に注目してください。
Ingressを作成する際には、IngressオブジェクトでingressClassName
フィールドを指定するために、その名前が必要になります(IngressSpec v1 reference を参照)。
ingressClassName
は古いannotation method の代替品です。
Ingressに対してIngressClassを指定せず、クラスターにはデフォルトとして設定されたIngressClassが1つだけある場合、KubernetesはIngressにクラスターのデフォルトIngressClassを適用 します。
IngressClassのingressclass.kubernetes.io/is-default-class
アノテーション を文字列"true"
に設定することで、デフォルトとしてIngressClassを設定します。
理想的には、すべてのIngressコントローラーはこの仕様を満たすべきですが、いくつかのIngressコントローラーはわずかに異なる動作をします。
備考: Ingressコントローラーのドキュメントを確認して、選択する際の注意点を理解してください。
次の項目
5 - Gateway API
Gateway APIは動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類のファミリーです。
拡張可能でロール指向な、プロトコルを意識した設定メカニズムを使用して、ネットワークサービスを利用可能にします。
Gateway API は、動的なインフラストラクチャの展開と高度なトラフィックルーティングを提供するAPIの種類 を含むアドオン です。
デザイン原則
Gateway APIのデザインとアーキテクチャは次の原則から成ります:
ロール指向: Gateway APIの種類は、Kubernetesのサービスネットワークの管理に対して責任を持つ組織のロールをモデルとしています:
インフラストラクチャプロバイダー: 複数の独立したクラスターをクラウドプロバイダーなどの複数のテナントに提供できるよう、インフラストラクチャを管理します。
クラスターオペレーター: クラスターを管理し、通常はポリシー、ネットワークアクセス、アプリケーションのパーミッションなどに関わります。
アプリケーション開発者: クラスター上で実行されるアプリケーションを管理し、通常はアプリケーションレベルの設定やService の構成に関わります。
ポータビリティ: Gateway APIの仕様はカスタムリソース として定義され、多くの実装 によってサポートされています。
豊富な機能: Gateway APIの種類は、ヘッダーベースのマッチング、トラフィックの重み付けといった、一般的なトラフィックルーティングのユースケースに対する機能をサポートしています。これは、Ingress ではカスタムアノテーションを使用することでのみ実現可能でした。
拡張可能: GatewayはカスタムリソースをAPIのさまざまなレイヤーでリンクさせることができます。これにより、APIの構造内の適切な場所で細かなカスタマイズが可能となります。
リソースモデル
Gateway APIには3つの安定版のAPIの種類があります:
GatewayClass: 共通の設定を持ち、クラスを実装するコントローラーによって管理されたゲートウェイの集合を定義します。
Gateway: クラウドロードバランサーなどのトラフィックを処理するインフラストラクチャのインスタンスを定義します。
HTTPRoute: Gatewayリスナーからバックエンドのネットワークエンドポイントへのトラフィックのマッピングに関する、HTTP固有のルールを定義します。
これらのエンドポイントは多くの場合、Service で表されます。
Gateway APIは、組織のロール指向の性質をサポートするために、相互に依存関係を持つ異なるAPIの種類によって構成されます。
Gatewayオブジェクトはただ一つのGatewayClassと関連づけられます。
GatewayClassは、このクラスのGatewayを管理する責任を持つGatewayコントローラーを記述します。
HTTPRouteのような1つ以上のルートの種類がGatewayに関連づけられます。
Gatewayは、そのリスナー
にアタッチされる可能性のあるルートをフィルタリングすることができ、ルートとの双方向の信頼モデルを形成します。
次の図は、3つの安定版のGateway APIの種類の関係を示しています:
GatewayClass
Gatewayは、通常異なる設定を持つ、異なるコントローラーによって実装されます。
Gatewayはクラスを実装したコントローラーの名前を含むGatewayClassを参照する必要があります。
最小のGatewayClassの例:
apiVersion : gateway.networking.k8s.io/v1
kind : GatewayClass
metadata :
name : example-class
spec :
controllerName : example.com/gateway-controller
この例では、Gateway APIを実装したコントローラーは、example.com/gateway-controller
という名前のコントローラーを持つGatewayClassを管理するように構成されます。
このクラスのGatewayは実装のコントローラーによって管理されます。
このAPIの種類の完全な定義については、GatewayClass のリファレンスを参照してください。
Gateway
Gatewayはトラフィックを処理するインフラストラクチャのインスタンスを記述します。
これは、Serviceのようなバックエンドに対して、フィルタリング、分散、分割などのようなトラフィック処理のために使用されるネットワークエンドポイントを定義します。
例えばGatewayは、HTTPトラフィックを受け付けるために構成された、クラウドロードバランサーやクラスター内のプロキシサーバーを表す場合があります。
最小のGatewayリソースの例:
apiVersion : gateway.networking.k8s.io/v1
kind : Gateway
metadata :
name : example-gateway
spec :
gatewayClassName : example-class
listeners :
- name : http
protocol : HTTP
port : 80
この例では、トラフィックを処理するインフラストラクチャのインスタンスは、80番ポートでHTTPトラフィックをリッスンするようにプログラムされています。
address
フィールドが指定されていないので、アドレスまたはホスト名はコントローラーの実装によってGatewayに割り当てられます。
このアドレスは、ルートで定義されたバックエンドのネットワークエンドポイントのトラフィックを処理するためのネットワークエンドポイントとして使用されます。
このAPIの種類の完全な定義については、Gateway のリファレンスを参照してください。
HTTPRoute
HTTPRouteの種類は、Gatewayリスナーからバックエンドのネットワークエンドポイントに対するHTTPリクエストのルーティングの振る舞いを指定します。
Serviceバックエンドに対して、実装はバックエンドのネットワークエンドポイントをService IPまたはServiceの背後のエンドポイントとして表すことができます。
基盤となるGatewayの実装に適用される設定はHTTPRouteによって表されます。
例えば、新しいHTTPRouteを定義することにより、クラウドロードバランサーやクラスター内のプロキシサーバーの追加のトラフィックルートを構成する場合があります。
最小のHTTPRouteの例:
apiVersion : gateway.networking.k8s.io/v1
kind : HTTPRoute
metadata :
name : example-httproute
spec :
parentRefs :
- name : example-gateway
hostnames :
- "www.example.com"
rules :
- matches :
- path :
type : PathPrefix
value : /login
backendRefs :
- name : example-svc
port : 8080
この例では、Host:ヘッダーにwww.example.com
が設定され、リクエストパスに/login
が指定されたHTTPトラフィックが、example-gateway
という名前のGatewayから、8080
番ポート上のexample-svc
という名前のServiceにルーティングされます。
このAPIの種類の完全な定義については、HTTPRoute のリファレンスを参照してください。
リクエストフロー
以下は、GatewayとHTTPRouteを使用してHTTPトラフィックをServiceにルーティングする簡単な例です:
この例では、リバースプロキシとして実装されたGatewayに対するリクエストフローは次のようになります:
クライアントはURL http://www.example.com
に対するHTTPリクエストの準備を開始します。
クライアントのDNSリゾルバは宛先の名前をクエリし、Gatewayに関連づけられた1つ以上のIPアドレスとのマッピングを学習します。
クライアントはGatewayのIPアドレスにリクエストを送信します。リバースプロキシはHTTPリクエストを受信し、Host:ヘッダーを使用してGatewayとそれに関連づけられたHTTPRouteから導かれた構成にマッチさせます。
オプションで、リバースプロキシはHTTPRouteのマッチングルールに基づいて、リクエストヘッダーもしくはパスのマッチングを実行することができます。
オプションで、リバースプロキシはリクエストを変更することができます。例えば、HTTPRouteのフィルタールールに従ってヘッダーを追加または削除します。
最後に、リバースプロキシはリクエストを1つ以上のバックエンドにフォワードします。
適合性
Gateway APIは幅広い機能をカバーし、広く実装されています。
この組み合わせは、APIがどこで使われても一貫した体験を提供することを保証するために、明確な適合性の定義とテストを必要とします。
リリースチャンネル、サポートレベル、そして適合テストの実行などの詳細を理解するためには、適合性 のドキュメントを参照してください。
Ingressからの移行
Gateway APIはIngress APIの後継です。
しかし、Ingressは含まれていません。
このため、既存のIngressリソースからGateway APIリソースへの変換を1度だけ行う必要があります。
IngressリソースからGateway APIリソースへの移行の詳細に関するガイドは、Ingressの移行 を参照してください。
次の項目
Gateway APIリソースをKubernetesでネイティブに実装する代わりに、幅広い実装 によってサポートされたカスタムリソース として仕様が定義されています。
Gateway API CRDをインストール するか、選んだ実装のインストール手順に従ってください。
実装をインストールした後、Getting Started ガイドを使用してGateway APIをすぐに使い始めることができます。
備考: 選択した実装のドキュメントを必ず確認し、注意点を理解するようにしてください。
すべてのGateway API種別の追加の詳細についてはAPI仕様 を参照してください。
6 - EndpointSlice
FEATURE STATE:
Kubernetes v1.17 [beta]
EndpointSlice は、Kubernetesクラスター内にあるネットワークエンドポイントを追跡するための単純な手段を提供します。EndpointSliceは、よりスケーラブルでより拡張可能な、Endpointの代わりとなるものです。
動機
Endpoint APIはKubernetes内のネットワークエンドポイントを追跡する単純で直観的な手段を提供してきました。
残念ながら、KubernetesクラスターやService が大規模になり、より多くのトラフィックを処理し、より多くのバックエンドPodに送信するようになるにしたがって、Endpoint APIの限界が明らかになってきました。
最も顕著な問題の1つに、ネットワークエンドポイントの数が大きくなったときのスケーリングの問題があります。
Serviceのすべてのネットワークエンドポイントが単一のEndpointリソースに格納されていたため、リソースのサイズが非常に大きくなる場合がありました。これがKubernetesのコンポーネント(特に、マスターコントロールプレーン)の性能に悪影響を与え、結果として、Endpointに変更があるたびに、大量のネットワークトラフィックと処理が発生するようになってしまいました。EndpointSliceは、この問題を緩和するとともに、トポロジカルルーティングなどの追加機能のための拡張可能なプラットフォームを提供します。
EndpointSliceリソース
Kubernetes内ではEndpointSliceにはネットワークエンドポイントの集合へのリファレンスが含まれます。
コントロールプレーンは、セレクター が指定されているKubernetes ServiceのEndpointSliceを自動的に作成します。
これらのEndpointSliceには、Serviceセレクターに一致するすべてのPodへのリファレンスが含まれています。
EndpointSliceは、プロトコル、ポート番号、およびサービス名の一意の組み合わせによってネットワークエンドポイントをグループ化します。
EndpointSliceオブジェクトの名前は有効なDNSサブドメイン名 である必要があります。
一例として、以下にexample
というKubernetes Serviceに対するサンプルのEndpointSliceリソースを示します。
apiVersion : discovery.k8s.io/v1beta1
kind : EndpointSlice
metadata :
name : example-abc
labels :
kubernetes.io/service-name : example
addressType : IPv4
ports :
- name : http
protocol : TCP
port : 80
endpoints :
- addresses :
- "10.1.2.3"
conditions :
ready : true
hostname : pod-1
topology :
kubernetes.io/hostname : node-1
topology.kubernetes.io/zone : us-west2-a
デフォルトでは、コントロールプレーンはEndpointSliceを作成・管理し、それぞれのエンドポイント数が100以下になるようにします。--max-endpoints-per-slice
kube-controller-manager フラグを設定することで、最大1000個まで設定可能です。
EndpointSliceは内部トラフィックのルーティング方法に関して、kube-proxy に対する唯一のソース(source of truth)として振る舞うことができます。EndpointSliceを有効にすれば、非常に多数のエンドポイントを持つServiceに対して性能向上が得られるはずです。
アドレスの種類
EndpointSliceは次の3種類のアドレスをサポートします。
IPv4
IPv6
FQDN (Fully Qualified Domain Name、完全修飾ドメイン名)
トポロジー
EndpointSliceに属する各エンドポイントは、関連するトポロジーの情報を持つことができます。この情報は、エンドポイントの場所を示すために使われ、対応するNode、ゾーン、リージョンに関する情報が含まれます。
値が利用できる場合には、コントロールプレーンはEndpointSliceコントローラーに次のようなTopologyラベルを設定します。
kubernetes.io/hostname
- このエンドポイントが存在するNodeの名前。
topology.kubernetes.io/zone
- このエンドポイントが存在するゾーン。
topology.kubernetes.io/region
- このエンドポイントが存在するリージョン。
これらのラベルの値はスライス内の各エンドポイントと関連するリソースから継承したものです。hostnameラベルは対応するPod上のNodeNameフィールドの値を表します。zoneとregionラベルは対応するNode上の同じ名前のラベルの値を表します。
管理
ほとんどの場合、コントロールプレーン(具体的には、EndpointSlice コントローラー )は、EndpointSliceオブジェクトを作成および管理します。EndpointSliceには、サービスメッシュの実装など、他のさまざまなユースケースがあり、他のエンティティまたはコントローラーがEndpointSliceの追加セットを管理する可能性があります。
複数のエンティティが互いに干渉することなくEndpointSliceを管理できるようにするために、KubernetesはEndpointSliceを管理するエンティティを示すendpointslice.kubernetes.io/managed-by
というラベル を定義します。
EndpointSliceを管理するその他のエンティティも同様に、このラベルにユニークな値を設定する必要があります。
所有権
ほとんどのユースケースでは、EndpointSliceはエンドポイントスライスオブジェクトがエンドポイントを追跡するServiceによって所有されます。
これは、各EndpointSlice上のownerリファレンスとkubernetes.io/service-name
ラベルによって示されます。これにより、Serviceに属するすべてのEndpointSliceを簡単に検索できるようになっています。
EndpointSliceのミラーリング
場合によっては、アプリケーションはカスタムEndpointリソースを作成します。これらのアプリケーションがEndpointリソースとEndpointSliceリソースの両方に同時に書き込む必要がないようにするために、クラスターのコントロールプレーンは、ほとんどのEndpointリソースを対応するEndpointSliceにミラーリングします。
コントロールプレーンは、次の場合を除いて、Endpointリソースをミラーリングします。
Endpointリソースのendpointslice.kubernetes.io/skip-mirror
ラベルがtrue
に設定されています。
Endpointリソースがcontrol-plane.alpha.kubernetes.io/leader
アノテーションを持っています。
対応するServiceリソースが存在しません。
対応するServiceリソースには、nil以外のセレクターがあります。
個々のEndpointリソースは、複数のEndpointSliceに変換される場合があります。これは、Endpointリソースに複数のサブセットがある場合、または複数のIPファミリ(IPv4およびIPv6)を持つエンドポイントが含まれている場合に発生します。サブセットごとに最大1000個のアドレスがEndpointSliceにミラーリングされます。
EndpointSliceの分散
それぞれのEndpointSliceにはポートの集合があり、リソース内のすべてのエンドポイントに適用されます。サービスが名前付きポートを使用した場合、Podが同じ名前のポートに対して、結果的に異なるターゲットポート番号が使用されて、異なるEndpointSliceが必要になる場合があります。これはサービスの部分集合がEndpointにグループ化される場合と同様です。
コントロールプレーンはEndpointSliceをできる限り充填しようとしますが、積極的にリバランスを行うことはありません。コントローラーのロジックは極めて単純で、以下のようになっています。
既存のEndpointSliceをイテレートし、もう必要のないエンドポイントを削除し、変更があったエンドポイントを更新する。
前のステップで変更された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
実装にあります。
次の項目
7 - ネットワークポリシー
IPアドレスまたはポートのレベル(OSI参照モデルのレイヤー3または4)でトラフィックフローを制御したい場合、クラスター内の特定のアプリケーションにKubernetesのネットワークポリシーを使用することを検討してください。ネットワークポリシーはアプリケーション中心の構造であり、Pod がネットワークを介して多様な「エンティティ」(「Endpoint」や「Service」のようなKubernetesに含まれる特定の意味を持つ共通の用語との重複を避けるため、ここではエンティティという単語を使用します。)と通信する方法を指定できます。
Podが通信できるエンティティは以下の3つの識別子の組み合わせによって識別されます。
許可されている他のPod(例外: Podはそれ自体へのアクセスをブロックできません)
許可されている名前空間
IPブロック(例外: PodまたはノードのIPアドレスに関係なく、Podが実行されているノードとの間のトラフィックは常に許可されます。)
Podベースもしくは名前空間ベースのネットワークポリシーを定義する場合、セレクター を使用してセレクターに一致するPodとの間で許可されるトラフィックを指定します。
一方でIPベースのネットワークポリシーが作成されると、IPブロック(CIDRの範囲)に基づいてポリシーが定義されます。
前提条件
ネットワークポリシーは、ネットワークプラグイン により実装されます。ネットワークポリシーを使用するには、NetworkPolicyをサポートするネットワークソリューションを使用しなければなりません。ネットワークポリシーを実装したコントローラーを使用せずにNetworkPolicyリソースを作成した場合は、何も効果はありません。
分離されたPodと分離されていないPod
デフォルトでは、Podは分離されていない状態(non-isolated)となるため、すべてのソースからのトラフィックを受信します。
Podを選択するNetworkPolicyが存在すると、Podは分離されるようになります。名前空間内に特定のPodを選択するNetworkPolicyが1つでも存在すると、そのPodはいずれかのNetworkPolicyで許可されていないすべての接続を拒否するようになります。(同じ名前空間内のPodでも、どのNetworkPolicyにも選択されなかった他のPodは、引き続きすべてのトラフィックを許可します。)
ネットワークポリシーは追加式であるため、競合することはありません。複数のポリシーがPodを選択する場合、そのPodに許可されるトラフィックは、それらのポリシーのingress/egressルールの和集合で制限されます。したがって、評価の順序はポリシーの結果には影響がありません。
NetworkPolicyリソース
リソースの完全な定義については、リファレンスのNetworkPolicy のセクションを参照してください。
以下は、NetworkPolicyの一例です。
apiVersion : networking.k8s.io/v1
kind : NetworkPolicy
metadata :
name : test-network-policy
namespace : default
spec :
podSelector :
matchLabels :
role : db
policyTypes :
- Ingress
- Egress
ingress :
- from :
- ipBlock :
cidr : 172.17.0.0 /16
except :
- 172.17.1.0 /24
- namespaceSelector :
matchLabels :
project : myproject
- podSelector :
matchLabels :
role : frontend
ports :
- protocol : TCP
port : 6379
egress :
- to :
- ipBlock :
cidr : 10.0.0.0 /24
ports :
- protocol : TCP
port : 5978
備考: 選択したネットワークソリューションがネットワークポリシーをサポートしていない場合には、これをクラスターのAPIサーバーにPOSTしても効果はありません。
必須フィールド : 他のKubernetesの設定と同様に、NetworkPolicyにも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の例では、次のようにネットワークポリシーを適用します。
"default"名前空間内にある"role=db"のPodを、内向きと外向きのトラフィックに対して分離する(まだ分離されていない場合)
(Ingressルール) "default"名前空間内の"role=db"ラベルが付いたすべてのPodのTCPの6379番ポートへの接続のうち、次の送信元からのものを許可する
"default"名前空間内のラベル"role=frontend"が付いたすべてのPod
ラベル"project=myproject"が付いた名前空間内のすべてのPod
172.17.0.0–172.17.0.255および172.17.2.0–172.17.255.255(言い換えれば、172.17.1.0/24の範囲を除く172.17.0.0/16)の範囲内のすべてのIPアドレス
(Egressルール) "role=db"というラベルが付いた"default"名前空間内のすべてのPodからの、TCPの5978番ポート上でのCIDR 10.0.0.0/24への接続を許可する
追加の例については、ネットワークポリシーを宣言する の説明を参照してください。
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でも、内向きと外向きのトラフィックが許可されないことを保証できます。
SCTPのサポート
FEATURE STATE:
Kubernetes v1.19 [beta]
ベータ版の機能として、これはデフォルトで有効化されます。
クラスターレベルでSCTPを無効化するために、クラスター管理者はAPIサーバーで--feature-gates=SCTPSupport=false,…
と指定して、SCTPSupport
フィーチャーゲート を無効にする必要があります。
備考: SCTPプロトコルのネットワークポリシーをサポートする
CNI プラグインを使用している必要があります。
ネットワークポリシーでできないこと(少なくともまだ)
Kubernetes1.20現在、ネットワークポリシーAPIに以下の機能は存在しません。
しかし、オペレーティングシステムのコンポーネント(SELinux、OpenVSwitch、IPTablesなど)、レイヤー7の技術(Ingressコントローラー、サービスメッシュ実装)、もしくはアドミッションコントローラーを使用して回避策を実装できる場合があります。
Kubernetesのネットワークセキュリティを初めて使用する場合は、ネットワークポリシーAPIを使用して以下のユーザーストーリーを(まだ)実装できないことに注意してください。これらのユーザーストーリーの一部(全てではありません)は、ネットワークポリシーAPIの将来のリリースで活発に議論されています。
クラスター内トラフィックを強制的に共通ゲートウェイを通過させる(これは、サービスメッシュもしくは他のプロキシで提供するのが最適な場合があります)。
TLS関連のもの(これにはサービスメッシュまたはIngressコントローラーを使用します)。
ノードの固有のポリシー(これらにはCIDR表記を使用できますが、Kubernetesのアイデンティティでノードを指定することはできません)。
名前空間またはサービスを名前で指定する(ただし、Podまたは名前空間をラベル で指定することができます。これは多くの場合で実行可能な回避策です)。
サードパーティによって実行される「ポリシー要求」の作成または管理
全ての名前空間もしくはPodに適用されるデフォルトのポリシー(これを実現できるサードパーティのKubernetesディストリビューションとプロジェクトがいくつか存在します)。
高度なポリシークエリと到達可能性ツール
単一のポリシー宣言でポートの範囲を指定する機能
ネットワークセキュリティイベント(例えばブロックされた接続や受け入れられた接続)をログに記録する機能
ポリシーを明示的に拒否する機能(現在、ネットワークポリシーのモデルはデフォルトで拒否されており、許可ルールを追加する機能のみが存在します)。
ループバックまたは内向きのホストトラフィックを拒否する機能(Podは現在localhostのアクセスやそれらが配置されているノードからのアクセスをブロックすることはできません)。
次の項目
8 - ServiceとPodに対するDNS
このページではKubernetesによるDNSサポートについて概観します。
イントロダクション
KubernetesのDNSはクラスター上でDNS PodとServiceをスケジュールし、DNSの名前解決をするために各コンテナに対してDNS ServiceのIPを使うようにKubeletを設定します。
何がDNS名を取得するか
クラスター内(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ディスカバリ を参照ください。
Service
A/AAAAレコード
"通常の"(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レコード
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
A/AAAAレコード
一般的に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のhostnameとsubdomainフィールド
現在、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
を任意のエンドポイントアドレスに対して指定できます。
備考: A(AAAA)レコードはPodの名前に対して作成されないため、hostname
はPodのA(AAAA)レコードが作成されるために必須となります。hostname
を持たないがsubdomain
を持つようなPodは、そのPodのIPアドレスを指し示すHeadless Service(default-subdomain.my-namespace.svc.cluster.local
)に対するA(AAAA)レコードのみ作成します。
PodのsetHostnameAsFQDNフィールド
FEATURE STATE:
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のサイズを制御することです。
PodのDNSポリシー
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 を参照ください。
備考: "Default"は、デフォルトのDNSポリシーではありません。もしdnsPolicy
が明示的に指定されていない場合、"ClusterFirst"が使用されます。
下記の例では、hostNetwork
フィールドがtrue
にセットされているため、dnsPolicy
が"ClusterFirstWithHostNet
"とセットされているPodを示します。
apiVersion : v1
kind : Pod
metadata :
name : busybox
namespace : default
spec :
containers :
- image : busybox:1.28
command :
- sleep
- "3600"
imagePullPolicy : IfNotPresent
name : busybox
restartPolicy : Always
hostNetwork : true
dnsPolicy : ClusterFirstWithHostNet
PodのDNS設定
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
DNS機能を利用可用なバージョン
PodのDNS設定と"None
"というDNSポリシーの利用可能なバージョンに関しては下記の通りです。
k8s version
Feature support
1.14
ステーブル
1.10
β版 (デフォルトで有効)
1.9
α版
次の項目
DNS設定の管理方法に関しては、DNS Serviceの設定
を確認してください。
9 - IPv4/IPv6デュアルスタック
FEATURE STATE:
Kubernetes v1.16 [alpha]
IPv4/IPv6デュアルスタックを利用すると、IPv4とIPv6のアドレスの両方をPod およびService に指定できるようになります。
KubernetesクラスターでIPv4/IPv6デュアルスタックのネットワークを有効にすれば、クラスターはIPv4とIPv6のアドレスの両方を同時に割り当てることをサポートするようになります。
サポートされている機能
KubernetesクラスターでIPv4/IPv6デュアルスタックを有効にすると、以下の機能が提供されます。
デュアルスタックのPodネットワーク(PodごとにIPv4とIPv6のアドレスが1つずつ割り当てられます)
IPv4およびIPv6が有効化されたService(各Serviceは1つのアドレスファミリーでなければなりません)
IPv4およびIPv6インターフェースを経由したPodのクラスター外向きの(たとえば、インターネットへの)ルーティング
前提条件
IPv4/IPv6デュアルスタックのKubernetesクラスターを利用するには、以下の前提条件を満たす必要があります。
Kubernetesのバージョンが1.16以降である
プロバイダーがデュアルスタックのネットワークをサポートしている(クラウドプロバイダーなどが、ルーティング可能なIPv4/IPv6ネットワークインターフェースが搭載されたKubernetesを提供可能である)
ネットワークプラグインがデュアルスタックに対応している(KubenetやCalicoなど)
IPv4/IPv6デュアルスタックを有効にする
IPv4/IPv6デュアルスタックを有効にするには、クラスターの関連コンポーネントでIPv6DualStack
フィーチャーゲート を有効にして、デュアルスタックのクラスターネットワークの割り当てを以下のように設定します。
kube-apiserver:
--feature-gates="IPv6DualStack=true"
--service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
kube-controller-manager:
--feature-gates="IPv6DualStack=true"
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
--service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
--node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6
デフォルトのサイズは、IPv4では/24、IPv6では/64です
kubelet:
--feature-gates="IPv6DualStack=true"
kube-proxy:
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
--feature-gates="IPv6DualStack=true"
備考: IPv4 CIDRの例: 10.244.0.0/16
(自分のクラスターのアドレス範囲を指定してください)
IPv6 CIDRの例: fdXY:IJKL:MNOP:15::/64
(これはフォーマットを示すための例であり、有効なアドレスではありません。詳しくはRFC 4193 を参照してください)
Service
クラスターでIPv4/IPv6デュアルスタックのネットワークを有効にした場合、IPv4またはIPv6のいずれかのアドレスを持つService を作成できます。Serviceのcluster IPのアドレスファミリーは、Service上に.spec.ipFamily
フィールドを設定することで選択できます。このフィールドを設定できるのは、新しいServiceの作成時のみです。.spec.ipFamily
フィールドの指定はオプションであり、Service とIngress でIPv4とIPv6を有効にする予定がある場合にのみ使用するべきです。このフィールドの設定は、外向きのトラフィック に対する要件には含まれません。
備考: クラスターのデフォルトのアドレスファミリーは、kube-controller-managerに--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 : 9376
Type LoadBalancer
IPv6が有効になった外部ロードバランサーをサポートしているクラウドプロバイダーでは、type
フィールドにLoadBalancer
を指定し、ipFamily
フィールドにIPv6
を指定することにより、クラウドロードバランサーをService向けにプロビジョニングできます。
外向きのトラフィック
パブリックおよび非パブリックでのルーティングが可能なIPv6アドレスのブロックを利用するためには、クラスターがベースにしているCNI プロバイダーがIPv6の転送を実装している必要があります。もし非パブリックでのルーティングが可能なIPv6アドレスを使用するPodがあり、そのPodをクラスター外の送信先(例:パブリックインターネット)に到達させたい場合、外向きのトラフィックと応答の受信のためにIPマスカレードを設定する必要があります。ip-masq-agent はデュアルスタックに対応しているため、デュアルスタックのクラスター上でのIPマスカレードにはip-masq-agentが利用できます。
既知の問題
Kubenetは、IPv4,IPv6の順番にIPを報告することを強制します(--cluster-cidr)
次の項目
10 - トポロジーを意識したルーティング
Topology Aware Routing は、ネットワークトラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。クラスター内のPod間で同じゾーンのトラフィックを優先することで、信頼性、パフォーマンス(ネットワークの待ち時間やスループット)の向上、またはコストの削減に役立ちます。
FEATURE STATE:
Kubernetes v1.23 [beta]
備考: Kubernetes 1.27より前には、この機能は、Topology Aware Hint として知られていました。
Topology Aware Routing は、トラフィックを発信元のゾーンに維持するようにルーティング動作を調整します。場合によっては、コストを削減したり、ネットワークパフォーマンスを向上させたりすることができます。
動機
Kubernetesクラスターは、マルチゾーン環境で展開されることが多くなっています。
Topology Aware Routing は、トラフィックを発信元のゾーン内に留めておくのに役立つメカニズムを提供します。EndpointSliceコントローラーはService のendpointを計算する際に、各endpointのトポロジー(リージョンとゾーン)を考慮し、ゾーンに割り当てるためのヒントフィールドに値を入力します。kube-proxy のようなクラスターコンポーネントは、次にこれらのヒントを消費し、それらを使用してトラフィックがルーティングされる方法に影響を与えることが可能です(トポロジー的に近いendpointを優先します)。
Topology Aware Routingを有効にする
備考: Kubernetes 1.27より前には、この動作はservice.kubernetes.io/topology-aware-hints
アノテーションを使用して制御されていました。
service.kubernetes.io/topology-mode
アノテーションをauto
に設定すると、サービスに対してTopology Aware Routingを有効にすることができます。各ゾーンに十分なendpointがある場合、個々のendpointを特定のゾーンに割り当てるために、トポロジーヒントがEndpointSliceに入力され、その結果、トラフィックは発信元の近くにルーティングされます。
最も効果的なとき
この機能は、次の場合に最も効果的に動作します。
1. 受信トラフィックが均等に分散されている
トラフィックの大部分が単一のゾーンから発信されている場合、トラフィックはそのゾーンに割り当てられたendpointのサブセットに過負荷を与える可能性があります。受信トラフィックが単一のゾーンから発信されることが予想される場合、この機能は推奨されません。
2. 1つのゾーンに3つ以上のendpointを持つサービス
3つのゾーンからなるクラスターでは、これは9つ以上のendpointがあることを意味します。ゾーン毎のendpointが3つ未満の場合、EndpointSliceコントローラーはendpointを均等に割り当てることができず、代わりにデフォルトのクラスター全体のルーティングアプローチに戻る可能性が高く(約50%)なります。
使い方
「Auto」ヒューリスティックは、各ゾーンに多数のendpointを比例的に割り当てようとします。このヒューリスティックは、非常に多くのendpointを持つサービスに最適です。
EndpointSliceコントローラー
このヒューリスティックが有効な場合、EndpointSliceコントローラーはEndpointSliceにヒントを設定する役割を担います。コントローラーは、各ゾーンに比例した量のendpointを割り当てます。この割合は、そのゾーンで実行されているノードの割り当て可能な CPUコアを基に決定されます。
たとえば、あるゾーンに2つのCPUコアがあり、別のゾーンに1つのCPUコアしかない場合、コントローラーは2つのCPUコアを持つゾーンに2倍のendpointを割り当てます。
次の例は、ヒントが入力されたときのEndpointSliceの様子を示しています。
apiVersion : discovery.k8s.io/v1
kind : EndpointSlice
metadata :
name : example-hints
labels :
kubernetes.io/service-name : example-svc
addressType : IPv4
ports :
- name : http
protocol : TCP
port : 80
endpoints :
- addresses :
- "10.1.2.3"
conditions :
ready : true
hostname : pod-1
zone : zone-a
hints :
forZones :
- name : "zone-a"
kube-proxy
kube-proxyは、EndpointSliceコントローラーによって設定されたヒントに基づいて、ルーティング先のendpointをフィルター処理します。ほとんどの場合、これはkube-proxyが同じゾーン内のendpointにトラフィックをルーティングできることを意味します。コントローラーが別のゾーンからendpointを割り当てて、ゾーン間でendpointがより均等に分散されるようにする場合があります。これにより、一部のトラフィックが他のゾーンにルーティングされます。
セーフガード
各ノードのKubernetesコントロールプレーンとkube-proxyは、Topology Aware Hintを使用する前に、いくつかのセーフガードルールを適用します。これらがチェックアウトされない場合、kube-proxyは、ゾーンに関係なく、クラスター内のどこからでもendpointを選択します。
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リリースに含まれています。これは限定的な実装であり、関連する妥当と思われる状況をまだカバーしていない可能性があります。
次の項目
11 - ServiceのClusterIPの割り当て
Kubernetesでは、Service はPodの集合上で実行しているアプリケーションを抽象的に公開する方法です。Serviceはクラスター内で仮想IPアドレス(type: ClusterIPのServiceを使用)を持つことができます。クライアントはその仮想IPアドレスを使用してServiceに接続することができます。そしてKubernetesは、そのServiceへのトラフィックを異なる背後のPod間で負荷分散します。
どのようにServiceのClusterIPが割り当てられるのか?
KubernetesがServiceに仮想IPアドレスを割り当てる必要がある場合、2つの方法の内どちらかの方法で行われます:
動的割り当て
クラスターのコントロールプレーンは自動的にtype: ClusterIP
のServiceのために設定されたIP範囲の中から未割り当てのIPアドレスを選びます。
静的割り当て
Serviceのために設定されたIP範囲の中から自身でIPアドレスを選びます。
クラスター全体を通して、ServiceのClusterIP
はユニークでなければいけません。割り当て済みのClusterIP
を使用してServiceを作成しようとするとエラーが返ってきます。
なぜServiceのClusterIPを予約する必要があるのか?
時には、クラスター内の他のコンポーネントやユーザーが利用できるように、Serviceをよく知られたIPアドレスで実行したい場合があります。
その最たる例がクラスターのDNS Serviceです。慣習として、一部のKubernetesインストーラーはServiceのIP範囲の10番目のIPアドレスをDNS Serviceに割り当てます。ServiceのIP範囲を10.96.0.0/16とするクラスターを構成し、DNS ServiceのIPを10.96.0.10にするとします。この場合、下記のようなServiceを作成する必要があります。
apiVersion : v1
kind : Service
metadata :
labels :
k8s-app : kube-dns
kubernetes.io/cluster-service : "true"
kubernetes.io/name : CoreDNS
name : kube-dns
namespace : kube-system
spec :
clusterIP : 10.96.0.10
ports :
- name : dns
port : 53
protocol : UDP
targetPort : 53
- name : dns-tcp
port : 53
protocol : TCP
targetPort : 53
selector :
k8s-app : kube-dns
type : ClusterIP
しかし、前述したように10.96.0.10のIPアドレスは予約されていません。他のServiceが動的割り当てよりも前に、または同時に作成された場合、このIPアドレスがそのServiceに割り当てられる可能性があります。その場合、競合エラーで失敗しDNS Serviceを作成することができません。
どのようにServiceのClusterIPの競合を回避するのか?
Kubernetesで実装されているServiceへのClusterIPの割り当て戦略は、衝突リスクを軽減します。
ClusterIP
の範囲は、min(max(16, cidrSize / 16), 256)
という式に基づいて分割されます。最小で16、最大でも256で、その範囲内で段階的に変化する ように表されます。
動的IP割り当てはデフォルトで上位の帯域を使用し、それが使い切られると下位の範囲を使用します。これにより、ユーザーは下位の帯域を使用して静的な割り当てを行うことができ、衝突のリスクを抑えることができます。
例
例1
この例ではServiceのIPアドレスとして、10.96.0.0/24(CIDR表記法)のIPアドレスの範囲を使用します。
範囲の大きさ: 28 - 2 = 254
帯域のオフセット(開始位置): min(max(16, 256/16), 256)
= min(16, 256)
= 16
静的割り当ての帯域の開始: 10.96.0.1
静的割り当ての帯域の終了: 10.96.0.16
範囲の終了: 10.96.0.254
pie showData
title 10.96.0.0/24
"静的割り当て" : 16
"動的割り当て" : 238
このコンテンツを表示するには、JavaScriptを有効に する必要があります
例2
この例では、ServiceのIPアドレスとして、10.96.0.0/20(CIDR表記法)のIPアドレスの範囲を使用します。
範囲の大きさ: 212 - 2 = 4094
帯域のオフセット(開始位置): min(max(16, 4096/16), 256)
= min(256, 256)
= 256
静的割り当ての帯域の開始: 10.96.0.1
静的割り当ての帯域の終了: 10.96.1.0
範囲の終了: 10.96.15.254
pie showData
title 10.96.0.0/20
"静的割り当て" : 256
"動的割り当て" : 3838
このコンテンツを表示するには、JavaScriptを有効に する必要があります
例3
この例ではServiceのIPアドレスとして、10.96.0.0/16(CIDR表記法)のIPアドレスの範囲を使用します。
範囲の大きさ: 216 - 2 = 65534
帯域のオフセット(開始位置): min(max(16, 65536/16), 256)
= min(4096, 256)
= 256
静的割り当ての帯域の開始: 10.96.0.1
静的割り当ての帯域の終了: 10.96.1.0
範囲の終了: 10.96.255.254
pie showData
title 10.96.0.0/16
"静的割り当て" : 256
"動的割り当て" : 65278
このコンテンツを表示するには、JavaScriptを有効に する必要があります
次の項目
12 - サービス内部トラフィックポリシー
FEATURE STATE:
Kubernetes v1.21 [alpha]
サービス内部トラフィックポリシー を使用すると、内部トラフィック制限により、トラフィックが発信されたノード内のエンドポイントにのみ内部トラフィックをルーティングできます。
ここでの「内部」トラフィックとは、現在のクラスターのPodから発信されたトラフィックを指します。これは、コストを削減し、パフォーマンスを向上させるのに役立ちます。
ServiceInternalTrafficPolicyの使用
ServiceInternalTrafficPolicy
フィーチャーゲート を有効にすると、.spec.internalTrafficPolicy
をLocal
に設定して、Service 内部のみのトラフィックポリシーを有効にすることができます。
これにより、kube-proxyは、クラスター内部トラフィックにノードローカルエンドポイントのみを使用するようになります。
備考: 特定のServiceのエンドポイントがないノード上のPodの場合、Serviceに他のノードのエンドポイントがある場合でも、Serviceは(このノード上のポッドの)エンドポイントがゼロであるかのように動作します。
次の例は、.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
です。
制約
ServiceでexternalTrafficPolicy
がLocal
に設定されている場合、サービス内部トラフィックポリシーは使用されません。同じServiceだけではなく、同じクラスター内の異なるServiceで両方の機能を使用することができます。
次の項目