做者 | 闞俊寶 阿里雲技術專家linux
導讀:雲原生存儲詳解系列文章將從雲原生存儲服務的概念、特色、需求、原理、使用及案例等方面,和你們一塊兒探討雲原生存儲技術新的機遇與挑戰。本文爲該系列文章的第二篇,會對容器存儲的相關概念進行講述,歡迎你們在留言區參與討論。
相關文章推薦:nginx
雲原生存儲詳解:雲原生應用的基石
雲原生存儲詳解:容器存儲與 K8s 存儲卷
雲原生存儲的兩個關鍵領域:Docker 存儲卷、K8s 存儲卷;web
容器服務之因此如此流行,一大優點即來自於運行容器時容器鏡像的組織形式。容器經過複用容器鏡像的技術,實如今相同節點上多個容器共享一個鏡像資源(更細一點說是共享某一個鏡像層),避免了每次啓動容器時都拷貝、加載鏡像文件,這種方式既節省了主機的存儲空間,又提升了容器啓動效率。docker
爲了提升節點存儲的使用效率,容器不光在不一樣運行的容器之間共享鏡像資源,並且還實現了在不一樣鏡像之間共享數據。共享鏡像數據的實現原理:鏡像是分層組合而成的,即一個完整的鏡像會包含多個數據層,每層數據相互疊加、覆蓋組成了最終的完整鏡像。json
爲了實現多個容器間共享鏡像數據,容器鏡像每一層都是隻讀的。而經過實踐咱們得知,使用鏡像啓動一個容器的時候,實際上是能夠在容器裏隨意讀寫的,這是如何實現的呢?後端
容器使用鏡像時,在多個鏡像分層的最上面還添加了一個讀寫層。每個容器在運行時,都會基於當前鏡像在其最上層掛載一個讀寫層,用戶針對容器的全部操做都在讀寫層中完成。一旦容器銷燬,這個讀寫層也隨之銷燬。api
如上圖所示例子,一個節點上共有 3 個容器,分別基於 2 個鏡像運行。安全
鏡像存儲層說明以下:網絡
該節點上共包含 6 個鏡像層:Layer 1~6。架構
- 鏡像 1 由:Layer 一、三、四、5 組成;
- 鏡像 2 由:Layer 二、三、五、6 組成。
因此兩個鏡像共享了 Layer 三、5 兩個鏡像層;
容器存儲說明:
- 容器 1:使用鏡像 1 啓動
- 容器 2:使用鏡像 1 啓動
- 容器 3:使用鏡像 2 啓動
容器 1 和容器 2 共享鏡像 1,且每一個容器有本身的可寫層;
容器 1(2)和容器 3 共享鏡像 2 個層(Layer三、5);
經過上述例子能夠看到,經過容器鏡像分層實現數據共享能夠大幅減小容器服務對主機存儲的資源需求。
上面給出了容器讀寫層結構,而讀寫的原則:
對於讀:容器由這麼多層的數據組合而成,當不一樣層次的數據重複時,讀取的原則是上層數據覆蓋下層數據;
對於寫:容器修改某個文件時,都是在最上層的讀寫層進行。主要實現技術有:寫時複製、用時配置。
寫時複製(CoW:copy-on-write),表示只在須要寫時纔去複製,是針對已有文件的修改場景。CoW 技術可讓全部的容器共享 image 的文件系統,全部數據都從 image 中讀取,只有當要對文件進行寫操做時,才從 image 裏把要寫的文件複製到最上面的讀寫層進行修改。因此不管有多少個容器共享同一個 image,所作的寫操做都是對從 image 中複製後在複本上進行,並不會修改 image 的源文件,且多個容器操做同一個文件,會在每一個容器的文件系統裏生成一個複本,每一個容器修改的都是本身的複本,相互隔離,相互不影響。
用時分配:在鏡像中本來沒有某個文件的場景,只有在要新寫入一個文件時才分配空間,這樣能夠提升存儲資源的利用率。好比啓動一個容器,並不會爲這個容器預分配一些磁盤空間,而是當有新文件寫入時,才按需分配新空間。
存儲驅動是指如何對容器的各層數據進行管理,已達到上述須要實現共享、可讀寫的效果。即:容器存儲驅動實現了容器讀寫層數據的存儲和管理。常見的存儲驅動:
以 AUFS 爲例,咱們來說述一下存儲驅動的工做原理:
AUFS 是一種聯合文件系統(UFS),是文件級的存儲驅動。
AUFS 是一個能透明疊加一個或多個現有文件系統的層狀文件系統,把多層文件系統合併成單層表示。即:支持將不一樣目錄掛載到同一個虛擬文件系統下的文件系統。
能夠一層一層地疊加修改文件,其底層都是隻讀的,只有最上層的文件系統是可寫的。
當須要修改一個文件時,AUFS 建立該文件的一個副本,使用 CoW 將文件從只讀層複製到可寫層進行修改,結果也保存在可寫層。
在 Docker 中,底下的只讀層就是 image,可寫層就是 Container 運行時。
其餘各類存儲驅動這裏再也不細講,有興趣的同窗能夠到網上查詢資料。
容器中的應用讀寫數據都是發生在容器的讀寫層,鏡像層+讀寫層映射爲容器內部文件系統、負責容器內部存儲的底層架構。當咱們須要容器內部應用和外部存儲進行交互時,須要一個相似於計算機 U 盤同樣的外置存儲,容器數據卷即提供了這樣的功能。
另外一方面:容器自己的存儲數據都是臨時存儲,在容器銷燬的時候數據會一塊兒刪除。而經過數據卷將外部存儲掛載到容器文件系統,應用能夠引用外部數據,也能夠將本身產出的數據持久化到數據卷中,因此容器數據卷是容器進行數據持久化的實現方式。
容器存儲組成:只讀層(容器鏡像) + 讀寫層 + 外置存儲(數據卷)
容器數據卷從做用範圍能夠分爲:單機數據卷 和 集羣數據卷。單機數據卷即爲容器服務在一個節點上的數據卷掛載能力,docker volume 是單機數據卷的表明實現;集羣數據卷則關注的是集羣級別的數據卷編排能力,K8s 數據卷則是集羣數據卷的主要應用方式。
Docker Volume 是一個可供多個容器使用的目錄,它繞過 UFS,包含如下特性:
Bind:將主機目錄/文件直接掛載到容器內部。
- 須要使用主機的上的絕對路徑,且能夠自動建立主機目錄;
- 容器能夠修改掛載目錄下的任何文件,是應用更具備便捷性,但也帶來了安全隱患。
Volume:使用第三方數據卷的時候使用這種方式。
- Volume命令行指令:docker volume (create/rm);
- 是Docker提供的功能,因此在非 docker 環境下沒法使用;
- 分爲命名數據卷和匿名數據卷,其實現是一致的,區別是匿名數據卷的名字爲隨機碼;
- 支持數據卷驅動擴展,實現更多外部存儲類型的接入。
Tmpfs:非持久化的卷類型,存儲在內存中。
數據易丟失。
-v: src:dst:opts 只支持單機版。
- Src:表示卷映射源,主機目錄或文件,須要是絕對地址;
- Dst:容器內目標掛載地址;
- Opts:可選,掛載屬性:ro, consistent, delegated, cached, z, Z;
- Consistent, delegated, cached:爲mac系統配置共享傳播屬性;
- Z、z:配置主機目錄的selinux label。
示例:
$ docker run -d --name devtest -v /home:/data:ro,rslave nginx $ docker run -d --name devtest --mount type=bind,source=/home,target=/data,readonly,bind-propagation=rslave nginx $ docker run -d --name devtest -v /home:/data:z nginx
-v: src:dst:opts 只支持單機版。
- Src:表示卷映射源,數據卷名、空;
- Dst:容器內目標目錄;
- Opts:可選,掛載屬性:ro(只讀)。
示例:
$ docker run -d --name devtest -v myvol:/app:ro nginx $ docker run -d --name devtest --mount source=myvol2,target=/app,readonly nginx
Docker 數據卷使用方式:
docker run -d -v /test:/data nginx
若是主機上沒有/test目錄,則默認建立此目錄。
數據卷容器是一個運行中的容器,其餘容器能夠繼承此容器中的掛載數據卷,則此容器的全部掛載都會在引用容器中體現。
docker run -d --volumes-from nginx1 -v /test1:/data1 nginx
繼承全部來自配置容器的數據卷,幷包含本身定義的卷。
Docker volume 支持掛載傳播的配置:Propagation。
示例:
$ docker run –d -v /home:/data:shared nginx 表示:主機/home下面掛載的目錄,在容器/data下面可用,反之可行; $ docker run –d -v /home:/data:slave nginx 表示:主機/home下面掛載的目錄,在容器/data下面可用,反之不行;
Volume 掛載可見性:
Bind 掛載可見性:以主機目錄爲準。
Docker 數據卷實現了將容器外部存儲掛載到容器文件系統的方式。爲了擴展容器對外部存儲類型的需求,docker 提出了經過存儲插件的方式掛載不一樣類型的存儲服務。擴展插件統稱爲 Volume Driver,能夠爲每種存儲類型開發一種存儲插件。
Docker Daemon 與 Volume driver 通訊方式有:
實現接口:
Create, Remove, Mount, Path, Umount, Get, List, Capabilities;
使用示例:
$ docker volume create --driver nas -o diskid="" -o host="10.46.225.247" -o path=」/nas1" -o mode="" --name nas1
Docker Volume Driver 適用在單機容器環境或者 swarm 平臺進行數據卷管理,隨着 K8s 的流行其使用場景已經愈來愈少,關於 VolumeDriver 的詳細介紹這裏不在細講,有興趣能夠參考:https://docs.docker.com/engine/extend/plugins_volume/
根據以前的描述,爲了實現容器數據的持久化咱們須要使用數據卷的功能,在 K8s 編排系統中如何爲運行的負載(Pod)定義存儲呢?K8s 是一個容器編排系統,其關注的是容器應用在整個集羣的管理和部署形式,因此在考慮 K8s 應用存儲的時候就須要從集羣角度考慮。K8s 存儲卷定義了在 K8s 系統中應用與存儲的關聯關係。其包含如下概念:
數據卷定義了外置存儲的細節,並內嵌到 Pod 中做爲 Pod 的一部分。其實質是外置存儲在 K8s 系統的一個記錄對象,當負載須要使用外置存儲的時候,從數據卷中查到相關信息並進行存儲掛載操做。
K8S Volume 經常使用類型:
一些 volume 模板示例以下:
volumes: - name: hostpath hostPath: path: /data type: Directory --- volumes: - name: disk-ssd persistentVolumeClaim: claimName: disk-ssd-web-0 - name: default-token-krggw secret: defaultMode: 420 secretName: default-token-krggw --- volumes: - name: "oss1" flexVolume: driver: "alicloud/oss" options: bucket: "docker" url: "oss-cn-hangzhou.aliyuncs.com"
PVC 是 PersistentVolumeClaim 的縮寫,譯爲存儲聲明;PVC 是在 K8s 中一種抽象的存儲卷類型,表明了某個具體類型存儲的數據卷表達。其設計意圖是:存儲與應用編排分離,將存儲細節抽象出來並實現存儲的編排(存儲卷)。這樣 K8s 中存儲卷對象獨立於應用編排而單獨存在,在編排層面使應用和存儲解耦。
PV 是 PersistentVolume 的縮寫,譯爲持久化存儲卷;PV 在 K8s 中表明一個具體存儲類型的卷,其對象中定義了具體存儲類型和卷參數。即目標存儲服務全部相關的信息都保存在 PV 中,K8s 引用 PV 中的存儲信息執行掛載操做。
應用負載、PVC、PV 的關聯關係爲:
從實現上看,只要有了 PV 既能夠實現存儲和應用的編排分離,也能實現數據卷的掛載,爲什麼要用 PVC + PV 兩個對象呢?K8s 這樣設計是從應用角度對存儲捲進行二次抽象;因爲 PV 描述的是對具體存儲類型,須要定義詳細的存儲信息,而應用層用戶在消費存儲服務的時候每每不但願對底層細節知道的太多,讓應用編排層面來定義具體的存儲服務不夠友好。這時對存儲服務再次進行抽象,只把用戶關係的參數提煉出來,用 PVC 來抽象更底層的 PV。因此 PVC、PV 關注的對象不同,PVC 關注用戶對存儲需求,給用戶提供統一的存儲定義方式;而 PV 關注的是存儲細節,能夠定義具體存儲類型、存儲掛載使用的詳細參數等。
使用時應用層會聲明一個對存儲的需求(PVC),而 K8s 會經過最佳匹配的方式選擇一個知足 PVC 需求的 PV,並與之綁定。因此從職責上 PVC 是應用所須要的存儲對象,屬於應用做用域(和應用處於一個名詞空間);PV 是存儲平面的存儲對象,屬於整個存儲域(不屬於某個名詞空間);
下面給出 PVC、PV 的一些屬性:
PVC 定義的模板以下:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: disk-ssd-web-0 spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi storageClassName: alicloud-disk-available volumeMode: Filesystem
PVC 定義的存儲接口包括:存儲的讀寫模式、資源容量、卷模式等;主要參數說明以下:
accessModes:存儲卷的訪問模式,支持:ReadWriteOnce、ReadWriteMany、ReadOnlyMany 三種模式。
- ReadWriteOnce 表示 pvc 只能同時被一個 pod 以讀寫方式消費;
- ReadWriteMany 能夠同時被多個 pod 以讀寫方式消費;
- ReadOnlyMany 表示能夠同時被多個 pod 以只讀方式消費;
注意:這裏定義的訪問模式只是編排層面的聲明,具體應用在讀寫存儲文件的時候是否可讀可寫,須要具體的存儲插件實現肯定。
storage:定義此 PVC 對象指望提供的存儲容量,一樣此處的數據大小也只是編排聲明的值,具體存儲容量要看底層存儲服務類型。
volumeMode:表示存儲卷掛載模式,支持 FileSystem、Block 兩種模式;
FileSystem:將數據卷掛載成文件系統的方式供應用使用;
Block:將數據卷掛載成塊設備的形式供應用使用。
下面爲雲盤數據卷 PV 對象的編排示例:
apiVersion: v1 kind: PersistentVolume metadata: labels: failure-domain.beta.kubernetes.io/region: cn-shenzhen failure-domain.beta.kubernetes.io/zone: cn-shenzhen-e name: d-wz9g2j5qbo37r2lamkg4 spec: accessModes: - ReadWriteOnce capacity: storage: 30Gi flexVolume: driver: alicloud/disk fsType: ext4 options: VolumeId: d-wz9g2j5qbo37r2lamkg4 persistentVolumeReclaimPolicy: Delete storageClassName: alicloud-disk-available volumeMode: Filesystem
- accessModes:存儲卷的訪問模式,支持:ReadWriteOnce、ReadWriteMany、ReadOnlyMany 三種模式;具體含義同 PVC 字段;
- capacity:定義存儲卷容量;
- persistentVolumeReclaimPolicy:定義回收策略,即刪除 pvc 的時候如何處理 PV;支持 Delete、Retain 兩種類型,動態數據卷部分會詳細說明此參數;
- storageClassName:表示存儲卷的使用的存儲類名字,動態數據卷部分會詳細說明此參數;
- volumeMode:同 PVC 中的 volumeMode 定義;
- Flexvolume:此字段表示具體的存儲類型,這裏 Flexvolume 爲一種抽象的存儲類型,並在 flexvolume 的子配置項中定義了具體的存儲類型、存儲參數。
PVC 只有綁定了 PV 以後才能被 Pod 使用,而 PVC 綁定 PV 的過程便是消費 PV 的過程,這個過程是有必定規則的,下面規則都知足的 PV 才能被 PVC 綁定:
知足上述全部須要的 PV 才能夠被 PVC 綁定。
若是同時有多個 PV 知足需求,則須要從 PV 中選擇一個更合適的進行綁定;一般選擇容量最小的,若是容量最小的也有多個,則隨機選擇。
若是沒有知足上述需求的 PV 存儲,則 PVC 會處於 Pending 狀態,等待有合適的 PV 出現了再進行綁定。
從上面的討論咱們瞭解到,PVC 是針對應用服務對存儲的二次抽象,具備簡潔的存儲定義接口。而 PV 是具備繁瑣存儲細節的存儲抽象,通常有專門的集羣管理人員定義、維護。
根據 PV 的建立方式能夠將存儲卷分爲動態存儲和靜態存儲卷:
通常先由集羣管理員分析集羣中存儲需求,並預先分配一些存儲介質,同時建立對應的 PV 對象,建立好的 PV 對象等待 PVC 來消費。若是負載中定義了 PVC 需求,K8s 會經過相關規則實現 PVC 和匹配的 PV 進行綁定,這樣就實現了應用對存儲服務的訪問能力。
由集羣管理員配置好後端的存儲池,並建立相應的模板(storageclass),等到有 PVC 須要消費 PV 的時候,根據 PVC 定義的需求,並參考 storageclass 的存儲細節,由 Provisioner 插件動態建立一個 PV。
兩種卷的比較:
提供動態存儲卷的優點:
當用戶聲明一個 PVC 時,若是在 PVC 中添加了 StorageClassName 字段,其意圖爲:當 PVC 在集羣中找不到匹配的 PV 時,會根據 StorageClassName 的定義觸發相應的 Provisioner 插件建立合適的 PV 供綁定,即建立動態數據卷;動態數據卷時由 Provisioner 插件建立的,並經過 StorageClassName 與 PVC 進行關聯。
StorageClass 可譯爲存儲類,表示爲一個建立 PV 存儲卷的模板;在 PVC 觸發自動建立PV的過程當中,即便用 StorageClass 對象中的內容進行建立。其內容包括:目標 Provisioner 名字,建立 PV 的詳細參數,回收模式等配置。
StorageClasss 模板定義以下:
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: alicloud-disk-topology parameters: type: cloud_ssd provisioner: diskplugin.csi.alibabacloud.com reclaimPolicy: Delete allowVolumeExpansion: true volumeBindingMode: WaitForFirstConsumer
用戶建立一個 PVC 聲明時,會在集羣尋找合適的 PV 進行綁定,若是沒有合適的 PV 與之綁定,則觸發下面流程:
某種存儲(阿里云云盤)在掛載屬性上有所限制,只能將相同可用區的數據卷和 Node 節點進行掛載,不在同一個可用區不能夠掛載。這種類型的存儲卷一般遇到以下問題:
StorageClass 中的 volumeBindingMode 字段正是用來解決此問題,若是將 volumeBindingMode 配置爲 WaitForFirstConsumer 值,則表示 Provisioner 在收到 PVC Pending 的時候不會當即進行數據卷建立,而是等待這個 PVC 被 Pod 消費的時候才執行建立流程。
其實現原理是:
經過上述流程可見:延遲綁定會先讓應用負載進行調度(肯定有充足的資源供 pod 使用),而後再觸發動態卷的建立流程,這樣就避免了數據卷所在可用區沒有資源的問題,也避免了存儲預規劃的不許確性問題。
在多可用區集羣環境中,更推薦使用延遲綁定的動態卷方案,目前阿里雲 ACK 集羣已經支持上述配置方案。
下面給出一個 pod 消費 PVC、PV 的例子:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nas-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 50Gi selector: matchLabels: alicloud-pvname: nas-csi-pv --- apiVersion: v1 kind: PersistentVolume metadata: name: nas-csi-pv labels: alicloud-pvname: nas-csi-pv spec: capacity: storage: 50Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain flexVolume: driver: "alicloud/nas" options: server: "***-42ad.cn-shenzhen.extreme.nas.aliyuncs.com" path: "/share/nas" --- apiVersion: apps/v1 kind: Deployment metadata: name: deployment-nas labels: app: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx1 image: nginx:1.8 - name: nginx2 image: nginx:1.7.9 volumeMounts: - name: nas-pvc mountPath: "/data" volumes: - name: nas-pvc persistentVolumeClaim: claimName: nas-pvc
模板解析:
根據 PVC、PV 綁定的邏輯,此 PV 符合 PVC 消費要求,則 PVC 會和此 PV 進行綁定,並供 pod 掛載使用。
此篇文章較爲詳細的講述了容器存儲的總體面貌,包括單機範圍的 Docker 數據卷、和集羣式的 K8s 數據卷;K8s 數據卷更多關注的時候集羣級別的存儲編排能力,同時也在節點上實現了具體的數據卷掛載流程。K8s 爲了實現上述複雜的存儲卷編排能力,其實現架構也較爲複雜,下節內容咱們將爲您介紹 K8s 的存儲架構和實現流程。
爲了更多開發者可以享受到 Serverless 帶來的紅利,這一次,咱們集結了 10+ 位阿里巴巴 Serverless 領域技術專家,打造出最適合開發者入門的 Serverless 公開課,讓你即學即用,輕鬆擁抱雲計算的新範式——Serverless。
點擊便可免費觀看課程:https://developer.aliyun.com/learning/roadmap/serverless
「 阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,作最懂雲原生開發者的公衆號。」