コンテンツにスキップ

Nodeコンポーネント@Kubernetes

はじめに

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


01. Nodeコンポーネントとは

ワーカーNode上で稼働するKubernetesコンポーネントのこと。


02. ワーカーNode

ワーカーNodeとは

ノードコンポーネントが稼働する。Kubernetesの実行時に自動的に作成される。

もし手動で作成する場合は、kubectlコマンドで--register-node=falseとする必要がある。


ワーカーNodeで待ち受けるポート番号

ワーカーNodeがパケットを待ち受けるデフォルトのポート番号は、以下の通りである。


03. Nodeグループ

Nodeグループとは

KubernetesにはNodeグループというリソースがなく、グループを宣言的に定義することはできない。

ただ、クラウドプロバイダーのサーバーオートスケーリング機能 (例:AWS EC2AutoScaling) を使用して、Nodeグループ (例:AWS EKS Nodeグループ) を実現できる。

同じ設定値 (.metadata.labelsキー、CPU、メモリ、など) や同じ役割を持ったNodeのグループのこと。

基本的には、Nodeグループは冗長化されたワーカーNodeで構成されており、IDは違えど、ワーカーNode名は全て同じである。

NodeグループをターゲットとするL7ロードバランサーでは、Nodeグループ内で冗長化ワーカーNodeのいずれかに対してルーティングすることになる。


Nodeグループの粒度

node affinityやnode selectorを実施できるように、.metadata.labelsキーにNodeグループ名を設定しておく。

キー名は、node.kubernetes.io/nodetypeとする。

Nodeグループ名の例と.metadata.labelsキー値 説明
appservice アプリコンテナを稼働させる。
batchjob 単発的なバッチ処理やジョブ (定期的なバッチ処理) のコンテナを配置する。
deploy 他のKubernetesリソースをデプロイするためのKubernetesリソース (例:ArgoCDのPod) のコンテナを配置する。Nodeグループ内に含めずに、異なるClusterに切り分けて管理しても良い。
ingressgateway ワーカーNodeへのインバウンド通信の入口になるリソース (例:Ingress、IngressGateway) のコンテナや、API Gatewayのアプリコンテナを配置する。これは単一障害点になりうるため、ワーカーNodeのCPUやメモリを潤沢にしようできるように、他のリソースのコンテナとは別のNodeグループにした方が良い。また、アップグレード時間の短縮にも繋がる。
master セルフマネージドなKubernetesコントロールプレーンNodeのコンテナを稼働させる。マネージドなコントロールプレーンNode (例:AWS EKS、Google Cloud GKE、Azure AKS、など) の場合、このNodeグループは不要になる。
system ログやメトリクスのデータポイントを収集するリソース (例:Prometheus、Alertmanager、のPod) のコンテナを配置する。また、セルフマネージドなサービスメッシュコントロールプレーンNodeのコンテナを稼働させる。マネージドなコントロールプレーンNode (例:AWS AppMesh、など) の場合、このNodeグループは不要になる。
apiVersion: v1
kind: Pod
metadata:
  name: foo-pod
  labels:
    node.kubernetes.io/nodetype: batch
spec: ...


ワーカーNodeのオートスケーリング

執筆時点 (2022/07/20) では、KubernetesのAPIにはワーカーNodeのオートスケーリング機能はない。

そのため、Node数は固定である。

ただし、cluster-autoscalerを使用すると、各クラウドプロバイダーのAPIからワーカーNodeのオートスケーリングを実行できるようになる。


04. kube-proxy

kube-proxyとは

kube-proxyは、各ワーカーNode上でDaemonSetとして稼働し、IPアドレスベースのサービスディスカバリー、検出したサービス (Pod) に対するL4ロードバランサー、として働く。


セットアップ

▼ 起動コマンド

$ kube-proxy \
    --config=/var/lib/kube-proxy/config.conf \
    --hostname-override=foo-node \
    ...


kube-proxyの仕組み

▼ CoreDNSと組み合わせたサービスディスカバリー

kube-proxyは、ワーカーNode上で稼働するパケットフィルタリング型ファイアウォール (iptablesによるL2L4の防御) やL4ロードバランサー (ipvs) に、EndpointSliceで管理するPodの宛先情報を追加/削除する。

Serviceネットワークさえ作成できていれば、ServiceとPodが同じワーカーNode上にあるか否かに限らず、Serviceは、ワーカーNodeの宛先情報ルールを使用してPodを動的に検出できる。

ただし、宛先のIPアドレスは動的に変化するため、別途CoreDNSも使用して、サービスディスカバリーを実装する。

kubernetes_kube-proxy

▼ PodのL4ロードバランサー

負荷分散方式によって、Serviceがルーティング先のPodを決める。

プロキシモードごとに、使用する負荷分散方式が異なる。

▼ 確認方法

iptableコマンドで、『KUBE-SERVICES』というチェインのターゲットを確認する。

ターゲットには、Serviceのルーティング先となるPod (異なるワーカーNode上にある場合もある) の宛先情報が登録されている。

source列に含まれるIPアドレスを持つパケットのみでルールが適用され、各ルールに対応するPodに送信する場合、宛先IPアドレスをdestination列のIPアドレスに変換する。

$ iptables -L -n KUBE-SERVICES -t nat --line-number

Chain KUBE-SERVICES (2 references)
num  target                     prot   opt   source      destination
1    KUBE-SVC-ERIFXISQEP7F7OF4  tcp    --    0.0.0.0/0   10.96.0.10           /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
2    KUBE-SVC-V2OKYYMBY3REGZOG  tcp    --    0.0.0.0/0   10.101.67.107        /* default/nginx-service cluster IP */ tcp dpt:8080
3    KUBE-SVC-NPX46M4PTMTKRN6Y  tcp    --    0.0.0.0/0   10.96.0.1            /* default/kubernetes:https cluster IP */ tcp dpt:443
4    KUBE-SVC-JD5MR3NA4I4DYORP  tcp    --    0.0.0.0/0   10.96.0.10           /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
5    KUBE-SVC-TCOU7JCQXEZGVUNU  udp    --    0.0.0.0/0   10.96.0.10           /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
6    KUBE-NODEPORTS             all    --    0.0.0.0/0   0.0.0.0/0            /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL


プロキシモードの種類

▼ iptablesプロキシモード

デフォルトのプロキシモードである。

項目 仕組み
IPアドレスベースのサービスディスカバリー ServiceとそのService配下のEndpointSliceの追加と削除を監視し、これらの増減に合わせて、ワーカーNode上で稼働するiptablesを更新する。
L4プロトコルの負荷分散方式 ランダム方式のみ。

kubernetes_kube-proxy_iptables

▼ userspaceプロキシモード

項目 仕組み
IPアドレスベースのサービスディスカバリー ServiceとそのService配下のEndpointSliceの追加と削除を監視し、これらの増減に合わせて、ワーカーNode上で稼働するiptablesを更新する。
L4プロトコルの負荷分散方式 ラウンドロビン方式のみ。

kubernetes_kube-proxy_userspace

▼ ipvsプロキシモード

kube-proxyの起動時に、--feature-gatesオプションにSupportIPVSProxyMode=true--proxy-modeオプションにipvsを設定する。

項目 仕組み
IPアドレスベースのサービスディスカバリー ServiceとそのService配下のEndpointSliceの追加と削除を監視し、これらの増減に合わせて、ワーカーNode上で稼働するipvsを更新する。
L4プロトコルの負荷分散方式 ラウンドロビン方式、コネクションの最低数、宛先ハッシュ値、送信元ハッシュ値、など。

kubernetes_kube-proxy_ipvs


その他のプロキシ

ワーカーNode外部からのインバウンド通信をPodにルーティングするためのプロキシが、他にもいくつかある。

  • kubectl proxyコマンド
  • minikube tunnelコマンド
  • LoadBalancer


05. kubelet

kubeletとは

各ワーカーNode上で直接デーモンとして常駐し、コンテナランタイムを操作することにより、Podを作成する。

また、ワーカーNodeやPodを監視し、メトリクスのデータポイントをkube-apiserverに提供する。

kubernetes_kubelet


セットアップ

▼ 起動コマンド

$ kubelet \
    --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf \
    `# kubeletの設定ファイル` \
    --kubeconfig=/etc/kubernetes/kubelet.conf \
    --config=/var/lib/kubelet/config.yaml \
    --authentication-token-webhook=true
    --authorization-mode=Webhook \
    --container-runtime=remote \
    `# コンテナランタイムの設定` \
    --container-runtime-endpoint=unix:///run/containerd/containerd.sock \
    --max-pods=250 \
    --node-ip=*.*.*.* \
    --rotate-server-certificates=true \
    --seccomp-default=true \
    --cgroup-driver=systemd \
    --runtime-cgroups=/system.slice/containerd.service \
    ...

kubelet-config.jsonファイル (KubeletConfiguration)

kubeletを設定する。

{
  "kind": "KubeletConfiguration",
  "apiVersion": "kubelet.config.k8s.io/v1beta1",
  "address": "0.0.0.0",
  "authentication":
    {
      "anonymous": {"enabled": "false"},
      "webhook": {"cacheTTL": "2m0s", "enabled": "true"},
      "x509": {"clientCAFile": "/etc/kubernetes/pki/ca.crt"},
    },
  "authorization":
    {
      "mode": "Webhook",
      "webhook": {"cacheAuthorizedTTL": "5m0s", "cacheUnauthorizedTTL": "30s"},
    },
  "clusterDomain": "cluster.local",
  "hairpinMode": "hairpin-veth",
  "readOnlyPort": 0,
  "cgroupDriver": "cgroupfs",
  "cgroupRoot": "/",
  "featureGates": {"RotateKubeletServerCertificate": "true"},
  "protectKernelDefaults": "true",
  "serializeImagePulls": "false",
  "serverTLSBootstrap": "true",
  "tlsCipherSuites":
    [
      "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
      "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
      "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
      "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
      "TLS_RSA_WITH_AES_256_GCM_SHA384",
      "TLS_RSA_WITH_AES_128_GCM_SHA256",
    ],
  # コンテナのログローテーションの閾値
  "containerLogMaxSize": "100Mi",
  # コンテナのログの最大世代数
  "containerLogMaxFiles": 2,
}


コンテナランタイムの操作

▼ ガベージコレクション

コンテナランタイム (例:Docker、Containerd、など) は、ベースイメージを含む各イメージレイヤーをキャッシュとしてローカルストレージ (例:var/lib/dockerディレクトリ、var/lib/containerdディレクトリ、など) に保管する。

kubeletは、使用されていないイメージレイヤー (5分ごと) やコンテナ (10分ごと) のキャッシュのガベージコレクションを実行する。

コンテナイメージのガベージコレクションであれば、Nodeのストレージ使用量が85%を超過していると、kubeletは80%未満になるようにコンテナイメージの残骸を削除する。

▼ ログローテション

kubeletは、Pod内のコンテナが標準出力に出力したログを取得し、サイズが一定量を超過するとNode上に.zip形式で圧縮して保管する。

また、ログローテーションの結果で作成されるファイルの世代数が一定数を超過すると、古い世代順に削除する。

これらは、containerLogMaxSizecontainerLogMaxFilesで設定できる。

kubeletではログの保管期間を設定できないため、もし保管期間を設定したい場合はNode上にログローテーションツール (例:logrotate) をインストールする必要がある。

kubernetes_kubelet_log-rotation


ログ

▼ kubeletのログの確認

kubeletは、ワーカーNodeでデーモンとして常駐しているため、journalctlコマンドでログを取得できる。

$ journalctl -u kubelet.service

-- Logs begin at Mon 2022-04-18 21:04:26 JST, end at Mon 2022-12-05 17:42:29 JST. --
04/21 14:21:55 foo-node systemd[1]: Started kubelet: The Kubernetes Node Agent.
...

▼ kubeletのバージョン

ログ内にkubeletのバージョンが定義されている。

$ journalctl -u kubelet.service | grep "Kubelet version"

kubelet[405976]: I0421 14:22:01.838974  405976 server.go:440] "Kubelet version" kubeletVersion="v1.22"


ユニットファイル

kubelet.serviceファイル

おおよそ、/etc/systemd/systemディレクトリにある。

ファイルの設定例は以下の通りである。

[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service sandbox-image.service
Requires=containerd.service sandbox-image.service

[Service]
Slice=runtime.slice
ExecStartPre=/sbin/iptables -P FORWARD ACCEPT -w 5
ExecStart=/usr/bin/kubelet \
          --config /etc/kubernetes/kubelet/kubelet-config.json \
          --kubeconfig /var/lib/kubelet/kubeconfig \
          --container-runtime-endpoint unix:///run/containerd/containerd.sock \
          --image-credential-provider-config /etc/eks/image-credential-provider/config.json \
          --image-credential-provider-bin-dir /etc/eks/image-credential-provider \
          $KUBELET_ARGS \
          $KUBELET_EXTRA_ARGS

Restart=on-failure
RestartForceExitStatus=SIGPIPE
RestartSec=5
KillMode=process
CPUAccounting=true
MemoryAccounting=true


06. コンテナランタイム (Containerd)

セットアップ

▼ Containerdのインストールの事前作業

(1)

/etc/modules-load.d/containerd.confファイルに、カーネルモジュールを設定する。

overlay
br_netfilter
(2)

カーネルモジュールを読み込む。

$ modprobe overlay
$ modprobe br_netfilter
(3)

/etc/sysctl.d/99-kubernetes-cri.confファイルに、カーネルパラメーターを設定する。

net.bridge.bridge-nf-call-iptables=1
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-ip6tables=1
(4)

カーネルに設定を反映する。

$ sysctl --system

▼ Containerdのインストール

(1)

要件のパッケージをインストールする。

$ apt-get update -y \
  && apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
(2)

Docker公式の提供するGPGキーを追加する。

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
(3)

リポジトリを追加する。

$ add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
(4)

Containerdをインストールする。

$ apt-get update && apt-get install containerd.io

▼ Containerdの設定ファイルの準備

(1)

設定ファイルとして、/etc/containerd/config.tomlファイルを作成する。

$ mkdir -p /etc/containerd
$ containerd config default | sudo tee /etc/containerd/config.toml
(2)

Containerdに設定を反映する。

$ systemctl restart containerd

▼ kubeletによるContainerdの指定

kubeletの起動時に、--container-runtimeオプションと--container-runtime-endpointオプションを使用する。

$ kubelet \
    --container-runtime=remote \
    --container-runtime-endpoint=unix:///run/containerd/containerd.sock
    ...


ログ

ワーカーNodeでデーモンとして常駐しているため、journalctlコマンドでログを取得できる。

$ journalctl -u containerd.service

-- Logs begin at Mon 2022-04-18 21:04:26 JST, end at Mon 2022-12-05 17:43:49 JST. --
04/19 18:10:17 fo-node systemd[1]: Starting containerd container runtime...


コンテナライフサイクル

▼ フェーズ

コンテナのライフサイクルにはフェーズがある。

フェーズ名 説明
Waiting RunningフェーズとTerminatedフェーズ以外のフェーズにある。
Running コンテナの起動が完了し、実行中である。
Terminated コンテナが正常/異常に停止した。