情境

Batch job 在系統設計中是一種很常見的一種解決方案,用來解決大量或者批次的工作。在 K8S 的環境中,我們會採用 CronJob 的物件型態宣告預期工作的執行方式。

系統設計

方法一

在這個故事中,原先是採用 light-weight cron job 的方式:也就是說,對於 cron job 來說,它本身只是輕量級的 trigger point,時間到了,它就透過一個 rest API call 的方式將此任務派送到遠方的 API server,由這 API server 接續處理大量的工作。這樣 trigger 型態的 cron job 是一種 fire and forget 的方式,不會等待遠方是否完成,或者遭遇異常錯誤。

方法二

當有新的人加入後,有不同的想法與提議。新的作法改變了原先 light-weight cron job 的設計理念,希望將 cron job 所要執行 heavy task 放置於一個專屬的 process,與原先的 API server 區隔開來,好處是可以避免影響線上 API server 的效能。

Screenshot 2023-05-25 at 9.17.12 PM.png

當初在程式架構有做適當的分層,並遵守 clean architecture 的一些規範,因此不管是採用方法一或者方法二對於程式碼的撰寫與測試都沒有太大的差別。方法一是在 cron job 端撰寫一個簡單的 rest API call,透過 HTTP request 向遠端 web server 請求執行一項服務。原先分層架構中,外層 (Input port) 是採用 controller 的方式呼叫內部核心的商業邏輯。在方法二中,直接在 script 裡面呼叫 controller 即可完成相同任務。這也顯示一個好的架構,不管是提供遠端的 HTTP request 或者是 job request,對於業務邏輯都不會產生任何影響,業務邏輯仍然保持在核心與圓的中心,不受外層呼叫端的影響。

比較

優點:

cron job 本身是 light weight

可以透過呼叫 API endpoint 執行 cron job 的任務

缺點:

無法得知執行結果

heavy cron job 跟一般的 http request 混雜,有可能因為 cron job 的執行影響 web server 的效能

實作細節

Cron job:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: my-heavy-job
spec:
  schedule: "30 18 * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      activeDeadlineSeconds: 60
      template:
        spec:
          containers:
            - name: my-heavy-job
              image: image.example.com/my-heavy-job
              resources:
                requests:
                  cpu: 1G
                  memory: 512Mi
                limits:
                  memory: 512Mi
              command:
                - sh
                - -c
                - npm run runMyJob