Docker容器數據卷
特性
- docker鏡像由多個只讀層疊加而成,啟動容器時,Docker會加載只讀鏡像層并在鏡像棧頂部添加一個讀寫層。
- 如果運行中的容器修改了現有的一個已經存在的文件,那么該文件將會從讀寫層下面的只讀層復制到讀寫層,該文件的只讀版本仍然存在,只是已經被讀寫層中該文件的副本所隱藏。
- 關閉并重啟容器,其數據不受影響;但刪除Docker容器,則其改變將會全部丟失。
存在的問題
- 存在于聯合文件系統(在docker中,只讀層以及在頂層的讀寫層組合被稱為聯合文件系統)中,不易于宿主機的訪問
- 容器間數據共享不便
- 刪除容器其數據會丟失
解決方案:“卷”
- “卷”是容器上的一個或多個“目錄”,此類目錄可繞過聯合文件系統,與宿主機上的某目錄“綁定”。
- “卷”可以在運行容器時即完成創建與綁定操作。當然,前提需要擁有對應的申明。
- “卷”的初衷就是數據持久化。
創建數據卷
C:\Users\dell>docker volume create -d local volume_demo
volume_demoC:\Users\dell>docker volume ls
DRIVER VOLUME NAME
local volume_demoC:\Users\dell>docker volume inspect volume_demo
[{"CreatedAt": "2025-07-27T10:54:30Z","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/volume_demo/_data",//數據卷的存儲目錄"Name": "volume_demo","Options": null,"Scope": "local"}
]
卷的分類
當在創建數據卷的時候,可以在創建容器時將主機本地的路徑掛載到容器內作為數據卷。也可以在創建后掛載數據卷到容器使用,可以在運行容器時通過指定-v或–mount參數來使用該volume,并且可以依據數據卷類型不同掛載不同格式的數據卷。
- Volume:普通數據卷,映射到特定位置(/var/lib/docker/volumes/)。
- Bind:綁定數據卷,映射到主機的任意位置。
- tmpfs:臨時數據卷,只存在于內存中。
使用默認的Volume數據卷,運行一個Nginx容器,source指定數據卷,destination指定source映射在容器中的目錄:
docker run -d \
--name=nginx_demo_docker \
--mount source=nginx_demo_volume,destination=/usr/share/nginx.html \
nginx:latest
可以通過使用-v來指定數據卷信息:
docker run -d \
--name=nginx_demo_docker \
-v nginx_demo_volume,/usr/share/nginx.html \
nginx:latest
使用bind指定Volume數據卷類型,運行一個nginx容器,source指定數據卷,destination指定source映射在容器的目錄:
docker run -d \
--name=nginx_demo_docker \
--mount type=bind, source=/usr/local/nginx_demo_volume,destination=/usr/share/ \
nginx:latest
可以通過使用-v來指定數據卷信息:
docker run -d \
--name=nginx_demo_docker \
-v /usr/local/nginx_demo_volume:/usr/share/ \
nginx:latest
掛載成功后,容器從/usr/share/目錄下讀取或寫入數據,實際上是從宿主機的/usr/local/nginx_demo_volume目錄中讀取或寫入數據。
使用Bind mount掛載宿主機目錄到一個容器中的非空目錄,那么此容器的非空目錄中的文件會被隱藏,容器訪問這個目錄時能夠訪問到的文件均來自宿主機目錄。這也是Bind mount模式和Volume模式在行為上最大的不同。
Volume和Bind mount模式能夠在宿主機和容器間共享文件,從而能夠將數據持久化到宿主機上,以避免寫入容器存儲層帶來的容器停止后數據丟失的問題。
如果使用Linux運行Docker,那么避免寫入數據到容器存儲層還有一個方案:tmpfs mount。tmpfs mounts,顧名思義,是一種非持久化的數據存儲。它只是將數據保存在宿主機的內存中,一旦容器停止運行,tmpfs mount會被移除,從而造成數據丟失。可以在運行容器時通過指定–tmpfs參數或–mount參數來使用tmpfs mount:
docker run -d --tmpfs /mydata:size=100m --name mycontainer myimage
數據卷容器
如果用戶需要在多個容器之間共享一些持續更新的數據,最簡單的方式是使用數據卷容器。
數據卷容器也是一個容器,但是它的目的是專門提供數據卷給其他容器掛載。
-
新建數據卷容器
docker run -it -v /dbdata --name db_data ubuntu
-
共享數據卷容器
docker run -it --volumes-from db_data --name db_data_test ubuntu
注意:db_data這個數據卷容器不能隨便關閉,如果關閉了,其他掛載了db_data里面數據卷的容器就會用不了。
數據遷移
-
備份
docker run --rm --volumes-from [數據卷容器id/name] -v [宿主機目錄]:[容器目錄] [鏡像名稱] [備份命令]# 創建備份的容器,并且掛載/backup,然后執行備份壓縮至/data docker run --rm --volumes-from vol-test -v /backup:/backup nginx tar zcf /backup/data.tar.gz /data
-
恢復
docker run --rm -itd --volumes-from [數據要到恢復的容器] -v [宿主機備份目錄]:[容器備份目錄] [鏡像名稱] [解壓命令]docker run --rm --volumes-from vol-test -v /backup:/backup nginx tar xf /backup/data.tar.gz -C /
k8s數據卷
-
通過emptyDir共享數據
EmptyDir是一個特殊的Volume類型,如果刪除Pod,emptyDir卷中的數據也將被刪除,所以一般emptyDir用于Pod中的不同Container共享數據,比如一個Pod存在兩個容器A和B,容器A需要使用容器B產生的數據,此時可以采用emptyDir共享數據,類似的使用如Filebeat收集容器內程序產生的日志。
-
使用HostPath掛載宿主機文件
HostPath卷可將節點上的文件或目錄掛載到Pod上,用于實現Pod和宿主機之間的數據共享,常用的示例有掛載宿主機的時區至Pod,或者將Pod的日志文件掛載到宿主機等。
以下為使用hostPath卷的示例,實現將主機的/etc/timezone文件掛載到Pod的/etc/timezone(掛載路徑可以不一致):
在配置HostPath時,有一個type的參數,用于表達不同的掛載類型,HostPath卷常用的type(類型)如下:- type為空字符串:默認選項,意味著掛載hostPath卷之前不會執行任何檢查。
- DirectoryOrCreate:如果給定的path不存在任何東西,那么將根據需要創建一個權限為0755的空目錄,和Kubelet具有相同的組和權限。
- Directory:目錄必須存在于給定的路徑下。
- FileOrCreate:如果給定的路徑不存儲任何內容,則會根據需要創建一個空文件,權限設置為0644,和Kubelet具有相同的組和所有權。
- File:文件,必須存在于給定路徑中。
- Socket:UNIX套接字,必須存在于給定路徑中。
- CharDevice:字符設備,必須存在于給定路徑中。
- BlockDevice:塊設備,必須存在于給定路徑中。
-
掛載NFS至容器
在生產環境中,考慮到數據的高可用性,仍然不建議使用NFS作為后端存儲。因為NFS存在單點故障,很多企業并非對其實現高可用,并且NFS的性能存在很大的瓶頸。所以生產中,建議使用分布式存儲,在公有云中建議使用公有云提供的NAS存儲來替代NFS,并且NAS性能更好,可用性更高。NFS作為一個比較流行的遠端存儲工具,在非生產環境中是一個比較好用的選擇,可以快速地搭建使用。接下來演示如何使用Volume掛載NFS為容器提供持久化存儲。
和 emptyDir 、 HostPath 的 配 置 方 法 類 似 , NFS 的 Volume 配 置 也 是 在Volumes字段中配置的,和emptyDir不同的是,NFS屬于持久化存儲的一種,在Pod刪除或者重啟后,數據依舊會存儲在NFS節點上。配置NFS也相對簡單。
-
PersistentVolume
雖然Volume已經可以接入大部分存儲后端,但是實際使用時還有諸多問題,比如:- 當某個數據卷不再被掛載使用時,里面的數據如何處理?
- 如果想要實現只讀掛載如何處理?
- 如果想要只能有一個Pod掛載如何處理?
如上所述,對于很多復雜的需求Volume可能難以實現,并且無法對存儲卷的生命周期進行管理。另一個很大的問題是,在企業內使用Kubernetes的不 僅 僅 是 Kubernetes 管 理 員 , 可 能 還 有 開 發 人 員 、 測 試 人 員 以 及 初 學Kubernetes的技術人員,對于Kubernetes的Volume或者相關存儲平臺的配置參數并不了解,所以無法自行完成存儲的配置。
為 此 , Kubernetes 引 入 了 兩 個 新 的 API 資 源 : PersistentVolume 和PersistentVolumeClaim。PersistentVolume(簡稱PV)是由Kubernetes管理員設置的存儲,PersistentVolumeClaim(簡稱PVC)是對PV的請求,表示需要什么類型的PV。它們同樣是集群中的一類資源,但其生命周期比較獨立,管理員可以單獨對PV進行增刪改查,不受Pod的影響,生命周期可能比掛載它
的其他資源還要長。如果一個Kubernetes集群的使用者并非只有Kubernetes管理員,那么可以通過提前創建PV,用以解決對存儲概念不是很了解的技術人員對存儲的需求。和單獨配置Volume類似,PV也可以使用NFS、GFS、CEPH等常用的存儲后端,并且可以提供更加高級的配置,比如訪問模式、空間大小以及回收策略等。目前PV的提供方式有兩種:靜態或動態。靜態PV由管理員提前創建,動態PV無須提前創建。-
PV回收策略
-
PV訪問策略
-
基于·NFS的PV
-
基于HostPath的PV
注意:使用hostPath需要固定Pod所在的節點,防止Pod飄移造成數據丟失。 -
PV的狀態
-
PersistentVolumeClaim
PVC是其他技術人員在Kubernetes上對存儲的申請,它可以標明一個程序需要用到什么樣的后端存儲、多大的空間以及以什么訪問模式進行掛載。這一點和Pod的QoS配置類似,Pod消耗節點資源,PVC消耗PV資源,Pod可以請求特定級別的資源(CPU和內存),PVC可以請求特定的大小和訪問模式的PV。例如申請一個大小為5Gi且只能被一個Pod只讀訪問的存儲。
在實際使用時,雖然用戶通過PVC獲取存儲支持,但是用戶可能需要具有不同性質的PV來解決不同的問題,比如使用SSD硬盤來提高性能。所以集群管理員需要根據不同的存儲后端來提供各種PV,而不僅僅是大小和訪問模式的區別,并且無須讓用戶了解這些卷的具體實現方式和存儲類型,達到了存儲的解藕,降低了存儲使用的復雜度。 -
案例如下:
apiVersion: v1 # API 版本 kind: PersistentVolume # 類型為持久卷 metadata:name: jenkins-pv # 持久卷的名稱 spec:capacity:storage: 2Gi # 容量為2GBaccessModes:#- ReadWriteMany # 訪問模式為多路讀寫- ReadWriteOnce # HostPath 通常只能支持單節點寫入,因此使用 ReadWriteOnce hostPath:path: /data/jenkins#nfs:# server: 192.168.16.2 # NFS 服務器地址# path: /data/jenkins # 共享的路徑---kind: PersistentVolumeClaim # 類型為持久卷聲明 apiVersion: v1 metadata:name: jenkins-pvc # 持久卷聲明的名稱namespace: jenkins # 使用的命名空間 spec:resources:requests:storage: 2Gi # 請求2GB的存儲空間accessModes:- ReadWriteOnce # 訪問模式與上面保持一致--- apiVersion: apps/v1 # 使用的 Kubernetes API 版本 kind: Deployment # 資源類型為 Deployment metadata:name: jenkins # Deployment 的名稱為 jenkinsnamespace: jenkins # 所屬的命名空間為 jenkins spec:replicas: 1 # 副本數為 1selector:matchLabels:app: jenkins # 選擇器選擇帶有標簽 app: jenkins 的 Podtemplate:metadata:labels:app: jenkins # Pod 的標簽為 app: jenkinsspec:serviceAccountName: jenkins-sa # 使用的服務賬戶為 jenkins-sanodeName: node1dnsConfig: #需要添加域名解析,不然容器內部可能無法訪問外網nameservers:- "8.8.8.8" # Google 的公共 DNS- "8.8.4.4" # Google 的公共 DNScontainers:- name: jenkins # 容器的名稱為 jenkinsimage: jenkins/jenkins:2.479.1 # 使用的鏡像為 jenkins/jenkins:latest,高版本的jenkins會有跨域問題ports:- containerPort: 8080 # 容器監聽的端口為 8080name: web # 端口名稱為 webprotocol: TCP # 使用的協議為 TCP- containerPort: 50000 # 容器監聽的端口為 50000name: agent # 端口名稱為 agentprotocol: TCP # 使用的協議為 TCPresources:limits:cpu: "1000m" # CPU 的資源限制為 1000m(相當于 1 核)memory: "1Gi" # 內存的資源限制為 1GiBrequests:cpu: "500m" # CPU 的資源請求為 500m(相當于 0.5 核)memory: "512Mi" # 內存的資源請求為 512MiBlivenessProbe: # 存活探針配置httpGet:path: /login # 使用 HTTP GET 請求路徑 /loginport: 8080 # 請求的端口為 8080initialDelaySeconds: 60 # 初始延遲時間為 60 秒timeoutSeconds: 5 # 超時時間為 5 秒failureThreshold: 12 # 失敗閾值為 12readinessProbe: # 就緒探針配置httpGet:path: /login # 使用 HTTP GET 請求路徑 /loginport: 8080 # 請求的端口為 8080initialDelaySeconds: 60 # 初始延遲時間為 60 秒timeoutSeconds: 5 # 超時時間為 5 秒failureThreshold: 12 # 失敗閾值為 12volumeMounts: # 掛載卷配置- name: jenkins-volume # 卷的名稱為 jenkins-volumesubPath: jenkins-home # 在卷中的子路徑為 jenkins-homemountPath: /var/jenkins_home # 掛載到容器中的路徑為 /var/jenkins_homevolumes: # 卷配置- name: jenkins-volume # 卷的名稱為 jenkins-volume#emptyDir: {}persistentVolumeClaim: # 持久卷聲明claimName: jenkins-pvc # 使用的持久卷聲明的名稱為 jenkins-pvc
-
動態存儲StorageClass
雖然使用PV和PVC能屏蔽一些存儲使用上的細節,降低了存儲使用的復雜度,但是也會有另一個問題無法解決。當公司Kubernetes集群很多,并且使用它們的技術人員過多時,對于PV的創建是一個很耗時、耗力的工作,并且達到一定規模后,過多的PV將難以維護。所以就需要某種機制用于自動管理PV的生命周期,比如創建、刪除、自動擴容等,于是Kubernetes就設計了一個名為StorageClass(縮寫為SC,沒有命名空間隔離性)的東西,通過它可以動態管理集群中的PV,這樣Kubernetes管理員就無須浪費大量的時間在PV的管理中。
具體如何使用,各位讀者就參考下文檔,這里就不介紹了。