K8s · 網路與服務 · 第 20 課 · · 9min read

DaemonSet 與 CronJob:每台 Node 都要跑一份 vs 定時任務

DaemonSet = 每個 Node 都要保證跑一份(適合監控、日誌收集);CronJob = K8s 版的 crontab(定時備份、清檔)。這篇實作兩個常見場景。

#Kubernetes #DaemonSet #CronJob #crontab
章節目錄 · 8

Deployment 解不了的兩個問題

到目前為止學的 Deployment、Service、DNS 都假設一件事:「我要 N 個 Pod」

但工作上有兩個情境,Deployment 用起來很彆扭:

  • 每台 Node 都要跑一份(日誌收集 agent、監控 agent)

  • 定時跑一次就結束(每天備份資料庫、每 5 分鐘清暫存)
  • 第一個情境用 replicas 死命湊數量,Scheduler 還可能把兩個 Pod 放在同一台 Node。
    第二個情境用 Deployment 根本不對 — Deployment 的 Pod 跑完會被自動拉起,陷入無限重啟

    K8s 各自有專門的 workload 對應這兩個情境:DaemonSetCronJob

    DaemonSet:每台 Node 一份

    「Daemon」就是 Linux 的守護程序。DaemonSet 確保每個 Node 上剛好跑一個 Pod,不多不少

    ┌─ Node 1 ──┐  ┌─ Node 2 ──┐  ┌─ Node 3 ──┐
    │ log-agent │  │ log-agent │  │ log-agent │
    └──────────┘  └──────────┘  └──────────┘

    新 Node 加入叢集 → 自動建 Pod
    Node 移除 → Pod 跟著消失
    完全不用手動管理副本數

    典型應用:

    • 日誌收集:Fluentd / Filebeat 收集每台機器上所有容器的 stdout
    • 監控 agent:Prometheus Node Exporter 收 CPU / 記憶體
    • 網路 plugin:kube-proxy 本身就是 DaemonSet
    $ kubectl get daemonsets -n kube-system
    NAME DESIRED CURRENT READY
    kube-proxy 3 3 3 # 你的叢集 3 台 Node

    DESIRED = 你的 Node 數量。K8s 自動算的,你不能(也不該)改。

    DaemonSet YAML

    apiVersion: apps/v1
    kind: DaemonSet            # ← 不是 Deployment
    metadata:
      name: log-collector
    spec:
      # ⚠️ 沒有 replicas!由 Node 數量決定
      selector:
        matchLabels:
          app: log-collector
      template:
        metadata:
          labels:
            app: log-collector
        spec:
          containers:
            - name: collector
              image: busybox:1.36
              command: ["sh", "-c", "while true; do echo \"[$(date)] $(hostname)\"; sleep 30; done"]

    跟 Deployment 唯一的差別kind: DaemonSet + 沒有 replicas

    部署後驗證:

    $ kubectl apply -f daemonset.yaml
    $ kubectl get pods -o wide -l app=log-collector
    NAME                  STATUS    NODE
    log-collector-abc12   Running   k3d-cluster-server-0
    log-collector-def34   Running   k3d-cluster-agent-0
    log-collector-ghi56   Running   k3d-cluster-agent-1

    每個 Node 上剛好一個 Pod,沒例外。

    CronJob:K8s 版的 crontab

    如果你寫過 Linux crontab,CronJob 對你就是直覺的延伸。

    CronJob = Cron 排程 + Job

    Job 在 K8s 裡是「跑一次就結束」的 workload。CronJob 按時程定時建立 Job。

    CronJob YAML

    apiVersion: batch/v1            # ← 不是 apps/v1
    kind: CronJob
    metadata:
      name: hello-cron
    spec:
      schedule: "*/1 * * * *"       # 每分鐘
      jobTemplate:
        spec:
          template:
            spec:
              restartPolicy: Never  # ← 必填,不能是 Always
              containers:
                - name: hello
                  image: busybox:1.36
                  command: ["sh", "-c", "echo 'Hello!'; date"]

    幾個關鍵:

  • apiVersion: batch/v1不是 apps/v1,跟 Deployment 不同 group)

  • schedule 用 5 欄位 cron 語法:分 / 時 / 日 / 月 / 週

  • restartPolicy: NeverOnFailure不能 Always,否則跑完又重啟)

  • 嵌套很深:CronJob → jobTemplate → template → containers(多了一層 Job)
  • 常用 schedule

    語法意思
    */1 * * * *每分鐘
    */5 * * * *每 5 分鐘
    0 * * * *每小時整點
    0 3 * * *每天凌晨 3 點
    0 0 * * 0每週日午夜

    觀察 CronJob 運作

    $ kubectl apply -f cronjob.yaml
    $ kubectl get cronjobs
    NAME         SCHEDULE      LAST SCHEDULE
    hello-cron   */1 * * * *   <none>             # 還沒到時間
    

    # 等一分鐘
    $ kubectl get jobs
    NAME COMPLETIONS AGE
    hello-cron-28503210 1/1 45s # 跑出第一個 Job

    $ kubectl get pods
    NAME STATUS
    hello-cron-28503210-xkz9p Completed # ← 不是 Running!

    $ kubectl logs hello-cron-28503210-xkz9p
    Hello!
    Sun Apr 27 03:00:01 UTC 2026

    重點:CronJob 的 Pod 狀態是 Completed 不是 Running。新手常以為出錯,其實這就是正常的 — 任務跑完就該結束。

    K8s 預設保留最近 3 個成功 Job + 1 個失敗 Job。要調整用:

    spec:
      successfulJobsHistoryLimit: 3
      failedJobsHistoryLimit: 1

    三者比較

    項目DeploymentDaemonSetCronJob
    副本數你指定 replicas每 Node 一個(自動)每次觸發一個
    Pod 狀態長期 Running長期 Running跑完 Completed
    用途無狀態應用節點級服務定時任務
    replicas
    API groupapps/v1apps/v1batch/v1

    對照 Docker

    DockerKubernetes
    docker run 在每台機器手動跑DaemonSet 自動每台一份
    宿主機 crontab + docker execCronJob
    Compose restart: 'no' 一次性容器Job(CronJob 內層)
    DaemonSet 取代了「手動 SSH 上每台機器跑 docker」的痛苦。CronJob 把 cron 和容器合在一起,schedule、重試、歷史紀錄全內建。

    踩坑:Pod 是 Completed 不是 Running

    CronJob 最常被誤判:

    $ kubectl get pods
    NAME                        STATUS
    hello-cron-...              Completed   # ← 正常!
    hello-cron-...              Error       # ← 真的有問題

    Completed = 任務成功跑完,正確結果。
    Error / CrashLoopBackOff = 真的有問題,看 logs。

    重點整理

    • DaemonSet:每 Node 一個,沒有 replicas。日誌、監控 agent 用這個。
    • CronJob:定時跑一次,apiVersion: batch/v1
    • CronJob 的 Pod 跑完是 Completed 不是 Running
    • restartPolicy: Never 必填,不能 Always。
    • 三者不重疊:應用 / 節點 / 排程,各管各的。

    下一步

    到第五堂結尾,你已經能用 K8s 部署完整的微服務架構:Deployment + Service + DNS。

    從外面進來還只能用 NodePort Node IP:30080。生產環境要乾淨的網域路徑(/api / /admin),就要用 Ingress 把 Service 串起來