Skip to content
Open Source

儲存檔案與備份磁碟區

為服務掛載持續保存的磁碟區、擴充容量,並用 K8up 將檔案備份到 S3 相容儲存空間。

應用程式要保留上傳的檔案時,進入服務 設定 → 儲存空間,新增磁碟區並指定應用程式寫入的路徑。套用後,寫進這個路徑的檔案在副本重新啟動或換節點後都會留著。同一處也能設定 K8up 備份,定期把磁碟區的檔案複製到 S3 相容儲存空間。

  • 知道應用程式寫入資料的路徑,例如 /data。容器裡不在磁碟區上的檔案,副本替換後就會消失。
  • 叢集有可用的儲存類別,你也有權新增磁碟區、修改服務。
  • 如果服務由 Helm、Argo CD 或 Flux 管理,要回到原本的 values 或 Git 儲存庫修改。
  • 要備份的話,叢集要裝 K8up,並準備可寫入的 S3 相容儲存空間和存取金鑰。管理員可以先登錄 S3 目的地,備份表單就能直接選用。
  • 升級 K8up 的 Helm chart 時,Helm 不會更新 crds/ 裡的 CRD,升級後要自己 kubectl apply 新版的 CRD。

開啟服務 設定 → 儲存空間。沒有磁碟區時按 新增磁碟區。已有磁碟區時先按 編輯,再按 新增磁碟區。

輸入 掛載路徑(例如 /data)和 大小(例如 5Gi,預設 1Gi)。掛載路徑要是絕對路徑,也不能和這個服務已有的掛載重複。

服務有多個容器時,在新磁碟區那一列的 掛載到容器 選擇要掛到哪個容器。檢查畫面也會寫出容器名稱。

在 儲存類別 選擇資料放在哪裡。每個選項會寫出能不能擴充,以及磁碟區和節點的關係:

  • 本機磁碟 最快,但固定在一個節點上,用到它的副本只能在那個節點執行。
  • 複寫磁碟 把資料複寫到多個節點,副本換節點時資料會跟著移動。
  • 其他儲存類別的行為由它的驅動程式決定。

不選就用叢集的預設儲存類別。選了不能擴充的類別,要一次給足容量。

要自訂名稱時,展開 進階 修改 名稱、PVC 名稱 和 存取模式。這個表單一定會建立新的 PVC,填已在使用的 PVC 名稱,叢集會拒絕。

按頁面下方 檢查變更,確認要建立的磁碟區、大小和掛載路徑,再套用。Zeabur 會先建立 PVC,再把它掛到容器的路徑上。

新增或修改掛載會更新 Pod template,持續執行的服務會替換副本。新磁碟區一開始是空的,容器原本在這個路徑下的檔案不會搬過去。要保留它們,先把檔案複製出來,掛上磁碟區後再放回去。

等磁碟區狀態變成 就緒、服務副本也就緒後,從應用程式上傳一個檔案,再 重新啟動 服務,確認檔案還在。

每個磁碟區那一列會寫出儲存類別、資料在哪個節點、能不能擴充,以及刪除磁碟區後資料會不會留下。叢集有收集 kubelet 的磁碟區指標時,也會顯示用量。PVC 名稱、背後的 PersistentVolume 和存取模式,到專案 物件 頁或用 kubectl 查。

StatefulSet 的磁碟區在建立時就固定了(volumeClaimTemplates 建立後不能改),所以這裡不能替它新增或移除磁碟區,只能擴充容量或修改掛載。Job 建立後不能改 Pod template,要替 Job 加磁碟區,編輯它的 YAML。

在 儲存空間 按 編輯,加大既有磁碟區的 大小,再從頁面下方檢查並套用。只有可以擴充的儲存類別能加大,而且不能縮小。多個服務掛同一個 PVC 時,它們一起用加大後的空間。

既有掛載也可以改 掛載路徑 或 唯讀。改路徑只會改變資料在容器裡的位置,應用程式設定的資料路徑也要跟著改。如果路徑寫在環境變數裡,可以和掛載放進同一份草稿一起檢查。

儲存類別不能擴充時,要在可以擴充的類別建立新磁碟區,把資料複製過去,再把服務改掛新的磁碟區。既有 PVC 不能換儲存類別。

在服務 設定 → 儲存空間 下方的 備份 找到磁碟區,按 備份這個磁碟區。叢集還沒裝 K8up 時,按 開啟 K8up 請管理員安裝。

表單上方會先寫出備份用哪個使用者執行,原則是和服務一致:

  • 服務指定了非 root 使用者,備份就用同一個使用者。
  • 服務指定 root,或沒有指定使用者、安全性也不是 嚴格,備份就用 root,才能讀到每個檔案。
  • 服務沒有指定使用者、安全性是 嚴格 時,備份用 K8up 的非 root 使用者,只有服務使用者能讀的檔案會略過。在服務的安全性設定指定使用者,就能一起備份。

專案只允許「嚴格」的服務、備份又要以 root 執行時,表單會說明原因,不讓你建立這個備份。按 開啟安全性設定 把服務改為 嚴格,或請管理員讓專案允許「相容」的服務。

選擇已登錄的 S3 目的地,或自己填 端點、儲存貯體、存取金鑰 ID、私密存取金鑰。儲存服務需要區域時,再填 區域。

資料夾 留白時是 <專案>/<PVC 名稱>,例如專案 shop 的 PVC data-redis-0 會放在 shop/data-redis-0。

排程 可選 每天 03:00(UTC) 或 每週日 03:00(UTC)。保留規則預設 保留最新幾份 3 份、每日備份保留天數 7 天、每週備份保留週數 4 週。K8up 會在備份一小時後(04:00 UTC)依這組規則清掉舊備份。

按面板的 檢查變更。這個面板有自己的套用步驟,不會加入上方儲存設定的草稿。

檢查畫面會列出存放金鑰和儲存庫密碼的 Secret、PVC 上的備份標籤和註解,以及 K8up Schedule。備份不以 root 執行時,還有一個 PodConfig。以 PVC data-redis-0 為例,Secret 和 Schedule 都叫 data-redis-0-backup。

確認後套用。選用已登錄的 S3 目的地時,金鑰會複製一份到專案的 Secret。之後目的地換了金鑰,這份副本要另外更新。

建立排程不會馬上備份。按該磁碟區的 立即備份,檢查並套用,K8up 會立刻執行一次。

在 這些磁碟區的備份 查看這次執行的狀態、完成時間和失敗訊息,顯示 成功 才表示檔案已經複製到 S3。

如果這個磁碟區原本就由一個涵蓋多個磁碟區的 K8up 排程備份,立即備份 會沿用那個排程的選取條件,其他磁碟區也會一起備份。

在 備份 有備份點的磁碟區旁,按 從備份點建立新磁碟區,選時間點,檢查並套用,K8up 會把那個時間點的檔案還原到新磁碟區,原本的磁碟區不變。StatefulSet 的磁碟區改用 停下服務、清空磁碟區、還原到原處。

沒有權限還原、要請管理員代為處理時,準備以下資料:

  • 來源 S3 端點、儲存貯體與資料夾,可從 K8up Schedule 的 spec.backend 確認。
  • S3 存取金鑰,以及備份 Secret 的 RESTIC_PASSWORD。
  • 要還原哪一份備份。Zeabur 的執行紀錄沒有 restic 快照 ID,要從備份儲存庫查詢。
  • 目標 PVC 與要接回它的服務。

有讀取 Secret 權限的人可以用 kubectl 取出備份 Secret。以專案 shop、PVC data-redis-0 為例:

kubectl -n shop get secret data-redis-0-backup -o yaml

這份 Secret 有 S3 金鑰和儲存庫密碼,存進團隊的密碼管理工具,不要貼進 issue 或聊天訊息。

備份區的 K8up:還原 連到 K8up 的還原說明。建議先還原到另一個新的磁碟區,確認檔案沒問題,再停掉服務的寫入、改掛新磁碟區。

  1. 照上面的步驟,把那個時間點還原到新磁碟區。不要按 把服務改掛到新磁碟區,那會讓執行中的服務改用舊檔案。

  2. 新磁碟區沒有任何服務掛載。用一個暫時的 Pod 掛上它。以下以專案 shop、新磁碟區 data-restored-1025 為例:

    kubectl -n shop apply -f - <<'EOF'
    apiVersion: v1
    kind: Pod
    metadata:
    name: restore-view
    spec:
    securityContext:
    runAsNonRoot: true
    runAsUser: 65532
    fsGroup: 65532
    seccompProfile:
    type: RuntimeDefault
    containers:
    - name: restore-view
    image: busybox:1.37
    command: ["sleep", "3600"]
    securityContext:
    allowPrivilegeEscalation: false
    capabilities:
    drop: ["ALL"]
    volumeMounts:
    - name: data
    mountPath: /restored
    volumes:
    - name: data
    persistentVolumeClaim:
    claimName: data-restored-1025
    EOF
    kubectl -n shop wait --for=condition=Ready pod/restore-view
  3. 整個資料夾打包成 backup.tgz,或只複製要的檔案:

    kubectl -n shop exec restore-view -- tar czf - -C /restored . > backup.tgz
    kubectl -n shop cp restore-view:/restored/path/to/file ./file
  4. 下載完刪掉暫時的 Pod 和新磁碟區:

    kubectl -n shop delete pod restore-view
    kubectl -n shop delete pvc data-restored-1025

資料庫用 更多 → 匯出 下載傾印檔,見下載資料庫傾印檔。要較早時間點的內容,先 還原成新的資料庫,再從新資料庫匯出。

K8up 備份時應用程式照常執行,直接複製磁碟區上的檔案。如果磁碟區放的是正在寫入的資料庫,複製出來的檔案可能不一致,改用資料庫自己的備份。要備份整個叢集的物件,見叢集備份。

磁碟區是一般的 PVC,備份是 K8up 的 Schedule 和 Secret。移除 Zeabur 之後,掛載和備份排程都照常運作。以下以專案 demo、Deployment web、磁碟區名稱 uploads 為例。

Zeabur 建立的 PVC 名稱是 <服務>-<磁碟區名稱>,存取模式預設 ReadWriteOnce。要指定儲存類別時,加上 storageClassName:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-uploads
namespace: demo
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 5Gi

再把它掛到容器上:在 Pod template 加一筆 volumes,在容器加一筆 volumeMounts。這和 Zeabur 寫入的 JSON Patch 相同:

kubectl -n demo patch deployment web --type=json -p='[
{"op":"add","path":"/spec/template/spec/volumes/-","value":{"name":"uploads","persistentVolumeClaim":{"claimName":"web-uploads"}}},
{"op":"add","path":"/spec/template/spec/containers/0/volumeMounts/-","value":{"name":"uploads","mountPath":"/srv/uploads"}}
]'

Pod template 還沒有 volumes 或容器還沒有 volumeMounts 時,/- 會失敗,改用 kubectl edit deployment web 直接加上這兩段。建立服務時就加了磁碟區的話,Zeabur 會把 Deployment 設成 1 個副本和 strategy: Recreate,避免新舊副本搶同一個 ReadWriteOnce 磁碟。之後才加磁碟區時不會改這兩項,檢查畫面會提醒多個副本共用 ReadWriteOnce 磁碟的限制。

加大 PVC 的 spec.resources.requests.storage。儲存類別要有 allowVolumeExpansion: true,而且不能縮小:

kubectl -n demo patch pvc web-uploads -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'

備份需要三樣東西:存放 S3 金鑰和 restic 密碼的 Secret、PVC 上的標籤和 annotation、K8up 的 Schedule。Secret 和 Schedule 都叫 <PVC 名稱>-backup:

apiVersion: v1
kind: Secret
metadata:
name: web-uploads-backup
namespace: demo
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: AKIAEXAMPLE
AWS_SECRET_ACCESS_KEY: change-me
RESTIC_PASSWORD: a-long-random-password
---
apiVersion: k8up.io/v1
kind: Schedule
metadata:
name: web-uploads-backup
namespace: demo
spec:
backend:
repoPasswordSecretRef:
name: web-uploads-backup
key: RESTIC_PASSWORD
s3:
endpoint: https://s3.example.com
bucket: backups/demo/web-uploads
accessKeyIDSecretRef:
name: web-uploads-backup
key: AWS_ACCESS_KEY_ID
secretAccessKeySecretRef:
name: web-uploads-backup
key: AWS_SECRET_ACCESS_KEY
podSecurityContext:
runAsUser: 0
backup:
schedule: "0 3 * * *"
labelSelectors:
- matchLabels:
zeabur.com/backup-volume: web-uploads
prune:
schedule: "0 4 * * *"
retention:
keepLast: 3
keepDaily: 7
keepWeekly: 4

bucket 是儲存貯體加上資料夾,Zeabur 預設的資料夾是 <專案>/<PVC 名稱>。S3 需要區域時,在 Secret 加上 AWS_DEFAULT_REGION,並在 backend 加上 envFrom: [{secretRef: {name: web-uploads-backup}}]。備份不以 root 執行時,Zeabur 會改寫一個同名的 K8up PodConfig,podSecurityContext 換成 podConfigRef。

接著替 PVC 加上 Schedule 選取的標籤,以及 K8up 的 annotation:

kubectl -n demo label pvc web-uploads zeabur.com/backup-volume=web-uploads
kubectl -n demo annotate pvc web-uploads k8up.io/backup=true

立即備份 是一個 K8up Backup,內容是 Schedule 的 backup,加上同樣的 backend 和 podSecurityContext:

apiVersion: k8up.io/v1
kind: Backup
metadata:
name: web-uploads-now
namespace: demo
spec:
labelSelectors:
- matchLabels:
zeabur.com/backup-volume: web-uploads
backend:
repoPasswordSecretRef:
name: web-uploads-backup
key: RESTIC_PASSWORD
s3:
endpoint: https://s3.example.com
bucket: backups/demo/web-uploads
accessKeyIDSecretRef:
name: web-uploads-backup
key: AWS_ACCESS_KEY_ID
secretAccessKeySecretRef:
name: web-uploads-backup
key: AWS_SECRET_ACCESS_KEY
podSecurityContext:
runAsUser: 0

停止備份 只刪 Schedule(和 PodConfig),保留 Secret:

kubectl -n demo delete schedule.k8up.io web-uploads-backup

在服務 設定 → 儲存空間,磁碟區狀態是 就緒,那一列寫出儲存類別。備份 下方的 這些磁碟區的備份 列出每次執行,顯示 成功 才表示檔案已經複製到 S3。

用 kubectl 確認:

kubectl -n demo get pvc web-uploads
kubectl -n demo get schedule.k8up.io,backup.k8up.io
kubectl -n demo get snapshot.k8up.io

PVC 的 STATUS 是 Bound,CAPACITY 是目前的大小。擴充時,等 CAPACITY 變成新的大小才算完成。Backup 跑完後 COMPLETION 會顯示 Succeeded,kubectl get snapshot.k8up.io 會多一筆這次的快照。

畫面或結果接著處理
磁碟區一直 等待儲存空間查看副本和 PVC 的事件,確認儲存類別可用。磁碟區那一列寫著 固定在第一個副本執行的節點 時,要等副本排到節點後才會建立。
副本換節點後無法啟動看磁碟區是不是固定在某個節點。本機磁碟的資料不會跟著副本搬到新節點。
多個副本無法同時掛載Deployment 的副本共用同一個 PVC。存取模式是 ReadWriteOnce 時,只有同一個節點上的副本能掛載,要改用支援多節點的儲存類別和存取模式。
這個磁碟區名稱超過 63 個字元,K8up 無法用標籤選取它。Zeabur 用 PVC 名稱當標籤值選取磁碟區,名稱太長就無法從這裡設定備份,要請管理員另外寫 K8up Schedule。
備份 失敗 或 部分失敗讀該次的失敗訊息,核對 S3 金鑰,以及備份使用者能不能讀檔案。
建立後沒有執行紀錄按 立即備份 跑一次,再看 K8up 和備份 Pod 的狀態。
看不到備份設定或結果備份區會說明是叢集沒裝 K8up,還是你沒有權限讀取。

還沒套用的儲存修改,按頁面下方 捨棄 放棄。已套用的掛載,在 編輯 按該列的移除按鈕,再檢查並套用。移除掛載只是讓容器不再掛這個磁碟區,PVC 和資料都還在。

要停止備份,在 備份 該磁碟區旁按 停止備份,檢查後按 停止備份。K8up 不再備份這個磁碟區,S3 上已完成的備份會留著。備份 Secret 也會保留,裡面有還原這些備份需要的密碼。只有專為這個磁碟區建立的排程有這個按鈕,涵蓋多個磁碟區的排程請管理員調整。

要完全刪除磁碟區,在 儲存空間 該磁碟區那一列按刪除按鈕,檢查後按 刪除磁碟區。服務不再掛載這個路徑,PVC 也會一起刪除。StatefulSet 與 Job 的磁碟區沒有這個按鈕。