Certified Kubernetes Administrator (CKA) #11 Workloads 2: DaemonSet、StatefulSet、Job、CronJob
#10 Workloads 1 では Deployment と ReplicaSet でステートレスアプリを動かし、rolling update と rollback を手に覚えました。ところがすべてのワークロードが「どのノードでも構わない同じ Pod を複数」で終わるわけではありません。ノードごとに正確に 1 つずつ立つべきエージェントがあり、各 Pod が固有の名前と自分専用のディスクを持つべきデータベースがあり、一度回って終わるべきバッチ作業があり、その作業を毎日明け方に自動で回す必要があるケースがあります。
この記事は Deployment では解けない 4 つのワークロードである DaemonSet、StatefulSet、Job、CronJob を扱います。それぞれがどんな問題を解くために存在するのか、Deployment と何が違うのか、そして試験によく出る YAML と kubectl のパターンを整理します。
Deployment ではなぜ足りないのか #
#10 で見た Deployment は ステートレス (stateless) アプリのためのワークロード です。同じ Pod を replicas の個数だけ立て、どのノードに立っても構わず、Pod 同士を区別する理由がありません。このモデルで解けない 4 つの要求がこの記事のテーマです。
| 要求 | 解いてくれるワークロード |
|---|---|
| すべてのノードに正確に 1 つずつ立つべき | DaemonSet |
| 各 Pod が固有の名前・順序・自分のディスクを持つべき | StatefulSet |
| 一度実行して完了まで責任を持つ | Job |
| 決まったスケジュールで繰り返し実行する | CronJob |
この 4 つを Deployment と比べると違いが明確になります。
| 区分 | Deployment | DaemonSet | StatefulSet | Job / CronJob |
|---|---|---|---|---|
| 目的 | ステートレスアプリ | ノードごとのエージェント | 状態保存アプリ | バッチ・一回限り・スケジュール作業 |
| Pod の個数 | replicas で指定 | ノード数に応じて自動 | replicas で指定 | completions で指定 |
| Pod 名 | ランダムハッシュ | ノードごとに 1 つ | 安定した序数 (0、1、2…) | ランダムハッシュ |
| 作成・終了の順序 | 保証なし | 保証なし | 順序保証 | completions まで繰り返し |
| ストレージ | 通常は共有・外部 | 通常はホストパス | Pod ごとに専用 PVC | 通常はなし |
| restartPolicy | Always | Always | Always | OnFailure/Never |
それでは各々を見ていきます。
DaemonSet: ノードごとに 1 つずつ #
DaemonSet は クラスターのすべて (または一部) のノードに Pod を正確に 1 つずつ 立てるワークロードです。ノードが新しく join するとその DaemonSet はそのノードにも自動で Pod を追加し、ノードが抜けるとその Pod も消えます。replicas フィールドがない理由はここにあります。個数を人が決めるのではなく、ノード数がそのまま Pod 数になります。
典型的な使いどころは、ノード単位で動作すべきインフラエージェントです。
- ログ収集器 (fluentd、fluent-bit)
- ノード監視 (node-exporter)
- ネットワークプラグイン (CNI)。kube-proxy 自体も DaemonSet で立つケースが多い
- ストレージデーモン
基本マニフェスト #
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
containers:
- name: node-exporter
image: prom/node-exporter:v1.8.0
ports:
- containerPort: 9100Deployment マニフェストで kind を DaemonSet に変えて replicas だけ消した形とほぼ同じです。selector と template のラベルが一致すべきというルールも同じです。
特定のノードにだけ立てる: nodeSelector #
すべてのノードではなく一部のノードにだけ載せるべきときは、template の Pod spec に nodeSelector を置きます。ラベルが一致するノードにだけ Pod が立ちます。
spec:
template:
spec:
nodeSelector:
disktype: ssdcontrol plane ノードにも立てる: tolerations #
デフォルト設定では control plane ノードには taint が掛かっており、一般 Pod が配置されません。ログ収集器のように control plane ノードにも立つべき DaemonSet なら、その taint に耐える toleration を入れる必要があります。taint と toleration の詳しい動作は #14 で扱います。
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule更新戦略 #
DaemonSet のデフォルトの更新戦略は RollingUpdate です。template を変えるとノードの Pod を 1 つずつ新しいバージョンに置き換え、maxUnavailable で同時に落とせるノード数を制御します。
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1もう 1 つの戦略は OnDelete です。template を変えても自動では置き換えず、該当の Pod を自分で削除してはじめてその場所に新しい template で再び立ちます。置き換えのタイミングを人が制御したいときに使います。
# 状態確認 (ノード数と desired/ready を比較)
k get daemonset -n monitoring
k rollout status ds/node-exporter -n monitoringStatefulSet: 安定 ID と順序 #
StatefulSet は 状態を保存すべきアプリ のためのワークロードです。データベース、メッセージブローカー、分散 KV ストアのように、各インスタンスが固有のアイデンティティと自分専用のデータを持つべきケースで使います。Deployment が与えられない 3 つを保証します。
- 安定したネットワーク ID。Pod 名が
名前-0、名前-1、名前-2のように序数で固定されます。Pod が死んで再び立っても、同じ名前と同じ DNS 名で戻ってきます。 - 順序保証。作成は 0 から順に (0 が Ready であってはじめて 1 を作成)、削除とスケールダウンは高い番号から逆順で進みます。
- Pod ごとの専用ストレージ。
volumeClaimTemplatesで各 Pod ごとに独立した PVC が作られ、Pod が再作成されても同じ番号の PVC に再び接続されます。
headless Service がまず必要だ #
StatefulSet の安定した DNS 名は headless Service があってはじめて動作します。headless Service は clusterIP: None で作る Service で、クラスター IP を割り当てる代わりに各 Pod の DNS レコードを直接公開します。その結果、各 Pod が Pod 名.サービス名.ネームスペース.svc.cluster.local の形の固有アドレスを持ちます。Service の種類と動作全般は #18 で扱います。
StatefulSet + headless Service の例 #
# headless Service: clusterIP が None
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
clusterIP: None
selector:
app: nginx
ports:
- name: web
port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: nginx # 上の headless Service の名前と一致すべき
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- name: web
containerPort: 80
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates: # Pod ごとに PVC を自動生成
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Giこのマニフェストが作るものを整理します。
- Pod は
web-0、web-1、web-2の順に作成されます。 - 各 Pod には
data-web-0、data-web-1、data-web-2という PVC が自動で付きます。 - DNS 名は
web-0.nginx.<namespace>.svc.cluster.localのように安定して保たれます。
# Pod 名が序数で立ったか確認
k get pods -l app=nginx
# 自動生成された PVC を確認
k get pvc
# headless Service のエンドポイント (各 Pod IP) を確認
k get endpoints nginxスケールと削除時の注意 #
スケールダウンすると Pod は高い番号から消えますが、volumeClaimTemplates で作った PVC は自動では削除されません。 データ損失を防ぐための意図された動作です。StatefulSet を消しても PVC は残るので、整理が必要なら PVC を自分で削除する必要があります。
# スケールダウン (web-2 が先に消え、PVC は残る)
k scale statefulset web --replicas=2
# StatefulSet だけ削除して Pod は残す (まれに使用)
k delete statefulset web --cascade=orphan更新戦略はデフォルトが RollingUpdate で、高い番号の Pod から逆順で置き換えます。partition の値を置くと、その番号以上だけを更新する段階的ロールアウト (canary) も可能です。
Job: 完了を目標に回る作業 #
Deployment と DaemonSet は Pod を 生かし続ける ワークロードです。一方 Job は 決まった回数だけ成功裏に完了したら終わる ワークロードです。データマイグレーション、バッチ計算、バックアップスクリプトのように、一度 (または決まった回数) 回って終わるべき作業に使います。
主要なフィールドは 4 つです。
| フィールド | 意味 |
|---|---|
completions | 成功すべき総 Pod 数。デフォルト 1 |
parallelism | 同時に回せる Pod 数。デフォルト 1 |
backoffLimit | 失敗時の再試行上限。超過すると Job 失敗 |
restartPolicy | OnFailure または Never のみ許可 (Always 不可) |
Deployment と違い、Job の Pod には restartPolicy: Always を使えません。完了を目標とする作業に無限再起動は意味をなさないからです。
基本 Job の例 #
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
completions: 4 # 計 4 回成功すべき完了
parallelism: 2 # 一度に 2 個ずつ並列実行
backoffLimit: 4 # 失敗時に最大 4 回まで再試行
template:
spec:
restartPolicy: Never
containers:
- name: pi
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]# Job の状態 (COMPLETIONS が 4/4 になれば完了)
k get jobs
# Job が作った Pod とログを確認
k get pods --selector=job-name=pi
k logs job/pi
# 完了した Job を整理
k delete job piactiveDeadlineSeconds を置くとその時間を超えた Job を強制終了でき、ttlSecondsAfterFinished を置くと完了した Job とその Pod を一定時間後に自動で整理します。
CronJob: スケジュールに合わせて Job を打ち出す #
CronJob は cron 式に従って周期的に Job を作成する ワークロードです。CronJob 自体は直接 Pod を作らず、スケジュールごとに Job オブジェクトを 1 つずつ作り、その Job が Pod を作って作業を実行します。夜間バックアップ、定期レポート、キャッシュ整理のような繰り返し作業に使います。
主要なフィールドは次のとおりです。
| フィールド | 意味 |
|---|---|
schedule | cron 式 (分 時 日 月 曜日) |
concurrencyPolicy | 前の実行が終わっていないときのポリシー |
successfulJobsHistoryLimit | 保管する成功 Job 数。デフォルト 3 |
failedJobsHistoryLimit | 保管する失敗 Job 数。デフォルト 1 |
startingDeadlineSeconds | 予定時刻を逃したときに許容する遅延上限 |
concurrencyPolicy の値は 3 つです。
Allow(デフォルト)。前の実行が終わっていなくても新しい Job を同時に開始します。Forbid。前の実行が終わっていなければ今回の実行をスキップします。Replace。前の実行をキャンセルして新しい Job に置き換えます。
CronJob の例 #
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 3 * * *" # 毎日明け方 3 時
concurrencyPolicy: Forbid # 前のバックアップが終わっていなければスキップ
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
startingDeadlineSeconds: 120
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: bitnami/postgresql:16
command: ["/bin/sh", "-c", "pg_dump ... > /backup/dump.sql"]schedule は 分 時 日 月 曜日 の 5 桁です。0 3 * * * は毎日 03:00、*/15 * * * * は 15 分ごと、0 0 * * 0 は毎週日曜日の深夜を意味します。
# CronJob の一覧と最後の実行時刻
k get cronjob
# CronJob が打ち出した Job を確認
k get jobs --watch
# 今すぐ一度手動実行 (テストするとき)
k create job manual-backup --from=cronjob/db-backupCronJob を一時的に止めるには spec.suspend: true を設定すると、新しい Job の作成が中断されます。点検中にバックアップが実行されるのを防ぐときに便利です。
試験ポイント #
CKA 実技でこれらのワークロードが出るとき、点数を分ける箇所を集めました。
命令型作成で時間節約。Job と CronJob は
kubectl createで骨組みを作ったあとdo(--dry-run=client -o yaml) でマニフェストを抜き出して編集する方が速いです。k create job test --image=busybox $do -- /bin/sh -c "echo hi" > job.yaml k create cronjob hello --image=busybox --schedule="*/1 * * * *" $do \ -- /bin/sh -c "date" > cron.yamlDaemonSet は
kubectl createで作れない。DaemonSet 専用の作成コマンドがないので、Deployment マニフェストをdoで抜き出したあとkindを DaemonSet に変えてreplicasを消すパターンを覚えておくと速いです。restartPolicy の罠。Job と CronJob の Pod template には
OnFailureまたはNeverのみ許可されます。デフォルト値 (Always) をそのまま置くとマニフェストが拒否されるので、必ず明示します。StatefulSet は serviceName と headless Service がセット。
serviceNameが指す headless Service (clusterIP: None) がないと安定した DNS が動作しません。両方を 1 つのマニフェストで一緒に提出する習慣が安全です。PVC は残る。StatefulSet をスケールダウンしたり削除したりしても
volumeClaimTemplatesが作った PVC は自動削除されません。問題が整理を求めるなら PVC を自分で消す必要があります。selector と template のラベル一致。4 つのワークロードはすべて #10 で見たように、
selector.matchLabelsとtemplate.metadata.labelsが一致しないと作成が拒否されます。
これらのワークロードを実際のクラスターで自分で作って壊してみる練習が、試験会場での手の速さを作ります。より広い背景が必要なら、Kubernetes 中級シリーズ で同じリソースを運用の観点から改めて整理してあります。
まとめ #
この記事で押さえたこと:
- DaemonSet。ノードごとに Pod 1 つ。replicas なし。
nodeSelectorで対象ノードを制限、tolerationsで control plane ノードまで拡張、RollingUpdate/OnDeleteの更新戦略 - StatefulSet。安定した序数 ID・作成/削除の順序保証・Pod ごとの専用 PVC。
serviceNameが指す headless Service (clusterIP: None) とvolumeClaimTemplatesが核心。PVC は自動削除されない - Job。完了を目標とする作業。
completions/parallelism/backoffLimit、restartPolicy はOnFailure/Neverのみ許可 - CronJob。スケジュールごとに Job を作成。
schedule(cron の 5 桁)、concurrencyPolicy(Allow/Forbid/Replace)、history limit、suspendで一時停止 - 試験ポイント。命令型作成で骨組みを確保、DaemonSet は変換で作成、restartPolicy の罠、StatefulSet は headless Service とセット、PVC 残存の処理
次へ: ConfigMap と Secret の深掘り #
ここまででワークロードの形をすべて押さえました。ところが Pod に設定値と秘密情報をどう注入するかはまだ扱っていません。
#12 ConfigMap と Secret の深掘り では、設定をコードから分離する ConfigMap、機微な情報を入れる Secret のタイプと base64 エンコーディング、そしてこの 2 つを環境変数とボリュームで Pod に注入する方法、値が変わったときの反映動作まで整理します。