備份與復原

本頁內容

復原點必須能夠還原完整的資料庫。EmDash 的 JSON 備份可用於檢查和自訂工具,但 EmDash 無法還原它們。在進行資料庫遷移或破壞性操作之前,請使用原始資料庫備份或時間點復原。

請單獨備份媒體二進位檔。如果站台儲存了加密的外掛設定,請把完整的 EMDASH_ENCRYPTION_KEY 輪替清單保存在單獨的金鑰管理員備份中。資料庫備份和匯出不包含這些檔案或金鑰。

選擇復原方法

請使用與站台所設定的資料庫轉接器相對應的方法:

資料庫復原點
Cloudflare D1記錄一個 Time Travel 書籤。當所需的保留期超過 Time Travel,或需要演練還原時,建立異地 SQL 傾印。
SQLite 或本機 libSQL使用 SQLite 的線上備份指令,然後對副本執行完整性檢查。
PostgreSQL使用服務供應商的時間點復原,或用 pg_dump 建立自訂格式的封存檔。
Hyperdrive透過直接連線到來源資料庫來備份其 PostgreSQL 來源。Hyperdrive 繫結不是備份連線。
Turso 代管的 libSQL使用 Turso 的時間點復原,或建立並演練 SQL 傾印。
其他遠端 libSQL 服務供應商使用服務供應商的完整資料庫匯出或時間點復原流程,並在隔離的資料庫上測試。

請把復原點與對應的應用程式建置產物、媒體備份和金鑰清單版本放在一起保存。在計畫的遷移之前,記錄確切的資料庫和環境;如果服務供應商的流程要求,請停止應用程式寫入;並確認復原點在預期的回溯視窗內一直可用。

備份與還原媒體物件

R2 和相容 S3 的儲存貯體除資料庫之外,還需要物件層級備份。下面的 AWS CLI 指令會把所有物件(包括 backups/ 下的 EmDash JSON 封存檔)複製到本機備份目錄。對於 AWS S3,請省略 --endpoint-url。

aws s3 sync s3://emdash-media ./emdash-media-backup \
  --endpoint-url https://<account-id>.r2.cloudflarestorage.com

請把 <account-id> 替換為 Cloudflare 帳戶 ID。例行備份工作請使用唯讀的儲存貯體憑證。把備份存放在正式環境帳戶或故障網域之外,並記錄同一時間建立的資料庫復原點。

請還原到一個空的復原儲存貯體,而不是在正式環境處理請求時覆寫它:

aws s3 sync ./emdash-media-backup s3://emdash-media-recovery \
  --endpoint-url https://<account-id>.r2.cloudflarestorage.com

只給還原工作復原儲存貯體的寫入權限。讓一個非正式環境的部署指向已還原的資料庫和儲存貯體,開啟幾個已知的媒體 URL,並上傳、刪除一個暫時檔案。只有在資料庫和媒體一起通過檢查之後,才修改正式環境的繫結或儲存貯體設定。

對於本機儲存,請複製由 local() 儲存轉接器設定的目錄。複製時請停止媒體寫入,然後驗證副本中具代表性的檔案。

用 Time Travel 復原 D1 資料庫

遷移之前,向 Time Travel 查詢資料庫目前的書籤,並把完整輸出與部署一起記錄下來:

npx wrangler d1 time-travel info my-database

Time Travel 復原點會過期。Cloudflare 目前在 Workers Free 方案中保留 7 天,在 Workers Paid 方案中保留 30 天。如果回溯視窗需要更長,請建立異地 D1 傾印。

如果需要復原,請停止對站台的寫入,並還原遷移之前記錄的那個確切書籤:

npx wrangler d1 time-travel restore my-database \
  --bookmark="<pre-migration-bookmark>"

請把 <pre-migration-bookmark> 替換為記錄下來的書籤。該指令會就地覆寫資料庫並取消正在執行的查詢。請保存還原指令輸出的先前書籤,以便復原這次還原本身。

Time Travel 會還原內容、使用者、設定、外掛資料和遷移記錄。它不會還原 R2 媒體物件或 EMDASH_ENCRYPTION_KEY。在重新開放流量之前,請還原對應的應用程式建置產物、媒體和金鑰。驗證登入、內容讀取、結構描述、一項加密的外掛設定以及一次寫入。

有關目前的保留期、需求和還原行為,請參閱 D1 Time Travel 文件。

建立異地 D1 傾印

要取得原始資料庫(包括使用者和驗證資料表)的 SQL 傾印,請使用 Wrangler。一旦任何集合啟用了搜尋,單純的 wrangler d1 export my-database --remote 就會因 D1 Export error: cannot export databases with Virtual Tables (fts5) 而失敗,因為 EmDash 把搜尋索引保存在 FTS5 虛擬資料表中。請依名稱匯出其他資料表,並把搜尋索引排除在外;還原後的站台會重建它。

傾印被拆分為四個檔案,以便還原能夠成功:

  • backup-schema.sql 建立資料表。
  • backup-folders.sql 保存媒體資料夾。大型匯入會分批執行,否則媒體項目可能先於它所屬的資料夾被插入,從而無法通過外鍵檢查。
  • backup-data.sql 保存所有其他資料列。
  • backup-indexes.sql 保存索引和觸發器,依資料表名稱匯出時會漏掉它們。它們最後建立,這樣匯入資料列時觸發器就不會觸發。

這些指令需要 jq。下面的指令會寫出這四個檔案:

npx wrangler d1 execute my-database --remote --json \
  --command "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT GLOB '_emdash_fts_*' AND name NOT GLOB 'sqlite_*' AND name NOT GLOB '_cf_*' AND name != 'media_folders'" \
  | jq -r '.[0].results[] | "--table=" + .name' > backup-tables.txt

npx wrangler d1 export my-database --remote --no-data --output=backup-schema.sql \
  --table=media_folders $(cat backup-tables.txt)
npx wrangler d1 export my-database --remote --no-schema --output=backup-folders.sql \
  --table=media_folders
npx wrangler d1 export my-database --remote --no-schema --output=backup-data.sql \
  $(cat backup-tables.txt)

npx wrangler d1 execute my-database --remote --json \
  --command "SELECT sql FROM sqlite_master WHERE type IN ('index', 'trigger') AND sql IS NOT NULL AND name NOT GLOB '_emdash_fts_*' AND tbl_name NOT GLOB '_emdash_fts_*' AND tbl_name NOT GLOB '_cf_*'" \
  | jq -r '.[0].results[] | .sql + ";"' > backup-indexes.sql

D1 會拒絕 100,000 位元組或更長的 SQL 陳述式,而匯出時每一個資料列都會作為單獨一行的 INSERT 寫出。包含較長值的資料列(例如一篇很長的條目或其某個修訂版本)可能超出該限制。請在建立檔案後立即檢查。下面的指令會輸出每一條對 D1 來說過長的陳述式所在的檔案和行號;如果這些檔案可以匯入,則不輸出任何內容:

LC_ALL=C awk 'length($0) >= 100000 { print FILENAME ":" FNR }' backup-*.sql

有關 Wrangler 的匯出選項和限制,請參閱 D1 匯入與匯出文件。

請把 SQL 檔案與對應的應用程式版本、同時建立的媒體備份,以及單獨保存的 EMDASH_ENCRYPTION_KEY 輪替清單副本放在一起保存。測試復原的方法是:把這些檔案匯入新建立的空 D1 資料庫,還原執行階段金鑰,把一個非正式環境的繫結更新為指向該資料庫,然後驗證站台。

如果建立傾印時沒有檢查過這些檔案,請先執行上面的長度檢查。然後依以下順序把檔案匯入空的復原資料庫:

npx wrangler d1 execute my-recovery-database --remote --file=backup-schema.sql
npx wrangler d1 execute my-recovery-database --remote --file=backup-folders.sql
npx wrangler d1 execute my-recovery-database --remote --file=backup-data.sql
npx wrangler d1 execute my-recovery-database --remote --file=backup-indexes.sql

還原後的資料庫沒有搜尋索引。EmDash 會在還原後的站台第一次呼叫搜尋 API(/_emdash/api/search)時重建它,例如透過管理介面的命令面板,或使用該 API 的搜尋框。在此之前,直接呼叫 search() 的頁面不會傳回結果。若要立即重建,請在站台執行後請求一次 /_emdash/api/search?q=a。

備份 SQLite 與本機 libSQL

離線備份時,請停止所有寫入資料庫的處理程序,並連同資料庫檔案一起複製旁邊所有的 -wal 和 -shm 檔案。-wal 檔案可能包含尚未寫入主檔案的已提交變更。

要取得一致的線上備份,請對資料庫轉接器所設定的路徑使用 SQLite 的備份指令:

sqlite3 emdash.db ".backup backup.db"

檢查複製出的資料庫。該指令必須輸出 ok:

sqlite3 backup.db "PRAGMA integrity_check"

在非正式環境中,用對應的應用程式版本開啟該副本,並驗證登入、公開內容、一項加密的外掛設定、一次編輯和一次媒體讀取。

要復原,請停止所有寫入資料庫的處理程序,保留損毀的資料庫及其 -wal 和 -shm 檔案的副本,用驗證過的備份取代它們,並在重新開放流量之前還原對應的應用程式建置產物、媒體和金鑰。

備份 PostgreSQL 與 Hyperdrive

如果 PostgreSQL 服務供應商提供可在修改正式環境之前測試的隔離還原,請使用其時間點復原或快照功能。記錄確切的時間戳記或快照 ID 以及保留期。

要取得可攜式備份,請直接連線到 PostgreSQL 來源資料庫,並建立自訂格式的封存檔:

pg_dump --format=custom --file=emdash.dump "$DATABASE_URL"

pg_dump 會在資料庫保持使用的同時建立交易一致的封存檔。在依賴該封存檔之前,請先檢查其目錄:

pg_restore --list emdash.dump > emdash.dump.list

準備一個空的復原資料庫。以標準的 EmDash 應用程式角色連線,並在還原時不把來源資料庫的擁有權帶入復原資料庫:

pg_restore --exit-on-error --no-owner \
  --dbname="$RECOVERY_DATABASE_URL" emdash.dump

RECOVERY_DATABASE_URL 中的角色會成為已還原的 EmDash 物件的擁有者。請使用復原後的應用程式將要使用的同一個穩定角色。讓一個非正式環境的部署指向復原資料庫,並驗證登入、內容讀取、結構描述、一項加密的外掛設定以及一次寫入。

對於 Hyperdrive,DATABASE_URL 必須是 migrationConnectionStringEnv 所指定的、直連 PostgreSQL 來源資料庫的連線;Worker 繫結無法傳給 PostgreSQL 備份工具。請遵循來源資料庫服務供應商的復原流程,並把 Hyperdrive 設定與應用程式建置產物一起保留。

有關服務供應商代管的復原、大型資料庫和持續封存,請參閱 PostgreSQL 備份文件。

備份 Turso 代管的 libSQL

Turso 的時間點復原會根據較早的時間戳記建立一個獨立的資料庫。請在遷移之前立即記錄一個 RFC 3339 時間戳記。如果需要復原,請從該時間點建立一個隔離的資料庫:

turso db create emdash-recovery \
  --from-db emdash-production \
  --timestamp <pre-migration-rfc3339-timestamp>

請把 <pre-migration-rfc3339-timestamp> 替換為記錄下來的時間戳記。為復原後的資料庫建立權杖,讓一個非正式環境的部署指向它,並在變更正式環境的連線之前驗證站台。有關站台方案所提供的保留期,請查看 Turso 時間點復原。

要取得異地副本,請傾印整個資料庫:

turso db shell emdash-production .dump > emdash.sql

透過用傾印建立一個隔離的資料庫來測試該傾印:

turso db create emdash-recovery --from-dump ./emdash.sql

對於其他遠端 libSQL 服務供應商,請遵循其完整資料庫匯出或時間點復原流程。在該流程產生復原點並且測試過隔離還原之前,不要套用遠端遷移。

將 JSON 備份用於檢查

JSON 備份包含:

  • 所有內容條目,包括草稿、已排程的文章和垃圾桶中的項目
  • 構成內容模型的集合和欄位定義
  • 分類法定義、詞彙,以及指派給每個條目的詞彙
  • 選單和選單項目、區段、小工具區域和小工具、SEO 記錄、修訂歷史、媒體中繼資料和資料庫遷移歷史
  • 站台設定,例如來自 site: 設定群組以及 emdash:site_title、emdash:site_tagline 和 emdash:locale 設定的標題、標語、URL、地區設定、標誌、顯示偏好、社群檔案和 SEO 預設值

它不包含:

  • 使用者帳戶、工作階段、通行金鑰、OAuth 資料、API 權杖和其他驗證資料
  • 外掛儲存和外掛設定,包括外掛密鑰
  • 留言和互動回應、重新導向和 404 記錄、署名、內容關聯與參照、稽核記錄、速率限制以及排程工作狀態
  • 媒體資料夾、記錄媒體使用位置的資料、未完成的上傳以及媒體檔案本身
  • 其他站台選項,包括 emdash:site_url、預覽簽署密鑰和備份排程

在 Settings → Backups 下,Download backup 會產生一個新的 JSON 檔案。設定了儲存時,Back up now 會在 backups/ 前綴下儲存一個。這些檔案使用與 EmDash 預覽相同的帶版本快照格式。

要排程 JSON 備份,請在 Settings → Backups 下啟用 Daily automatic backups,並選擇保留 1–30 個檔案。它們在排程維護期間執行。在 Cloudflare 上,Worker 需要一個 cron 觸發器;在 Node 上,內建排程器會執行該工作。

如果儲存貯體有公開的 R2 網域或內容傳遞網路來源,請把該來源限制為媒體物件,或透過 EmDash 提供媒體。EmDash 的媒體路由拒絕提供 backups/,但公開的儲存貯體來源可能透過 URL 暴露每一個物件。

要把內容、設定和媒體複製到新的 EmDash 站台,請使用站台套件。站台套件不會還原使用者、API 權杖、外掛資料或密鑰,因此不能取代資料庫復原點。