Kubernetes DaemonSet でノードサービスをデプロイする

KubernetesBeginner
オンラインで実践に進む

はじめに

固定されたレプリカ数ではなく、条件を満たすすべてのノードで 1 つずつ実行すべきソフトウェアがあります。ログ収集ツール、監視エージェント、ネットワークコンポーネントなどでは、このようなノード単位の配置に DaemonSet がよく使われます。

この実験では、用意されたノードにラベルを付け、ノードレポーターをデプロイし、その所有関係を確認します。さらに、ノードの適格性を変更し、ローリングアップデートを実行します。単一ノードの環境を使うことで、より大規模なクラスターと同じコントローラーの動作を保ちながら、配置ルールを簡単に観察できます。

この実験を通して考えるべき重要な問いは、「レプリカをいくつ実行するか」ではなく、「どのノードが適格か」です。DaemonSet はこの問いに継続的に答え、条件に一致する各ノード上に 1 つの Pod を作成します。この環境にはノードが 1 つしかないため、適格性を変更するたびに、1 から 0、または 0 から 1 への変化として明確に確認できます。

適格なノードをマークする

このステップでは、ノードに用途を示すラベルを追加します。ラベルは、コントローラーが選択に利用できるノードの属性を表します。

まず、ノードと既存のラベルを確認します。出力は横に長くなるため、内容を暗記する必要はありません。ラベルとは、Node オブジェクトに付加されたキーと値の情報であることを確認できれば十分です。

cd /home/labex/project/daemonset-workloads
kubectl get nodes --show-labels

新しいラベルを追加します。セットアップ時に古い値が削除されているため、ここでは --overwrite は必要ありません。ただし、競合する値がすでに存在する場合は、kubectl label が通知します。

kubectl label node labex-v135 practice-tier=operations
kubectl get node labex-v135 -L practice-tier

-L practice-tier は、そのラベルを表の列として追加します。これでノードは、実習用の運用エージェントを配置できるノードであることを示します。

2 つ目のコマンドでは、非常に長い --show-labels の行ではなく、必要な情報だけを絞り込んで表示できます。

NAME         STATUS   ROLES           VERSION   PRACTICE-TIER
labex-v135   Ready    control-plane   v1.35.5   operations

必要な根拠は PRACTICE-TIER=operations です。ラベルを付けただけでは何も実行されません。次のステップでワークロードがこのラベルを選択すると、初めて意味を持ちます。

DaemonSet をデプロイする

このステップでは、新しいラベルが付いたノードを対象とする DaemonSet を作成します。

入力する前に、マニフェストを 3 つのつながった判断として読み解きましょう。セレクターと Pod ラベルの両方で app: node-reporter を使用し、コントローラーが自分の Pod を識別できるようにしています。次に、nodeSelector によって、practice-tier=operations のラベルが付いたノードだけを配置対象にします。最後に、Downward API を使って、選択された Pod の spec.nodeName をコンテナ内の NODE_NAME にコピーします。

ループによってデモ用エージェントを実行し続けます。実際の DaemonSet では、ログ収集ツールや監視エージェントなどが動作するでしょう。

cat <<'EOF' > node-reporter.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-reporter
  namespace: practice-daemon
spec:
  selector:
    matchLabels:
      app: node-reporter
  template:
    metadata:
      labels:
        app: node-reporter
    spec:
      nodeSelector:
        practice-tier: operations
      containers:
        - name: reporter
          image: busybox:1.36
          imagePullPolicy: IfNotPresent
          command: ["sh", "-c", "while true; do echo node=${NODE_NAME} version=v1; sleep 15; done"]
          env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
EOF

Namespace を作成し、ファイルを適用して、DaemonSet のロールアウトが完了するまで待ちます。

kubectl create namespace practice-daemon
kubectl apply -f node-reporter.yaml
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
kubectl get daemonset,pods -n practice-daemon -o wide

Downward API の fieldRef により、ノード名を固定値として書き込まずに、スケジュール先のノード名を取得できます。この 1 ノードのクラスターでは、desired、current、ready の各カウントがすべて 1 になるはずです。

Pod 名には自動生成されたサフィックスが付きますが、NODE 列は labex-v135 になります。これは、適格性のルールとスケジューリング結果が一致していることを直接示す証拠です。

1 ノードのテストクラスターでは、重要な列は次のようになります。

NAME            DESIRED   CURRENT   READY   NODE SELECTOR
node-reporter   1         1         1       practice-tier=operations

NAME                  READY   STATUS    NODE
node-reporter-...      1/1     Running   labex-v135

最初の行を左から読み取ると、セレクターに一致するノードが 1 つあり、Pod が 1 つ存在し、その Pod が Ready 状態であることが分かります。より大規模なクラスターでは、適格なノード数に応じてこれらのカウントが増加します。

適格性と所有関係を確認する

このステップでは、DaemonSet の Pod 数を決めるのは replicas フィールドではなく、ノードの適格性であることを確認します。

まず、正常な状態の基準を取得します。カスタム列によって生成された Pod と DaemonSet の関係を確認でき、ログによって Downward API からノード名が渡されたことを証明できます。

kubectl get pods -n practice-daemon -l app=node-reporter -o custom-columns='POD:.metadata.name,NODE:.spec.nodeName,OWNER:.metadata.ownerReferences[0].kind,READY:.status.containerStatuses[0].ready'
kubectl logs -n practice-daemon -l app=node-reporter --tail=3

所有関係の表とログは、次のようになります。

POD                   NODE         OWNER       READY
node-reporter-...     labex-v135   DaemonSet   true
node=labex-v135 version=v1

OWNER フィールドは、これがコントローラーによって管理されている Pod であることを示します。ログは、NODE_NAME が固定値ではなく、Pod の実際の spec.nodeName から取得されたことを証明します。

ラベルを一時的に削除し、Pod が消える様子を確認します。

kubectl label node labex-v135 practice-tier-
kubectl get daemonset node-reporter -n practice-daemon
kubectl wait --for=delete pod -n practice-daemon -l app=node-reporter --timeout=60s

末尾に - を付けると、ラベルキーが削除されます。適格性を元に戻し、コントローラーが新しい Pod を作成するまで待ちます。

ラベルを削除すると、DaemonSet は一時的に次の安定した状態を示します。

NAME            DESIRED   CURRENT   READY
node-reporter   0         0         0

これは障害ではなく、適格性の変更に対する正常な反応です。現在、DaemonSet の nodeSelector に一致するノードがないため、正しい desired 数は 0 です。

kubectl label node labex-v135 practice-tier=operations
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s

ラベルを変更する前後で Pod 名を比較してください。新しく作成された Pod には新しい Pod ID が割り当てられますが、DaemonSet 名と目指す動作は変わりません。これが、コントローラーによる再調整(reconciliation)の実例です。

DaemonSet を更新する

このステップでは、保存済みのマニフェストを更新し、DaemonSet がノードエージェントを置き換える様子を確認します。ファイルと実行中のオブジェクトの状態を一致させておくと、望ましい状態を再現しやすくなります。

まず、cp で作業用コピーを作成します。次に、「Kubernetes 入門」で紹介したのと同じ、対象を絞ったテキスト編集方法を使います。sed -i 's/old/new/' file はファイル内の一致する文字列を直接置換し、grep -n は一致した行を行番号付きで表示するため、適用前に変更内容を確認できます。

cp node-reporter.yaml node-reporter-v2.yaml
sed -i 's/version=v1/version=v2/' node-reporter-v2.yaml
grep -n 'version=' node-reporter-v2.yaml
kubectl apply -f node-reporter-v2.yaml
kubectl rollout status daemonset/node-reporter -n practice-daemon --timeout=60s
kubectl rollout history daemonset/node-reporter -n practice-daemon
kubectl logs -n practice-daemon -l app=node-reporter --tail=3

コピーしたファイルには、比較用として元の v1 の例が残っています。1 回の置換で、コンテナコマンド内のバージョン文字列だけが変更されます。更新したファイルを適用すると Pod テンプレートが変更され、新しいコントローラーリビジョンと置き換え用の Pod が作成されます。ログには version=v2 が含まれるはずです。

rollout history によって、Kubernetes が新しいリビジョンを記録したことを確認できます。DaemonSet は Deployment と同様に、テンプレートを制御しながら更新できます。ただし、置き換えはレプリカ数ではなく、適格なノードを基準に行われます。

確認時の出力には、2 つのリビジョンと新しいログメッセージが含まれていました。

REVISION  CHANGE-CAUSE
1         <none>
2         <none>

node=labex-v135 version=v2

CHANGE-CAUSE=<none> は、人間が読める変更理由のアノテーションが記録されていないことを示すだけです。リビジョン 2 と、置き換えられた Pod のログにある version=v2 が、ロールアウトが実行されたことを示す重要な証拠です。

まとめ

ノードラベルを使って DaemonSet の適格性を制御し、適格なノードごとに 1 つの Pod を配置する再調整の動作を確認しました。また、コントローラーの所有関係を追跡し、ノードエージェントを更新しました。これで、DaemonSet とレプリカ指向の Deployment、完了指向の Job を比較できるようになりました。