Contents

Docker排程

在 Docker 容器中執行排程任務(Cron Job)是常見的需求,但 Docker 容器的設計原則是「一個容器執行一個程序」,因此直接在容器內跑 cron 並不是最理想的做法。本文整理幾種常見方案,以及各自的優缺點。

方式一:在容器內安裝並執行 cron

最直接的方式是在 Dockerfile 中安裝 cron,並讓它作為主程序執行。

Dockerfile 範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
FROM debian:bullseye-slim

# 安裝 cron
RUN apt-get update && apt-get install -y cron && rm -rf /var/lib/apt/lists/*

# 複製 crontab 設定
COPY crontab /etc/cron.d/my-cron

# 設定正確的權限
RUN chmod 0644 /etc/cron.d/my-cron

# 套用 crontab
RUN crontab /etc/cron.d/my-cron

# 建立 log 檔案
RUN touch /var/log/cron.log

# 啟動 cron 並輸出 log
CMD cron && tail -f /var/log/cron.log

crontab 檔案內容

1
2
3
4
5
# 每天凌晨 2 點執行備份腳本
0 2 * * * root /scripts/backup.sh >> /var/log/cron.log 2>&1

# 每 5 分鐘執行一次
*/5 * * * * root /scripts/check.sh >> /var/log/cron.log 2>&1

注意事項

  • 環境變數問題:cron 執行時不會載入 Docker 的環境變數(-e 傳入的),需在腳本中明確讀取或用 /proc/1/environ 導入
  • 時區問題:容器預設時區為 UTC,可設定 TZ 環境變數或掛載 /etc/localtime
1
2
3
# docker-compose.yml 設定時區
environment:
  - TZ=Asia/Taipei

方式二:使用 docker-compose 與獨立 cron 容器

將排程邏輯分離到獨立容器,保持主應用容器乾淨。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# docker-compose.yml
version: '3.8'
services:
  app:
    image: my-app:latest
    ports:
      - "8080:80"

  cron:
    image: my-app:latest
    command: cron -f  # 前台執行 cron
    volumes:
      - ./scripts:/scripts
    environment:
      - TZ=Asia/Taipei
    depends_on:
      - app

方式三:使用 Ofelia 排程工具

Ofelia 是專為 Docker 設計的排程工具,可透過 docker-compose.yml 標籤設定,不需要修改現有容器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
version: '3.8'
services:
  ofelia:
    image: mcuadros/ofelia:latest
    depends_on:
      - app
    command: daemon --docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  app:
    image: my-app:latest
    labels:
      ofelia.enabled: "true"
      ofelia.job-exec.backup.schedule: "0 2 * * *"
      ofelia.job-exec.backup.command: "/scripts/backup.sh"

方式四:Kubernetes CronJob(生產環境推薦)

若使用 Kubernetes,可使用內建的 CronJob 資源,不需要在容器中跑 cron daemon:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
apiVersion: batch/v1
kind: CronJob
metadata:
  name: my-backup-job
spec:
  schedule: "0 2 * * *"  # 每天凌晨 2 點
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: my-app:latest
            command: ["/scripts/backup.sh"]
          restartPolicy: OnFailure

各方式比較

方式 適用情境 優點 缺點
容器內 cron 簡單專案、單機部署 簡單直接 環境變數問題、違反單一職責
獨立 cron 容器 Docker Compose 專案 責任分離 需要額外容器
Ofelia Docker 環境 設定簡潔 需要存取 Docker Socket
Kubernetes CronJob 生產環境、K8s 叢集 官方支援、可靠 需要 K8s 環境

參考資料