1 - Namespaceに対する最小および最大メモリー制約の構成

このページでは、Namespaceで実行されるコンテナが使用するメモリーの最小値と最大値を設定する方法を説明します。 LimitRange で最小値と最大値のメモリー値を指定します。 PodがLimitRangeによって課される制約を満たさない場合、そのNamespaceではPodを作成できません。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

クラスター内の各ノードには、少なくとも1GiBのメモリーが必要です。

Namespaceの作成

この演習で作成したリソースがクラスターの他の部分から分離されるように、Namespaceを作成します。

kubectl create namespace constraints-mem-example

LimitRangeとPodを作成

LimitRangeの設定ファイルです。

apiVersion: v1
kind: LimitRange
metadata:
  name: mem-min-max-demo-lr
spec:
  limits:
  - max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

LimitRangeを作成します。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints.yaml --namespace=constraints-mem-example

LimitRangeの詳細情報を表示します。

kubectl get limitrange mem-min-max-demo-lr --namespace=constraints-mem-example --output=yaml

出力されるのは、予想通りメモリー制約の最小値と最大値を示しています。 しかし、LimitRangeの設定ファイルでデフォルト値を指定していないにもかかわらず、 自動的に作成されていることに気づきます。

  limits:
  - default:
      memory: 1Gi
    defaultRequest:
      memory: 1Gi
    max:
      memory: 1Gi
    min:
      memory: 500Mi
    type: Container

constraints-mem-exampleNamespaceにコンテナが作成されるたびに、 Kubernetesは以下の手順を実行するようになっています。

  • コンテナが独自のメモリー要求と制限を指定しない場合は、デフォルトのメモリー要求と制限をコンテナに割り当てます。

  • コンテナに500MiB以上のメモリー要求があることを確認します。

  • コンテナのメモリー制限が1GiB以下であることを確認します。

以下は、1つのコンテナを持つPodの設定ファイルです。設定ファイルのコンテナ(containers)では、600MiBのメモリー要求と800MiBのメモリー制限が指定されています。これらはLimitRangeによって課される最小と最大のメモリー制約を満たしています。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo
spec:
  containers:
  - name: constraints-mem-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "600Mi"

Podの作成

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod.yaml --namespace=constraints-mem-example

Podのコンテナが実行されていることを確認します。

kubectl get pod constraints-mem-demo --namespace=constraints-mem-example

Podの詳細情報を見ます

kubectl get pod constraints-mem-demo --output=yaml --namespace=constraints-mem-example

出力は、コンテナが600MiBのメモリ要求と800MiBのメモリー制限になっていることを示しています。これらはLimitRangeによって課される制約を満たしています。

resources:
  limits:
     memory: 800Mi
  requests:
    memory: 600Mi

Podを消します。

kubectl delete pod constraints-mem-demo --namespace=constraints-mem-example

最大メモリ制約を超えるPodの作成の試み

これは、1つのコンテナを持つPodの設定ファイルです。コンテナは800MiBのメモリー要求と1.5GiBのメモリー制限を指定しています。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-2
spec:
  containers:
  - name: constraints-mem-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "1.5Gi"
      requests:
        memory: "800Mi"

Podを作成してみます。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-2.yaml --namespace=constraints-mem-example

出力は、コンテナが大きすぎるメモリー制限を指定しているため、Podが作成されないことを示しています。

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-2.yaml":
pods "constraints-mem-demo-2" is forbidden: maximum memory usage per Container is 1Gi, but limit is 1536Mi.

最低限のメモリ要求を満たさないPodの作成の試み

これは、1つのコンテナを持つPodの設定ファイルです。コンテナは100MiBのメモリー要求と800MiBのメモリー制限を指定しています。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-3
spec:
  containers:
  - name: constraints-mem-demo-3-ctr
    image: nginx
    resources:
      limits:
        memory: "800Mi"
      requests:
        memory: "100Mi"

Podを作成してみます。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-3.yaml --namespace=constraints-mem-example

出力は、コンテナが小さすぎるメモリー要求を指定しているため、Podが作成されないことを示しています。

Error from server (Forbidden): error when creating "examples/admin/resource/memory-constraints-pod-3.yaml":
pods "constraints-mem-demo-3" is forbidden: minimum memory usage per Container is 500Mi, but request is 100Mi.

メモリ要求や制限を指定しないPodの作成

これは、1つのコンテナを持つPodの設定ファイルです。コンテナはメモリー要求を指定しておらず、メモリー制限も指定していません。

apiVersion: v1
kind: Pod
metadata:
  name: constraints-mem-demo-4
spec:
  containers:
  - name: constraints-mem-demo-4-ctr
    image: nginx

Podを作成します。

kubectl apply -f https://k8s.io/examples/admin/resource/memory-constraints-pod-4.yaml --namespace=constraints-mem-example

Podの詳細情報を見ます

kubectl get pod constraints-mem-demo-4 --namespace=constraints-mem-example --output=yaml

出力を見ると、Podのコンテナのメモリ要求は1GiB、メモリー制限は1GiBであることがわかります。 コンテナはどのようにしてこれらの値を取得したのでしょうか?

resources:
  limits:
    memory: 1Gi
  requests:
    memory: 1Gi

コンテナが独自のメモリー要求と制限を指定していなかったため、LimitRangeから与えられのです。 コンテナが独自のメモリー要求と制限を指定していなかったため、LimitRangeからデフォルトのメモリー要求と制限が与えられたのです。

この時点で、コンテナは起動しているかもしれませんし、起動していないかもしれません。このタスクの前提条件は、ノードが少なくとも1GiBのメモリーを持っていることであることを思い出してください。それぞれのノードが1GiBのメモリーしか持っていない場合、どのノードにも1GiBのメモリー要求に対応するのに十分な割り当て可能なメモリーがありません。たまたま2GiBのメモリーを持つノードを使用しているのであれば、おそらく1GiBのメモリーリクエストに対応するのに十分なスペースを持っていることになります。

Podを削除します。

kubectl delete pod constraints-mem-demo-4 --namespace=constraints-mem-example

最小および最大メモリー制約の強制

LimitRangeによってNamespaceに課される最大および最小のメモリー制約は、Podが作成または更新されたときにのみ適用されます。LimitRangeを変更しても、以前に作成されたPodには影響しません。

最小・最大メモリー制約の動機

クラスター管理者としては、Podが使用できるメモリー量に制限を課したいと思うかもしれません。

例:

  • クラスター内の各ノードは2GBのメモリーを持っています。クラスタ内のどのノードもその要求をサポートできないため、2GB以上のメモリーを要求するPodは受け入れたくありません。

  • クラスターは運用部門と開発部門で共有されています。 本番用のワークロードでは最大8GBのメモリーを消費しますが、開発用のワークロードでは512MBに制限したいとします。本番用と開発用に別々のNamespaceを作成し、それぞれのNamespaceにメモリー制限を適用します。

クリーンアップ

Namespaceを削除します。

kubectl delete namespace constraints-mem-example

次の項目

クラスター管理者向け

アプリケーション開発者向け

2 - Windowsノードの追加

FEATURE STATE: Kubernetes v1.18 [beta]

Kubernetesを使用してLinuxノードとWindowsノードを混在させて実行できるため、Linuxで実行するPodとWindowsで実行するPodを混在させることができます。このページでは、Windowsノードをクラスターに登録する方法を示します。

始める前に

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: 1.17. バージョンを確認するには次のコマンドを実行してください: kubectl version.

目標

  • Windowsノードをクラスターに登録する
  • LinuxとWindowsのPodとServiceが相互に通信できるようにネットワークを構成する

はじめに: クラスターへのWindowsノードの追加

ネットワーク構成

LinuxベースのKubernetesコントロールプレーンノードを取得したら、ネットワーキングソリューションを選択できます。このガイドでは、簡単にするためにVXLANモードでのFlannelの使用について説明します。

Flannel構成

  1. FlannelのためにKubernetesコントロールプレーンを準備する

    クラスター内のKubernetesコントロールプレーンでは、多少の準備が推奨されます。Flannelを使用する場合は、iptablesチェーンへのブリッジIPv4トラフィックを有効にすることをお勧めします。すべてのLinuxノードで次のコマンドを実行する必要があります:

    sudo sysctl net.bridge.bridge-nf-call-iptables=1
    
  2. Linux用のFlannelをダウンロードして構成する

    最新のFlannelマニフェストをダウンロード:

    wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
    

    VNIを4096、ポートを4789に設定するために、flannelマニフェストのnet-conf.jsonセクションを変更します。次のようになります:

    net-conf.json: |
        {
          "Network": "10.244.0.0/16",
          "Backend": {
            "Type": "vxlan",
            "VNI" : 4096,
            "Port": 4789
          }
        }
    
    備考: Linux上のFlannelがWindows上のFlannelと相互運用するには、VNIを4096およびポート4789に設定する必要があります。これらのフィールドの説明については、VXLANドキュメントを参照してください。
    備考: L2Bridge/Host-gatewayモードを使用するには、代わりにTypeの値を"host-gw"に変更し、VNIPortを省略します。
  3. Flannelマニフェストを適用して検証する

    Flannelの構成を適用しましょう:

    kubectl apply -f kube-flannel.yml
    

    数分後、Flannel Podネットワークがデプロイされていれば、すべてのPodが実行されていることがわかります。

    kubectl get pods -n kube-system
    

    出力結果には、実行中のLinux flannel DaemonSetが含まれているはずです:

    NAMESPACE     NAME                                      READY        STATUS    RESTARTS   AGE
    ...
    kube-system   kube-flannel-ds-54954                     1/1          Running   0          1m
    
  4. Windows Flannelとkube-proxy DaemonSetを追加する

    これで、Windows互換バージョンのFlannelおよびkube-proxyを追加できます。 互換性のあるバージョンのkube-proxyを確実に入手するには、イメージのタグを置換する必要があります。 次の例は、Kubernetesv1.21.14の使用方法を示していますが、 独自のデプロイに合わせてバージョンを調整する必要があります。

    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/kube-proxy.yml | sed 's/VERSION/v1.21.14/g' | kubectl apply -f -
    kubectl apply -f https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/flannel-overlay.yml
    
    備考: ホストゲートウェイを使用している場合は、代わりに https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/flannel-host-gw.yml を使用してください。
    備考:

    Windowsノードでイーサネット(「Ethernet0 2」など)ではなく別のインターフェースを使用している場合は、次の行を変更する必要があります:

    wins cli process run --path /k/flannel/setup.exe --args "--mode=overlay --interface=Ethernet"
    

    flannel-host-gw.ymlまたはflannel-overlay.ymlファイルで、それに応じてインターフェースを指定します。

    # 例
    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/flannel-overlay.yml | sed 's/Ethernet/Ethernet0 2/g' | kubectl apply -f -
    

Windowsワーカーノードの参加

備考: Containers機能をインストールし、Dockerをインストールする必要があります。 行うための指示としては、Dockerエンジンのインストール - Windowsサーバー上のエンタープライズを利用できます。
備考: Windowsセクションのすべてのコードスニペットは、 Windowsワーカーノードの(管理者)権限を持つPowerShell環境で実行されます。
  1. wins、kubelet、kubeadmをインストールします。

    curl.exe -LO https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/PrepareNode.ps1
    .\PrepareNode.ps1 -KubernetesVersion v1.21.14
    
  2. kubeadmを実行してノードに参加します

    コントロールプレーンホストでkubeadm initを実行したときに提供されたコマンドを使用します。 このコマンドがなくなった場合、またはトークンの有効期限が切れている場合は、kubeadm token create --print-join-command (コントロールプレーンホスト上で)を実行して新しいトークンを生成します。

インストールの確認

次のコマンドを実行して、クラスター内のWindowsノードを表示できるようになります:

kubectl get nodes -o wide

新しいノードがNotReady状態の場合は、flannelイメージがまだダウンロード中の可能性があります。 kube-system名前空間のflannel Podを確認することで、以前と同様に進行状況を確認できます:

kubectl -n kube-system get pods -l app=flannel

flannel Podが実行されると、ノードはReady状態になり、ワークロードを処理できるようになります。

次の項目

3 - Windowsノードのアップグレード

FEATURE STATE: Kubernetes v1.18 [beta]

このページでは、kubeadmで作られたWindowsノードをアップグレードする方法について説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: 1.17. バージョンを確認するには次のコマンドを実行してください: kubectl version.

ワーカーノードをアップグレード

kubeadmをアップグレード

  1. Windowsノードから、kubeadmをアップグレードします。:

    # v1.21.14を目的のバージョンに置き換えます
    curl.exe -Lo C:\k\kubeadm.exe https://dl.k8s.io/v1.21.14/bin/windows/amd64/kubeadm.exe
    

ノードをドレインする

  1. Kubernetes APIにアクセスできるマシンから、 ノードをスケジュール不可としてマークして、ワークロードを削除することでノードのメンテナンスを準備します:

    # <node-to-drain>をドレインするノードの名前に置き換えます
    kubectl drain <node-to-drain> --ignore-daemonsets
    

    このような出力結果が表示されるはずです:

    node/ip-172-31-85-18 cordoned
    node/ip-172-31-85-18 drained
    

kubeletの構成をアップグレード

  1. Windowsノードから、次のコマンドを呼び出して新しいkubelet構成を同期します:

    kubeadm upgrade node
    

kubeletをアップグレード

  1. Windowsノードから、kubeletをアップグレードして再起動します:

    stop-service kubelet
    curl.exe -Lo C:\k\kubelet.exe https://dl.k8s.io/v1.21.14/bin/windows/amd64/kubelet.exe
    restart-service kubelet
    

ノードをオンライン状態に

  1. Kubernetes APIにアクセスできるマシンから、 スケジュール可能としてマークして、ノードをオンラインに戻します:

    # <node-to-drain>をノードの名前に置き換えます
    kubectl uncordon <node-to-drain>
    

kube-proxyをアップグレード

  1. Kubernetes APIにアクセスできるマシンから、次を実行します、 もう一度v1.21.14を目的のバージョンに置き換えます:

    curl -L https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/kube-proxy.yml | sed 's/VERSION/v1.21.14/g' | kubectl apply -f -
    

4 - EndpointSliceの有効化

このページはKubernetesのEndpointSliceの有効化の概要を説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

概要

EndpointSliceは、KubernetesのEndpointsに対してスケーラブルで拡張可能な代替手段を提供します。Endpointsが提供する機能のベースの上に構築し、スケーラブルな方法で拡張します。Serviceが多数(100以上)のネットワークエンドポイントを持つ場合、それらは単一の大きなEndpointsリソースではなく、複数の小さなEndpointSliceに分割されます。

EndpointSliceの有効化

FEATURE STATE: Kubernetes v1.17 [beta]
備考: EndpointSliceは、最終的には既存のEndpointsを置き換える可能性がありますが、多くのKubernetesコンポーネントはまだ既存のEndpointsに依存しています。現時点ではEndpointSliceを有効化することは、Endpointsの置き換えではなく、クラスター内のEndpointsへの追加とみなされる必要があります。

EndpoitSliceはベータ版の機能です。APIとEndpointSliceコントローラーはデフォルトで有効です。kube-proxyはデフォルトでEndpointSliceではなくEndpointsを使用します。

スケーラビリティと性能向上のため、kube-proxy上でEndpointSliceProxyingフィーチャーゲートを有効にできます。この変更はデータソースをEndpointSliceに移します、これはkube-proxyとKubernetes API間のトラフィックの量を削減します。

EndpointSliceの使用

クラスター内でEndpointSliceを完全に有効にすると、各Endpointsリソースに対応するEndpointSliceリソースが表示されます。既存のEndpointsの機能をサポートすることに加えて、EndpointSliceはトポロジーなどの新しい情報を含みます。これらにより、クラスター内のネットワークエンドポイントのスケーラビリティと拡張性が大きく向上します。

次の項目

5 - KubernetesクラスターでNodeLocal DNSキャッシュを使用する

FEATURE STATE: Kubernetes v1.18 [stable]
このページでは、KubernetesのNodeLocal DNSキャッシュの機能の概要について説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

イントロダクション

NodeLocal DNSキャッシュは、クラスターノード上でDNSキャッシュエージェントをDaemonSetで稼働させることで、クラスターのDNSパフォーマンスを向上させます。現在のアーキテクチャーにおいて、ClusterFirstのDNSモードでのPodは、DNSクエリー用にkube-dnsのService IPに疎通します。これにより、kube-proxyによって追加されたiptablesを介してkube-dns/CoreDNSのエンドポイントへ変換されます。この新しいアーキテクチャーによって、Podは同じノード上で稼働するDNSキャッシュエージェントに対して疎通し、それによってiptablesのDNATルールとコネクショントラッキングを回避します。ローカルのキャッシュエージェントはクラスターのホスト名(デフォルトではcluster.localというサフィックス)に対するキャッシュミスがあるときはkube-dnsサービスへ問い合わせます。

動機

  • 現在のDNSアーキテクチャーでは、ローカルのkube-dns/CoreDNSがないとき、DNSへの秒間クエリー数が最も高いPodは他のノードへ疎通する可能性があります。ローカルでキャッシュを持つことにより、この状況におけるレイテンシーの改善に役立ちます。

  • iptables DNATとコネクショントラッキングをスキップすることはconntrackの競合を減らし、UDPでのDNSエントリーがconntrackテーブルを満杯にすることを避けるのに役立ちます。

  • ローカルのキャッシュエージェントからkube-dnsサービスへの接続がTCPにアップグレードされます。タイムアウトをしなくてはならないUDPエントリーと比べ、TCPのconntrackエントリーはコネクションクローズ時に削除されます(デフォルトの nf_conntrack_udp_timeout は30秒です)。

  • DNSクエリーをUDPからTCPにアップグレードすることで、UDPパケットの欠損や、通常30秒(10秒のタイムアウトで3回再試行する)であるDNSのタイムアウトによるテイルレイテンシーを減少させます。NodeLocalキャッシュはUDPのDNSクエリーを待ち受けるため、アプリケーションを変更する必要はありません。

  • DNSクエリーに対するノードレベルのメトリクスと可視性を得られます。

  • DNSの不在応答のキャッシュも再度有効にされ、それによりkube-dnsサービスに対するクエリー数を減らします。

アーキテクチャー図

この図はNodeLocal DNSキャッシュが有効にされた後にDNSクエリーがあったときの流れとなります。

NodeLocal DNSCache flow

Nodelocal DNSCacheのフロー

この図は、NodeLocal DNSキャッシュがDNSクエリーをどう扱うかを表したものです。

設定

備考: NodeLocal DNSキャッシュ用のローカルに待ち受けているIPアドレスは、169.254.20.0/16の範囲のIPか、既存のIPと衝突しないことが保証されている他のIPとなります。このドキュメントでは例として169.254.10を使用します。

この機能は、下記の手順により有効化できます。

  • nodelocaldns.yamlと同様のマニフェストを用意し、nodelocaldns.yamlという名前で保存してください。

  • マニフェスト内の変数を正しい値に置き換えてください。

    • kubedns=kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}

    • domain=<cluster-domain>

    • localdns=<node-local-address>

    <cluster-domain>はデフォルトで"cluster.local"です。<node-local-address> はNodeLocal DNSキャッシュ用に確保されたローカルの待ち受けIPアドレスです。

    • kube-proxyがIPTABLESモードで稼働中のとき:

      sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml
      

      __PILLAR__CLUSTER__DNS____PILLAR__UPSTREAM__SERVERS__はnode-local-dnsというPodによって生成されます。 このモードでは、node-local-dns Podは<node-local-address>とkube-dnsのサービスIPの両方で待ち受けるため、PodはIPアドレスでもDNSレコードのルップアップができます。

    • kube-proxyがIPVSモードで稼働中のとき:

       sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
      

      このモードでは、node-local-dns Podは<node-local-address>上のみで待ち受けます。node-local-dnsのインターフェースはkube-dnsのクラスターIPをバインドしません。なぜならばIPVSロードバランシング用に使われているインターフェースは既にこのアドレスを使用しているためです。 __PILLAR__UPSTREAM__SERVERS__ はnode-local-dns Podにより生成されます。

  • kubectl create -f nodelocaldns.yamlを実行してください。

  • kube-proxyをIPVSモードで使用しているとき、NodeLocal DNSキャッシュが待ち受けている<node-local-address>を使用するため、kubeletに対する--cluster-dnsフラグを修正する必要があります。IPVSモード以外のとき、--cluster-dnsフラグの値を修正する必要はありません。なぜならNodeLocal DNSキャッシュはkube-dnsのサービスIPと<node-local-address>の両方で待ち受けているためです。

一度有効にすると、クラスターの各Node上で、kube-systemという名前空間でnode-local-dns Podが、稼働します。このPodはCoreDNSをキャッシュモードで稼働させるため、異なるプラグインによって公開された全てのCoreDNSのメトリクスがNode単位で利用可能となります。

kubectl delete -f <manifest>を実行してDaemonSetを削除することによって、この機能を無効にできます。また、kubeletの設定に対して行った全ての変更をリバートすべきです。

6 - Serviceトポロジーを有効にする

このページでは、Kubernetes上でServiceトポロジーを有効にする方法の概要について説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

はじめに

Serviceトポロジーは、クラスターのノードのトポロジーに基づいてトラフィックをルーティングできるようにする機能です。たとえば、あるServiceのトラフィックに対して、できるだけ同じノードや同じアベイラビリティゾーン上にあるエンドポイントを優先してルーティングするように指定できます。

前提

トポロジーを考慮したServiceのルーティングを有効にするには、以下の前提を満たしている必要があります。

  • Kubernetesバージョン1.17以降である
  • Kube-proxyがiptableモードまたはIPVSモードで稼働している
  • Endpoint Sliceを有効にしている

Serviceトポロジーを有効にする

FEATURE STATE: Kubernetes v1.17 [alpha]

Serviceトポロジーを有効にするには、すべてのKubernetesコンポーネントでServiceTopologyEndpointSliceフィーチャーゲートを有効にする必要があります。

--feature-gates="ServiceTopology=true,EndpointSlice=true"

次の項目

7 - クラウドコントローラーマネージャーの運用管理

FEATURE STATE: Kubernetes v1.11 [beta]

クラウドプロバイダーはKubernetesプロジェクトとは異なるペースで開発およびリリースされるため、プロバイダー固有のコードを`cloud-controller-manager`バイナリに抽象化することでクラウドベンダーはKubernetesのコアのコードとは独立して開発が可能となりました。

cloud-controller-managerは、cloudprovider.Interfaceを満たす任意のクラウドプロバイダーと接続できます。下位互換性のためにKubernetesのコアプロジェクトで提供されるcloud-controller-managerkube-controller-managerと同じクラウドライブラリを使用します。Kubernetesのコアリポジトリですでにサポートされているクラウドプロバイダーは、Kubernetesリポジトリにあるcloud-controller-managerを使用してKubernetesのコアから移行することが期待されています。

運用

要件

すべてのクラウドには動作させるためにそれぞれのクラウドプロバイダーの統合を行う独自の要件があり、kube-controller-managerを実行する場合の要件とそれほど違わないようにする必要があります。一般的な経験則として、以下のものが必要です。

  • クラウドの認証/認可: クラウドではAPIへのアクセスを許可するためにトークンまたはIAMルールが必要になる場合があります
  • kubernetesの認証/認可: cloud-controller-managerは、kubernetes apiserverと通信するためにRBACルールの設定を必要とする場合があります
  • 高可用性: kube-controller-managerのように、リーダー選出を使用したクラウドコントローラーマネージャーの高可用性のセットアップが必要になる場合があります(デフォルトでオンになっています)。

cloud-controller-managerを動かす

cloud-controller-managerを正常に実行するにはクラスター構成にいくつかの変更が必要です。

  • kube-apiserverkube-controller-managerは**--cloud-providerフラグを指定してはいけません**。これによりクラウドコントローラーマネージャーによって実行されるクラウド固有のループが実行されなくなります。将来このフラグは非推奨になり削除される予定です。
  • kubelet--cloud-provider=externalで実行する必要があります。これは作業をスケジュールする前にクラウドコントローラーマネージャーによって初期化する必要があることをkubeletが認識できるようにするためです。

クラウドコントローラーマネージャーを使用するようにクラスターを設定するとクラスターの動作がいくつか変わることに注意してください。

  • --cloud-provider=externalを指定したkubeletは、初期化時にNoSchedulenode.cloudprovider.kubernetes.io/uninitialized汚染を追加します。これによりノードは作業をスケジュールする前に外部のコントローラーからの2回目の初期化が必要であるとマークされます。クラウドコントローラーマネージャーが使用できない場合クラスター内の新しいノードはスケジュールできないままになることに注意してください。スケジューラーはリージョンやタイプ(高CPU、GPU、高メモリ、スポットインスタンスなど)などのノードに関するクラウド固有の情報を必要とする場合があるためこの汚染は重要です。
  • クラスター内のノードに関するクラウド情報はローカルメタデータを使用して取得されなくなりましたが、代わりにノード情報を取得するためのすべてのAPI呼び出しはクラウドコントローラーマネージャーを経由して行われるようになります。これはセキュリティを向上させるためにkubeletでクラウドAPIへのアクセスを制限できることを意味します。大規模なクラスターではクラスター内からクラウドのほとんどすべてのAPI呼び出しを行うため、クラウドコントローラーマネージャーがレートリミットに達するかどうかを検討する必要があります。

クラウドコントローラーマネージャーは以下を実装できます。

  • ノードコントローラー - クラウドAPIを使用してkubernetesノードを更新し、クラウドで削除されたkubernetesノードを削除します。
  • サービスコントローラー - タイプLoadBalancerのサービスに対応してクラウド上のロードバランサーを操作します。
  • ルートコントローラー - クラウド上でネットワークルートを設定します。
  • Kubernetesリポジトリの外部にあるプロバイダーを実行している場合はその他の機能の実装。

現在Kubernetesのコアでサポートされているクラウドを使用していて、クラウドコントローラーマネージャーを利用する場合は、kubernetesのコアのクラウドコントローラーマネージャーを参照してください。

Kubernetesのコアリポジトリにないクラウドコントローラーマネージャーの場合、クラウドベンダーまたはsigリードが管理するリポジトリでプロジェクトを見つけることができます。

すでにKubernetesのコアリポジトリにあるプロバイダーの場合、クラスター内でデーモンセットとしてKubernetesリポジトリ内部のクラウドコントローラーマネージャーを実行できます。以下をガイドラインとして使用してください。

# This is an example of how to setup cloud-controller-manager as a Daemonset in your cluster.
# It assumes that your masters can run pods and has the role node-role.kubernetes.io/master
# Note that this Daemonset will not work straight out of the box for your cloud, this is
# meant to be a guideline.

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cloud-controller-manager
  namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: system:cloud-controller-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: cloud-controller-manager
  namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    k8s-app: cloud-controller-manager
  name: cloud-controller-manager
  namespace: kube-system
spec:
  selector:
    matchLabels:
      k8s-app: cloud-controller-manager
  template:
    metadata:
      labels:
        k8s-app: cloud-controller-manager
    spec:
      serviceAccountName: cloud-controller-manager
      containers:
      - name: cloud-controller-manager
        # for in-tree providers we use k8s.gcr.io/cloud-controller-manager
        # this can be replaced with any other image for out-of-tree providers
        image: k8s.gcr.io/cloud-controller-manager:v1.8.0
        command:
        - /usr/local/bin/cloud-controller-manager
        - --cloud-provider=<YOUR_CLOUD_PROVIDER>   # Add your own cloud provider here!
        - --leader-elect=true
        - --use-service-account-credentials
        # these flags will vary for every cloud provider
        - --allocate-node-cidrs=true
        - --configure-cloud-routes=true
        - --cluster-cidr=172.17.0.0/16
      tolerations:
      # this is required so CCM can bootstrap itself
      - key: node.cloudprovider.kubernetes.io/uninitialized
        value: "true"
        effect: NoSchedule
      # this is to have the daemonset runnable on master nodes
      # the taint may vary depending on your cluster setup
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      # this is to restrict CCM to only run on master nodes
      # the node selector may vary depending on your cluster setup
      nodeSelector:
        node-role.kubernetes.io/master: ""

制限

クラウドコントローラーマネージャーの実行にはいくつかの制限があります。これらの制限は今後のリリースで対処されますが、本番のワークロードにおいてはこれらの制限を認識することが重要です。

ボリュームのサポート

ボリュームの統合にはkubeletとの調整も必要になるためクラウドコントローラーマネージャーはkube-controller-managerにあるボリュームコントローラーを実装しません。CSI(コンテナストレージインターフェイス)が進化してFlexボリュームプラグインの強力なサポートが追加されるにつれ、クラウドがボリュームと完全に統合できるようクラウドコントローラーマネージャーに必要なサポートが追加されます。Kubernetesリポジトリの外部にあるCSIボリュームプラグインの詳細についてはこちらをご覧ください。

スケーラビリティ

cloud-controller-managerは、クラウドプロバイダーのAPIにクエリーを送信して、すべてのノードの情報を取得します。非常に大きなクラスターの場合、リソース要件やAPIレートリミットなどのボトルネックの可能性を考慮する必要があります。

鶏と卵

クラウドコントローラーマネージャープロジェクトの目標はKubernetesのコアプロジェクトからクラウドに関する機能の開発を切り離すことです。残念ながら、Kubernetesプロジェクトの多くの面でクラウドプロバイダーの機能がKubernetesプロジェクトに緊密に結びついているという前提があります。そのため、この新しいアーキテクチャを採用するとクラウドプロバイダーの情報を要求する状況が発生する可能性がありますが、クラウドコントローラーマネージャーはクラウドプロバイダーへのリクエストが完了するまでその情報を返すことができない場合があります。

これの良い例は、KubeletのTLSブートストラップ機能です。TLSブートストラップはKubeletがすべてのアドレスタイプ(プライベート、パブリックなど)をクラウドプロバイダー(またはローカルメタデータサービス)に要求する能力を持っていると仮定していますが、クラウドコントローラーマネージャーは最初に初期化されない限りノードのアドレスタイプを設定できないためapiserverと通信するためにはkubeletにTLS証明書が必要です。

このイニシアチブが成熟するに連れ、今後のリリースでこれらの問題に対処するための変更が行われます。

次の項目

独自のクラウドコントローラーマネージャーを構築および開発するにはクラウドコントローラーマネージャーの開発を参照してください。

8 - クラウドコントローラーマネージャーの開発

cloud-controller-managerは クラウド特有の制御ロジックを組み込むKubernetesのcontrol planeコンポーネントです。クラウドコントロールマネージャーは、クラスターをクラウドプロバイダーAPIをリンクし、クラスタのみで相互作用するコンポーネントからクラウドプラットフォームで相互作用するコンポーネントを分離します。

Kubernetesと下のクラウドインフラストラクチャー間の相互運用ロジックを分離することで、cloud-controller-managerコンポーネントはクラウドプロバイダを主なKubernetesプロジェクトと比較し異なるペースで機能をリリース可能にします。

背景

クラウドプロバイダーはKubernetesプロジェクトとは異なる速度で開発しリリースすることから、プロバイダー特有なコードをcloud-controller-managerバイナリから抽象化することで、クラウドベンダーはコアKubernetesコードから独立して発展することができます。

Kubernetesプロジェクトは、(クラウドプロバイダーの)独自実装を組み込めるGoインターフェースを備えたcloud-controller-managerのスケルトンコードを提供しています。これは、クラウドプロバイダーがKubernetesコアからパッケージをインポートすることでcloud-controller-managerを実装できることを意味します。各クラウドプロバイダーは利用可能なクラウドプロバイダーのグローバル変数を更新するためにcloudprovider.RegisterCloudProviderを呼び出し、独自のコードを登録します。

開発

Kubernetesには登録されていない独自クラウドプロバイダー

Kubernetesには登録されていない独自のクラウドプロバイダーのクラウドコントローラーマネージャーを構築するには、

  1. cloudprovider.Interfaceを満たす go パッケージを実装します。
  2. Kubernetesのコアにあるcloud-controller-managerのmain.goをあなたのmain.goのテンプレートとして利用します。上で述べたように、唯一の違いはインポートされるクラウドパッケージのみです。
  3. クラウドパッケージを main.go にインポートし、パッケージに cloudprovider.RegisterCloudProvider を実行するための init ブロックがあることを確認します。

多くのクラウドプロバイダーはオープンソースとしてコントローラーマネージャーのコードを公開しています。新たにcloud-controller-managerをスクラッチから開発する際には、既存のKubernetesには登録されていない独自クラウドプロバイダーのコントローラーマネージャーを開始地点とすることができます。

Kubernetesに登録されているクラウドプロバイダー

Kubernetesに登録されているクラウドプロバイダーであれば、DaemonSetを使ってあなたのクラスターで動かすことができます。詳細についてはKubernetesクラウドコントローラーマネージャーを参照してください。

9 - サービスディスカバリーにCoreDNSを使用する

このページでは、CoreDNSのアップグレードプロセスと、kube-dnsの代わりにCoreDNSをインストールする方法を説明します。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.9. バージョンを確認するには次のコマンドを実行してください: kubectl version.

CoreDNSについて

CoreDNSは、KubernetesクラスターDNSとして稼働させることができる柔軟で拡張可能なDNSサーバーです。Kubernetesと同様に、CoreDNSプロジェクトはCNCFによってホストされています。

既存のデプロイでkube-dnsを置き換えるか、クラスターのデプロイとアップグレードを代行してくれるkubeadmのようなツールを使用することで、クラスターでkube-dnsの代わりにCoreDNSを使用することができます。

CoreDNSのインストール

kube-dnsの手動デプロイや置き換えについては、CoreDNS GitHub projectのドキュメントを参照してください。

CoreDNSへの移行

kubeadmを使用した既存のクラスターのアップグレード

Kubernetesバージョン1.10以降では、kube-dnsを使用しているクラスターをkubeadmを使用してアップグレードするときに、CoreDNSに移行することもできます。この場合、kubeadmは、kube-dns ConfigMapをベースにしてCoreDNS設定("Corefile")を生成し、フェデレーション、スタブドメイン、および上流のネームサーバーの設定を保持します。

kube-dnsからCoreDNSに移行する場合は、アップグレード時に必ずCoreDNSフィーチャーゲートをtrueに設定してください。たとえば、v1.11.0のアップグレードは次のようになります:

kubeadm upgrade apply v1.11.0 --feature-gates=CoreDNS=true

Kubernetesバージョン1.13以降では、CoreDNSフィーチャーゲートが削除され、CoreDNSがデフォルトで使用されます。アップグレードしたクラスターでkube-dnsを使用する場合は、こちらのガイドに従ってください。

1.11以前のバージョンでは、Corefileはアップグレード中に作成されたものによって上書きされます。カスタマイズしている場合は、既存のConfigMapを保存する必要があります。 新しいConfigMapが稼働したら、カスタマイズを再適用できます。

Kubernetesバージョン1.11以降でCoreDNSを実行している場合、アップグレード中、既存のCorefileは保持されます。

kubeadmを使用してCoreDNSの代わりにkube-dnsをインストールする

備考: Kubernetes 1.11では、CoreDNSは一般利用可能(GA)にアップグレードされ、デフォルトでインストールされます。
警告: Kubernetes 1.18では、kubeadmでのkube-dns使用は非推奨となり、将来のバージョンでは削除されます。

1.13以前のバージョンにkube-dnsをインストールするには、CoreDNSフィーチャーゲートの値をfalseに設定します:

kubeadm init --feature-gates=CoreDNS=false

バージョン1.13以降の場合は、こちらに記載されているガイドに従ってください。

CoreDNSのアップグレード

CoreDNSはv1.9以降のKubernetesで使用できます。Kubernetesに同梱されているCoreDNSのバージョンと、CoreDNSに加えられた変更はこちらで確認できます。

CoreDNSだけをアップグレードしたい場合や、独自のカスタムイメージを使用したい場合は、CoreDNSを手動でアップグレードすることができます。スムーズなアップグレードのために役立つガイドラインとウォークスルーが用意されています。

CoreDNSのチューニング

リソース使用率が問題になる場合は、CoreDNSの設定を調整すると役立つ場合があります。詳細は、CoreDNSのスケーリングに関するドキュメントを参照してください。

次の項目

CoreDNSは、Corefileを変更することで、kube-dnsよりも多くのユースケースをサポートするように設定することができます。詳細はCoreDNSサイトを参照してください。

10 - ネットワークポリシーを宣言する

このドキュメントでは、Pod同士の通信を制御するネットワークポリシーを定義するための、KubernetesのNetworkPolicy APIを使い始める手助けをします。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.8. バージョンを確認するには次のコマンドを実行してください: kubectl version.

ネットワークポリシーをサポートしているネットワークプロバイダーが設定済みであることを確認してください。さまざまなネットワークプロバイダーがNetworkPolicyをサポートしています。次に挙げるのは一例です。

備考: 上記のリストは製品名のアルファベット順にソートされていて、推奨順や好ましい順にソートされているわけではありません。このページの例は、Kubernetesクラスターでこれらのどのプロバイダーを使用していても有効です。

nginx Deploymentを作成してService経由で公開する

Kubernetesのネットワークポリシーの仕組みを理解するために、まずはnginx Deploymentを作成することから始めましょう。

kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

nginxという名前のService経由でDeploymentを公開します。

kubectl expose deployment nginx --port=80
service/nginx exposed

上記のコマンドを実行すると、nginx Podを持つDeploymentが作成され、そのDeploymentがnginxという名前のService経由で公開されます。nginxのPodおよびDeploymentはdefault名前空間の中にあります。

kubectl get svc,pod
NAME                        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/kubernetes          10.100.0.1    <none>        443/TCP    46m
service/nginx               10.100.0.16   <none>        80/TCP     33s

NAME                        READY         STATUS        RESTARTS   AGE
pod/nginx-701339712-e0qfq   1/1           Running       0          35s

もう1つのPodからアクセスしてServiceを検証する

これで、新しいnginxサービスに他のPodからアクセスできるようになったはずです。default名前空間内の他のPodからnginx Serviceにアクセスするために、busyboxコンテナを起動します。

kubectl run busybox --rm -ti --image=busybox -- /bin/sh

シェルの中で、次のコマンドを実行します。

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

nginx Serviceへのアクセスを制限する

nginx Serviceへのアクセスを制限するために、access: trueというラベルが付いたPodだけがクエリできるようにします。次の内容でNetworkPolicyオブジェクトを作成してください。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      app: nginx
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"

NetworkPolicyオブジェクトの名前は、有効なDNSサブドメイン名でなければなりません。

備考: このNetworkPolicyには、ポリシーを適用するPodのグループを選択するためのpodSelectorが含まれています。このポリシーは、ラベルapp=nginxの付いたPodを選択していることがわかります。このラベルは、nginx Deployment内のPodに自動的に追加されたものです。空のpodSelectorは、その名前空間内のすべてのPodを選択します。

Serviceにポリシーを割り当てる

kubectlを使って、上記のnginx-policy.yamlファイルからNetworkPolicyを作成します。

kubectl apply -f https://k8s.io/examples/service/networking/nginx-policy.yaml
networkpolicy.networking.k8s.io/access-nginx created

accessラベルが定義されていない状態でServiceへのアクセスをテストする

nginx Serviceに正しいラベルが付いていないPodからアクセスを試してみると、リクエストがタイムアウトします。

kubectl run busybox --rm -ti --image=busybox -- /bin/sh

シェルの中で、次のコマンドを実行します。

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
wget: download timed out

accessラベルを定義して再テストする

正しいラベルが付いたPodを作成すると、リクエストが許可されるようになるのがわかります。

kubectl run busybox --rm -ti --labels="access=true" --image=busybox -- /bin/sh

シェルの中で、次のコマンドを実行します。

wget --spider --timeout=1 nginx
Connecting to nginx (10.100.0.16:80)
remote file exists

11 - ノードのトポロジー管理ポリシーを制御する

FEATURE STATE: Kubernetes v1.18 [beta]

近年、CPUやハードウェア・アクセラレーターの組み合わせによって、レイテンシーが致命的となる実行や高いスループットを求められる並列計算をサポートするシステムが増えています。このようなシステムには、通信、科学技術計算、機械学習、金融サービス、データ分析などの分野のワークロードが含まれます。このようなハイブリッドシステムは、高い性能の環境で構成されます。

最高のパフォーマンスを引き出すために、CPUの分離やメモリーおよびデバイスの位置に関する最適化が求められます。しかしながら、Kubernetesでは、これらの最適化は分断されたコンポーネントによって処理されます。

トポロジーマネージャー はKubeletコンポーネントの1つで最適化の役割を担い、コンポーネント群を調和して機能させます。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

作業するKubernetesサーバーは次のバージョン以降のものである必要があります: v1.18. バージョンを確認するには次のコマンドを実行してください: kubectl version.

トポロジーマネージャーはどのように機能するか

トポロジーマネージャー導入前は、KubernetesにおいてCPUマネージャーやデバイスマネージャーはそれぞれ独立してリソースの割り当てを決定します。 これは、マルチソケットのシステムでは望ましくない割り当てとなり、パフォーマンスやレイテンシーが求められるアプリケーションは、この望ましくない割り当てに悩まされます。 この場合の望ましくない例として、CPUやデバイスが異なるNUMAノードに割り当てられ、それによりレイテンシー悪化を招くことが挙げられます。

トポロジーマネージャーはKubeletコンポーネントであり、信頼できる情報源として振舞います。それによって、他のKubeletコンポーネントはトポロジーに沿ったリソース割り当ての選択を行うことができます。

トポロジーマネージャーは Hint Providers と呼ばれるコンポーネントのインターフェースを提供し、トポロジー情報を送受信します。トポロジーマネージャーは、ノード単位のポリシー群を保持します。ポリシーについて以下で説明します。

トポロジーマネージャーは Hint Providers からトポロジー情報を受け取ります。トポロジー情報は、利用可能なNUMAノードと優先割り当て表示を示すビットマスクです。トポロジーマネージャーのポリシーは、提供されたヒントに対して一連の操作を行い、ポリシーに沿ってヒントをまとめて最適な結果を得ます。もし、望ましくないヒントが保存された場合、ヒントの優先フィールドがfalseに設定されます。現在のポリシーでは、最も狭い優先マスクが優先されます。

選択されたヒントはトポロジーマネージャーの一部として保存されます。設定されたポリシーにしたがい、選択されたヒントに基づいてノードがPodを許可したり、拒否することができます。 トポロジーマネージャーに保存されたヒントは、Hint Providers が使用しリソース割り当てを決定します。

トポロジーマネージャーの機能を有効にする

トポロジーマネージャーをサポートするには、TopologyManager フィーチャーゲートを有効にする必要があります。Kubernetes 1.18ではデフォルトで有効です。

トポロジーマネージャーのスコープとポリシー

トポロジーマネージャは現在:

  • 全てのQoAクラスのPodを調整する
  • Hint Providerによって提供されたトポロジーヒントから、要求されたリソースを調整する

これらの条件が合致した場合、トポロジーマネージャーは要求されたリソースを調整します。

この調整をどのように実行するかカスタマイズするために、トポロジーマネージャーは2つのノブを提供します: スコープポリシーです。

スコープはリソースの配置を行う粒度を定義します(例:podcontainer)。そして、ポリシーは調整を実行するための実戦略を定義します(best-effort, restricted, single-numa-node等)。

現在利用可能なスコープポリシーの値について詳細は以下の通りです。

備考: PodのSpecにある他の要求リソースとCPUリソースを調整するために、CPUマネージャーを有効にし、適切なCPUマネージャーのポリシーがノードに設定されるべきです。CPU管理ポリシーを参照してください。
備考: PodのSpecにある他の要求リソースとメモリー(およびhugepage)リソースを調整するために、メモリーマネージャーを有効にし、適切なメモリーマネージャーポリシーがノードに設定されるべきです。メモリーマネージャー のドキュメントを確認してください。

トポロジーマネージャーのスコープ

トポロジーマネージャーは、以下の複数の異なるスコープでリソースの調整を行う事が可能です:

  • container (デフォルト)
  • pod

いずれのオプションも、--topology-manager-scopeフラグによって、kubelet起動時に選択できます。

containerスコープ

containerスコープはデフォルトで使用されます。

このスコープでは、トポロジーマネージャーは連続した複数のリソース調整を実行します。つまり、Pod内の各コンテナは、分離された配置計算がされます。言い換えると、このスコープでは、コンテナを特定のNUMAノードのセットにグループ化するという概念はありません。実際には、トポロジーマネージャーは各コンテナのNUMAノードへの配置を任意に実行します。

コンテナをグループ化するという概念は、以下のスコープで設定・実行されます。例えば、podスコープが挙げられます。

podスコープ

podスコープを選択するには、コマンドラインで--topology-manager-scope=podオプションを指定してkubeletを起動します。

このスコープでは、Pod内全てのコンテナを共通のNUMAノードのセットにグループ化することができます。トポロジーマネージャーはPodをまとめて1つとして扱い、ポッド全体(全てのコンテナ)を単一のNUMAノードまたはNUMAノードの共通セットのいずれかに割り当てようとします。以下の例は、さまざまな場面でトポロジーマネージャーが実行する調整を示します:

  • 全てのコンテナは、単一のNUMAノードに割り当てられます。
  • 全てのコンテナは、共有されたNUMAノードのセットに割り当てられます。

Pod全体に要求される特定のリソースの総量は有効なリクエスト/リミットの式に従って計算されるため、この総量の値は以下の最大値となります。

  • 全てのアプリケーションコンテナのリクエストの合計。
  • リソースに対するinitコンテナのリクエストの最大値。

podスコープとsingle-numa-nodeトポロジーマネージャーポリシーを併用することは、レイテンシーが重要なワークロードやIPCを行う高スループットのアプリケーションに対して特に有効です。両方のオプションを組み合わせることで、Pod内の全てのコンテナを単一のNUMAノードに配置できます。そのため、PodのNUMA間通信によるオーバーヘッドを排除することができます。

single-numa-nodeポリシーの場合、可能な割り当ての中に適切なNUMAノードのセットが存在する場合にのみ、Podが許可されます。上の例をもう一度考えてみましょう:

  • 1つのNUMAノードのみを含むセット - Podが許可されます。
  • 2つ以上のNUMAノードを含むセット - Podが拒否されます(1つのNUMAノードの代わりに、割り当てを満たすために2つ以上のNUMAノードが必要となるため)。

要約すると、トポロジーマネージャーはまずNUMAノードのセットを計算し、それをトポロジーマネージャーのポリシーと照合し、Podの拒否または許可を検証します。

トポロジーマネージャーのポリシー

トポロジーマネージャーは4つの調整ポリシーをサポートします。--topology-manager-policyというKubeletフラグを通してポリシーを設定できます。 4つのサポートされるポリシーがあります:

  • none (デフォルト)
  • best-effort
  • restricted
  • single-numa-node
備考: トポロジーマネージャーが pod スコープで設定された場合、コンテナはポリシーによって、Pod全体の要求として反映します。 したがって、Podの各コンテナは 同じ トポロジー調整と同じ結果となります。

none ポリシー

これはデフォルトのポリシーで、トポロジーの調整を実行しません。

best-effort ポリシー

Pod内の各コンテナに対して、best-effort トポロジー管理ポリシーが設定されたkubeletは、各Hint Providerを呼び出してそれらのリソースの可用性を検出します。 トポロジーマネージャーはこの情報を使用し、そのコンテナの推奨されるNUMAノードのアフィニティーを保存します。アフィニティーが優先されない場合、トポロジーマネージャーはこれを保存し、Podをノードに許可します。

Hint Providers はこの情報を使ってリソースの割り当てを決定します。

restricted ポリシー

Pod内の各コンテナに対して、restricted トポロジー管理ポリシーが設定されたkubeletは各Hint Providerを呼び出してそれらのリソースの可用性を検出します。 トポロジーマネージャーはこの情報を使用し、そのコンテナの推奨されるNUMAノードのアフィニティーを保存します。アフィニティーが優先されない場合、トポロジーマネージャーはPodをそのノードに割り当てることを拒否します。この結果、PodはPodの受付失敗となりTerminated 状態になります。

Podが一度Terminated状態になると、KubernetesスケジューラーはPodの再スケジューリングを試み ません 。Podの再デプロイをするためには、ReplicasetかDeploymenを使用してください。Topology Affinityエラーとなったpodを再デプロイするために、外部のコントロールループを実行することも可能です。

Podが許可されれば、 Hint Providers はこの情報を使ってリソースの割り当てを決定します。

single-numa-node ポリシー

Pod内の各コンテナに対して、single-numa-nodeトポロジー管理ポリシーが設定されたkubeletは各Hint Prociderを呼び出してそれらのリソースの可用性を検出します。 トポロジーマネージャーはこの情報を使用し、単一のNUMAノードアフィニティが可能かどうか決定します。 可能な場合、トポロジーマネージャーは、この情報を保存し、Hint Providers はこの情報を使ってリソースの割り当てを決定します。 不可能な場合、トポロジーマネージャーは、Podをそのノードに割り当てることを拒否します。この結果、Pod は Pod の受付失敗となりTerminated状態になります。

Podが一度Terminated状態になると、KubernetesスケジューラーはPodの再スケジューリングを試みません。Podの再デプロイをするためには、ReplicasetかDeploymentを使用してください。Topology Affinityエラーとなったpodを再デプロイするために、外部のコントロールループを実行することも可能です。

Podとトポロジー管理ポリシーの関係

以下のようなpodのSpecで定義されるコンテナを考えます:

spec:
  containers:
  - name: nginx
    image: nginx

requestslimitsも定義されていないため、このPodはBestEffortQoSクラスで実行します。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

requestsがlimitsより小さい値のため、このPodはBurstableQoSクラスで実行します。

選択されたポリシーがnone以外の場合、トポロジーマネージャーは、これらのPodのSpecを考慮します。トポロジーマネージャーは、Hint Providersからトポロジーヒントを取得します。CPUマネージャーポリシーがstaticの場合、デフォルトのトポロジーヒントを返却します。これらのPodは明示的にCPUリソースを要求していないからです。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "2"
        example.com/device: "1"

整数値でCPUリクエストを指定されたこのPodは、requestslimitsが同じ値のため、GuaranteedQoSクラスで実行します。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"
      requests:
        memory: "200Mi"
        cpu: "300m"
        example.com/device: "1"

CPUの一部をリクエストで指定されたこのPodは、requestslimitsが同じ値のため、GuaranteedQoSクラスで実行します。

spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      limits:
        example.com/deviceA: "1"
        example.com/deviceB: "1"
      requests:
        example.com/deviceA: "1"
        example.com/deviceB: "1"

CPUもメモリもリクエスト値がないため、このPodは BestEffort QoSクラスで実行します。

トポロジーマネージャーは、上記Podを考慮します。トポロジーマネージャーは、Hint ProvidersとなるCPUマネージャーとデバイスマネージャーに問い合わせ、トポロジーヒントを取得します。

整数値でCPU要求を指定されたGuaranteedQoSクラスのPodの場合、staticが設定されたCPUマネージャーポリシーは、排他的なCPUに関するトポロジーヒントを返却し、デバイスマネージャーは要求されたデバイスのヒントを返します。

CPUの一部を要求を指定されたGuaranteedQoSクラスのPodの場合、排他的ではないCPU要求のためstaticが設定されたCPUマネージャーポリシーはデフォルトのトポロジーヒントを返却します。デバイスマネージャーは要求されたデバイスのヒントを返します。

上記のGuaranteedQoSクラスのPodに関する2ケースでは、noneで設定されたCPUマネージャーポリシーは、デフォルトのトポロジーヒントを返却します。

BestEffortQoSクラスのPodの場合、staticが設定されたCPUマネージャーポリシーは、CPUの要求がないためデフォルトのトポロジーヒントを返却します。デバイスマネージャーは要求されたデバイスごとのヒントを返します。

トポロジーマネージャーはこの情報を使用してPodに最適なヒントを計算し保存します。保存されたヒントは Hint Providersが使用しリソースを割り当てます。

既知の制限

  1. トポロジーマネージャーが許容するNUMAノードの最大値は8です。8より多いNUMAノードでは、可能なNUMAアフィニティを列挙しヒントを生成する際に、生成する状態数が爆発的に増加します。

  2. スケジューラーはトポロジーを意識しません。そのため、ノードにスケジュールされた後に実行に失敗する可能性があります。

12 - 拡張リソースをNodeにアドバタイズする

このページでは、Nodeに対して拡張リソースを指定する方法を説明します。拡張リソースを利用すると、Kubernetesにとって未知のノードレベルのリソースをクラスター管理者がアドバタイズできるようになります。

始める前に

Kubernetesクラスターが必要、かつそのクラスターと通信するためにkubectlコマンドラインツールが設定されている必要があります。 このチュートリアルは、コントロールプレーンのホストとして動作していない少なくとも2つのノードを持つクラスターで実行することをおすすめします。 まだクラスターがない場合、minikubeを使って作成するか、 以下のいずれかのKubernetesプレイグラウンドも使用できます:

バージョンを確認するには次のコマンドを実行してください: kubectl version.

Nodeの名前を取得する

kubectl get nodes

この練習で使いたいNodeを1つ選んでください。

Nodeの1つで新しい拡張リソースをアドバタイズする

Node上の新しい拡張リソースをアドバタイズするには、HTTPのPATCHリクエストをKubernetes APIサーバーに送ります。たとえば、Nodeの1つに4つのドングルが接続されているとします。以下に、4つのドングルリソースをNodeにアドバタイズするPATCHリクエストの例を示します。

PATCH /api/v1/nodes/<選択したNodeの名前>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "add",
    "path": "/status/capacity/example.com~1dongle",
    "value": "4"
  }
]

Kubernetesは、ドングルとは何かも、ドングルが何に利用できるのかを知る必要もないことに注意してください。上のPATCHリクエストは、ただNodeが4つのドングルと呼ばれるものを持っているとKubernetesに教えているだけです。

Kubernetes APIサーバーに簡単にリクエストを送れるように、プロキシーを実行します。

kubectl proxy

もう1つのコマンドウィンドウを開き、HTTPのPATCHリクエストを送ります。<選択したNodeの名前>の部分は、選択したNodeの名前に置き換えてください。

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1dongle", "value": "4"}]' \
http://localhost:8001/api/v1/nodes/<選択したNodeの名前>/status
備考: 上のリクエストにある~1は、PATCHのパスにおける/という文字をエンコーディングしたものです。JSON-Patch内のoperationのpathはJSON-Pointerとして解釈されます。詳細については、IETF RFC 6901のsection 3を読んでください。

出力には、Nodeがキャパシティー4のdongleを持っていることが示されます。

"capacity": {
  "cpu": "2",
  "memory": "2049008Ki",
  "example.com/dongle": "4",

Nodeの説明を確認します。

kubectl describe node <選択したNodeの名前>

出力には、再びdongleリソースが表示されます。

Capacity:
 cpu:  2
 memory:  2049008Ki
 example.com/dongle:  4

これで、アプリケーション開発者は特定の数のdongleをリクエストするPodを作成できるようになりました。詳しくは、拡張リソースをコンテナに割り当てるを読んでください。

議論

拡張リソースは、メモリやCPUリソースと同様のものです。たとえば、Nodeが持っている特定の量のメモリやCPUがNode上で動作している他のすべてのコンポーネントと共有されるのと同様に、Nodeが搭載している特定の数のdongleが他のすべてのコンポーネントと共有されます。そして、アプリケーション開発者が特定の量のメモリとCPUをリクエストするPodを作成できるのと同様に、Nodeが搭載している特定の数のdongleをリクエストするPodが作成できます。

拡張リソースはKubernetesには詳細を意図的に公開しないため、Kubernetesは拡張リソースの実体をまったく知りません。Kubernetesが知っているのは、Nodeが特定の数の拡張リソースを持っているということだけです。拡張リソースは整数値でアドバタイズしなければなりません。たとえば、Nodeは4つのdongleをアドバタイズできますが、4.5のdongleというのはアドバタイズできません。

Storageの例

Nodeに800GiBの特殊なディスクストレージがあるとします。この特殊なストレージの名前、たとえばexample.com/special-storageという名前の拡張リソースが作れます。そして、そのなかの一定のサイズ、たとえば100GiBのチャンクをアドバタイズできます。この場合、Nodeはexample.com/special-storageという種類のキャパシティ8のリソースを持っているとアドバタイズします。

Capacity:
 ...
 example.com/special-storage: 8

特殊なストレージに任意のサイズのリクエストを許可したい場合、特殊なストレージを1バイトのサイズのチャンクでアドバタイズできます。その場合、example.com/special-storageという種類の800Giのリソースとしてアドバタイズします。

Capacity:
 ...
 example.com/special-storage:  800Gi

すると、コンテナは好きなバイト数の特殊なストレージを最大800Giまでリクエストできるようになります。

クリーンアップ

以下に、dongleのアドバタイズをNodeから削除するPATCHリクエストを示します。

PATCH /api/v1/nodes/<選択したNodeの名前>/status HTTP/1.1
Accept: application/json
Content-Type: application/json-patch+json
Host: k8s-master:8080

[
  {
    "op": "remove",
    "path": "/status/capacity/example.com~1dongle",
  }
]

Kubernetes APIサーバーに簡単にリクエストを送れるように、プロキシーを実行します。

kubectl proxy

もう1つのコマンドウィンドウで、HTTPのPATCHリクエストを送ります。<選択したNodeの名前>の部分は、選択したNodeの名前に置き換えてください。

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "remove", "path": "/status/capacity/example.com~1dongle"}]' \
http://localhost:8001/api/v1/nodes/<選択したNodeの名前>/status

dongleのアドバタイズが削除されたことを検証します。

kubectl describe node <選択したNodeの名前> | grep dongle

(出力には何も表示されないはずです)

次の項目

アプリケーション開発者向け

クラスター管理者向け