GitHub Actions 排程還是主機 cron:我們的資料庫備份靜靜斷了三天之後

·約 8 分鐘閱讀·拓 ERP 團隊
實戰日記維運備份

2026 年 8 月 20 日,我們的 GitHub 組織因為 Actions 用量超額、扣款失敗,整個組織的 Actions 被停掉。每天凌晨跑資料庫邏輯備份的排程也在裡面。三個正式資料庫的最後一份備份停在台灣時間 8 月 20 日凌晨 3 點 02 分,之後三天沒有任何一則告警。這篇是那次之後我們怎麼把備份搬回主機 cron、搬的時候又踩了哪些坑,最後附一張可以直接拿去問你的系統商的檢查表。

先講結論

  • GitHub Actions 的排程適合跑測試與部署,不適合當備份的唯一排程。它停擺的原因可能跟你的系統完全無關,例如帳單。
  • GitHub 官方文件寫明兩件事:帳上沒有有效付款方式時,免費額度用完就會擋下用量;負載高時排程可能延遲,排隊中的 job 甚至可能被丟掉。
  • 搬回主機 cron 之後最常見的坑是手動跑得過、半夜跑不過:cron 的 PATH 與 HOME 跟你登入時不一樣。
  • 備份腳本要把「看起來成功其實失敗」的情況全部變成失敗:檔案驗證、大小下限、異地上傳失敗都要讓腳本以非零結束。
  • 我們目前還缺一塊:失敗會留在 log,但沒有主動通知。這件事寫在文末,沒做完的不假裝做完。

一、那三天發生了什麼

拓 ERP 的正式站與兩個客戶站跑在同一台 EC2 上,資料庫放在 AWS RDS。RDS 本身有執行個體層級的自動快照,我們另外每天做一份 pg_dump 邏輯備份。兩者用途不同:快照只能整台還原回 RDS;邏輯備份可以單獨還原一個資料庫、搬到任何一台 PostgreSQL,也能離線打開來檢查內容。

那份邏輯備份原本由 GitHub Actions 的 schedule 觸發,每天凌晨 SSH 進主機跑 dump。組織是 GitHub Free 方案,私有 repo 每月有 2,000 分鐘的免費額度。8 月 20 日額度用完,扣款沒有成功,整個組織的 Actions 停了兩天。

停的不只是 CI。備份那支 workflow 根本沒有啟動,所以也不會失敗,自然沒有失敗通知。我們在 8 月 22 日才發現最後一份邏輯備份停在 8 月 19 日 19:02 UTC,換算台灣時間是 20 日凌晨 3 點 02 分。三個正式庫都是這樣。

那三天實際上沒有資料遺失,RDS 的自動快照一直在跑。但如果那三天裡主機或 RDS 出了需要邏輯備份才救得回來的事,我們手上最新的可攜備份就是三天前的。

二、為什麼備份不該只掛在 Actions 上

回頭看,問題在於我們把一件「每天一定要發生」的事,交給一個會因為跟它無關的原因停擺的系統。GitHub 的 Actions 計費文件寫得很清楚:「If your account does not have a valid payment method on file, usage is blocked once you use up your quota.」用量被擋的時候,被擋的是整個帳號或組織,不分哪支 workflow 比較重要。

另一個比較少人注意的限制寫在觸發事件文件的 schedule 那一節:負載高時排程可能延遲,「If the load is sufficiently high enough, some queued jobs may be dropped.」也就是說,就算帳單正常,排程也不保證每一次都會執行。公開 repo 還多一條:60 天沒有活動,排程會被自動停用。

這三種狀況有個共通點:workflow 沒有跑,所以不會有任何失敗紀錄。大部分人設的告警是「失敗時通知」,對「沒有發生」這件事完全看不見。

三、GitHub Actions 排程與主機 cron 差在哪

兩邊都能做到每天定時跑一支腳本,差異在它們會因為什麼原因停下來,以及停下來時你看不看得到:

比較項目GitHub Actions schedule主機上的 cron
會因為帳單停擺會,額度用完又無有效付款方式時整個組織被擋只有主機本身停機時
準時程度官方註明高負載時可能延遲或丟掉排隊中的 job跟著主機時鐘,準時
沒跑的時候沒有任何紀錄,也不會觸發失敗通知同樣看不見,除非另外監控
執行環境每次乾淨的 runner,要另外 SSH 或用憑證連進正式環境就在正式環境裡,但 PATH、HOME 跟登入時不同
佔用的資源計入 Actions 分鐘數(私有 repo)佔主機的 CPU 與磁碟
適合拿來做測試、建置、部署、手動補跑入口每天一定要發生的維運工作

表上「沒跑的時候」那一列兩邊都是看不見,這點搬家不會解決。搬到 cron 解決的是「停擺的原因跟系統無關」,主動監控得另外做,第七節會再講。

四、搬回 cron:兩個半夜才會出現的坑

8 月 22 日我們把原本寫在 workflow 裡的腳本抽成獨立檔案 scripts/backup-db.sh,裝到主機的 /etc/cron.d/,每天 18:30 UTC(台灣凌晨 2 點 30 分)執行。時間是刻意挑的,避開同一台機器上其他系統的備份與磁碟檢查排程,不讓兩支吃 I/O 的工作撞在一起。

隔天補上 S3 異地副本時,撞到兩個典型的 cron 問題,兩個都是手動執行完全正常、交給 cron 才會壞:

  1. PATH 找不到指令。主機上的 AWS CLI 是用 snap 裝的,位置在 /snap/bin。cron 的預設 PATH 沒有這個目錄,所以半夜執行會得到 command not found。解法是在 cron 檔裡把 PATH 寫完整,腳本裡也用找不到就退回絕對路徑的寫法。
  2. HOME 不對,憑證讀不到。AWS 憑證放在 /home/ubuntu/.aws,cron 的 HOME 不一定指向那裡。指令找得到,但會以沒有憑證的身分去上傳,然後失敗。cron 檔裡要明寫 HOME=/home/ubuntu。

驗證方式是用 env -i 清空環境變數再執行一次,模擬 cron 那種什麼都沒有的環境。在登入的 shell 裡直接跑,等於沒測。

原本那支 GitHub workflow 沒有刪,改成只能手動觸發,呼叫的是主機上同一支腳本。這樣還原前想先留一份當下狀態時有入口可按,而且邏輯只有一份,不會變成兩支腳本各改各的。

五、備份腳本該把哪些「成功」當成失敗

這次真正學到的是:備份的風險大多不是「失敗了」,是「看起來成功了」。所以腳本的設計方向是把每一種假成功都轉成非零離開碼:

保留策略分兩層:主機上每個資料庫留最近 14 份,給快速還原用;S3 上每個資料庫一個獨立路徑,30 天後轉到低頻存取、180 天後刪除,bucket 關閉所有公開存取並開啟伺服器端加密。上傳失敗的路徑也測過:故意指向一個不存在的 bucket,確認腳本真的回非零。

六、順便把 Actions 分鐘數砍到原本的三分之一

備份搬走只處理了一半,另一半是不要再超額。9 月底我們重新看了每一支 workflow 花的分鐘數,做了三件事:

結果是一張 PR 的檢查從約 12 分鐘降到約 4 分鐘。直接推到主要分支的驗證,改在本機的 git hook 跑完整版,不佔 runner。

七、還沒做完的事

現在備份失敗時,腳本會以非零結束、錯誤寫進 /var/log/tuoerp-backup.log。但 cron 不會因為非零就通知任何人,所以「失敗了沒人知道」在主機端仍然可能發生,只是機率比之前低。

我們打算補的是反過來的監控:不是「失敗時通知」,而是「超過 26 小時沒有看到新的成功備份就通知」。這種寫法同時抓得到腳本失敗、cron 沒跑、主機停機三種情況,也正好是 8 月那次缺的那一塊。做完會在這篇補上日期。

八、問你的系統商:備份五問

不管你用的是哪一套 ERP,這五題都可以直接拿去問。答不出來,或答案是「應該有」,就值得追問:

  1. 備份是由什麼排程觸發的?它會不會因為跟你的系統無關的原因停擺,例如 CI 平台的帳單或額度?
  2. 最近一份成功的備份是什麼時候?請對方當場查出日期與時間。「每天都有」不算答案。
  3. 有沒有可攜的邏輯備份?雲端主機的快照只能還原回同一家雲端;能匯出到別台資料庫的格式才是換廠商時帶得走的那份。
  4. 異地副本放在哪?跟正式主機在同一台、同一個帳號,還是不同地方?
  5. 多久沒有新備份會有人被通知?這題問的是「該發生卻沒發生」時有沒有人知道。

第五題我們自己目前也只答得出一半。把它列在這裡,是因為 8 月那三天讓我們知道,這題答不出來的時候,通常不會有人發現。

常見問題

GitHub Actions 的排程可以拿來做資料庫備份嗎?

可以跑,但不建議當唯一的排程。GitHub 官方文件寫明,帳上沒有有效付款方式時免費額度用完就會擋下用量,負載高時排程也可能延遲或丟掉排隊中的 job。這些情況下 workflow 根本沒啟動,不會有失敗通知。比較穩的做法是由主機 cron 執行,Actions 只留手動補跑入口。

為什麼備份腳本手動執行正常,交給 cron 就失敗?

cron 的環境跟登入的 shell 不同。最常見的是 PATH 少了某些目錄(例如 snap 安裝的 AWS CLI 在 /snap/bin),以及 HOME 沒有指向放憑證的家目錄。在 cron 檔裡把 PATH 與 HOME 寫完整,並用 env -i 清空環境變數再測一次,才算模擬到 cron 的條件。

雲端資料庫已經有自動快照,還需要邏輯備份嗎?

需要。快照只能整台還原回同一家雲端;pg_dump 這類邏輯備份可以單獨還原一個資料庫、搬到任何一台 PostgreSQL,也能離線檢查內容。換廠商或只想救回一個資料庫時,用得上的是邏輯備份。

怎麼知道備份是不是真的有在跑?

設定「超過一段時間沒有新的成功備份就通知」,而不是只設失敗通知。排程沒啟動、主機停機、腳本失敗三種情況,前兩種都不會產生失敗紀錄,只有檢查最近一次成功的時間才抓得到。

想看看這些在實際系統裡長什麼樣?

拓 ERP 把進銷存、會計四表、401 申報所需明細與電子發票處理放在同一套資料上。申請試用可取得 15 天完整功能的體驗環境。