はじめに
固定されたレプリカ数ではなく、条件を満たすすべてのノードで 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 を比較できるようになりました。


