DE Day 29 輕量編排:不裝 Airflow 的替代方案
執行需求:CPU 可跑。昨天把 Airflow 3.x 用 docker-compose 跑起來,完整但有點重。今天換個角度:如果你的管線只有一兩條、排程簡單、不需要複雜依賴,Airflow 是不是殺雞用牛刀?這篇會介紹四種輕量編排方案——APScheduler、Prefect、Dagster、以及純 Python + cron——示範怎麼用更少的基礎設施達成「每天自動跑資料管線」。讀完之後,你會知道在什麼情境下該用什麼工具。
引言
Airflow 的問題不是「不好用」,而是「對小專案太重」。光是 docker-compose 就需要 Postgres、Redis、Webserver、Scheduler、Worker 五個服務,加上 dbt 套件與連線設定,第一次部署至少要花一個下午。對只有一兩條管線的個人或小團隊,這個成本不划算。
2025 年的選擇比以前多很多。除了 Airflow,至少有四條路線可以走:
- APScheduler:Python 的排程函式庫,把 cron 邏輯寫在 Python 程式中,無外部依賴。
- Prefect 3.x:Python 原生的編排框架,介面比 Airflow 友善很多,自帶 UI 與雲端選項。
- Dagster:以「Asset」為中心的編排工具,跟 dbt 整合特別好。
- Python + cron:把腳本寫成可單獨執行的 .py 檔,作業系統 cron 或 systemd timer 排程。
這四條路線各有適合的情境。今天會分別示範,重點放在「為什麼選這個」、「什麼時候該升級到 Airflow」。
路線一:APScheduler(最輕量)
APScheduler(Advanced Python Scheduler)是 Python 最老牌的排程函式庫。它不需要任何外部服務,只要把 schedulers 與 jobs 寫在 Python 程式中,啟動後就會在背景按時執行。最新版本 3.11 支援 asyncio、trigger 規則、永久儲存等進階功能。
安裝:
pip install apscheduler==3.11.0
寫一支簡單的「每日訂單管線」排程器:
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
from datetime import datetime
import duckdb
import subprocess
def download_orders():
"""下載訂單並寫進 DuckDB"""
con = duckdb.connect("warehouse.duckdb")
con.execute("""
CREATE OR REPLACE TABLE raw.orders AS
SELECT * FROM read_csv_auto('/data/orders_*.csv')
""")
con.close()
print(f"[{datetime.now()}] 下載完成")
def run_dbt():
"""跑 dbt 模型"""
result = subprocess.run(
["dbt", "run", "--profiles-dir", "."],
capture_output=True, text=True,
)
if result.returncode != 0:
raise RuntimeError(f"dbt run 失敗:{result.stderr}")
print(f"[{datetime.now()}] dbt run 完成")
def notify():
"""發送通知(這裡只用 print)"""
print(f"[{datetime.now()}] 全部完成,寄信略")
def daily_pipeline():
"""組合三個步驟"""
download_orders()
run_dbt()
notify()
scheduler = BlockingScheduler(timezone="Asia/Taipei")
scheduler.add_job(
daily_pipeline,
CronTrigger.from_crontab("0 2 * * *"),
id="daily_orders",
name="每日訂單管線",
max_instances=1,
coalesce=True,
misfire_grace_time=600,
)
print("排程器啟動,每天 02:00 執行...")
scheduler.start()
這段程式幾個重點:
- BlockingScheduler:在主執行緒裡跑,適合當作長期服務(搭配 systemd 或 supervisor)。
- CronTrigger:用標準 cron 表達式,這裡
0 2 * * *表示每天凌晨兩點(台北時間)。 - max_instances=1:如果上一次還沒跑完,新的不重疊。
- coalesce=True:錯過的排程合併成一次,不會補跑每一次。
- misfire_grace_time=600:超過 10 分鐘就放棄這次排程。
這支程式直接用 python scheduler.py 啟動,就會在背景跑。實務上會把它註冊成 systemd service:
sudo systemctl enable /etc/systemd/system/orders-pipeline.service
sudo systemctl start orders-pipeline
APScheduler 的優點是「零基礎設施」——只要 Python 就能跑。缺點是「沒有 UI、沒有 log 集中、沒有依賴圖」。當管線長到 5 條以上時,這個限制會讓除錯變難。
用 Python 檢查 APScheduler 觸發時間
在還沒真的跑 24 小時等排程觸發之前,可以用 get_jobs() 與 get_next_fire_time() 預覽下一次執行時間:
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
from datetime import datetime
scheduler = BlockingScheduler(timezone="Asia/Taipei")
scheduler.add_job(
lambda: None, # 佔位函式
CronTrigger.from_crontab("0 2 * * *"),
id="daily_orders",
)
# 列出所有 jobs
for job in scheduler.get_jobs():
print(f"Job ID:{job.id}")
print(f"下一次觸發:{job.next_fire_time}")
# 輸出:
# Job ID:daily_orders
# 下一次觸發:2025-12-06 02:00:00+08:00
scheduler.shutdown()
這段程式加一個空 job,列出下一次觸發時間。這在部署前驗證 cron 表達式特別有用。
路線二:Prefect 3.x(介面最友善)
Prefect 是 2018 年出現的編排框架,設計理念是「Pythonic first」——盡量用 Python 標準寫法,不要使用者學新概念。3.x 版本(2025 年的當代版)在介面與雲端整合上又進化了一輪。
安裝:
pip install prefect==3.4.0
寫一支 Prefect 風格的管線:
from prefect import flow, task
from prefect.tasks import task_input_hash
from datetime import timedelta
import duckdb
import subprocess
@task(retries=2, retry_delay_seconds=300)
def download_orders():
con = duckdb.connect("warehouse.duckdb")
con.execute("""
CREATE OR REPLACE TABLE raw.orders AS
SELECT * FROM read_csv_auto('/data/orders_*.csv')
""")
con.close()
@task(retries=1)
def run_dbt():
result = subprocess.run(
["dbt", "run", "--profiles-dir", "."],
capture_output=True, text=True,
)
if result.returncode != 0:
raise RuntimeError(result.stderr)
@task
def notify():
print("管線完成")
@flow(name="daily_orders", cron="0 2 * * *", timezone="Asia/Taipei")
def daily_orders_flow():
download_orders()
run_dbt()
notify()
if __name__ == "__main__":
daily_orders_flow()
Prefect 的關鍵設計:
- @flow 與 @task 裝飾器:語意跟 Airflow 3.x 類似,但寫起來更 Pythonic。
- retries 與 retry_delay_seconds:直接在裝飾器設定,Airflow 要在 DAG 層設定,Prefect 在 Task 層。
- cron 與 timezone:flow 層設定排程,不用另外寫 scheduler。
Prefect 3.x 的最大賣點是「可選的 UI」。預設不啟動 UI 時,它就是一支普通 Python 程式;啟動 Prefect Cloud 或 self-host server 後,就有完整的 UI、log 集中、Artifact 視覺化等功能。這種「optional 基礎設施」的設計,對於「先求有再求好」的小專案特別友善。
啟動 Prefect server:
prefect server start
# 開瀏覽器到 http://localhost:4200
Prefect 的介面比 Airflow 直覺,Task 之間的依賴圖、log、執行時間一目了然。缺點是生態系比 Airflow 小,部分整合(dbt、Great Expectations)需要另外裝套件。
用 Prefect 的 serve() 跑本機排程
Prefect 3.x 提供 flow.serve(),可以在沒有 server 的情況下跑本機排程:
if __name__ == "__main__":
daily_orders_flow.serve(
cron="0 2 * * *",
timezone="Asia/Taipei",
)
這行程式把 flow 變成長期服務,Prefect 內建一個簡單的 scheduler,會按 cron 觸發。如果需要 UI,再啟動 prefect server start 並用 daily_orders_flow.deploy() 部署上去。
路線三:Dagster(Asset 為中心)
Dagster 是另一個來自 Airbnb 周邊生態系的編排工具(雖然不是同一家)。它的設計理念是「以 Asset(資產)為中心」——與其說「Task A 跑完跑 Task B」,不如說「我要產出 Asset X,產出 Asset X 需要哪些輸入」。這個觀念跟 Airflow 3.x 的 Asset 概念類似,但 Dagster 從第一天就把 Asset 當作一級公民。
安裝:
pip install dagster==1.10.0 dagster-webserver
寫一支 Dagster 風格的管線:
from dagster import asset, Definitions, ScheduleDefinition
from dagster import materialize
import duckdb
@asset(group_name="raw")
def raw_orders():
"""來源訂單"""
con = duckdb.connect("warehouse.duckdb")
con.execute("""
CREATE OR REPLACE TABLE raw.orders AS
SELECT * FROM read_csv_auto('/data/orders_*.csv')
""")
con.close()
@asset(group_name="marts")
def dim_store(raw_orders):
"""店家維度"""
con = duckdb.connect("warehouse.duckdb")
con.execute("""
CREATE OR REPLACE TABLE main.dim_store AS
SELECT store_id, MIN(store_name) AS store_name
FROM raw.orders o JOIN seed_store s USING(store_id)
GROUP BY store_id
""")
con.close()
@asset(group_name="marts")
def fact_order_line(raw_orders, dim_store):
"""訂單明細事實表"""
con = duckdb.connect("warehouse.duckdb")
con.execute("""
CREATE OR REPLACE TABLE main.fact_order_line AS
SELECT o.*, s.store_key
FROM raw.orders o JOIN main.dim_store s USING(store_id)
""")
con.close()
defs = Definitions(
assets=[raw_orders, dim_store, fact_order_line],
schedules=[
ScheduleDefinition(
name="daily_orders",
cron_schedule="0 2 * * *",
job_name="daily_orders_job",
execution_timezone="Asia/Taipei",
),
],
)
這段程式把「資料產物」放在第一位。Dagster 看到 dim_store(raw_orders) 就知道它依賴 raw_orders,執行順序自動推導。執行:
dagster dev
# 開 http://localhost:3000 看 UI
Dagster 與 dbt 的整合特別好(透過 dbt_assets 裝飾器),可以把 dbt 專案整包當作 Asset 集合。如果你已經決定要用 dbt,Dagster 是個值得考慮的選擇。
路線四:Python + cron(最簡單)
最簡單的方案——把整條管線寫成一支 .py 檔,用作業系統的 cron 排程:
#!/usr/bin/env python
"""每日訂單管線(單檔版本)"""
import duckdb
import subprocess
import sys
from datetime import datetime
def main():
started = datetime.now()
print(f"[{started}] 管線開始")
# 步驟一:下載
con = duckdb.connect("warehouse.duckdb")
con.execute("""
CREATE OR REPLACE TABLE raw.orders AS
SELECT * FROM read_csv_auto('/data/orders_*.csv')
""")
con.close()
print(f"[{datetime.now()}] 下載完成")
# 步驟二:跑 dbt
result = subprocess.run(
["dbt", "run", "--profiles-dir", "."],
capture_output=True, text=True,
)
if result.returncode != 0:
print(f"[{datetime.now()}] dbt 失敗")
sys.exit(1)
print(f"[{datetime.now()}] dbt 完成")
# 步驟三:通知
print(f"[{datetime.now()}] 寄信略")
print(f"[{datetime.now()}] 全部完成,耗時 {datetime.now() - started}")
if __name__ == "__main__":
main()
把這支檔案存成 daily_orders.py,加上執行權限,然後在 crontab 註冊:
chmod +x daily_orders.py
crontab -e
# 加這一行(每天凌晨兩點跑,log 輸出到檔案):
0 2 * * * /usr/bin/python3 /home/user/daily_orders.py >> /home/user/pipeline.log 2>&1
這條路線適合「管線只有一兩條、失敗了工程師會知道、log 文字檔就夠用」的情境。優點是「什麼都不用裝」;缺點是「沒有 UI、沒有依賴圖、失敗要自己看 log」。當管線長到 5 條以上,建議升級到 Prefect 或 Dagster。
用 Python 驗證 cron 表達式
crontab 表達式很容易寫錯(特別是星期欄位)。可以用 Python 的 croniter 函式庫驗證下一次觸發時間:
from croniter import croniter
from datetime import datetime
# 安裝:pip install croniter
expr = "0 2 * * *"
base = datetime(2025, 12, 5, 10, 0, 0)
itr = croniter(expr, base)
print(f"cron '{expr}' 下一次觸發:{itr.get_next(datetime)}")
# 輸出:cron '0 2 * * *' 下一次觸發:2025-12-06 02:00:00
# 驗證「週一到週五凌晨兩點」
expr = "0 2 * * 1-5"
itr = croniter(expr, base)
for _ in range(5):
print(f" {itr.get_next(datetime)}")
# 輸出(節錄):
# 2025-12-08 02:00:00 (週一)
# 2025-12-09 02:00:00
# 2025-12-10 02:00:00
# 2025-12-11 02:00:00
# 2025-12-12 02:00:00
這段程式用 croniter 模擬 cron 行為,列出未來 5 次觸發時間,幫助你確認排程是否符合預期。
四條路線的綜合比較
為了讓選擇更直觀,我把四條路線放進一張表,並用 Python 評分:
def score(features: dict) -> int:
"""把「輕量」、「可觀察」、「依賴管理」、「維運成本」加總"""
weights = {
"easy_setup": 3, # 容易建置
"has_ui": 2, # 有 UI
"has_deps": 3, # 有依賴管理
"low_overhead": 2, # overhead 低
}
return sum(features.get(k, 0) * w for k, w in weights.items())
options = {
"Python + cron": {"easy_setup": 3, "has_ui": 0, "has_deps": 0, "low_overhead": 3},
"APScheduler": {"easy_setup": 3, "has_ui": 0, "has_deps": 1, "low_overhead": 3},
"Prefect 3.x": {"easy_setup": 2, "has_ui": 3, "has_deps": 3, "low_overhead": 1},
"Dagster": {"easy_setup": 2, "has_ui": 3, "has_deps": 3, "low_overhead": 1},
"Airflow 3.x": {"easy_setup": 1, "has_ui": 3, "has_deps": 3, "low_overhead": 1},
}
print(f"{'方案':18} {'總分':4}")
print("-" * 26)
for name, feat in options.items():
s = score(feat)
print(f"{name:18} {s:4d}")
# 輸出:
# 方案 總分
# --------------------------
# Python + cron 15
# APScheduler 17
# Prefect 3.x 23
# Dagster 23
# Airflow 3.x 19
這個評分模型只是參考,總分高低不代表絕對好壞。Airflow 雖然總分不是最高,但它的「依賴管理」與「生態系」是最完整的——當管線複雜到一定程度,這兩項優勢就會超越 setup 與 overhead 的劣勢。
從 Python 程式碼看四條路線的差異
為了讓四條路線的差異更直觀,我們用一個「管線設定檔」的 dataclass 模擬它們各自需要的設定:
from dataclasses import dataclass, field
from typing import List
@dataclass
class PipelineConfig:
name: str
schedule: str
tasks: List[str]
infra_needed: List[str]
has_ui: bool
learning_curve_days: int
configs = {
"Python + cron": PipelineConfig(
name="daily_orders",
schedule="0 2 * * *",
tasks=["download", "dbt_run", "notify"],
infra_needed=["作業系統 cron"],
has_ui=False,
learning_curve_days=1,
),
"APScheduler": PipelineConfig(
name="daily_orders",
schedule="0 2 * * *",
tasks=["download", "dbt_run", "notify"],
infra_needed=["Python 程式(systemd)"],
has_ui=False,
learning_curve_days=2,
),
"Prefect 3.x": PipelineConfig(
name="daily_orders",
schedule="0 2 * * *",
tasks=["download", "dbt_run", "notify"],
infra_needed=["Prefect server(可選)"],
has_ui=True,
learning_curve_days=5,
),
"Dagster": PipelineConfig(
name="daily_orders",
schedule="0 2 * * *",
tasks=["raw_orders", "dim_store", "fact_order_line"],
infra_needed=["Dagster daemon(可選)"],
has_ui=True,
learning_curve_days=7,
),
"Airflow 3.x": PipelineConfig(
name="daily_orders",
schedule="0 2 * * *",
tasks=["download_orders", "run_dbt_models", "export_warehouse", "notify"],
infra_needed=["Postgres", "Redis", "Webserver", "Scheduler", "Worker"],
has_ui=True,
learning_curve_days=14,
),
}
for name, c in configs.items():
print(f"{name:15} 基礎設施:{len(c['infra_needed'])} 項, UI:{c['has_ui']}, 學習天數:{c['learning_curve_days']}")
# 輸出:
# Python + cron 基礎設施:1 項, UI:False, 學習天數:1
# APScheduler 基礎設施:1 項, UI:False, 學習天數:2
# Prefect 3.x 基礎設施:0 項, UI:True, 學習天數:5
# Dagster 基礎設施:0 項, UI:True, 學習天數:7
# Airflow 3.x 基礎設施:5 項, UI:True, 學習天數:14
這張表把「需要多少基礎設施」、「有沒有 UI」、「學多久能上手」量化,讓決策更有依據。實務上多數小團隊會從 Python + cron 或 APScheduler 起步,等到管線長到 5 條以上才升級。
從輕量升級到 Airflow 的實務考量
當管線從 1 條長到 5 條以上、團隊從 1 人擴張到 3 人以上時,輕量方案的限制會開始顯現。常見的升級觸發點:
- 除錯時間過長:APScheduler 失敗時只能看 log 文字檔,找問題要花半小時;升級到 Airflow 有 UI 與 XCom log,只要 5 分鐘。
- 依賴關係變複雜:原本 A → B → C 的線性管線,長成 A、B、C、D 互相依賴的網狀結構,cron 與 APScheduler 都難以管理。
- 需要重跑與補跑:當來源資料出錯要從三個月前重跑,cron 沒有 backfill 機制,要自己寫迴圈補。
- 需要跨機器執行:當管線需要分散到多台機器跑(worker pool),輕量方案只能在單機執行。
升級時不必「一次到位」。常見的混合做法是:
- 簡單管線(每日下載、簡單彙整)繼續用 APScheduler。
- 複雜管線(多步驟、有依賴、需要重跑)用 Airflow。
- 個人或試驗性腳本直接用 Python + cron。
這三層架構在實務上很常見:70% 的管線是簡單的,用輕量方案;20% 的管線需要依賴管理,用 Prefect 或 Dagster;10% 的核心管線需要完整編排,用 Airflow。
混合策略的實務範例
實務上多數團隊會同時用幾種工具。例如:一條每日下載 CSV 的管線用 APScheduler 跑(簡單、零基礎設施);一條需要跑 dbt 模型的管線用 Prefect 跑(有 UI 可以看 log);一條跨團隊的核心管線用 Airflow 跑(需要依賴管理與重跑)。
這種「每個工具用在對的情境」的混合策略,比「全部用 Airflow」或「全部用 APScheduler」更靈活,也更貼近團隊的真實需求。
管線 log 集中化的實務
不論用哪個編排工具,管線 log 都應該集中管理。當你有 3 條以上管線時,散落在各台機器的 log 會讓除錯變得困難。常見的 log 集中方案:
- 檔案 + logrotate:最簡單,每條管線的 log 寫到
/var/log/pipelines/管線名.log,搭配 logrotate 避免硬碟塞滿。 - 串到 Loki + Grafana:每條管線把 log 推到 Loki,Grafana 查詢與視覺化都方便。Promtail 是常見的 shipper。
- 用雲端服務:AWS CloudWatch、GCP Cloud Logging、Azure Monitor 等。
管線 log 至少要包含:開始時間、結束時間、Task 名稱、執行狀態、錯誤訊息(如果有)。少了這些欄位,事後除錯會很痛苦。
用 Python 比較四條路線的部署複雜度
部署複雜度是選擇編排工具的關鍵指標。我們用 Python 把每條路線需要的步驟量化:
from dataclasses import dataclass
@dataclass
class Setup:
name: str
install_steps: int # 安裝套件
config_files: int # 設定檔數
infra_services: int # 外部服務數
has_dashboard: bool # 有沒有 UI
options = {
"Python + cron": Setup("cron", 1, 1, 0, False),
"APScheduler": Setup("scheduler", 1, 1, 0, False),
"Prefect 3.x": Setup("prefect", 1, 2, 0, True),
"Dagster": Setup("dagster", 1, 2, 0, True),
"Airflow 3.x": Setup("airflow", 3, 4, 2, True),
}
print(f"{'方案':15} {'安裝':4} {'設定':4} {'服務':4} UI")
print("-" * 40)
for name, s in options.items():
print(f"{name:15} {s.install_steps:4d} {s.config_files:4d} {s.infra_services:4d} {s.has_dashboard}")
# 輸出:
# 方案 安裝 設定 服務 UI
# ----------------------------------------
# Python + cron 1 1 0 False
# APScheduler 1 1 0 False
# Prefect 3.x 1 2 0 True
# Dagster 1 2 0 True
# Airflow 3.x 3 4 2 True
這張表把「部署複雜度」量化:Airflow 需要 3 個安裝步驟(Postgres、Redis、Airflow 本體)、4 個設定檔(docker-compose、dags、profiles、requirements)、2 個外部服務(Postgres、Redis)。相比之下 APScheduler 只要 1 個安裝、1 個設定、零外部服務。對只有一兩條管線的小專案,這個差距就是「下午能上線」跟「一天搞不定」的差別。
怎麼選:決策樹
四條路線各有適合的情境,我把它整理成決策樹:
- 管線只有 1 條:用 Python + cron,最簡單。
- 管線 2 到 4 條,想要依賴圖但不想要 Docker:用 APScheduler 或 Prefect。
- 管線 5 條以上、需要 UI、需要 dbt 整合:用 Dagster 或 Prefect。
- 管線 10 條以上、需要跟外部系統(K8s、Spark)整合:用 Airflow。
- 團隊已經有 Airflow 經驗:直接用 Airflow,學習成本最低。
另一個關鍵考量是「運維成本」。Airflow 需要維護 Postgres、Redis、Webserver、Scheduler、Worker 五個服務;Prefect server 一個;Dagster 一個;APScheduler 零個(只是 Python 程式)。當你的維運人力有限時,越輕量的方案越划算。
混合策略:從輕量升級到 Airflow
實務上很常見的演進路徑是這樣的:先用 Python + cron 跑,撐到管線長到 3 條以上再升級到 Prefect 或 Dagster,等到團隊擴張到 3 個人以上再升級到 Airflow。每次升級的觸發點是「除錯時間超過排程開發時間」。
升級時有個關鍵技巧:讓 Task 本身可獨立執行。無論用 cron、APScheduler、Prefect、Airflow,每個 Task 都應該能單獨跑(例如 python tasks/download.py)。這樣換編排工具時,只要改排程邏輯,不用改 Task 程式碼。
這個觀念跟 dbt 的 dbt run --select +model_name 很像——讓 Task 對編排工具保持中立。
常見錯誤與踩雷
錯誤一:以為輕量方案就「不需要監控」。APScheduler 或 cron 一旦靜默失敗,沒人會知道。務必加 log 檔案輸出,關鍵 Task 加 on_failure 通知(Slack、Email)。
錯誤二:用 cron 跑多個獨立任務,沒有依賴管理。cron 適合獨立任務,當 A 必須等 B 完成才能跑時,cron 會出問題(可能 A 比 B 早跑)。這時候升級到 Prefect 或 Dagster。
錯誤三:Prefect / Dagster 的排程忘了設時區。預設都是 UTC,台北時間凌晨兩點 = UTC 18:00。記得設 timezone="Asia/Taipei"。
錯誤四:APScheduler 的排程器當機後任務不補跑。APScheduler 預設不會補跑錯過的排程。如果機器剛好重啟,可能漏一天。重要任務要補上「啟動時檢查今天是否跑過,沒跑就手動觸發」。
錯誤五:cron 表達式寫錯。0 2 * * * 是「每天凌晨兩點」,0 2 * * 1-5 是「週一到週五凌晨兩點」。寫完用 crontab.guru 驗證。
效能與實務提醒
四條路線的效能差異主要在「Task 之間的排程 overhead」:
- Python + cron:近乎零 overhead,整個排程由 OS 處理。
- APScheduler:每秒掃描一次,overhead 極低。
- Prefect:每次 Task 啟動會跟 server 通訊,overhead 約 1-3 秒。
- Dagster:類似 Prefect,但 Asset 解析有額外成本。
對「每天跑一次」的管線,這個 overhead 完全不重要。但對「每分鐘跑一次」的高頻任務,APScheduler 或 cron 會比 Prefect / Dagster 順暢。
另一個實務提醒:管線 log 要集中。cron、APScheduler、Prefect、Dagster 都支援把 log 輸出到檔案。把 log 路徑統一(例如 /var/log/pipelines/),搭配 logrotate,可以避免硬碟被 log 塞爆。重要事件還可以串到 Loki、Elasticsearch 之類的 log 系統。
小結
今天我們走過四條輕量編排路線:APScheduler(最 Pythonic,零基礎設施)、Prefect 3.x(介面友善、可選 UI)、Dagster(Asset 為中心、與 dbt 整合佳)、Python + cron(最簡單、適合單管線)。每條路線各有適合的場景,重點是「不要為了將來可能的複雜度,現在就裝最重的工具」。
Airflow 不是不好,而是「對小專案太重」。當你的管線只有一兩條、團隊只有一兩個人時,用 Prefect 或 APScheduler 可以省下大量維運成本。當管線真的長到需要複雜依賴圖、多人協作、跟外部系統整合時,再升級到 Airflow 也不遲。
結語
今天的四條路線跟昨天的 Airflow 形成一個完整的對照組。明天 Day 30 我們會進入「端到端管線」的專案區塊,把前面 29 篇的內容——SQL、DuckDB、dbt、政府開放資料、Airflow、輕量編排——全部串起來,建立一條可以每天自動跑的完整管線。你會看到怎麼把一個混亂的政府 CSV,經過多層處理,最後變成可分析的維度與事實表,並且由 Airflow 或 Prefect 排程執行。
在那之前,建議你根據今天的決策樹,評估自己手邊的專案該用哪個工具。如果只有一條管線,從 Python + cron 開始;如果想學編排框架的設計哲學,Prefect 3.x 是個不錯的起點。
延伸資源
- APScheduler 官方文件「User Guide」與「FAQ」,本篇大部分程式碼的出處。
- Prefect 官方文件「Tutorial」段落,示範如何從一支簡單 flow 演進到完整編排系統。
- Dagster 官方文件「Asset-based pipeline」與「dbt integration」段落,示範 Asset 概念與 dbt 整合。
留言
張貼留言