コンテンツにスキップ

コントロールプレーンコンポーネント@Kubernetes

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. コントロールプレーンコンポーネントとは

『マスターコンポーネント』ともいう。

コントロールプレーンNode上で稼働するコンポーネントのこと。

コントロールプレーンコンポーネントは、Cluster内のワーカーNode自体と、ワーカーNode内のPodを管理する。

これは、コントロールプレーンNode上でデーモンとして直接的に常駐させる場合と、DeploymentやDaemonSetでコピーされたPod内でコンテナとして常駐させる場合がある。


02. コントロールプレーンNode

コントロールプレーンNode (kubernetesマスター) とは

kubernetesマスターともいう。コントロールプレーンコンポーネントが稼働する。

クライアント (kubectlクライアント、Kubernetesリソース) がKubernetesリソースを操作しようとリクエストを送信すると、まず最初に、コントロールプレーンNode上のkube-apiserverがリクエストを受信する。

クライアント

クライアント (kubectlクライアント、Kubernetesリソース) は、kube-apiserverにリクエストを送信し、Kubernetesリソースを操作する。


コントロールプレーンNodeで待ち受けるポート番号

コントロールプレーンコンポーネントのために、コントロールプレーンNodeがパケットを待ち受けるデフォルトのポート番号は、以下の通りである。


Controller

コントロールプレーンNodeでは様々なControllerが稼働している。

Controllerは、マニフェストで宣言されたKubernetesリソースと同じ実体を作成し、状態を維持する。


高可用性構成

▼ 高可用性構成とは

control-plane-node_ha-architecture

コントロールプレーンNodeの可用性を高める方法には、デザインパターンがある。

補足として、仮想IPアドレスを管理するkeepalivedと、リクエストを受信して負荷分散するHAProxyを組み合わせ、L7ロードバランサーとして使用する多い。

▼ Stacked-etcd-topologyパターン

各コントロールプレーンNode内にetcdのストレージを配置するデザインパターン。

▼ External-etcd-topologyパターン

各コントロールプレーンNode外にetcdのストレージを配置するデザインパターン。


03. cloud-controller-manager

cloud-controller-managerとは

クラウドインフラを操作するcloud-controllerを一括で管理する。

cloud-controllerを使用して、kube-apiserverがクラウドインフラを操作できるようにする。

kubernetes_cloud-controller-manager


AWS EKSの場合

AWS EKSの場合、LoadBalancer Serviceを作成すると、AWS EKS内のcloud-controller-managerがAWS CLBを自動的にプロビジョニングする。

もしAWS ALBやAWS NLBを作成したい場合、AWS Load Balancerコントローラーが必要である。


04. etcd (エトセディー)

etcdとは

kubernetes_etcd

Cluster内のKubernetesリソースの設定値をキーバリュー型で永続化し、またサービスレジストリとして働く。

語尾の『d』は、分散 (distribution) の意味である。リクエストを受信したkube-apiserverは、etcdからKubernetesリソースの情報を参照する。

Kubernetesに標準で組み込まれているが、別のOSSである。

デフォルトでは、コントロールプレーンNodeで直接的に稼働させる場合でも、あるいはPod内で稼働させる場合でも、/var/lib/etcdディレクトリをローカルストレージとする。


セットアップ

▼ 起動コマンド

$ etcd \
    --advertise-client-urls=https://*.*.*.*:2379 \
    `# HTTPSリクエストを受信するためのSSL証明書` \
    --cert-file=/etc/kubernetes/pki/etcd/server.crt \
    `# HTTPSリクエストを送信するためのクライアント証明書` \
    --client-cert-auth=true \
    `# マニフェストを保管するローカルストレージ` \
    --data-dir=/var/lib/etcd \
    --initial-advertise-peer-urls=https://*.*.*.*:2380 \
    --initial-cluster=foo-node=https://*.*.*.*:2380 \
    `# SSL証明書とペアになる秘密鍵` \
    --key-file=/etc/kubernetes/pki/etcd/server.key \
    --listen-client-urls=https://127.0.0.1:2379,https://*.*.*.*:2379 \
    --listen-metrics-urls=http://127.0.0.1:2381 \
    --listen-peer-urls=https://*.*.*.*:2380 \
    `# etcdが稼働するコントロールプレーンNode` \
    --name=foo-node \
    --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt \
    --peer-client-cert-auth=true \
    --peer-key-file=/etc/kubernetes/pki/etcd/peer.key \
    --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
    --snapshot-count=10000 \
    --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt


05. kube-apiserver

kube-apiserverとは

kubernetes_kube-apiserver

クライアントにコントロールプレーンNodeを公開する。

クライアントがリクエストを送信すると、コントロールプレーンNode上のkube-apiserverがコールされ、他のコンポーネントとHTTPプロトコルでパケットを送受信する。

存在しないリソース定義をリクエストされると、kube-apiserverはリソース定義を見つけられず、以下のエラーレスポンスを返信する。

the server could not find the requested resource


アーキテクチャ

kubernetes_kube-apiserver_architecture

レイヤー 責務
UI ハンドラーチェインで、認証認可を実施する。
リソースハンドラーのadmissionで、バリデーションを実施する。
アプリケーション + ドメイン + インフラ リソースハンドラーのREST logicで、リソースの状態をetcdに永続化する。


セットアップ

▼ 起動コマンド

$ kube-apiserver \
    --advertise-address=*.*.*.* \
    --allow-privileged=true \
    --audit-policy-file=/etc/kubernetes/audit-policy.yaml \
    --audit-webhook-batch-buffer-size=500 \
    --audit-webhook-batch-max-size=40 \
    --audit-webhook-batch-throttle-burst=400 \
    --audit-webhook-batch-throttle-qps=300 \
    --audit-webhook-config-file=/etc/kubernetes/audit-webhook.config \
    --audit-webhook-mode=batch \
    --audit-webhook-truncate-enabled=true \
    `# 認証フェーズの設定` \
    --authentication-token-webhook-config-file=/etc/kubernetes/ais/authentication-webhook.yaml \
    `# 認可タイプ` \
    --authorization-mode=Node,RBAC \
    `# 他のコンポーネントにHTTPSリクエストを送信するためのクライアント証明書` \
    --client-ca-file=/etc/kubernetes/pki/ca.crt \
    `# 有効化しているadmissionアドオン` \
    --enable-admission-plugins=NodeRestriction,PodTolerationRestriction \
    --enable-bootstrap-token-auth=true \
    --encryption-provider-config=/etc/kubernetes/pki/encryption_config.yaml \
    --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt \
    `# etcdにHTTPSリクエストを送信するためのクライアント証明書` \
    --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt \
    `# クライアント証明書とペアになる秘密鍵` \
    --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key \
    `# etcdの宛先情報` \
    --etcd-servers=https://127.0.0.1:2379 \
    --feature-gates=ServiceAccountIssuerDiscovery=true,IPv6DualStack=false \
    --kubelet-certificate-authority=/etc/kubernetes/pki/ca.crt \
    `# kubeletにHTTPSリクエストを送信するためのクライアント証明書` \
    --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt \
    `# クライアント証明書とペアになる秘密鍵` \
    --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key \
    --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \
    --profiling=false \
    `# front-proxyにHTTPSリクエストを送信するためのクライアント証明書` \
    --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt \
    `# クライアント証明書とペアになる秘密鍵` \
    --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key \
    --requestheader-allowed-names=front-proxy-client \
    --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt \
    --requestheader-extra-headers-prefix=X-Extra- \
    --requestheader-group-headers=X-Group \
    --requestheader-username-headers=X-User \
    --secure-port=6444 \
    --service-account-issuer=https://kubernetes.default.svc.cluster.local \
    `# 他のKubernetesリソースが持つServiceAccountの秘密鍵とペアになる公開鍵` \
    --service-account-key-file=/etc/kubernetes/pki/sa.pub \
    --service-account-max-token-expiration=48h \
    --service-account-signing-key-file=/etc/kubernetes/pki/sa.key \
    --service-cluster-ip-range=*.*.*.*/* \
    --tls-cert-file=/etc/kubernetes/pki/apiserver.crt \
    --tls-cipher-suites=***** \
    --tls-private-key-file=/etc/kubernetes/pki/apiserver.key \
    ...


kube-apiserverの仕組み

▼ 認証

kubernetes_kube-apiserver_flow

アプリケーションの認証と同じように、許可されたクライアントか否かを検証する。

Kubernetesリソース (特に、Pod) からのリクエストの場合はServiceAccountで、反対にクライアントからの場合はUserAccountに基づいて、クライアントを認証する。

ServiceAccountを作成すると、Bearerトークン (『***-***-***-***-***-***』のような形式) がSecretに格納される。

クライアントは、AuthorizationヘッダーにBearerトークンを割り当て、リクエストを送信する必要がある。

このトークンは、Kubernetes v1.22以降で定期的に更新されるようになった。

▼ 認可

kubernetes_kube-apiserver_flow

アプリケーションの認可と同じように、クライアントの権限の範囲 (認可スコープ) を検証する。

認証済みのServiceAccountやUserAccountを、RoleBindingされているRoleに基づいて認可する。


公開するエンドポイント

▼ Kubernetesリソース情報

指定したKubernetesリソースの情報を取得する。

# apps/v1の場合
$ kubectl get --raw /apis/apps/v1 | jq .

{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "apps/v1",
  "resources": [
    ...
  ]
}

複数のAPIバージョンが存在するKubernetesリソースの場合、利用可能なバージョンと推奨バージョンを確認できる。

# autoscalingの場合
$ kubectl get --raw /apis/autoscaling | jq .

{
  "kind": "APIGroup",
  "apiVersion": "v1",
  "name": "autoscaling",
  # 利用可能なバージョン
  "versions": [
    {
      "groupVersion": "autoscaling/v2",
      "version": "v2"
    },
    {
      "groupVersion": "autoscaling/v1",
      "version": "v1"
    }
  ],
  # 推奨バージョン
  "preferredVersion": {
    "groupVersion": "autoscaling/v2",
    "version": "v2"
  }
}

▼ ヘルスチェック

kube-apiserverは、ヘルスチェック (Healthy、LivenessProbe、ReadinessProbe) ごとにエンドポイントを持つ。

kubectl getコマンドでヘルスチェックを実行できる。

# kube-apiserverのreadinessエンドポイントにリクエストを送信する。
$ kubectl get --raw=/readyz?verbose

[+]ping ok
[+]log ok
[+]etcd ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/start-apiextensions-informers ok
[+]poststarthook/start-apiextensions-controllers ok
[+]poststarthook/crd-informer-synced ok
[+]poststarthook/bootstrap-controller ok
[+]poststarthook/rbac/bootstrap-roles ok
[+]poststarthook/scheduling/bootstrap-system-priority-classes ok
[+]poststarthook/start-cluster-authentication-info-controller ok
[+]poststarthook/start-kube-aggregator-informers ok
[+]poststarthook/apiservice-registration-controller ok
[+]poststarthook/apiservice-status-available-controller ok
[+]poststarthook/kube-apiserver-autoregistration ok
[+]autoregister-completion ok
[+]poststarthook/apiservice-openapi-controller ok
healthz check passed


SLI/SLO

kube-apiserverには、SLIとSLOが設定されている。


他のコンポーネントとの通信

kube-apiserverは、クライアントからKubernetesリソースの作成/更新/削除リクエストを受信すると、他のコンポーネントと通信してKubernetesリソースを間接的に操作する。ここでは、Podの作成リクエストが送信された場合の流れを記載する。

kubernetes_kube-apiserver_communication

(1)

クライアントやKubernetesリソースがPodの作成リクエストを送信する。

(2)

kube-apiserverはリクエストを受信し、Podの作成宣言の情報をetcdに永続化する。

(3)

しばらくすると、kube-controllerは、kube-apiserverを経由してetcdにwatchイベントを送信する。

kube-controllerは、etcdとNode上のKubernetesリソースの間に差分があることを検知する。さらに、kube-schedulerにPodのスケジューリングをコールする。

(4)

kube-schedulerは、フィルタリングとスコアリングの結果に基づいて、Podのスケジューリング対象となるNodeを決める。

(5)

kube-apiserverは、バインディング情報 (スケジューリング対象NodeとPod間の紐付き情報) をetcdに永続化する。

(6)

しばらくすると、kube-controllerは、kube-apiserverを経由してetcdにwatchイベントを送信する。

kube-controllerは、バインディング情報が永続化されたことを検知する。さらに、etcdのバインディング情報に基づいて、特定のNode上のkubeletにPodの作成をコールする。

(7)

kubeletは、コンテナランタイム (例:Docker、Containerd) のデーモンにコンテナの作成をコールする。

(8)

コンテナランタイムのデーモンは、コンテナを作成する。

(9)

kubeletは、Podが作成されたことをkube-apiserverに返信する。

(10)

kube-apiserverは、Podの作成完了をetcdに永続化する。


拡張apiverver (aggregated apiserver)

▼ 拡張apiververとは

標準のkube-apiserverを拡張したapiserverのこと。

▼ 拡張apiserverの例
  • metrics-server
  • kube-discovery


06. kube-controller-manager

kube-controller-managerとは

kube-controllerを一括で管理する。

kube-controllerを使用して、kube-apiserverがKubernetesリソースを操作できるようにする。


セットアップ

▼ 起動コマンド

$ kube-controller-manager \
    --allocate-node-cidrs=true \
    --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf \
    --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf \
    --bind-address=127.0.0.1 \
    `# kube-apiserverにHTTPSリクエストを送信するためのクライアント証明書` \
    --client-ca-file=/etc/kubernetes/pki/ca.crt \
    --cluster-cidr=*.*.*.*/* \
    --cluster-name=foo-cluster \
    --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt \
    --cluster-signing-key-file=/etc/kubernetes/pki/ca.key \
    --controllers=*,bootstrapsigner,tokencleaner \
    --feature-gates=IPv6DualStack=false \
    --kubeconfig=/etc/kubernetes/controller-manager.conf \
    --leader-elect=true \
    `# コントロールプレーンNodeのサブネットマスク` \
    --node-cidr-mask-size=23 \
    --port=0 \
    --profiling=false \
    --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt \
    `# ルート認証局のCA証明書` \
    --root-ca-file=/etc/kubernetes/pki/ca.crt \
    `# kube-apiserverの認証/認可を通過するために必要なServiceAccountの秘密鍵` \
    `# kube-apiserverには、これとペアになる公開鍵が割り当てられている。` \
    --service-account-private-key-file=/etc/kubernetes/pki/sa.key \
    --service-cluster-ip-range=*.*.*.*/* \
    --terminated-pod-gc-threshold=1000 \
    --use-service-account-credentials=true \
    ...


kube-controller

▼ kube-controllerとは

kube-controllerは、kube-apiserverを経由して、etcdにwatchイベントを送信している。

Kubernetesリソースのマニフェストを何らかの方法 (例:kubectl applyコマンド、kubectl editコマンドなど) でetcd上に永続化したとする。

すると、kube-controllerはetcd上でKubernetesリソースのマニフェストを検知し、実際にKubernetesリソースを作成/変更する。

クライアントからのマニフェストの作成/変更は、etcd上のマニフェストの設定値を変更しているのみで、実際のKubernetesリソースを作成/変更しているわけではないことに注意する。

▼ kube-controllerの種類

各Kubernetesリソースに対応して、kube-controllerがいる。

  • deployment-controller
  • replicaset-controller
  • daemonset-controller

...


kube-controller-managerの仕組み

▼ reconciliationループ

kube-controller-managerは、kube-controllerを反復的に実行する。

これにより、Kubernetesリソースはリソース定義の宣言通りに定期的に修復される。

注意点として、reconciliationループを実現しているのはkube-controllerではなくkube-controller-managerである。

kubernetes_reconciliation-loop


07. kube-scheduler

kube-schedulerとは

Nodeが複数ある場合、NodeとPodのスペックを基に、PodをスケジューリングさせるべきNodeを選定する。

また、kubeletによるヘルスチェックでNodeがNotReadyになった場合に、kube-schedulerはこれを検知し、新しいNodeを作成する。

なお、kube-schedulerは一度スケジューリングしたPodを再スケジューリングできず、deschedulerを使用する必要がある。

kubernetes_kube-scheduler


セットアップ

▼ 起動コマンド

$ kube-scheduler \
    --authentication-kubeconfig=/etc/kubernetes/scheduler.conf \
    --authorization-kubeconfig=/etc/kubernetes/scheduler.conf \
    --bind-address=127.0.0.1 \
    --feature-gates=IPv6DualStack=false \
    --kubeconfig=/etc/kubernetes/scheduler.conf \
    --leader-elect=true \
    --port=0 \
    --profiling=false \
    --secure-port=10259 \
    ...


kube-schedulerの仕組み

(1)

全てのNodeの一覧を取得する。

(2)

Predicatesフェーズである。

条件 (例:.spec.nodeSelectorキー、ハードウェアリソースの空き容量) に応じて、Nodeをフィルタリングする。

(3)

Prioritiesフェーズである。

フィルタリングで選定されたNodeに点数をつける。

(4)

点数に基づいて、Pod作成に最も望ましいNodeを選定する。

kubernetes_kube-scheduler_flow