2持久卷
PV和PVC
在Pod級別定義儲存卷有兩個弊端 卷物件的生命週期無法獨立於Pod而存在 使用者必須要足夠熟悉可用的儲存及其詳情才能在Pod上配置和使用卷 PV和PVC可用於降低這種耦合關係 PV(Persistent Volume)是叢集級別的資源,負責將儲存空間引入到叢集中,通常由管理員定義 PVC(Persistent Volume Claim)是名稱空間級別的資源,由使用者定義,用於在空閒的PV中申請使用符合過濾 條件的PV之一,與選定的PV是“一對一”的關係 使用者在Pod上透過pvc外掛請求繫結使用定義好的PVC資源 StorageClass資源支援PV的動態預配(Provision) PV, PVC PV置備(provision)方式: 靜態置備:也可以使用StorageClass,分組,亦可作為篩選條件之一,可選項; 動態置備:依賴於StorageClass(模板),必選項#後端儲存需要支援動態置備功能
PV資源 PV是標準的資源型別,除了負責關聯至後端儲存系統外,它通常還需要定義支援的儲存特性 Volume Mode:當前PV卷提供的儲存空間模型,分為塊裝置和檔案系統兩種 StorageClassName:當前PV隸屬的儲存類; #檔案系統沒有限制,塊裝置系統只能單路讀寫訪問 AccessMode:支援的訪問模型,分為單路讀寫、多路讀寫和多路只讀三種 Size:當前PV允許使用的空間上限 在物件後設資料上,還能夠根據需要定義標籤 #Retain:保留資料,PV變為release狀態,待人工介入;Recycle:資料清除,PV可再次被其他PVC繫結(危險,被廢棄) #Delete:刪除PV,但後端儲存空間的資料沒被刪 一般需要定義回收策略:Retain、Recycle和Delete PVC資源 PVC也是標準的資源型別,它允許使用者按需指定期望的儲存特性,並以之為條件,按特定的條件順序進行PV過濾 VolumeMode → LabelSelector → StorageClassName → AccessMode → Size 支援動態預配的儲存類,還可以根據PVC的條件按需完成PV建立
基於NFS的靜態PV和PVC示例
#NFS PV 資源定義示例 apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-demo spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: #這裡可以把支援的型別都宣告出來 - ReadWriteMany persistentVolumeReclaimPolicy: Retain #回收策略 mountOptions: #掛載選項,非必須項 - hard - nfsvers=4.1 nfs: path: "/data/redis" server: nfs.magedu.com #PVC 資源定義示例 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-demo namespace: default #可省略 spec: accessModes: ["ReadWriteMany"] volumeMode: Filesystem resources: #定義資源 requests: #最小不能小於 storage: 3Gi limits: #最大不能超過 storage: 10Gi #在Pod上使用PVC卷 apiVersion: v1 kind: Pod metadata: name: volumes-pvc-demo namespace: default spec: containers: - name: redis image: redis:alpine imagePullPolicy: IfNotPresent ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redis-rbd-vol volumes: - name: redis-rbd-vol persistentVolumeClaim: claimName: pvc-demo #使用上面定義的pvc名字
實際操作示例
#在nfs伺服器上建立資料夾匯出 [root@ubuntu ~]#mkdir -pv /data/pv001 [root@ubuntu ~]#vim /etc/exports /data/pv001/ 10.0.0.0/16(rw,async,no_subtree_check,no_root_squash) #重新匯出 [root@ubuntu ~]#exportfs -arv #在k8s的控制節點上 [root@master01 volumes]#vim pv-nfs-demo.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-nfs-demo spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteMany - ReadOnlyMany - ReadWriteOnce persistentVolumeReclaimPolicy: Retain mountOptions: - hard - nfsvers=4.1 nfs: path: "/data/pv001" server: 10.0.0.155 [root@master01 volumes]#kubectl apply -f pv-nfs-demo.yaml #檢視pv (這裡沒定義STORAGECLASS,等下pvc也不能定義分類,否則找不到) [root@master01 volumes]#kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pv-nfs-demo 5Gi RWX Retain Available <unset> 41s [root@master01 volumes]#vim pvc-demo.yaml #會從Available狀態的PV中進行篩選 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-demo spec: accessModes: ["ReadWriteMany"] #能相容對方就可以 volumeMode: Filesystem resources: requests: #儘量接近3G storage: 3Gi limits: storage: 10Gi [root@master01 volumes]#kubectl apply -f pvc-demo.yaml #獲取pvc要指名稱空間,default可以不指,狀態為bound繫結,一建立立即繫結 [root@master01 volumes]#kubectl get pvc -n default NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE pvc-demo Bound pv-nfs-demo 5Gi RWO,ROX,RWX <unset> 11m #檢視pv也能看出被繫結狀態 [root@master01 volumes]#kubectl get pv #定義pod使用pvc [root@master01 volumes]#vim pod-with-pvc-demo.yaml apiVersion: v1 kind: Pod metadata: name: redis-dyn-pvc spec: containers: - name: redis image: redis:7-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redis-pvc-vol volumes: - name: redis-pvc-vol persistentVolumeClaim: claimName: pvc-demo [root@master01 volumes]#kubectl apply -f pod-with-pvc-demo.yaml #進入容器驗證 [root@master01 volumes]#kubectl exec -it redis-dyn-pvc -- /bin/sh /data # redis-cli 127.0.0.1:6379> set mykey "Hello" OK 127.0.0.1:6379> BGSAVE Background saving started #nfs伺服器上檢視 [root@ubuntu ~]#ls /data/pv001/ dump.rdb #刪除pod只要不刪pvc,那隻要引用pvc,所有資料都在,卷也在
StorageClass資源 Kubernetes支援的標準資源型別之一 為管理PV資源之便而按需建立的儲存資源類別(邏輯組) 是PVC篩選PV時的過濾條件之一 為動態建立PV提供“模板” 需要儲存服務提供管理API StorageClass資源上配置接入API的各種引數 定義在parameters欄位中 還需要使用provisioner欄位指明儲存服務型別 一般由叢集管理員定義,隸屬叢集級別 #StorageClass資源示例 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-csi provisioner: nfs.csi.k8s.io #後端儲存服務由哪個供應商提供的 parameters: server: nfs-server.default.svc.cluster.local #nfs伺服器地址 share: / #nfs伺服器暴露的路徑 reclaimPolicy: Delete #動態置備的pv拿到的回收策略 volumeBindingMode: Immediate#卷繫結模式,Immediate立即繫結PV和PVC,若用本地儲存用延遲繫結(等pod建立出來) mountOptions: #nfs卷使用引數,有些服務不需要指定mountOptions - hard - nfsvers=4.1
有些儲存型別預設並不支援動態PV機制,如下圖。可以加CSI讓它支援 多數CSI儲存都支援動態PV,且支援卷擴充套件和卷快照等功能 #PVC向StorageClass申請繫結PV apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs-dynamic spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi storageClassName: nfs-csi
local持久卷 local卷外掛用於將本地儲存裝置(如磁碟、分割槽或目錄)配置為卷 基於網路儲存的PV通常效能損耗較大 直接使用節點本地的SSD磁碟可獲取較好的IO效能,更適用於儲存類的服務,例如MongoDB、Ceph等 hostPath卷在Pod被重建後可能被排程至其它節點而無法再次使用此前的資料,而基於local卷,排程器能自行完成排程繫結 hostPath卷允許Pod訪問節點上的任意路徑,也存在一定程度的安全風險 基於local的PV,需要管理員透過nodeAffinity宣告其定義在的節點 使用者可透過PVC關聯至local型別的PV 然後,在Pod上配置使用該PVC即可 排程器將基於nodeAffinity將執行Pod排程 local卷只能關聯靜態置備的PV,目前尚不支援動態置備 #Local PV資源示例 apiVersion: v1 kind: PersistentVolume metadata: name: local-pv-demo spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: #local卷在某個主機上的路徑 path: /disks/vol1 nodeAffinity: #節點選擇器 required: nodeSelectorTerms: #節點過濾器 - matchExpressions: #匹配條件 - key: kubernetes.io/hostname #主機上要有該標籤 operator: In #主機名得是k8s-node01.magedu.com values: - k8s-node01.magedu.com
PVC遲延繫結 ◼ 配置PVC繫結local PV時,通常要建立一個StorageClass ◆provisioner欄位的值“no-provisioner”表示不使用動態置備PV,因為local外掛不支援 ◆volumeBindingMode欄位的值“WaitForFirstConsumer”表示等待消費者(Pod)申請使用PVC時(即第一次被排程時)再進行PV繫結,即“延遲繫結” ◆延遲繫結機制,提供了基於消費者的需求來判定將PVC繫結至哪個PV的可能性 ◼ 延遲繫結機制下,PVC建立後將處於“Pending”狀態,直至被Pod消費 #實際操作示例: #作為分組使用,不作為動態置備作用 [root@master01 local-pv-demo]#vim storageclass-local.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local provisioner: kubernetes.io/no-provisioner #固定格式,沒有動態置備就不需要沒有供應商 volumeBindingMode: WaitForFirstConsumer #延遲繫結(等待首次被消費) [root@master01 local-pv-demo]#kubectl apply -f storageclass-local.yaml #檢視,storageclass可以簡寫為sc [root@master01 local-pv-demo]#kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE local kubernetes.io/no-provisioner Delete WaitForFirstConsumer false 61 #定義pv,使用local型別卷 [root@master01 local-pv-demo]#vim local-pv-demo.yaml apiVersion: v1 kind: PersistentVolume metadata: name: local-pv-demo spec: capacity: storage: 5Gi #儲存空間 volumeMode: Filesystem #卷模式 accessModes: - ReadWriteOnce #訪問模式 persistentVolumeReclaimPolicy: Delete #回收策略 storageClassName: local #儲存型別,透過local來定義 local: #卷外掛型別 path: /disks/vol1 #指定本地磁碟路徑 nodeAffinity: #節點選擇器 required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node01 #在節點1上,事先創好路徑 [root@master01 local-pv-demo]#kubectl apply -f local-pv-demo.yaml #檢視 [root@master01 local-pv-demo]#kubectl get pv #定義pvc [root@master01 local-pv-demo]#vim pvc-localpv-demo.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc-localpv-demo spec: accessModes:#訪問模式 - ReadWriteOnce resources: requests: #需要空間大小 storage: 5Gi storageClassName: local #透過哪個儲存類(兩種要麼統一儲存類,要麼都不屬於任何儲存類) [root@master01 local-pv-demo]#kubectl apply -f pvc-localpv-demo.yaml #檢視 此時處於pending狀態 [root@master01 local-pv-demo]#kubectl get pvc #定義pod 注意:pod排程器會結合pv控制器來判定該被排程到哪個節點上(hostpath卷不會,它不會使用pv control,無法影響pod schedule) [root@master01 local-pv-demo]#vim pod-with-localpv.yaml apiVersion: v1 kind: Pod metadata: name: pod-with-localpv spec: containers: - name: redis image: redis:7-alpine ports: - containerPort: 6379 name: redis volumeMounts: - mountPath: "/data" name: data volumes: - name: data persistentVolumeClaim: claimName: pvc-localpv-demo #使用上面定義的pvc [root@master01 local-pv-demo]#kubectl apply -f pod-with-localpv.yaml #檢視,建立在node1節點上 [root@master01 local-pv-demo]#kubectl get -f pod-with-localpv.yaml -o wide #此時檢視pvc,處於繫結狀態 [root@master01 local-pv-demo]#kubectl get pvc
儲存卷的具體的管理操作由相關的控制器向卷外掛發起呼叫請求來完成 ◼ AD控制器:負責儲存裝置的Attach/Detach操作 ◆Attach:將裝置附加到目標節點 ◆Detach:將裝置從目標節點上拆除 ◼ 儲存卷管理器:負責完成卷的Mount/Umount操作,以及裝置的格式化操作等 ◼ PV控制器:負責PV/PVC的繫結、生命週期管理,以及儲存卷的Provision/Delete操作 Scheduler:特定儲存外掛的排程決策會受到目標節點上的儲存卷的影響
CSI簡介
◼ 容器儲存介面規範,與平臺無關
◼ 驅動程式元件
◆CSI Controller:負責與儲存服務的API通訊從而完成後端儲存的管理操作
◆Node Plugin:也稱為CSI Node,負責在節點級別完成儲存卷的管理
CSI Controller:由StatefulSet控制器物件編排執行,副本量需要設定為1,以保證只會該儲存服務執行單個CSI Controller例項;
Node Plugin:由DaemonSet控制器物件編排執行,以確保每個節點上精確執行一個相關的Pod副本
動態置備儲存
Kuberentes社群 主社群:Kubernetes專案,及各關鍵子專案 SIG: Special Intresting Group, #csi-driver-nfs在特別興趣組專案下(github上) kubernetes-sigs/ csi-driver-nfs csi-driver-lvm ... 專案:csi-driver-nfs 負責為現有NFS Server提供PV的動態置備能力,它依賴於一個現有的NFS Server; 測試環境中,部署NFS Server的方法: (1)在Kubernetes上部署一個NFS Server;#只能在測試環境中使用 kubectl create namespace nfs kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/deploy/example/nfs-provisioner/nfs-server.yaml \ -n nfs #指定建立在nfs服務空間 訪問nfs服務的入口:nfs-server.nfs.svc.cluster.local (2)自行在Kubernetes叢集外部準備一個NFS Server; #購買網上的服務 匯出目錄時要使用的匯出選項:(rw,fsid=0,async,no_subtree_check,no_auth_nlm,insecure,no_root_squash) 部署csi-driver-nfs: remote install: curl -skSL https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/v4.6.0/deploy/install-driver.sh | bash -s v4.6.0 -- local install: git clone https://github.com/kubernetes-csi/csi-driver-nfs.git cd csi-driver-nfs ./deploy/install-driver.sh v4.6.0 local 預設部署在kube-system名稱空間,各Image的倉庫地址是registry.k8s.io;#可能需要上外網 #操作例項 #部署csi-driver-nfs,採用local install方式 [root@master01 ~]#git clone https://github.com/kubernetes-csi/csi-driver-nfs.git [root@master01 ~]#cd csi-driver-nfs/deploy #直接部署4.6.0版本 (每個節點下載映象並執行) [root@master01 deploy]#kubectl apply -f v4.6.0/ #檢視(預設在kube-system空間下) [root@master01 deploy]#kubectl get pods -n kube-system -o wide #nfs伺服器上 #建立路徑作為動態置備 [root@ubuntu ~]#mkdir /nfspv [root@ubuntu ~]#vim /etc/exports /nfspv 10.0.0.0/16(rw,fsid=0,async,no_subtree_check,no_auth_nlm,insecure,no_root_squash) #執行重新匯出 [root@ubuntu ~]#exportfs -arv #k8s控制節點主機上 #準備StroageClass,以完成csi-driver-nfs和nfs-server之間的對接: #要使用nfs上的儲存驅動,需要定義storageclass來完成 [root@master01 ~]#vim storageclass-csi-nfs.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nfs-csi provisioner: nfs.csi.k8s.io parameters: server: 10.0.0.155 #nfs伺服器地址 share: /nfspv #nfs對應地址(會在該路徑下自動建立子目錄作為動態置備儲存路徑) reclaimPolicy: Delete #生產中用delete回收策略有風險 volumeBindingMode: Immediate [root@master01 ~]#kubectl apply -f storageclass-csi-nfs.yaml #檢視儲存類 [root@master01 ~]#kubectl get sc #建立PVC進行測試(只需要建立pvc即可,pv會自動建立) #路徑在: csi-driver-nfs/deploy/example/pvc-nfs-csi-dynamic.yaml [root@master01 ~]#vim csi-driver-nfs/deploy/example/pvc-nfs-csi-dynamic.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs-dynamic namespace: default spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi storageClassName: nfs-csi #建立pvc [root@master01 ~]#kubectl create -f csi-driver-nfs/deploy/example/pvc-nfs-csi-dynamic.yaml #檢視pvc (顯示狀態已經Bound繫結了) [root@master01 ~]#kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE pvc-nfs-dynamic Bound pvc-f6797db7-bf8e-47fa-8bc1-6c26c6b35876 10Gi RWX nfs-csi <unset> 65s #檢視pv(pv已經被自動建立出來) [root@master01 ~]#kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pvc-f6797db7-bf8e-47fa-8bc1-6c26c6b35876 10Gi RWX Delete Bound default/pvc-nfs-dynamic nfs-csi <unset> 104s #在nfs服務上檢視,在路徑上能看到對應子目錄 [root@ubuntu ~]#cd /nfspv/ [root@ubuntu nfspv]#ls pvc-f6797db7-bf8e-47fa-8bc1-6c26c6b35876 #定義一個pod使用該pvc做測試 [root@master01 volumes]#vim pod-with-dyn-pvc.yaml apiVersion: v1 kind: Pod metadata: name: redis-pvc spec: containers: - name: redis image: redis:7-alpine imagePullPolicy: IfNotPresent ports: - containerPort: 6379 name: redisport volumeMounts: - mountPath: /data name: redis-pvc-vol volumes: - name: redis-pvc-vol persistentVolumeClaim: claimName: pvc-nfs-dynamic #使用上面的pvc [root@master01 volumes]#kubectl apply -f pod-with-dyn-pvc.yaml [root@master01 volumes]#kubectl exec -it redis-pvc -- /bin/sh /data # redis-cli 127.0.0.1:6379> set a "hi hello" OK 127.0.0.1:6379> BGSAVE Background saving started 127.0.0.1:6379> exit /data # ls dump.rdb #nfs伺服器上檢視到資料 [root@ubuntu nfspv]#ls pvc-f6797db7-bf8e-47fa-8bc1-6c26c6b35876/ dump.rdb
示例: 部署mysql, 使用csi-driver-nfs的動態置備儲存
注意: mysql如果用網路卷作為儲存,效能會有很大影響, 最好使用local卷
#pvc和pod建立在同一個目錄中,檔案可以部署多個資源 [root@master01 pods]#vim mydb.yaml --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-nfs-mydb spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi storageClassName: nfs-csi --- #上一個資源內容,用---隔開 apiVersion: v1 kind: Pod metadata: name: mydb spec: containers: - name: mydb image: mysql:8.0 env: - name: MYSQL_RANDOM_ROOT_PASSWORD #超級使用者賬號 value: M@geEdu - name: MYSQL_DATABASE value: wpdb - name: MYSQL_USER value: wpuser - name: MYSQL_PASSWORD value: wpP@ss volumeMounts: #呼叫下面的卷掛載 - name: mydb-stor mountPath: /var/lib/mysql/ #掛載點(mysql預設寫在該路徑下) volumes: - name: mydb-stor persistentVolumeClaim: claimName: pvc-nfs-mydb #呼叫上面pvc #建立名稱空間 [root@master01 pods]#kubectl create namespace dev [root@master01 pods]#kubectl apply -f mydb.yaml -n dev [root@master01 pods]#kubectl get pvc -n dev NAME STATUS VOLUME CAPACITY ACCESS MODES pvc-nfs-mydb Bound pvc-478ac6b1-c1c4-4fa1-a6b5-d77000011f45 10Gi RWX [root@master01 pods]#kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE pvc-478ac6b1-c1c4-4fa1-a6b5-d77000011f45 10Gi RWX Delete Bound dev/pvc-nfs-mydb nfs-csi <unset> 10m #nfs伺服器上 [root@ubuntu nfspv]#ls pvc-478ac6b1-c1c4-4fa1-a6b5-d77000011f45/ auto.cnf ca.pem ib_buffer_pool mysql public_key.pem undo_002 binlog.000001 client-cert.pem ibdata1 mysql.ibd server-cert.pem wpdb binlog.000002 client-key.pem ibtmp1 mysql.sock server-key.pem binlog.index '#ib_16384_0.dblwr' '#innodb_redo' performance_schema sys ca-key.pem '#ib_16384_1.dblwr' '#innodb_temp' private_key.pem undo_001
CAS(Container Attached Storage)簡介
容器附加儲存(Container Attached Storage) ◼ Kubernetes的卷通常是基於外部檔案系統或塊儲存實現,這種儲存方案稱為共享儲存(Shared Storage) ◼ CAS則是將儲存系統自身部署為Kubernetes叢集上的一種較新的儲存解決方案 ◆儲存系統自身(包括儲存控制器)在Kubernetes上以容器化微服務的方式執行 ◆使得工作負載更易於移植,且更容易根據應用程式的需求改動使用的儲存 ◆通常基於工作負載或者按叢集部署,因此消除了共享儲存的跨工作負載甚至是跨叢集的爆炸半徑 ◼ 儲存在 CAS 中的資料可以直接從叢集內的容器訪問,從而能顯著減少讀/寫時間 ◼ OpenEBS是CAS儲存機制的著名實現之一,由CNCF孵化 基於CAS的儲存解決方案,通常包含兩類元件 ◼ 控制平面 ◆負責配置卷以及其他同儲存相關任務 ◆由儲存控制器、儲存策略以及如何配置資料平面的指令組成 ◼ 資料平面 ◆接收並執行來自控制平面的有關如何儲存和訪問容器資訊的指令 ◆主要元件是實現池化儲存的儲存引擎,這類引擎本質上負責輸入/輸出卷路徑 ◆OpenEBS支援儲存引擎包括Mayastor、cStor、Jiva和OpenEBS LocalPV等 OPenEBS: CAS風格的儲存解決方案之一;由控制平面和資料平面,其中的資料平面的主要組成部分是儲存引擎 支援的儲存引擎,MayaStor, cStor, Jiva, LocalPV
CAS的重要特徵
◼ 儲控制器分解為可以彼此獨立執行的組成部分
◼ 每個工作負載都有自己的一個或多個控制器
OpenEBS簡介 ◼ OpenEBS能夠將Kubernetes工作節點上可用的任何儲存轉換為本地卷或分散式複製卷 ◼ 最初由MayaData構建,後捐贈給了CNCF,目前是CNCF的沙箱級專案 #分散式複製卷,一份資料存兩份,放到2個不同的節點上 OPenEBS: 卷型別:本地卷和分散式複製卷 本地卷:磁碟、分割槽、檔案系統目錄(local Volume)、ZFS、LVM 複製卷: iSCSI: cStor和Jiva NVMEoF: MayaStor Jiva或cStor要建立在本地卷功能的基礎之上; Jiva --> 依賴於本地捲來解決每個節點上的儲存需求 #OpenEBS架構 OpenEBS存在著眾多元件,他們大體可以分以兩大類 ◼ 資料引擎 ◼ 控制平面
如何選擇資料引擎 ◼ 應用程式處於生產狀態且不需要儲存級複製,則首選 LocalPV ◼ 應用程式處於生產狀態並且需要儲存級複製,則首選 cStor ◼ 應用程式較小、需要儲存級複製但不需要快照或克隆,則首選 Jiva ◼ 應用程式需要低延遲和接近磁碟的吞吐量,需要儲存級複製,並且工作節點具有效能較高的CPU、RAM和NVME,那麼 Mayastor 是首選 NDM(Node Disk Manager) ◼ 部署OpenEBS的過程中,NDM由專用DaemonSet編排執行於每個節點上 ◆負責發現裸裝置並過濾掉不支援使用的裝置,例如已經帶有檔案系統的磁碟 ◆需要特權模式,訪問/dev、/proc和/sys目錄來監視連線的裝置,並使用各種探測器獲取這些裝置的詳細資訊 ◼ 根據過濾器(filters)檢測附加到節點上的裸磁碟裝置,並將它們識別為“塊裝置CRD” ◆NDM支援使用include filters或exclude filters ◆filter的配置儲存於ConfigMap中 ◼ 基於節點上的裸磁碟裝置提供PV的儲存引擎,會依賴於NDM實現其功能,這包括Local PV device 和 cStor
部署使用OpenEBS的基本流程 ◼ 在各節點上部署iSCSI client #儲存引擎cStor,Jiva需要,其他不需要部署 ◼ 在Kubernetes叢集上部署OpenEBS ◼ 選擇要使用的資料引擎 ◼ 為選擇的資料引擎準備StorageClass
實際操作示例 (本地卷,無法跨節點, 自動置備pv儲存)
#安裝openebs環境 #部署pod [root@master01 ~]#kubectl apply -f https://openebs.github.io/charts/openebs-operator.yaml #會建立獨立名稱空間 openebs [root@master01 ~]#kubectl get ns #檢視名稱空間中的資源,會建立很多pod [root@master01 ~]#kubectl get all -n openebs #每個節點上都會跑一個ndm(用於發現獨立的裸磁碟裝置,配置為獨立的block device) [root@master01 ~]#kubectl get pods -n openebs -o wide #檢視CRD 自定義資源定義(沒發現一個塊磁碟,就會建立一個blockdevices,目前系統沒有,不用管) [root@master01 ~]#kubectl get crd blockdevices.openebs.io 2024-11-21T14:53:41Z #自動建立兩個儲存類(因為用的都是本地卷,所以採用延遲繫結機制) 可改儲存路徑,刪儲存類重建或修改下,並建目錄 [root@master01 ~]#kubectl get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE openebs-device openebs.io/local Delete WaitForFirstConsumer false 12h openebs-hostpath openebs.io/local Delete WaitForFirstConsumer false 12h #自此openebs環境準備完成,如果對lvm等有需求,openebs官網再部署其他pod #準備pvc檔案 [root@master01 local-pv-hostpath]#vim openebs-local-hostpath-pvc.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata: name: openebs-local-hostpath-pvc spec: storageClassName: openebs-hostpath #部署完openebs自動生成的storageClass,會自動置備儲存 accessModes: - ReadWriteOnce #此處必須是單路讀寫 resources: requests: #請求使用空間5個g storage: 5G [root@master01 local-pv-hostpath]#kubectl apply -f openebs-local-hostpath-pvc.yaml root@master01 local-pv-hostpath]#kubectl get pvc NAME STATUS STORAGECLASS VOLUMEATTRIBUTESCLASS AGE openebs-local-hostpath-pvc Pending openebs-hostpath <unset> 17s #準備pod (執行pod會自動置備pv儲存) [root@master01 local-pv-hostpath]#vim redis-with-openebs-local-hostpath.yaml apiVersion: v1 kind: Pod metadata: name: redis-with-openebs-local-hostpath spec: containers: - name: redis image: redis:7-alpine ports: - containerPort: 6379 name: redis volumeMounts: - mountPath: /data name: local-storage volumes: - name: local-storage persistentVolumeClaim: claimName: openebs-local-hostpath-pvc [root@master01 local-pv-hostpath]#kubectl apply -f redis-with-openebs-local-hostpath.yaml #檢視建立的pods,會是pending狀態(首次消費pv,pv有初始化過程,會下載一初始化pod在openebs空間,準備好會自動刪除) [root@master01 local-pv-hostpath]#kubectl get pods redis-with-openebs-local-hostpath 0/1 Pending 0 27s #等到running狀態後,檢視資料存到哪了 [root@master01 local-pv-hostpath]#kubectl get sc [root@master01 local-pv-hostpath]#kubectl get sc openebs-hostpath -o yaml ... Default value is /var/openebs/local ... #/var/openebs/local pod所在節點下,在該目錄下建立子目錄作為pv儲存後端 [root@master01 local-pv-hostpath]#kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE openebs-local-hostpath-pvc Bound pvc-a4125f1e-eaf3-4020-b7e8-1ff770ca7430 5G RWO openebs-hostpath <unset> 157m #節點3上檢視 [root@node03 ~]#ls /var/openebs/local pvc-a4125f1e-eaf3-4020-b7e8-1ff770ca7430
實際操作示例 (複製卷,跨節點冗餘)
#複製卷後臺用的還是本地卷,在本地卷基礎上加了複製的引擎(需要單獨的pod來執行) #這裡採用jiva (cstor和jiva的區別在於cstor可使用快照) #基於上面openebs環境部署完成後 #安裝jiva-operator [root@master01 ~]#kubectl apply -f https://openebs.github.io/charts/jiva-operator.yaml #每個節點上安裝jiva驅動節點 [root@master01 ~]#kubectl get pods -n openebs #定義jiva卷策略 [root@master01 jiva-csi]#vim openebs-jivavolumepolicy-demo.yaml apiVersion: openebs.io/v1alpha1 kind: JivaVolumePolicy #安裝jiva建立的新的jivavolumepolicy metadata: name: jivavolumepolicy-demo namespace: openebs #一般放在這個名稱空間下 spec: replicaSC: openebs-hostpath #複製時使用的儲存類,這裡用基於目錄儲存(上面用的本地卷方式相同) target: # This sets the number of replicas for high-availability # replication factor <= no. of (CSI) nodes replicationFactor: 2 #複製因子,資料打算冗餘幾份 [root@master01 jiva-csi]#kubectl apply -f openebs-jivavolumepolicy-demo.yaml #檢視建立的jivavolumepolicy資源型別 [root@master01 jiva-csi]#kubectl get jivavolumepolicy -n openebs NAME AGE jivavolumepolicy-demo 32s #建立儲存類 [root@master01 jiva-csi]#vim openebs-jiva-csi-storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: openebs-jiva-csi provisioner: jiva.csi.openebs.io allowVolumeExpansion: true #是否支援卷擴縮容(使用路徑不支援,用LVM支援) parameters: cas-type: "jiva" policy: "jivavolumepolicy-demo" #在哪個policy管控jiva儲存卷 [root@master01 jiva-csi]#kubectl apply -f openebs-jiva-csi-storageclass.yaml [root@master01 jiva-csi]#kubectl get sc #建立pvc [root@master01 jiva-csi]#vim openebs-jiva-csi-pvc.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata: name: openebs-jiva-csi-pvc spec: storageClassName: openebs-jiva-csi #使用上面的儲存類 accessModes: - ReadWriteOnce resources: requests: storage: 5Gi [root@master01 jiva-csi]#kubectl apply -f openebs-jiva-csi-pvc.yaml #檢視pvc,直接繫結了(繫結的是jiva卷) [root@master01 jiva-csi]#kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE openebs-jiva-csi-pvc Bound pvc-1057bf10-245f-4718-a0ca-f91cd899d952 5Gi RWO openebs-jiva-csi <unset> 31s #檢視jiva卷(jiva卷要複製2份資料到兩個節點上) Unknown是因為初始化pod還沒有執行完成(openebs空間下) [root@master01 jiva-csi]#kubectl get jivavolume -n openebs NAME REPLICACOUNT PHASE STATUS pvc-1057bf10-245f-4718-a0ca-f91cd899d952 2 Ready RW #openebs名稱空間下建立了3個pod,一個是jiva卷微控制器,另外兩個是兩份資料副本 [root@master01 jiva-csi]#kubectl get pods -n openebs pvc-1057bf10-245f-4718-a0ca-f91cd899d952-jiva-ctrl-6c75c9d5z4vl 2/2 Running pvc-1057bf10-245f-4718-a0ca-f91cd899d952-jiva-rep-0 1/1 Running pvc-1057bf10-245f-4718-a0ca-f91cd899d952-jiva-rep-1 1/1 Running #在openebs自動建立了2個pvc [root@master01 jiva-csi]#kubectl get pvc -n openebs openebs-pvc-1057bf10-245f-4718-a0ca-f91cd899d952-jiva-rep-0 openebs-pvc-1057bf10-245f-4718-a0ca-f91cd899d952-jiva-rep-1 #要使用jiva的pvc存資料,在所有節點(包括master)上安裝iSCSI client [root@node01 ~]#apt-get install open-iscsi [root@node01 ~]#systemctl enable --now iscsid #到此為止,jiva的環境設定完成 #使用redis例項 [root@master01 jiva-csi]#vim redis-with-openebs-jiva-pvc.yaml apiVersion: v1 kind: Pod metadata: name: redis-with-openebs-jiva-pvc spec: containers: - name: redis image: redis:7-alpine ports: - containerPort: 6379 name: redis volumeMounts: - mountPath: /data name: local-storage volumes: - name: local-storage persistentVolumeClaim: claimName: openebs-jiva-csi-pvc #使用上面基於jiva的pvc [root@master01 jiva-csi]#kubectl apply -f redis-with-openebs-jiva-pvc.yaml #測試 [root@master01 jiva-csi]#kubectl exec -it redis-with-openebs-jiva-pvc -- /bin/sh /data # redis-cli 127.0.0.1:6379> SET test "testing" OK 127.0.0.1:6379> BGSAVE Background saving started 127.0.0.1:6379> exit /data # ls dump.rdb lost+found#說明是二進位制裝置儲存空間 #檢視存在哪個捲上 [root@master01 jiva-csi]#kubectl get pvc -n openebs [root@master01 jiva-csi]#kubectl get pvc openebs-pvc-1057bf10-245f-4718-a0ca-f91cd899d952-jiva-rep-0 -o yaml -n openebs ... volume.kubernetes.io/selected-node: node01 #node1節點檢視(大部分rdb檔案被封裝了,因這是複製引擎) [root@node01 ~]#ls /var/openebs/local/pvc-023b9bd4-5f23-4e91-a16e-545bc1aeaeef/ log.info volume-head-001.img volume-snap-bc8e1cfc-55f9-41e2-b093-92ec82447d8f.img replica.log volume-head-001.img.meta volume-snap-bc8e1cfc-55f9-41e2-b093-92ec82447d8f.img.meta revision.counter volume.meta
要使用多路讀寫, 只能建nfs server。或者做一個jiva卷, 在jiva卷的上面再部署nfs服務(支援不太成熟,在官網上有寫)
1 ConfigMap和Secret基礎
特殊卷:configMap/secret 介紹
應用配置: 臨時卷:empytDir 本地卷:hostPath, local 網路卷:nfs, ... 特殊卷:configMap,secret, downwardAPI 卷擴充套件:CSI 特殊卷:configMap,secret, downwardAPI configMap/secret: 幾重概念: (1) 資源型別 (2) 卷外掛 資源型別:configMap、secret 儲存於api server中的資源物件, 最終儲存於etcd中 卷外掛: 在Pod上,基於該型別卷外掛,以卷的形式引用configmap/secret資源物件 容器配置引數: 環境變數: 1、啟動容器時,自定義要執行的命令及選項、引數; Dockerfile, CMD, ENTRYPOINT 2、透過環境進行配置; Dockerfile, entrypoint.sh指令碼來代理預處理環境變數 3、將配置焙進Image,重製Image; 4、將配置檔案放置在捲上,由容器透過卷載入; 卷:ConfigMap/Secret configMap:儲存非敏感的配置,資料以明文儲存 secret: 儲存敏感配置,資料以base64編碼格式儲存 資料格式: Key: Value Value:分為兩類 單行值:單行字串 多行值:檔案格式的配置資訊 每個物件中,可以儲存多個kv資料; ConfigMap和Secret是Kubernetes系統上兩種特殊型別的儲存卷 ◼ ConfigMap用於為容器中的應用提供配置資料以定製程式的行為,而敏感的配置資訊,例如金鑰、證書等則通常由Secret來配置 ◼ ConfigMap和Secret將相應的配置資訊儲存於資源物件中,而後在Pod物件上支援以儲存卷的形式將其掛載並載入相關的配置,從而降低了配置與映象檔案的耦合關係,提高了映象複用能力 ◼ Kubernetes藉助於ConfigMap物件實現了將配置檔案從容器映象中解耦,從而增強了工作負載的可移植性,使其配置更易於更改和管理,並避免了將配置資料硬編碼到Pod配置清單中 此二者都屬於名稱空間級別,只能被同一名稱空間中的Pod引用
◼ ConfigMap和Secret資源都是資料承載類的元件,是Kubernetes API的標準資源型別,是一等公民 ◼ 主要負責提供key-value格式的資料項,其值支援 ◆單行字串:常用於儲存環境變數值,或者命令列引數等 ◆多行字串:常用於儲存配置檔案的內容 #定義示例 apiVersion: v1 kind: ConfigMap metadata: name: myapp-confs data: PORT: "8080" myserver-status.cfg: | #多行的v用|加換行縮排的方式(將來轉為檔案會頂格寫不會有縮排) location /nginx-status { stub_status on; access_log off; } ◼ 從Kubernetes v1.19版本開始,ConfigMap和Secret支援使用immutable欄位建立不可變例項
環境變數
◼ 將configmap物件上的某key的值賦值給(valueFrom)指定的環境變數
卷
◼ 在Pod上基於configMap卷外掛引用configmap物件
◼ 在Container上掛載configMap卷
◆每個kv會分別被對映為一個檔案,檔名同key,value將成為檔案內容
2 ConfigMap
建立ConfigMap物件的方法有兩種 ◼ 命令式命令 ◆ 字面量:kubectl create configmap NAME --from-literal=key1=value1 ◆ 從檔案載入:kubectl create configmap NAME --from-file=[key=]/PATH/TO/FILE ◆ 從目錄載入: kubectl create configmap NAME --from-file=[key=]/PATH/TO/DIR/ ◼ 配置檔案 ◆命令式:kubectl create -f ◆宣告式:kubectl apply -f 提示:基於檔案內容生成時,可以使用命令式命令以dry-run模式生成並儲存 建立方法: 指令式命令: kubectl create configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1] [--dry-run=server|client|none] 直接值:直接於命令列給出kv 從檔案載入:從檔案載入生成kv 物件配置: kubectl create -f /path/to/configmap_manifest_file #例: #建立demoapp-cfg名的configmap;--from-literal指定kv;可以指定名稱空間,這裡預設 ~]#kubectl create configmap demoapp-cfg --from-literal=listen="127.0.0.1" --from-literal=port="8080" #檢視configmap,可以簡稱cm ~]#kubectl get cm #不建立configmap只生成yaml ~]#kubectl create configmap demoapp-cfg --from-literal=listen="127.0.0.1" --from-literal=port="8080" --dry-run=client -o yaml #從檔案中載入 ~]#kubectl create configmap nginx-cfg --from-file=./myserver.conf --dry-run=client -o yaml apiVersion: v1 data: myserver.conf: | server { listen 8080; server_name www.ik8s.io; include /etc/nginx/conf.d/myserver-*.cfg; location / { root /usr/share/nginx/html; } } kind: ConfigMap metadata: creationTimestamp: null name: nginx-cfg #可以指定鍵名,修改將來掛載的檔名 ~]#kubectl create configmap nginx-cfg --from-file=my.conf=./myserver.conf --dry-run=client -o yaml apiVersion: v1 data: my.conf: | server { listen 8080; server_name www.ik8s.io; include /etc/nginx/conf.d/myserver-*.cfg; location / { root /usr/share/nginx/html; } } kind: ConfigMap metadata: creationTimestamp: null name: nginx-cfg #載入多個檔案 ~]#kubectl create configmap nginx-cfg --from-file=./myserver.conf --from-file=./myserver-status.cfg --dry-run=client -o yaml #載入當前目錄下的所有檔案(無法改檔名) ~]#kubectl create configmap nginx-cfg --from-file=./ --dry-run=client -o yaml #使用示例(從環境變數中直接引用) [root@master01 configmaps_and_secrets]#vim configmaps-env-demo.yaml --- apiVersion: v1 kind: ConfigMap metadata: name: demoapp-config namespace: default data: demoapp.port: "8080" demoapp.host: 127.0.0.1 --- apiVersion: v1 kind: Pod metadata: name: configmaps-env-demo namespace: default spec: containers: - image: ikubernetes/demoapp:v1.0 name: demoapp env: - name: PORT valueFrom: #引用 configMapKeyRef: #configmap中的鍵引用 name: demoapp-config #configmap名字 key: demoapp.port #鍵名 optional: false #如在ConfigMap中找不到指定的key,K8s將會報錯 - name: HOST valueFrom: configMapKeyRef: name: demoapp-config key: demoapp.host optional: true #建立 [root@master01 configmaps_and_secrets]#kubectl apply -f configmaps-env-demo.yaml configmap/demoapp-config created pod/configmaps-env-demo created #檢視ConfigMap [root@master01 configmaps_and_secrets]#kubectl get cm NAME DATA AGE demoapp-config 2 27s [root@master01 configmaps_and_secrets]#kubectl get pods NAME READY STATUS RESTARTS AGE configmaps-env-demo 1/1 Running 0 58s #檢視是否監聽8080 [root@master01 configmaps_and_secrets]#kubectl exec configmaps-env-demo -- netstat -tnl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN #可以透過env列印出全部的環境變數 [root@master01 configmaps_and_secrets]#kubectl exec configmaps-env-demo -- env #一般不建議修改configmap,但是可以修改,改法有兩種 #第一種,直接修改configmap ]#vim configmaps-env-demo.yaml ... demoapp.port: "10080" #只是變了configmap ]#kubectl apply -f configmaps-env-demo.yaml #檢視configmap的yaml形式,埠已變10080 ]#kubectl get cm demoapp-config -o yaml #檢視環境變數PORT依然是8080 [root@master01 configmaps_and_secrets]#kubectl exec configmaps-env-demo -- env #pod裡面監聽的埠還是8080,除非重啟整個pod #使用示例(基於卷引用方式,卷外掛方式) 這種方式修改會重新注入到pod內部(能不能生效看系統是否自動裝載新配置) #建立configmap [root@master01 configmaps_and_secrets]#kubectl create configmap nginx-config-files --from-file=./nginx-conf.d/ #呼叫 [root@master01 configmaps_and_secrets]#vim configmaps-volume-demo.yaml apiVersion: v1 kind: Pod metadata: name: configmaps-volume-demo namespace: default spec: containers: - image: nginx:alpine name: nginx-server volumeMounts: - name: ngxconfs mountPath: /etc/nginx/conf.d/ #掛載點 readOnly: true #只讀 volumes: - name: ngxconfs configMap: name: nginx-config-files #引用nginx-config-files的configMap optional: false #必選 [root@master01 configmaps_and_secrets]#kubectl apply -f configmaps-volume-demo.yaml #進入pod容器 [root@master01 configmaps_and_secrets]#kubectl exec -it configmaps-volume-demo -- /bin/sh /etc/nginx/conf.d # ls -l total 0 lrwxrwxrwx 1 root root 24 Nov 23 08:27 myserver-gzip.cfg -> ..data/myserver-gzip.cfg lrwxrwxrwx 1 root root 26 Nov 23 08:27 myserver-status.cfg -> ..data/myserver-status.cfg lrwxrwxrwx 1 root root 20 Nov 23 08:27 myserver.conf -> ..data/myserver.conf /etc/nginx/conf.d # ls -al total 12 drwxrwxrwx 3 root root 4096 Nov 23 08:27 . drwxr-xr-x 3 root root 4096 Nov 12 02:02 .. drwxr-xr-x 2 root root 4096 Nov 23 08:27 ..2024_11_23_08_27_23.4057447185 lrwxrwxrwx 1 root root 32 Nov 23 08:27 ..data -> ..2024_11_23_08_27_23.4057447185 lrwxrwxrwx 1 root root 24 Nov 23 08:27 myserver-gzip.cfg -> ..data/myserver-gzip.cfg lrwxrwxrwx 1 root root 26 Nov 23 08:27 myserver-status.cfg -> ..data/myserver-status.cfg lrwxrwxrwx 1 root root 20 Nov 23 08:27 myserver.conf -> ..data/myserver.conf #實際上配置檔案被關聯到..data路徑下,而它其實是..2024_11_23_08_27_23.4057447185這個隱藏目錄,名字是時間戳 #這個時間戳代表configMap版本,每次改了configMap,configMap版本會變 #修改configMap內容測試 #直接線上修改configMap(這樣改有風險,不建議) [root@master01 ~]#kubectl edit cm nginx-config-files #隨便改寫內容儲存 #每個pod會等待一段時間(一般幾秒~幾十秒),pod內檔案才會變更,時間k8s自行控制 #新版本可以發現配置檔案變更並生效。1.20及之前的版本做不到 #檢視內容,時間戳已發生改變 /etc/nginx/conf.d # ls -al total 12 drwxrwxrwx 3 root root 4096 Nov 23 08:43 . drwxr-xr-x 3 root root 4096 Nov 12 02:02 .. drwxr-xr-x 2 root root 4096 Nov 23 08:43 ..2024_11_23_08_43_42.901207710 lrwxrwxrwx 1 root root 31 Nov 23 08:43 ..data -> ..2024_11_23_08_43_42.901207710 lrwxrwxrwx 1 root root 24 Nov 23 08:27 myserver-gzip.cfg -> ..data/myserver-gzip.cfg lrwxrwxrwx 1 root root 26 Nov 23 08:27 myserver-status.cfg -> ..data/myserver-status.cfg lrwxrwxrwx 1 root root 20 Nov 23 08:27 myserver.conf -> ..data/myserver.conf #檢視裡面nginx的配置已變更 #nginx -T可以檢視當前nginx載入的配置,經確認也已變更(說明自動過載新配置了) /etc/nginx/conf.d #nginx -T #注:這種方式修改會重新注入到pod內部,是否生效看系統是否自動裝載新配置(新的服務都可以,早期java可能要重啟pod)
Secret資源型別: 型別之分: kubectl create secret (docker-registry | generic | tls) [options] docker-registry:專用於認證到docker registry服務上的認證資訊型別 kubectl create secret docker-registry NAME --docker-username=user --docker-password=password --docker-email=email[--docker-server=string] 引用方式:pods.spec.imagePullSecrets,列表型資料 第二種引用方式:pods.spec.serviceAccountName serviceaccount.imagePullSecrets tls:專用配置服務基於ssl/tls通訊時使用的數字證書和私鑰的型別 kubectl create secret tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run=server|client|none] 證書的鍵名:tls.crt 私鑰的鍵名:tls.key generic: 通用型別,可劃分為多種子型別 --type
建立Secret資源
secret和configmap的顯著區別是儘量不要用載入環境變數的方式去載入secret(導致敏感資訊洩露), 用卷的方式載入
但有些場景沒辦法, 像mysql必須使用環境變數提供
支援類似於ConfigMap的建立方式,但Secret有型別子命令,而且不同型別在data或stringData欄位中支援巢狀使用的key亦會有所有同; 命令式命令 ◼ generic ◆kubectl create secret generic NAME [--type=string] [--from-file=[key=]source] [--from-literal=key1=value1] ◆除了後面docker-registry和tls命令之外的其它型別,都可以使用該命令中的--type選項進行定義,但有些型別有key的特定要求 ◼ tls ◆kubectl create secret tls NAME --cert=path/to/cert/file --key=path/to/key/file ◆通常,其儲存cert檔案內容的key為tls.crt,而儲存private key的key為tls.key ◼ docker-registry ◆kubectl create secret docker-registry NAME --docker-username=user --docker-password=password --docker-email=email [--docker-server=string] [--from-file=[key=]source] ◆通常,從已有的json格式的檔案載入生成的就是dockerconfigjson型別,命令列直接量生成的也是該型別 #generic示例 (使用環境變數引用,不太靠譜,資訊依然會洩露): #建立secret generic型別,名稱叫mysql-secret (自動做base64編碼進行儲存) [root@master01 ~]#kubectl create secret generic mysql-secret --from-literal=root.password='M@geEdu' --from-literal=user.password="wpP@ss" -n wordpress #檢視secret (不歸類的都定義為Opaque) DATA為2表示有2項 [root@master01 pods]#kubectl get secret -n wordpress NAME TYPE DATA AGE mysql-secret Opaque 2 14s [root@master01 pods]#kubectl get secret -o yaml -n wordpress apiVersion: v1 items: - apiVersion: v1 data: root.password: TUBnZUVkdQ== user.password: d3BQQHNz kind: Secret metadata: creationTimestamp: "2024-11-23T10:04:35Z" name: mysql-secret namespace: default resourceVersion: "634108" uid: 3b51fdcf-ba8c-437a-8b65-1b6273ea3f03 type: Opaque kind: List metadata: resourceVersion: "" #定義mysql的pod,引用上面的secret ]#vim pod-mydb.yaml apiVersion: v1 kind: Pod metadata: name: mydb namespace: wordpress spec: containers: - name: mydb image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD #超級使用者賬號 valueFrom: secretKeyRef: name: mysql-secret key: root.password optional: false #必選 - name: MYSQL_DATABASE #放在secret或configmap都行 value: wpdb - name: MYSQL_USER #放在secret或configmap都行 value: wpuser - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: user.password [root@master01 ~]#kubectl apply -f pod-mydb.yaml [root@master01 ~]#kubectl get -f pod-mydb.yaml #進入pod [root@master01 ~]#kubectl exec -ti mydb -n wordpress -- /bin/sh #檢視環境變數可以看到密碼 sh-5.1# env #docker-registry示例 ]#kubectl create secret docker-registry magedu-dockerhub --docker-username=magedu --docker-password=M@geEdu --docker-email=mage@magedu.com --dry-run=client -o yaml apiVersion: v1 data: .dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJtYWdlZHUiLCJwYXNzd29yZCI6Ik1AZ2VFZHUiLCJlbWFpbCI6Im1hZ2VAbWFnZWR1LmNvbSIsImF1dGgiOiJiV0ZuWldSMU9rMUFaMlZGWkhVPSJ9fX0= kind: Secret metadata: creationTimestamp: null name: magedu-dockerhub type: kubernetes.io/dockerconfigjson #引用:建立pod時修改pods.spec.imagePullSecrets欄位(列表型資料),把Secret值寫入 #tls示例 #準備好私鑰和證書 [root@master01 certs.d]#ls nginx.crt nginx.key #不建立tls型別的secret,生成yaml [root@master01 certs.d]#kubectl create secret tls nginx-cert --cert=./nginx.crt --key=./nginx.key --dry-run -o yaml W1123 11:13:44.112377 194065 helpers.go:703] --dry-run is deprecated and can be replaced with --dry-run=client. apiVersion: v1 data: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURsVENDQW4yZ0F3SUJBZ0lVR2Zya05FeGZiS2N5Yy9LYkpLUXJ5MzRTcHI0d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1dqRUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdNQjBKbGFXcHBibWN4RURBT0JnTlZCQWNNQjBKbAphV3BwYm1jeER6QU5CZ05WQkFvTUJrUmxkazl3Y3pFV01CUUdBMVVFQXd3TmQzZDNMbWxzYVc1MWVDNXBiekFlCkZ3MHlNREEwTVRVd05UQXdORE5hRncweU1EQTFNVFV3TlRBd05ETmFNRm94Q3pBSkJnTlZCQVlUQWtOT01SQXcKRGdZRFZRUUlEQWRDWldscWFXNW5NUkF3RGdZRFZRUUhEQWRDWldscWFXNW5NUTh3RFFZRFZRUUtEQVpFWlhaUApjSE14RmpBVUJnTlZCQU1NRFhkM2R5NXBiR2x1ZFhndWFXOHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCCkR3QXdnZ0VLQW9JQkFRRE1pNSsyUStZTlNxeS9vVVhNQzAraklqc2puS2M5SzdjVzYweFhrQzZOa3lSY3BZSmwKdWM4ckFYRUxyZjUxMmJUWGhxb0hqVG5JeFExVFROeDNRbE9oTHBYVjJCbGtObVNzY0w0Uy9IL1VEWTlQayt0cwpiOGlEZSszdlBEQ1ZiQytvOEFYYUhUaktaQ0pXc0oxY0RJY0JGenQ0MFQzUWswL1hQcVYrM1pERWFNcW9LYklJCmRvbENLLzZiN1BlaElXVVFWeFVDK3NoZ0xVbjJReXJmK0UrRC9TQmZFOVd3UGp0YXdCeHdxaDZOczV1dEVsL0cKVmltbEMxM2tsWTNGQ1RMWEhFU3hNKzdGNlU2VUdpYm1CWFRsLzZlV1I2bmlVdW1kMjhyU2NneXVDTnA1NGY2cAp1VGFMK0ZNbG0wN0NiS3lLWHhDZHpVNXVrSFlOYXlqa3p3ck5BZ01CQUFHalV6QlJNQjBHQTFVZERnUVdCQlE1ClhNbkhJanZYZFVCb1BEd3BpbjdiVWN6SHJqQWZCZ05WSFNNRUdEQVdnQlE1WE1uSElqdlhkVUJvUER3cGluN2IKVWN6SHJqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQlVwU1pSNUE5Rgo2bVZhSU16TUl0anZadXFpRDNpeG9PN3NEdEZCcGVsYVZNV0dIaTZ3cFlTL21kNkNmS0hQQ3pySDhnNmpWTGhaClRpRWd5OVREUG5wMmU0VWNBUzdYMVBPM21GWTVscGpVakxJZjR4bUZrUy9FdFQ1ZE5TbUF1NGxGN2ZrRkEraEYKc244b2s5bk9DNk05OVBxbmQ1SlpVR1pwRFJCK1NQblpIVzZ3R2JiSVd3R3hPQmNtejBBMHNJNTkyVm9POThYKwoxK2w4MEVmdkhjRDJQRmdmSzhqS3g5eEl0UjEwcnBtVE4yQmtPQlBiREZ4SHZEcTNjRlRBSVVhMGhWS04xT0xJCk1oSEsvVnJmRUxHanJvY0pTZEFxcGpPTFl0R2JQN09sWURHTzFPRDk2ZEVTYStWWTRXYnFqYzc5S0luREQrWnkKWVlqbkJKR2F5VExyCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBekl1ZnRrUG1EVXFzdjZGRnpBdFBveUk3STV5blBTdTNGdXRNVjVBdWpaTWtYS1dDClpiblBLd0Z4QzYzK2RkbTAxNGFxQjQwNXlNVU5VMHpjZDBKVG9TNlYxZGdaWkRaa3JIQytFdngvMUEyUFQ1UHIKYkcvSWczdnQ3end3bFd3dnFQQUYyaDA0eW1RaVZyQ2RYQXlIQVJjN2VORTkwSk5QMXo2bGZ0MlF4R2pLcUNteQpDSGFKUWl2K20rejNvU0ZsRUZjVkF2cklZQzFKOWtNcTMvaFBnLzBnWHhQVnNENDdXc0FjY0tvZWpiT2JyUkpmCnhsWXBwUXRkNUpXTnhRa3kxeHhFc1RQdXhlbE9sQm9tNWdWMDVmK25sa2VwNGxMcG5kdkswbklNcmdqYWVlSCsKcWJrMmkvaFRKWnRPd215c2lsOFFuYzFPYnBCMkRXc281TThLelFJREFRQUJBb0lCQUNjeWpvMndIMUxtdjRvTgpqc0dXWFZHR3lzeDlSYk04UUY3ZEFvazVNU0tpVXZLS0tSM3phSmIyTk1Lbk9qODlWQ0dGUmVvaWp6TkJSOWR4CndFSCtiT1pUZGhVL3owWGNBcGpsRmhldldaTzZjWDh2ZW9zU05OdTFrUmdxY2FrQXpYVlRZZHUxZzkrTkp1TnoKL3dQWHhydFh4MmJVdWtMUktCaTRnYUI1TnpmY0FSdzBIWG9aUjExbVRydkhLSWRSQUx4Q21KR09aTlg5RjhGMQpsQ1dCN2hjWVNkRVg5MTBkT2VFTlRzUUNMeVJvaDdXWkJSejBEUVBYNnNJd2FJTGtFT0puSVQ0K3JYNldCZGdkCmRkRjN3L0ZYcGxFdW9BSVplSDJmZnkvMzNyTFpSSXlSZWFEVnJUeHFUMnVOQlVkbkJPN1NFa2VYV25kUEh2V2MKaUpRUGxTRUNnWUVBOXdGQmpzOGk2a0YrWDM1N09ueFpuZXB4WnVZMGhqRk5BcVdPSGlUMWsvcWt6T2pRSG1kMgpHSnJZZjc0OUc3VHB2R2Y2TytzalIvZm96ODVsdFViSnh5Wk1IK2JqQzgwM0VUeXp2TmlXZzE2MVp6Nyt5NGpKClI5dlhCS05vd0NlazVFYVRyZ3owZkp5bjJ1WlRDak1aUE1WVVFBRmlYc0RtUTAybUp6dVdwTmtDZ1lFQTAvNkkKTjg2a2szalRzaHhvTEM5VUR0K0x4VTVaUE8vS0t0a21SUEorYU1IbS9uUXJYazZ6a1pJVUF2OHIrOWZXVXdHbQpYUmd2QjVlaHAvT1htdkxLWEhSVmhhVjc3QTZvMEVQVFRrcU5iZU5GSHg1cVFObU85ZHdIVTNSTW5PeWk0TnluCmFJNlljNGIvMWJXdXlBYlVlc0tEdXI3L0FMOFo5UU1XVUVUb2pSVUNnWUF2bzY1aFBOSWZIRUtqYUdHY0JoL0MKdFZUcDQ3eDlwVVNWSGhrcTl6WG1OSkZVZEJLdnlvU2Nla0VIWWttbTdsMm1XT2VLWnUrSEVlbDFLdm15M05STgo5TFQ1OGk0WU9KeEdWczdUdlhKS0pCb1lyNjIwMDh6K2J3Z3BmTnJYTk00NHVPUUN6YnpaeTkwVCt4aEkvMUgrCnhwQlpSK3NSRzJOTjE4d1VCUW9wQVFLQmdFdnNHWDdiRytmUTJ3Z3Iwa2NZd0NML2ZvQXdPaGR2elZpaEltcUkKNmlxOFh1ejhUOWZibWNYbHFoTVVyZnpvNU5JZmdpUlBGL0RCSmwwUENWbXQ0RGxTVkpxamxJa0xDdnhqZmhiSQo3blBQZEI3YjlyTzQ5dEVvZHRzMWlJYWUzUXBwRys5L09pd055aXdRZ0VNVTV4Mzc4Yzk4dmJqWHVBVWVrT3c0CmNZeXRBb0dBTEtNazBwMUUrZHJtVzVuS3VMa2dyNDYzbmtlenBld2Y4eVJ2UHZQcUo4UkdobTBGblphOW1mQ0YKd1JCMDh1MkZ6YTRHSldEY0p2VG9kV3NtSmpCVHRMQ0wzaFlCSmNNQXNVNEdEbXBCZDdQMVBQQ1ZCb0JMeVJjbApyK2VTOFJzZTRwTERjOWdpQk9ZSGp4Q2hsVktSdVhESmh2V2k0azZVNGY5bStBNmp4STg9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== kind: Secret metadata: creationTimestamp: null name: nginx-cert type: kubernetes.io/tls #tls示例,建立nginx服務,配https證書和秘鑰 [root@master01 nginx-ssl-conf.d]#vim myserver.conf server { listen 443 ssl; server_name www.ik8s.io; ssl_certificate /etc/nginx/certs/tls.crt; #secret掛載點,鍵名為檔名(會自動base64解碼) ssl_certificate_key /etc/nginx/certs/tls.key; #secret掛載點,鍵名為key(會自動base64解碼) ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; include /etc/nginx/conf.d/myserver-*.cfg; location / { root /usr/share/nginx/html; } } server { listen 80; server_name www.ilinux.io; return 301 https://$host$request_uri; } #pod檔案 [root@master01 configmaps_and_secrets]#vim secrets-volume-demo.yaml apiVersion: v1 kind: Pod metadata: name: secrets-volume-demo namespace: default spec: containers: - image: nginx:alpine name: ngxserver volumeMounts: - name: nginxcerts mountPath: /etc/nginx/certs/ readOnly: true - name: nginxconfs #載入配置檔案 mountPath: /etc/nginx/conf.d/ readOnly: true volumes: - name: nginxcerts secret: secretName: nginx-ssl-secret #secret名稱 - name: nginxconfs configMap: name: nginx-sslvhosts-confs optional: false #不加,預設為false #建立secret tls型別 [root@master01 configmaps_and_secrets]#kubectl create secret tls nginx-ssl-secret --cert=./certs.d/nginx.crt --key=./certs.d/nginx.key [root@master01 configmaps_and_secrets]#kubectl get secret #建立configmap [root@master01 configmaps_and_secrets]#kubectl create configmap nginx-sslvhosts-confs --from-file=./nginx-ssl-conf.d/ [root@master01 configmaps_and_secrets]#kubectl get cm #建立pod [root@master01 configmaps_and_secrets]#kubectl apply -f secrets-volume-demo.yaml #進入pod檢視 [root@master01 configmaps_and_secrets]#kubectl exec -it secrets-volume-demo -- /bin/sh / # netstat -tnl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN / # cd /etc/nginx/certs/ /etc/nginx/certs # ls tls.crt tls.key #和configmap的引用邏輯相同 /etc/nginx/certs # ls -al total 4 drwxrwxrwt 3 root root 120 Nov 23 12:42 . drwxr-xr-x 1 root root 4096 Nov 23 12:42 .. drwxr-xr-x 2 root root 80 Nov 23 12:42 ..2024_11_23_12_42_04.1485291318 lrwxrwxrwx 1 root root 32 Nov 23 12:42 ..data -> ..2024_11_23_12_42_04.1485291318 lrwxrwxrwx 1 root root 14 Nov 23 12:42 tls.crt -> ..data/tls.crt lrwxrwxrwx 1 root root 14 Nov 23 12:42 tls.key -> ..data/tls.key #配置資訊發生改變,要確保應用程式能自動過載(邏輯同configmap)
4 DownwardAPI 和 Projected
DownwardAPI ◼ 與ConfigMap和Secret不同,DownwardAPI自身並非一種獨立的API資源型別 ◼ DownwardAPI只是一種將Pod的metadata、spec或status中的欄位值注入到其內部Container裡的方式 DownwardAPI提供了兩種方式用於將 POD 的資訊注入到容器內部 ◼ 環境變數:用於單個變數,可以將 POD 資訊和容器資訊直接注入容器內部 #一般採用這種 ◼ Volume掛載:將 POD 資訊生成為檔案,直接掛載到容器內部中去
在容器上基於DownwardAPI引用Pod後設資料,可透過兩種欄位完成
◼ fieldRef:引用常規的後設資料
◼ resourceFieldRef:引用同資源限制和資源需求相關的後設資料
fieldRef 如圖
有關容器資源限制和資源需求的資訊則要透過resourceFieldRef 欄位注入
◼ 這些資訊都能夠基於環境變數和卷的方式注入到容器中
resourceFieldRef 如圖
#透過環境變數示例 #下面的配置片斷擷取自由三個RabbitMQ Pod構建的RabbitMQ Cluster配置示例之上 containers: - name: rabbitmq image: registry.magedu.com/rabbitmq/rabbitmq:3.12-management ports: - containerPort: 15672 name: discovery - containerPort: 5672 name: amqp env: - name: RABBIT_POD_NAME valueFrom: fieldRef: apiVersion: v1 #pod api版本號 fieldPath: metadata.name - name: RABBIT_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: RABBITMQ_NODENAME #引用變數$()實現 value: rabbit@$(RABBIT_POD_NAME).rabbitmq.$(RABBIT_POD_NAMESPACE).svc.cluster.local - name: RABBITMQ_USE_LONGNAME value: "true" - name: RABBITMQ_CONFIG_FILE value: "/config/rabbitmq" - name: RABBITMQ_ERLANG_COOKIE valueFrom: secretKeyRef: name: rabbit-secret key: RABBITMQ_ERLANG_COOKIE - name: K8S_HOSTNAME_SUFFIX value: .rabbitmq.$(RABBIT_POD_NAMESPACE).svc.cluster.local #透過volume掛載示例(一般不這麼用) 必須是上面支援使用卷引用的欄位 ]#vim pod-use-downwardapi.yaml apiVersion: v1 kind: Pod metadata: labels: run: pod-use-downwardapi name: pod-use-downwardapi spec: containers: - image: ikubernetes/demoapp:v1.0 name: pod-use-downwardapi volumeMounts: - name: dapi mountPath: /dapi volumes: - name: dapi downwardAPI: items: - path: "name" #掛載路徑下檔名 fieldRef: #引用欄位名稱 fieldPath: metadata.name #進入容器 ]#ls /dapi/ name ]#cat name pod-use-downwardapi
Projected Volume是一種特殊的卷型別,它能夠將已存在的多個卷投射進同一個掛載點目錄中 Projected Volume僅支援對如下四種型別的卷(資料來源)進行投射操作,這類的卷一般都 是用於為容器提供預先定義好的資料 ◼ Secret:投射Secret 物件 ◼ ConfigMap:投射ConfigMap物件 ◼ DownwardAPI:投射Pod後設資料 ◼ ServiceAccountToken:投射ServiceAccount Token #例 volumes: - name: local-storage persistentVolumeClaim: claimName: openebs-jiva-csi-pvc - name: kube-api-access-9s76w projected: defaultMode: 420 sources: #指定哪些卷投射進同一路徑下 - serviceAccountToken: expirationSeconds: 3607 path: token - configMap: items: - key: ca.crt path: ca.crt name: kube-root-ca.crt - downwardAPI: items: - fieldRef: apiVersion: v1 fieldPath: metadata.namespace #內容 path: namespace #以namespace檔名掛載 #示例: 修改pod檔案 ]#vim pod-use-projected.yaml apiVersion: v1 kind: Pod metadata: labels: run: pod-use-projected name: pod-use-projected spec: containers: - image: ikubernetes/demoapp:v1.0 name: pod-use-downwardapi volumeMounts: - name: proj mountPath: /proj volumes: - name: proj projected: defaultMode: 0644 sources: - configMap: items: - key: myserver.conf path: my.conf #換個名字 name: nginx-config-files - secret: items: - key: tls.crt path: nginx.crt #換個名字 name: nginx-ssl-secret - downwardAPI: items: - fieldRef: apiVersion: v1 fieldPath: metadata.namespace #內容 path: namespace #以namespace檔名掛載 ]#kubectl apply -f pod-use-projected.yaml #進入容器 ]#kubectl exec -it pod-use-projected -- /bin/sh ]#ls /proj ]#my.conf namespace nginx.crt
總結:
為Pod提供配置:
ConfigMap、Secret
API Server支援資源型別:需要先定義出資源物件,而後引用
downwardAPI
非為資源型別,可直接引用,因為它們自身即為Pod屬性
fieldRef
resourceFieldRef
Projected
非為資源型別,但其要引用現有的configmap、secret資源物件,或downwardAPI中的後設資料資訊
投射進同一個掛載點