コンテンツにスキップ

Nodeコンポーネント@Kubernetes

はじめに

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


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

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


02. ワーカーNode

ワーカーNodeとは

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

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


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

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


03. Nodeグループ

Nodeグループとは

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

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

複数の Node に同じ設定値 (.metadata.labels キー、CPU、メモリなど) をもたせ、冗長化できるようになる。

マイクロサービスごとに、Node に必要な非機能要件(性能、耐障害性、ネットワークセグメント)が異なる場合、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、egress` ワーカーNodeへのインバウンド通信の入口になるリソース (例:Ingress、IngressGateway) のコンテナや、APIゲートウェイのアプリを配置する。これは単一障害点になりうるため、ワーカーNodeのCPUやメモリを潤沢にしようできるように、他のリソースのコンテナとは別のNodeグループにしたほうがよい。また、アップグレード時間の短縮にも繋がる。
master セルフマネージドなKubernetesコントロールプレーンNodeのコンテナを稼働させる。マネージドなコントロールプレーンNode (例:Amazon EKS、Google Cloud GKE、Azure AKSなど) の場合、このNodeグループは不要になる。
system ログやメトリクスの元になるデータポイントを収集するリソース (例:Prometheus、Alertmanager、のPod) のコンテナを配置する。また、セルフマネージドなサービスメッシュコントロールプレーンNodeのコンテナを稼働させる。マネージドなコントロールプレーンNode (例:Amazon VPC Latticeなど) の場合、この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 は、サービス検出と L4 ロードバランシングを実行する。

このとき、ワーカーNode をクライアント、コントロールプレーンをサービスレジストリ、としたクライアントサイドパターンのサービス検出を実現する。

各ワーカーNode 上で DaemonSet として稼働する。

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

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

kubernetes_kube-proxy


セットアップ

▼ 起動コマンド

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


その他のプロキシ

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

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


04-02.プロキシモードの種類

iptablesプロキシモード (デフォルト)

▼ iptablesプロキシモード

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

項目 仕組み
サービス検出 ServiceとそのService配下のEndpointSliceの追加と削除を監視し、これらの増減に合わせて、ワーカーNode上で稼働するiptablesにIPアドレスを追加/削除する。
L4 ロードバランサー ランダム方式のみ。

kubernetes_kube-proxy_iptables

▼ サービス検出

iptable 方式の場合、kube-proxy は iptables に Pod の IP アドレスを追加/削除する。

iptables コマンドで、『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

L4 ロードバランシング

iptable 方式の場合、kube-proxy によって検出された Pod の IP アドレスに対して、L4 ロードバランシングを実行する。


userspaceプロキシモード

▼ userspaceプロキシモード

項目 仕組み
サービス検出 ServiceとそのService配下のEndpointSliceの追加と削除を監視し、これらの増減に合わせて、ワーカーNode上で稼働するiptablesにIPアドレスを追加/削除する。
L4 ロードバランサー ラウンドロビン方式のみ。

kubernetes_kube-proxy_userspace


ipvsプロキシモード

▼ ipvsプロキシモード

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

項目 仕組み
サービス検出 ServiceとそのService配下のEndpointSliceの追加と削除を監視し、これらの増減に合わせて、ワーカーNode上で稼働するipvsにハッシュ値を追加/削除する。
L4 ロードバランサー ラウンドロビン方式、接続の最低数、宛先ハッシュ値、送信元ハッシュ値など。

kubernetes_kube-proxy_ipvs


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 コンテナが正常/異常に停止した。