Kubernetes Volume 使用指南

什麼是 Volume?

Kubernetes Volume 是一個 API 物件,用於為 Pod 中的容器提供持久化或共享的儲存空間。Volume 解決了容器檔案系統的臨時性問題,讓資料能夠在容器重啟後保留,或在多個容器之間進行共享。

Volume 的主要功能:

  • 資料持久化:確保重要資料在容器重啟或 Pod 重新調度後不會遺失
  • 容器間共享:讓同一 Pod 內的多個容器能夠共享資料
  • 外部儲存整合:連接到各種外部儲存系統,如雲端儲存、網路檔案系統等
  • 臨時儲存管理:提供高效能的臨時工作空間

為什麼使用 Volume?

相較於依賴容器內建的檔案系統,Volume 提供了更可靠的資料管理方式:

  1. 資料安全性:避免因容器故障導致的資料遺失
  2. 靈活性:支援多種儲存後端,適應不同的應用需求
  3. 效能最佳化:可選擇適合的儲存介質來提升應用效能
  4. 統一管理:集中管理所有持久化資料的儲存策略

Volume 類型介紹

Kubernetes 提供了多種不同的 Volume 類型,包括 emptyDir、hostPath、PersistentVolume、ConfigMap、Secret、NFS 等數十種選項。每種類型都有其特定的用途和適用場景。由於 Volume 類型眾多,本指南將重點介紹作者認為在日常開發和運維工作中最重要且最常用的幾種類型,幫助讀者快速掌握核心概念和實用技能。

emptyDir

emptyDir 類型的 Volume 會在 Pod 被分配到節點時建立,當 Pod 從節點刪除時,emptyDir 中的資料也會一併刪除。它的生命週期與 Pod 綁定,適合用來存放暫存檔、快取資料,或像應用程式日誌這類不需要跨 Pod 長期保留的檔案。

使用場景:

  • 多容器 Pod 中容器間共享資料
  • 快取儲存,提升應用程式效能
  • 臨時工作目錄或暫存檔案
  • 應用程式產生的暫時性日誌檔案

特性:

  • 資料儲存在節點的本地磁碟或記憶體中
  • 容器重啟時資料通常會保留,但 Pod 刪除後資料會消失
  • 可以設定儲存介質為磁碟或記憶體(Memory)
  • 支援容量限制設定

注意事項:

  • 資料不具持久性,Pod 刪除後資料會消失
  • 如果使用記憶體作為儲存介質,需注意記憶體限制
  • 適合臨時資料儲存,不適合重要資料持久化

emptyDir 實作範例

以下範例展示如何在 Spring Boot Deployment 中使用 emptyDir,將 Pod 暫存磁碟掛載到應用程式日誌目錄,供 logback 寫入檔案日誌:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 部署 Spring Boot 應用程式,並設定容器映像、環境變數與健康檢查。
apiVersion: apps/v1
kind: Deployment
metadata:
name: dockertest-deployment
namespace: dockertest
labels:
# metadata labels 可協助識別此 Deployment 的環境、應用與版本。
environment: dev
app: dockertest
version: latest
spec:
# 開發環境先維持單一副本;正式環境可依需求調整 replicas。
replicas: 1
selector:
matchLabels:
# selector 必須與 template.metadata.labels 相符,Deployment 才能管理這些 Pod。
app: dockertest
template:
metadata:
labels:
# Pod labels 供 Deployment selector 與 Service selector 使用。
app: dockertest
version: latest
spec:
containers:
- name: dockertest
# 應用程式映像來源,需搭配下方 imagePullSecrets 才能從私有 registry 拉取。
image: inner.gitlab.dev:5050/pcion123/docker-test:latest
# Always 會在每次建立 Pod 時嘗試拉取最新映像,適合 latest tag 的開發部署。
imagePullPolicy: Always
ports:
# Spring Boot 預設服務埠,Service 也會轉發到這個 containerPort。
- containerPort: 8080
volumeMounts:
# 將 Pod 暫存磁碟掛載到應用程式日誌目錄,供 logback 寫入檔案日誌。
- name: app-logs
mountPath: /var/log/app/logs
env:
# JVM 記憶體與 Spring profile 設定。
- name: JAVA_OPTS
value: "-Xmx512m -Xms256m -Dspring.profiles.active=dev"
# 指定應用啟動模式;目前設定為 minikube-secret,供程式依本機叢集環境調整行為。
- name: START_MODE
value: "minikube-secret"
# 設定容器時區,避免日誌與應用時間和台灣時間不一致。
- name: TZ
value: "Asia/Taipei"
# livenessProbe 用來判斷容器是否仍健康;失敗過多時 Kubernetes 會重啟容器。
livenessProbe:
httpGet:
path: /hello
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
# readinessProbe 用來判斷 Pod 是否可接收流量;未就緒時 Service 不會導流到此 Pod。
readinessProbe:
httpGet:
path: /hello
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
imagePullSecrets:
# 指定上方建立的 registry Secret,供 Kubernetes 拉取私有映像使用。
- name: gitlab-registry-secret
volumes:
# emptyDir 會隨 Pod 建立而產生,Pod 刪除時內容也會一併移除,適合暫存應用日誌。
- name: app-logs
emptyDir: {}
配置說明
  • volumeMounts: 定義容器內的掛載點

    • name: 對應 volumes 中定義的 Volume 名稱
    • mountPath: 容器內的掛載路徑,範例中為 /var/log/app/logs
  • volumes: 定義 Pod 可使用的儲存卷

    • name: Volume 名稱,必須與 volumeMounts.name 相同
    • emptyDir: 使用 Pod 暫存儲存空間,Pod 刪除後內容會一併移除
部署與驗證
部署應用

本次驗證使用的部署檔會一併建立 namespace、registry Secret、Deployment 與 Service:

1
kubectl apply -f apply_secret.yml
驗證 Volume 掛載

可以開一個終端機持續觀察 dockertest namespace 中的資源狀態,確認 Deployment、ReplicaSet、Pod 與 Service 都已建立:

1
watch -n 2 kubectl get all -n dockertest

接著查看應用程式啟動日誌,確認 logback 使用的檔案日誌路徑為 /var/log/app/logs/dockertest.log

1
kubectl logs -f -n dockertest <pod-name>

應用程式啟動後,進入 Pod 檢查 emptyDir 掛載的日誌目錄,並直接讀取檔案日誌:

1
2
3
kubectl exec -it -n dockertest <pod-name> -- sh
cd /var/log/app/logs
tail -f dockertest.log
驗證 emptyDir 生命週期

emptyDir 的生命週期與 Pod 綁定,而不是與容器行程綁定。因此可以先終止容器中的 Java 行程,讓 Kubernetes 重新啟動容器,再觀察同一個 Pod 的 RESTARTS 數量增加。此時 Pod 沒有被刪除,所以 emptyDir 中的 dockertest.log 仍會保留。

1
2
3
kubectl exec -it -n dockertest <pod-name> -- sh -c "kill -TERM 7"
kubectl get all -n dockertest
kubectl exec -it -n dockertest <pod-name> -- sh -c "tail -n 20 /var/log/app/logs/dockertest.log"

如果刪除整個 Pod,Deployment 會重新建立新的 Pod,而新的 Pod 會取得新的 emptyDir,原本 emptyDir 內的檔案就不會保留。這點與上面的「容器重啟」不同。

hostPath

hostPath 類型的 Volume 會將 Kubernetes 節點檔案系統上的檔案或目錄掛載到 Pod 中。這種類型適合需要讀寫節點本機路徑的情境,例如在單節點開發環境中保留應用程式日誌。需要注意的是,hostPath 與特定節點綁定,Pod 重新調度到其他節點時,資料不會跟著移動。

使用場景:

  • 需要訪問節點上的檔案或目錄
  • 單節點開發或測試環境
  • 應用程式日誌需要保留在節點目錄中
  • 日誌收集系統需要讀取主機日誌

注意事項:

  • 資料與特定節點綁定,Pod 遷移時資料不會跟隨
  • 存在安全風險,應謹慎限制可掛載的路徑
  • 不建議作為多節點生產環境的一般持久化方案

hostPath 實作範例

以下範例展示如何在 Spring Boot Deployment 中使用 hostPath,將節點上的固定目錄掛載到應用程式日誌目錄,讓 Pod 重建後仍可在同一個節點保留日誌檔案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 部署 Spring Boot 應用程式,並設定容器映像、環境變數與健康檢查。
apiVersion: apps/v1
kind: Deployment
metadata:
name: dockertest-deployment
namespace: dockertest
labels:
# metadata labels 可協助識別此 Deployment 的環境、應用與版本。
environment: dev
app: dockertest
version: latest
spec:
# 開發環境先維持單一副本;正式環境可依需求調整 replicas。
replicas: 1
selector:
matchLabels:
# selector 必須與 template.metadata.labels 相符,Deployment 才能管理這些 Pod。
app: dockertest
template:
metadata:
labels:
# Pod labels 供 Deployment selector 與 Service selector 使用。
app: dockertest
version: latest
spec:
containers:
- name: dockertest
# 應用程式映像來源,需搭配下方 imagePullSecrets 才能從私有 registry 拉取。
image: inner.gitlab.dev:5050/pcion123/docker-test:latest
# Always 會在每次建立 Pod 時嘗試拉取最新映像,適合 latest tag 的開發部署。
imagePullPolicy: Always
ports:
# Spring Boot 預設服務埠,Service 也會轉發到這個 containerPort。
- containerPort: 8080
volumeMounts:
# 將節點上的固定目錄掛載到應用程式日誌目錄,供 logback 寫入檔案日誌。
- name: app-logs
mountPath: /var/log/app/logs
env:
# JVM 記憶體與 Spring profile 設定。
- name: JAVA_OPTS
value: "-Xmx512m -Xms256m -Dspring.profiles.active=dev"
# 指定應用啟動模式;目前設定為 minikube-secret,供程式依本機叢集環境調整行為。
- name: START_MODE
value: "minikube-secret"
# 明確指定 logback 的檔案日誌路徑,需與上方 volumeMounts.mountPath 一致。
- name: LOG_PATH
value: "/var/log/app/logs"
# 設定容器時區,避免日誌與應用時間和台灣時間不一致。
- name: TZ
value: "Asia/Taipei"
# livenessProbe 用來判斷容器是否仍健康;失敗過多時 Kubernetes 會重啟容器。
livenessProbe:
httpGet:
path: /hello
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
# readinessProbe 用來判斷 Pod 是否可接收流量;未就緒時 Service 不會導流到此 Pod。
readinessProbe:
httpGet:
path: /hello
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
imagePullSecrets:
# 指定上方建立的 registry Secret,供 Kubernetes 拉取私有映像使用。
- name: gitlab-registry-secret
volumes:
# hostPath 會使用 Kubernetes 節點上的實體目錄;Pod 重建後日誌仍保留在同一個節點。
- name: app-logs
hostPath:
path: /var/log/k8s-logs
type: DirectoryOrCreate
配置說明
  • volumeMounts: 定義容器內的掛載點

    • name: 對應 volumes 中定義的 Volume 名稱
    • mountPath: 容器內的掛載路徑,範例中為 /var/log/app/logs
  • env.LOG_PATH: 明確指定應用程式寫入檔案日誌的位置,需與 volumeMounts.mountPath 保持一致

  • volumes: 定義 Pod 可使用的儲存卷

    • name: Volume 名稱,必須與 volumeMounts.name 相同
    • hostPath.path: 節點上的實體目錄,範例中為 /var/log/k8s-logs
    • hostPath.type: 路徑類型,DirectoryOrCreate 表示如果目錄不存在,Kubernetes 會自動建立
部署與驗證
部署應用

本次 hostPath 驗證使用另一份部署檔,差異在於 volumes 改成掛載節點上的固定目錄:

1
kubectl apply -f apply_secret_hostpath.yml
驗證 Volume 掛載

可以開一個終端機持續觀察 dockertest namespace 中的資源狀態:

1
watch -n 2 kubectl get all -n dockertest

另開一個終端機查看 Pod 的應用程式啟動日誌,確認 logback 寫入的檔案路徑為 /var/log/app/logs/dockertest.log

1
kubectl logs -f -n dockertest <pod-name>
驗證資料持久性

接著回到 Kubernetes 節點上,進入 hostPath 對應的主機目錄。本次實際驗證環境使用的是家目錄下的 k8s-logs,如果你的 YAML 設定為 /var/log/k8s-logs,就改到該路徑檢查。

1
2
3
cd ~/k8s-logs
ls
tail -f dockertest.log

當應用程式收到請求或重新啟動時,可以同時在 kubectl logs -f 與節點上的 tail -f dockertest.log 看到日誌持續寫入。這代表容器內的 /var/log/app/logs/dockertest.log 實際上已經寫到節點的 hostPath 目錄。

若刪除 Pod 後 Deployment 在同一個節點重新建立 Pod,hostPath 目錄中的 dockertest.log 仍會保留,新的 Pod 會繼續寫入同一個節點目錄:

1
2
kubectl delete pod -n dockertest <pod-name>
kubectl get pods -n dockertest -l app=dockertest
最佳實務建議
  1. 開發環境使用: hostPath 主要適用於開發和測試環境,或需要讀取節點特定檔案的系統元件
  2. 路徑管理: 確保節點路徑具有適當的權限設定,避免掛載過大的系統範圍
  3. 資料備份: 如果日誌或資料具有保留需求,仍需搭配備份或集中式日誌收集
  4. 生產環境替代: 在生產環境中,建議使用 PersistentVolume、StorageClass 或雲端儲存解決方案
清理資源

如果是透過前面的部署檔建立資源,建議直接使用同一份 YAML 清理,避免只刪除 Deployment 卻留下 Service、Secret 或 namespace 等資源:

1
kubectl delete -f apply_secret.yml

如果目前套用的是 hostPath 版本,則改刪除 hostPath 對應的部署檔:

1
kubectl delete -f apply_secret_hostpath.yml

也可以手動刪除 dockertest namespace,連同 namespace 內的 Deployment、Pod、Service 與 Secret 一併清除:

1
kubectl delete namespace dockertest

需要注意的是,hostPath 寫入節點上的檔案不會因為刪除 Kubernetes 資源而自動移除。如果要清掉測試時產生的日誌,需要到節點上的 hostPath 目錄手動刪除,例如:

1
rm -f ~/k8s-logs/dockertest.log

Volume 類型總覽

以下表格整理了 Kubernetes 中常見的 Volume 類型及其主要用途:

Volume 類型 持久性 用途說明 適用場景
emptyDir 臨時 Pod 內容器間共享資料,Pod 刪除後資料消失 臨時檔案、快取、容器間通訊
hostPath 節點綁定 掛載主機檔案系統到容器中 開發測試、系統監控、日誌收集
PersistentVolume (PV) 持久 獨立於 Pod 生命週期的持久化儲存 資料庫、檔案儲存、狀態應用
ConfigMap 組態 以檔案形式提供組態資料 應用程式組態、環境變數
Secret 機密 以檔案形式提供敏感資料 密碼、憑證、API 金鑰
NFS 網路共享 透過 NFS 協定共享網路儲存 多 Pod 共享資料、檔案伺服器
AWS EBS 雲端持久 使用 AWS Elastic Block Store AWS 環境的持久化儲存
Azure Disk 雲端持久 使用 Azure 磁碟儲存 Azure 環境的持久化儲存
GCE PD 雲端持久 使用 Google Cloud 持久磁碟 GCP 環境的持久化儲存
CSI 彈性 Container Storage Interface 標準介面 第三方儲存解決方案整合

選擇建議

開發階段:

  • 使用 emptyDir 進行容器間資料共享
  • 使用 hostPath 進行本地開發和測試
  • 使用 ConfigMapSecret 管理組態和敏感資料

生產環境:

  • 使用 PersistentVolume 確保資料持久性
  • 根據雲端平台選擇對應的儲存類型(AWS EBS、Azure Disk、GCE PD)
  • 使用 CSI 整合企業級儲存解決方案

注意事項:

  • 避免在生產環境使用 hostPath,除非有特殊需求
  • emptyDir 適合臨時資料,不應用於重要資料儲存
  • 選擇 Volume 類型時需考慮效能、成本和可用性需求