Certified Kubernetes Application Developer (CKAD) #6 Workloads 2: DaemonSet, StatefulSet
#5 Workloads 1: Deployment、ReplicaSet、ローリングアップデートとロールバック では、Deployment で stateless な Pod の束を回し、ローリングアップデートとロールバックを扱いました。しかし Kubernetes のワークロードコントローラーは Deployment 一つだけではありません。すべてのノードに同じ Pod を 1 つずつ配置しなければならない作業、そして Pod ごとに固有のアイデンティティと専用ストレージが必要な作業 は、Deployment では解けません。
この記事では、その 2 つを担うコントローラーである DaemonSet と StatefulSet を実技の観点から整理します。CKAD は空のターミナルに直接作り出す試験なので、2 つのリソースの概念を押さえたうえで、すぐに YAML と kubectl で手に馴染ませてみます。
Deployment では解けない 2 つのこと #
Deployment は 相互に交換可能な stateless Pod の集合 を前提とします。replicas を 3 にすると、どのノードに何番目の Pod が立つかはスケジューラーが自分で決め、どの Pod が死んでも同一の新しい Pod に置き換われば済みます。名前も web-7d8f... のようなランダムなハッシュが付きます。
しかし実務には、この前提が当てはまらない作業があります。
- すべてのノードに正確に 1 つずつ 動かなければならない Pod。ログ収集器やノード監視エージェントは、ノード数と Pod 数が同じである必要があります。このときは DaemonSet を使います。
- 各 Pod が固有の名前と専用ディスクを保持 しなければならない Pod。データベースクラスターの各ノードは自分だけのデータを持ち、再起動しても同じアイデンティティで戻ってこなければなりません。このときは StatefulSet を使います。
この 2 つのコントローラーが、この記事のテーマです。
DaemonSet: すべてのノードに Pod を 1 つずつ #
DaemonSet は クラスターのすべての (または一部の) ノードに Pod を正確に 1 つずつ 配置するコントローラーです。ノードが新しく追加されると、そのノードにも自動的に Pod が立ち、ノードが外れると該当する Pod も一緒に消えます。replicas という概念がありません。ノード数がそのまま Pod 数 です。
どこで使うか #
DaemonSet は、ノード単位で動作しなければならないシステムコンポーネントに主に使われます。
- ログ収集器。Fluentd や Fluent Bit のように各ノードのログをかき集めて中央へ送るエージェント
- ノード監視。node-exporter のようにノードの CPU・メモリ・ディスク指標を収集するエージェント
- CNI とストレージプラグイン。各ノードにネットワークやストレージ機能をインストールするコンポーネント
nodeSelector と tolerations で一部のノードだけに限定 #
デフォルトでは、DaemonSet は control plane の taint が掛かったノードを除く すべてのワーカーノード に Pod を立てます。特定のノードだけに立てたい場合は、nodeSelector でラベルを指定します。
spec:
template:
spec:
nodeSelector:
disktype: ssd逆に control plane ノードのように taint が掛かったノードにも Pod を立てる必要があるなら、tolerations でその taint を許可します。監視エージェントは control plane ノードも見なければならないので、この設定をよく使います。
spec:
template:
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoScheduleアップデート戦略 #
DaemonSet の updateStrategy は 2 種類です。
- RollingUpdate (デフォルト)。
maxUnavailableで一度に置き換える Pod 数を制限し、順次置き換えます。 - OnDelete。自動的に置き換えず、ユーザーが既存の Pod を直接削除したときだけ新しいバージョンが立ちます。
DaemonSet YAML の例 #
DaemonSet には kubectl create generator がありません。試験では、Deployment の骨組みを generator で抜き出したあと kind を DaemonSet に変え、replicas と strategy の行を消す方法が速いです。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
namespace: logging
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
containers:
- name: fluent-bit
image: fluent/fluent-bit:2.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi作成と確認は次のとおりです。
k apply -f ds.yaml
k get daemonset -n logging
k get pods -n logging -o wide # ノードごとに 1 つずつ立っているか確認DESIRED と CURRENT の値がノード数と一致すれば正常です。
StatefulSet: アイデンティティとストレージを持つ Pod #
StatefulSet は 各 Pod に安定した固有のアイデンティティ を付与するコントローラーです。Deployment がランダムなハッシュ名を付けるのとは違い、StatefulSet は Pod に web-0、web-1、web-2 のような 順序のある固定名 を与えます。
StatefulSet が保証する 3 つのこと #
- 安定したネットワーク ID。各 Pod は
web-0のような固定名と、web-0.web.default.svc.cluster.localの形の固定 DNS 名を持ちます。Pod が再起動しても名前と DNS はそのまま維持されます。 - 順序保証された生成と削除。Pod は
web-0→web-1→web-2の順に順番に生成され、削除とスケールダウンは逆順で進みます。前の Pod が Running になってから次の Pod が立ちます。 - 安定したストレージ。各 Pod は
volumeClaimTemplatesで作られた自分専用の PVC を持ちます。Pod が再スケジュールされても同じ PVC に再接続され、データが保存されます。
headless Service が必要な理由 #
StatefulSet は headless Service を一緒に定義しなければなりません。headless Service は clusterIP: None で指定した Service で、クラスター IP を割り当てずに 各 Pod の個別の DNS レコード を作ってくれます。この Service の名前を StatefulSet の serviceName に書くと、web-0.web、web-1.web のように Pod 一つひとつに直接アクセスできます。データベースクラスターで特定のノード (例: プライマリ) に直接接続しなければならないとき、この固定アドレスが必須です。
volumeClaimTemplates で Pod ごとの PVC #
volumeClaimTemplates は、StatefulSet が Pod ごとに PVC を自動で打ち出す型です。replicas が 3 なら data-web-0、data-web-1、data-web-2 という PVC がそれぞれ生成されます。注意すべき点は、StatefulSet を削除してもこの PVC は自動では消されない ということです。データ保存が目的なので意図された動作であり、整理するには PVC を直接削除しなければなりません。
いつ使うか #
StatefulSet は、各インスタンスが固有のアイデンティティとデータを持つワークロードに使います。
- データベース。PostgreSQL、MySQL のレプリケーション構成のように、ノードごとにデータが異なる場合
- 分散システム。Kafka、ZooKeeper、Elasticsearch のように、メンバー間の役割と順序が重要なクラスター
StatefulSet YAML の例 (headless Service + volumeClaimTemplates) #
StatefulSet も generator がないので直接書きます。headless Service と StatefulSet を 1 つのファイルに一緒に置くと管理が楽です。
apiVersion: v1
kind: Service
metadata:
name: web # serviceName と一致させること
namespace: default
spec:
clusterIP: None # headless: クラスター IP を割り当てない
selector:
app: web
ports:
- port: 80
name: http
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
namespace: default
spec:
serviceName: web # 上の headless Service の名前
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
name: http
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi作成後、Pod 名と PVC が順番に作られたか確認します。
k apply -f sts.yaml
k get statefulset web
k get pods -l app=web # web-0, web-1, web-2 の順に生成
k get pvc # data-web-0, data-web-1, data-web-2スケール調整は Deployment と同じコマンドで行いますが、Pod は逆順で整理されます。
k scale statefulset web --replicas=5 # web-3, web-4 を追加
k scale statefulset web --replicas=2 # web-4, web-3, web-2 を逆順で削除Deployment vs StatefulSet vs DaemonSet の比較 #
3 つのコントローラーの違いを 1 つの表に整理すると次のとおりです。
| 項目 | Deployment | StatefulSet | DaemonSet |
|---|---|---|---|
| Pod のアイデンティティ | ランダムなハッシュ名 | 順序のある固定名 (web-0,1,2) | ノードごとに 1 つ |
| Pod 数 | replicas で指定 | replicas で指定 | ノード数と同じ |
| 生成・削除の順序 | 順序保証なし | 順序保証 (生成は正順、削除は逆順) | ノードの追加・削除に連動 |
| 専用ストレージ | なし (共有または無状態) | Pod ごとの PVC (volumeClaimTemplates) | 通常はノードの hostPath |
| headless Service | 不要 | 必須 (serviceName) | 不要 |
| 代表的な用途 | stateless な Web・API | DB・分散システム | ログ・監視・CNI |
核心となる判断基準はシンプルです。相互に交換可能なら Deployment、固有のアイデンティティと専用データが必要なら StatefulSet、ノードごとに 1 つ立てなければならないなら DaemonSet です。
試験ポイント #
CKAD でこの 2 つのコントローラーが出るときに、よく引っかかる部分を整理します。
- generator がない。DaemonSet と StatefulSet は
kubectl createで骨組みを作れません。Deployment の骨組みをk create deploy ... $do > x.yamlで抜き出したあとkindを変え、不要なフィールドを手直しする方法が最も速いです。 - DaemonSet に変えるときに消すもの。Deployment の骨組みから
replicasとstrategy、statusの行を削除してこそ有効な DaemonSet になります。 - StatefulSet の serviceName の漏れ。
serviceNameは必須フィールドであり、指す headless Service (clusterIP: None) が実際に存在してこそ Pod の DNS が動作します。どちらか一方でも欠けると減点です。 - volumeClaimTemplates と volumeMounts の name の一致。
volumeClaimTemplatesのmetadata.nameとコンテナのvolumeMountsのnameが同じでこそ PVC がマウントされます。 - PVC は残る。StatefulSet を消しても PVC は自動削除されません。問題で整理まで要求するなら PVC を別途削除します。
- DaemonSet の Pod 数の確認。正答の検証は
k get pods -o wideで、ノードごとに 1 つずつ立っているかを見ます。control plane ノードにも立てなければならないなら、tolerations を漏らさないようにします。
まとめ #
この記事で押さえたものは次のとおりです。
- DaemonSet はすべての (または一部の) ノードに Pod を 1 つずつ 配置するコントローラー。ログ収集器・ノード監視・CNI に使い、
nodeSelectorとtolerationsで対象ノードを限定します。 - StatefulSet は安定したネットワーク ID、順序保証された生成・削除、専用ストレージ を提供します。DB・分散システムに使い、headless Service と
volumeClaimTemplatesが核心です。 - headless Service は
clusterIP: Noneで各 Pod の個別 DNS を作り、web-0.webのような固定アドレスを提供します。 - 3 つのコントローラーの選択基準。相互交換可能なら Deployment、固有のアイデンティティ・データなら StatefulSet、ノードごとに 1 つなら DaemonSet です。
- 試験の注意。generator なし、serviceName 必須、volume name の一致、PVC が残存。
ワークロードコントローラーの大きな絵がもっと必要なら、Kubernetes 中級トラック で同じリソースを運用の観点からさらに深く扱います。
次: Workloads 3 #
DaemonSet と StatefulSet まで習得し、常時実行されるワークロードは整理できました。残っているのは 一度または周期的に実行されて終わる作業 です。
#7 Workloads 3: Job、CronJob (バックオフ、並行性) では、バッチ作業を担う Job と CronJob を扱います。completions と parallelism で並列実行を制御する方法、backoffLimit と activeDeadlineSeconds で失敗とタイムアウトを扱う方法、CronJob の concurrencyPolicy とスケジュール表記まで YAML で作ってみながら整理します。