背景
Job 表示短週期的作業,定時 Job 表示按照預定的時間運行Job,或者按照某一頻率週期性的運行 Job。比如:
許多傳統企業使用 Linux 自帶的 crontab 來做定時任務的方案,該方案非常簡單,適合做主機上的運維工作,比如定時清理日誌、週期性做健康檢查。隨着信息化時代的高速發展,業務變得越來越複雜,很多場景都需要定時任務,但是 crontab 方案存在高可用問題,不適合應用在業務應用上。
在雲原生時代,K8s CronJob 設計了一套高可用的定時任務解決方案,保障了業務的穩定。但是把 K8s CronJob 應用在生產上,發現定時任務真的出問題的時候排查起來很麻煩,於是越來越多用戶對定時任務的可觀測有了更多的訴求,阿里雲也推出了自己的雲原生定時任務解決方案,可以託管原生 K8s CronJob,提供可報警、可觀測、可運維等能力,幫助企業提效。
Linux Crontab 方案面臨的問題
什麼是 Crontab
Crontab 是 Linux 系統中的一個服務,用於創建、編輯和管理定時任務。通過 crontab 命令,用戶可以設置系統在指定時間自動執行某個命令或腳本。
Crontab 命令的語法分爲兩部分,分別是時間表達式和命令。時間表達式如下:
# ┌───────────── 分鐘 (0 - 59)
# │ ┌───────────── 小時 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(週日到週一;在某些系統上,7 也是星期日)
# │ │ │ │ │ 或者是 sun,mon,tue,web,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
命令常用來執行某個腳本,舉個例子:
- 每隔 5 分鐘執行 hello.sh:*/5 * * * * sh /root/script/hello.sh
- 每天早上 6 點半執行 world.py: 30 6 * * * python /root/script/world.py
Crontab 的工作原理
Crontab 由一個名爲"Crond"的守護進程負責調度任務,當 Crond 啓動的時候,就會從配置文件(路徑在 /var/spool/cron 下)加載所有的定時任務。當執行 crontab 命令的時候,會動態的添加新的定時任務,並加入到配置文件中。Crontab 每次執行任務,都會產生執行記錄,目錄在 /var/log/cron 下。
Crontab 的痛點問題
使用 crontab 主要有如下痛點:
- 無高可用:爲了保證業務冪等執行,需要在不同的機器配置不同的 crontab 任務。crontab 只能調度本機器上的定時任務,如果某一個機器掛了,那上面的定時任務也都不會執行了,有穩定性風險。
- 無自動負載均衡:不同的腳本放在不同的機器上,需要手動負載均衡,如果腳本比較多,運維代價很高。
- 無權限隔離:一般企業生產的機器只有運維才能登陸,但是開發要新增/修改腳本和定時任務,也需要登錄到生產的機器上,沒法做到權限隔離。
雲原生 K8s CronJob 方案的優勢
什麼是 K8s CronJob
Job 是 K8s 中的一種資源,用來處理短週期的 Pod,相當於一次性任務,跑完就會把 Pod 銷燬,不會一直佔用資源,可以節省成本,提高資源利用率。CronJob 也是 K8s 中的資源,用來週期性的重複調度 Job。
下面是一個 CronJob 的示例,每隔 5 分鐘調度腳本 edas/schedulerx-job.sh:
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "/root/script/edas/schedulerx-job.sh"]
restartPolicy: OnFailure
K8s CronJob 的優勢
與單純使用 Crontab 相比,使用 K8s CronJob 帶來了如下優勢:
- 高可用:K8s 會保證集羣的高可用,如集羣中有節點掛了,都不會影響定時任務的調度。
- 自動負載均衡:Pod 默認選擇負載最低的 node 執行,支持 NodeSelector 和親和性等多種負載均衡策略。
- 權限隔離:只有運維可以登錄 master 和 worker 節點,開發通過管控或者 ApiServer 來創建和更新 CronJob,並且支持命名空間隔離,RBAC 權限管理。
K8s CronJob 的進階能力
Linux Crontab 只能週期性調度本機的腳本,功能比較簡單,K8s 定時任務支持更多的進階能力:
- 在 Job 資源上
- 並行執行:通常一個 Job 只啓動一個 Pod,可以通過配置 spec.completions 參數,來決定一個 Job 要執行多少個 Pod。
- 索引任務:並行執行通常需要和索引任務結合使用,當配置 .spec.completionMode="Indexed" 時,這個 Job 就是一個索引任務,每個 Pod 會獲得一個不同的索引值,介於 0 和 .spec.completions-1 之間,這樣就可以讓不同的 Pod 根據索引值處理不同的數據。
- 並行限流:並行執行的時候,通常還需要做限流,可以配置 .spec.parallelism 參數,來控制一個 Job 最多同時跑多少個 Pod。
- 失敗自動重試:可以配置 .spec.backoffLimit,來設置 Job 失敗重試次數。
- 超時:可以配置 .spec.activeDeadlineSeconds,來設置 Job 超時的時間。
- 在 CronJob 資源上
- 時區:可以通過設置 .spec.timeZone 參數,決定 CronJob 按照哪個時區的時間來調度任務。
- 併發性規則:當一個 Job 還在執行,下次調度時間到了,是否執行新的 Job,可以通過 .spec.concurrencyPolicy 來配置,取值爲 Allow/Forbid/Replace。
- 任務歷史限制:可以通過配置 .spec.successfulJobsHistoryLimit 和 .spec.failedJobsHistoryLimit 來決定保留多少成功和失敗的 Job。
阿里雲 K8s CronJob 提效新模式
阿里雲分佈式任務調度 SchedulerX 和雲原生結合,推出可視化 K8s Job 解決方案。針對腳本使用者,屏蔽了容器服務的細節,不用構建鏡像就可以讓不熟悉容器的同學(比如運維和運營同學)玩轉 K8s Job,受益容器服務帶來的降本增效福利。針對容器使用者,SchedulerX 不但完全兼容原生的 K8s Job,還能支持歷史執行記錄、日誌服務、重跑任務、報警監控、可視化任務編排等能力,爲企業級應用保駕護航。
快速遷移 Crontab 腳本任務
通過上面的章節,我們知道 Linux Crontab 存在許多問題,遷移到 K8s CronJob 可以帶來很多好處,但是要從 crontab 遷移到 K8s CronJob 還是挺麻煩的,這裏以通過 python 腳本訪問數據庫爲例,來對比兩種方案的差異。
K8s 原生解決方案
1. 將 crontab 腳本拷貝到本地,取名爲 edas/schedulerx-job.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import MySQLdb
# 打開數據庫連接
db = MySQLdb.connect("localhost", "testuser", "test123", "TESTDB", charset='utf8' )
# 使用cursor()方法獲取操作遊標
cursor = db.cursor()
# SQL 查詢語句
sql = "SELECT * FROM EMPLOYEE \
WHERE INCOME > %s" % (1000)
try:
# 執行SQL語句
cursor.execute(sql)
# 獲取所有記錄列表
results = cursor.fetchall()
for row in results:
fname = row[0]
lname = row[1]
age = row[2]
sex = row[3]
income = row[4]
# 打印結果
print "fname=%s,lname=%s,age=%s,sex=%s,income=%s" % \
(fname, lname, age, sex, income )
except:
print "Error: unable to fetch data"
# 關閉數據庫連接
db.close()
2. 在本地編寫 Dockerfile
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY edas/schedulerx-job.py /root/edas/schedulerx-job.py
CMD [ "python", "/root/edas/schedulerx-job.py" ]
3. 製作 docker 鏡像,推到鏡像倉庫中
docker build -t registry.cn-beijing.aliyuncs.com/demo/edas/schedulerx-job:1.0.0 .
docker push registry.cn-beijing.aliyuncs.com/demo/edas/schedulerx-job:1.0.0
4. 編寫 K8s CronJob 的 YAML 文件,image 選擇第 3 步製作的鏡像,command 的命令爲執行腳本
apiVersion: batch/v1
kind: CronJob
metadata:
name: demo-python
spec:
schedule: "*/5 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: demo-python
image: registry.cn-beijing.aliyuncs.com/demo/edas/schedulerx-job:1.0.0
imagePullPolicy: IfNotPresent
command: ["python", "/root/edas/schedulerx-job.py"]
restartPolicy: OnFailure
我們看到把一個 contab 遷移到 K8s CronJob,就需要這麼多步驟,如果之後要修改腳本,還需要重新構建鏡像和重新發布 K8s CronJob,這裏先不計算開始之前的學習成本,單純從使用角度來看,有着較高的上手成本。
阿里雲解決方案
阿里任務調度 SchedulerX 結合雲原生技術,提出了一套可視化的腳本任務解決方案,通過任務調度系統來管理腳本,直接在線編寫腳本,不需要構建鏡像,就可以將腳本以 Pod 的方式在用戶的 K8s 集羣當中運行起來,使用非常方便,如下圖:
1. 在你的 K8s 集羣中部署一個 schedulerx-deployment(只需要裝一次),註冊到 SchedulerX 上來,讓 SchedulerX 可以調度你的 K8s 上的 Pod
2. 在 SchedulerX 任務管理新建一個 K8s 任務,資源類型選擇 Python-Script(當前支持 shell/python/php/nodejs 四種腳本類型),把腳本拷貝進去,然後配置定時表達式
這裏的鏡像只需要構建一個基礎鏡像即可,如果腳本內容有修改,只要依賴的庫沒有改變,就不需要重新構建鏡像。
3. 等調度時間到了,或者通過控制檯手動運行一次,可以在 K8s 集羣中看到以 Pod 的方式運行腳本,Pod 名稱爲 schedulerx-python-{JobId}
下面通過一個表格更方便的看到兩個方案的差異:
K8s原生解決方案 | 阿里雲解決方案 | |
腳本管理 | 不支持 | 支持,通過SchedulerX控制檯可以進行腳本管理 |
開發效率 | 慢,每次修改腳本都需要重新構建鏡像 | 快,在線修改腳本,不需要構建鏡像,自動部署 |
學習成本 | 高,需要學習Docker和K8s等容器相關知識 | 低,不需要容器相關知識,會寫腳本就行 |
增強原生 K8s CronJob
SchedulerX 不但能夠快速開發 K8s 腳本任務,屏蔽容器服務的細節,給不熟悉容器服務的同學帶來福音,同時還能託管原生 K8s Job/CronJob,增強可運維可觀測等能力。
K8s 原生解決方案
以官方提供的 CronJob 爲例。
1. 編寫 hello.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "* * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: perl:5.34
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(100)"]
restartPolicy: OnFailure
2. 在 K8s 集羣中運行該 CronJob,查看 pod 歷史記錄和日誌
發現原生的 CronJob 只能查看最近 3 條執行記錄和日誌,想要查看更久之前的記錄無法看到,這在業務出現問題想排查的時候就變得尤爲困難。雖然可以通過配置 .spec.successfulJobsHistoryLimit 和 .spec.failedJobsHistoryLimit 來保留更多的 Pod 歷史記錄,但是保留更多的 Pod,就會更加佔用 K8s 集羣的資源,因爲 Job 已經跑完了,只是爲了查看日誌保留更多歷史記錄,成本太高了。
阿里雲解決方案
阿里任務調度 SchedulerX 可以託管原生 K8s Job/CronJob,方便移植,使用 SchedulerX 託管,可以具有更強的可運維可觀測能力,比如任務重跑、日誌服務、報警監控等。
1. 新建 K8s 任務,任務類型選擇 K8s,資源類型選擇 Job-YAML,打印 bpi(-1)
2. 通過工具來生成 cron 表達式,比如每小時第 8 分鐘跑
3. 調度時間還沒到,也可以手動點擊“運行一次”來進行測試
4. 在 K8s 集羣中可以看到 Job 和 Pod 啓動成功,每個任務只會保留最近一次調度的 Pod,減少 K8s 集羣的資源佔用
5. 在 SchedulerX 控制檯也可以看到歷史執行記錄,發現運行失敗
6. 在 SchedulerX 控制檯可以看到任務運行日誌,查看失敗原因
7. 在線修改任務的 YAML,打印 bpi(100)
8. 不需要刪除 Job,通過控制檯來重跑任務
9. 任務重跑成功,且能看到新的日誌
下面通過一個表格來對比兩個方案的差異:
K8s原生解決方案 | 阿里雲解決方案 | |
手動運行一次 | 不支持 | 支持 |
手動重跑任務 | 不支持 | 支持 |
Cron定時調度 | 支持,YAML配置 | 支持,兼容開源CronJob的YAML,也支持通過控制檯動態配置 |
K8s資源佔用 | 高,保留最近3次Pod | 低,僅保留最近1次Pod |
歷史記錄 | 最近3次 | 最近300次 |
日誌 | 最近3次 | 最近2周,支持搜索 |
報警 | 不支持 | 支持,企業級報警通知服務 |
操作記錄 | 不支持 | 支持 |
總結
在雲原生時代,使用 K8s CronJob 在很多場景下可以作爲 Linux Crontab 替換的解決方案,解決了crontab的一系列痛點問題。通過阿里雲 SchedulerX 來調度你的 K8s CronJob,能夠降低學習成本,加快開發效率,讓你的任務失敗可報警,出問題可排查,打造雲原生可觀測體系下的定時任務。
作者: 黃曉萌(學仁)
本文爲阿里雲原創內容,未經允許不得轉載。