關於 Kubernetes 中的 Volume 與 GlusterFS 分散式儲存
容器中持久化的檔案生命週期是短暫的,如果容器中程式崩潰宕機,kubelet 就會重新啟動,容器中的檔案將會丟失,所以對於有狀態的應用容器中持久化儲存是至關重要的一個環節;另外很多時候一個 Pod 中可能包含多個 Docker 映象,在 Pod 內資料也需要相互共享,Kubernetes 中 Pod 也可以增加副本數量,遇到故障時 Pod 可以轉移到其它節點,為了浮動節點都能夠訪問統一的持久化儲存以及容器間共享資料,Kubernetes 中定義了 Volume 來解決這些問題 ,從本質上講,Volume 只是一個目錄,可能包含一些資料,Pod 中的容器可以訪問它。該目錄是何種形式,是由所使用的 Volume 型別決定的。
Volume 資料卷型別
如果使用公有云,根據不同的雲廠商提供的服務可以選擇如下型別
awsElasticBlockStore
azureDisk
azureFile
以下介紹一些常用的型別
emptyDir
一般適用與臨時檔案場景,如上傳圖片執行時生成的流檔案,Pod 中的容器都能夠完全讀寫,但是 Pod 如果被移除,資料也就被刪除,容器宕機不會刪除 Pod ,因此不會造成資料丟失。要使用 Volume ,Pod 中需要使用.spec.volumes 配置定義型別,然後使用 .spec.containers.volumeMounts 配置定義掛載的資訊。
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume emptyDir: {}
hostPath
hostPath Volume 為 Pod 掛載宿主機上的目錄或檔案,使得容器可以使用宿主機的高速檔案系統進行儲存。缺點是,Pod 是動態在各個節點上排程。當一個 Pod 在當前節點上啟動並通過 hostPath儲存了檔案到本地以後,下次排程到另一個節點上啟動時,就無法使用在之前節點上儲存的檔案。
apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - image: test-container name: test-name volumeMounts: - name: test-volume mountPath: /cache volumes: - name: test-volume hostPath: path: /data
nfs
我們前面使用的 hostPath 和 emptyDir 型別的 Volume 有可能被 kubelet 清理掉,也不能被“遷移”到其他節點上,不具備持久化特性。 NFS(網路檔案系統)服務需要搭建好,共享到 Pod 中,與 emptyDir
移除 Pod 時刪除的內容不同,NFS
卷的內容將被保留,下次執行 Pod 可以繼續使用,對於一些 IO 與網路要求不高的的場景可以使用。
apiVersion: v1 kind: PersistentVolume metadata: name: test--nfs-pv spec: capacity: storage: 5Gi accessModes: - ReadWriteMany flexVolume: driver: "k8s/nfs" fsType: "nfs" options: server: "192.168.10.100" # NFS 伺服器地址 path: "/"
cephfs
Cephfs 是一個分散式儲存系統,誕生於2004年,最早致力於開發下一代高效能分散式檔案系統的專案。提前是也需要提前搭建好儲存叢集服務,也可以使用 Rook (支援 Ceph),現屬於 CNCF 孵化專案。
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: fast provisioner: kubernetes.io/rbd parameters: monitors: 10.16.153.105:6789 adminId: kube adminSecretName: ceph-secret adminSecretNamespace: kube-system pool: kube userId: kube userSecretName: ceph-secret-user userSecretNamespace: default fsType: ext4 imageFormat: "2" imageFeatures: "layering"
glusterfs
GlusterFS 是一個開源的分散式檔案系統,具有強大的橫向擴充套件能力,通過擴充套件能夠支援數 PB 儲存容量和處理數千客戶端。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: slow provisioner: kubernetes.io/glusterfs parameters: resturl: "http://127.0.0.1:8081" clusterid: "630372ccdc720a92c681fb928f27b53f" restauthenabled: "true" restuser: "admin" secretNamespace: "default" secretName: "heketi-secret" gidMin: "40000" gidMax: "50000" volumetype: "replicate:3"
配置 GlusterFS
準備三臺伺服器
172.23.216.48 gfs_node_1 172.23.216.49 gfs_node_2 172.23.216.50 gfs_node_3
安裝 Glusterfs
$ yum install centos-release-gluster $ yum install glusterfs-server $ systemctl start glusterd.service $ systemctl enable glusterd.service $ systemctl status glusterd.service
建立儲存目錄
$ mkdir /opt/gfs_data
新增節點
$ gluster peer probe node2
$ gluster peer probe node3
檢視節點
$ gluster peer status
Number of Peers: 2 Hostname: 172.23.216.49 Uuid: 4dcfad42-e327-4a79-8a5a-a55dc92982ba State: Peer in Cluster (Connected) Hostname: 172.23.216.50 Uuid: 84e90bcf-af22-4cac-a6b1-e3e0d87d7eb4 State: Peer in Cluster (Connected)
建立資料卷(測試使用分散式模式,生產勿用)
# 複製模式 $ gluster volume create k8s-volume replica 3 transport tcp gfs_node_1:/opt/gfs_data gfs_node_2:/opt/gfs_data gfs_node_3:/opt/gfs_data force # 分佈卷(預設模式) $ gluster volume create k8s-volume transport tcp 172.23.216.48:/opt/gfs_data 172.23.216.49:/opt/gfs_data 172.23.216.50:/opt/gfs_data force
備註:其他卷模式
啟動資料卷
$ gluster volume start k8s-volume
$ gluster volume info
Volume Name: k8s-volume
Type: Distribute
Volume ID: 1203a7ab-45c5-49f0-a920-cbbe8968fefa
Status: Started
Snapshot Count: 0
Number of Bricks: 3
Transport-type: tcp
Bricks:
Brick1: 172.23.216.48:/opt/gfs_data
Brick2: 172.23.216.49:/opt/gfs_data
Brick3: 172.23.216.50:/opt/gfs_data
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
相關命令
#為儲存池新增/移除伺服器節點 $ gluster peer probe $ gluster peer detach $ gluster peer status #建立/啟動/停止/刪除卷 $ gluster volume create [stripe | replica ] [transport [tcp | rdma | tcp,rdma]] ... $ gluster volume start $ gluster volume stop $ gluster volume delete 注意,刪除卷的前提是先停止卷。 #檢視卷資訊 $ gluster volume list $ gluster volume info [all] $ gluster volume status [all] $ gluster volume status [detail| clients | mem | inode | fd] #檢視本節點的檔案系統資訊: $ df -h [] #檢視本節點的磁碟資訊: $ fdisk -l
Kubernetes 中配置 GlusterFS
修改如下:
glusterfs-endpoints
{ "kind": "Endpoints", "apiVersion": "v1", "metadata": { "name": "glusterfs-cluster" }, "subsets": [ { "addresses": [ { "ip": "172.23.216.48" } ], "ports": [ { "port": 1000 } ] }, { "addresses": [ { "ip": "172.23.216.49" } ], "ports": [ { "port": 1000 } ] }, { "addresses": [ { "ip": "172.23.216.50" } ], "ports": [ { "port": 1000 } ] } ] }
glusterfs-service.json
{ "kind": "Service", "apiVersion": "v1", "metadata": { "name": "glusterfs-cluster" }, "spec": { "ports": [ {"port": 1000} ] } }
glusterfs-pod.json(建立測試 Pod)
{ "apiVersion": "v1", "kind": "Pod", "metadata": { "name": "glusterfs" }, "spec": { "containers": [ { "name": "glusterfs", "image": "nginx", "volumeMounts": [ { "mountPath": "/mnt/glusterfs", "name": "glusterfsvol" } ] } ], "volumes": [ { "name": "glusterfsvol", "glusterfs": { "endpoints": "glusterfs-cluster", "path": "k8s-volume", "readOnly": true } } ] } }
依次執行
$ kubectl apply -f glusterfs-endpoints.json $ kubectl get ep $ kubectl apply -f glusterfs-service.json $ kubectl get svc
# 檢視測試 Pod
$ kubectl apply -f glusterfs-pod.json $ kubectl get pods $ kubectl describe pods/glusterfs $ kubectl exec glusterfs -- mount | grep gluster
持久儲存卷(Persistent Volume,PV)和持久儲存卷宣告(Persistent Volume Claim,PVC)
在 Kubernetes 中還引入了一組叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 物件,很大程度簡化了使用者宣告和使用持久化 Volume 的門檻。比如 PV 是群集中已由管理員配置的一塊儲存,一般系統管理員建立 endpoint、Service、PV ;PVC 是由開發人員進行配置 ,Pod 掛載到 PVC 中,PVC 可以向 PV 申請指定大小的儲存資源並設定訪問模式,而不需要關注儲存卷採用何種技術實現。
- PersistentVolume(PV)是叢集中由管理員配置的一塊儲存。它是叢集中的資源,就和節點是叢集資源一樣。PV 是卷外掛比如 Volumes,但是它的生命週期獨立於使用 PV 的任何 pod 個體。該API物件捕獲實現儲存的詳細資訊,包括 NFS、iSCSI 或著是雲服務商特定的儲存系統。
- PersistentVolumeClaim(PVC)是使用者關於儲存的請求。它類似於一個 pod,pod 消耗節點資源,而 PVC 消耗 PV 資源。Pods 可以請求特定級別的資源(CPU和內容),而Claim可以請求特定的大小和訪問模式(例如,可以一次讀/寫或者多次只讀)。
定義 PV
$ vi glusterfs-pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: gluster-dev-volume spec: capacity: storage: 8Gi accessModes: - ReadWriteMany glusterfs: endpoints: "glusterfs-cluster" path: "k8s-volume" readOnly: false
執行
$ kubectl apply -f glusterfs-pv.yaml
$ kubectl get pv
定義 PVC
$ cat glusterfs-pvc.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata: name: glusterfs-nginx spec: accessModes: - ReadWriteMany resources: requests: storage: 2Gi
執行
$ kubectl apply -f glusterfs-pvc.yaml
$ kubectl get pvc
備註:訪問模式
ReadWriteOnce – the volume can be mounted as read-write by a single node ReadOnlyMany – the volume can be mounted read-only by many nodes ReadWriteMany – the volume can be mounted as read-write by many nodes
可以在 Dashboard 中檢視儲存卷的資訊
測試資料卷
$ wget https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/application/deployment.yaml $ vi deployment.yaml apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 2 # tells deployment to run 2 pods matching the template template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 volumeMounts: - name: gluster-dev-volume mountPath: "/usr/share/nginx/html" volumes: - name: gluster-dev-volume persistentVolumeClaim: claimName: glusterfs-nginx
執行
$ kubectl apply -f deployment.yaml $ kubectl describe deployment nginx-deployment $ kubectl get pods -l app=nginx
$ kubectl get pods -l app=nginx NAME READY STATUS RESTARTS AGE nginx-deployment-5c689d88bb-7rx7d 1/1 Running 0 2d21h nginx-deployment-5c689d88bb-hfqzm 1/1 Running 0 2d21h nginx-deployment-5c689d88bb-tlwmn 1/1 Running 0 2d21h
建立檔案
$ kubectl exec -it nginx-deployment-5c689d88bb-7rx7d -- touch index.html
最後查詢 GlusterFS資料捲上是否有資料驗證下即可。
Dynamic Provisioning 與 Storage Classes
前面介紹 PV 和 PVC 的時候,一般 PV 這個物件的建立,是由運維人員完成,PVC 可能是開發人員定義。在大規模的生產環境裡,可能有很多 PVC ,這意味著運維人員必須得事先創建出成千上萬個 PV,這其實是一個非常麻煩的工作,所以在 Kubernetes 還引入了可以自動建立 PV 的機制 Dynamic Provisioning 概念,Dynamic Provisioning 機制工作的核心,在於一個名叫 StorageClass 物件。管理員可以定義 Storage Class 來描述他們提供的儲存型別,根據 Storage Class 物件中設定的引數自動分配 PV ,比如可以分別定義兩種 Storage Class :slow 和 fast。slow 對接 sc1(機械硬碟),fast 對接 gp2(固態硬碟)。應用可以根據業務的效能需求,分別選擇不同的儲存方式。下面建立 StorageClass 使用 StorageClass 連線 Heketi,根據需要自動建立 GluserFS 的 Volume,StorageClass 還是要系統管理員建立,不同的 PVC 可以用同一個 StorageClass 配置。
官方的文件與視訊介紹:
備註:並不是所有的儲存方式都支援 Dynamic Provisioning 特性,官方文件列出了預設支援 Dynamic Provisioning 的內建儲存外掛,當然也可以擴充套件第三方的儲存元件 kubernetes-incubator/external-storage 實現。
配置 Heketi
GlusterFS 是個開源的分散式檔案系統,而 Heketi 在其上提供了 REST 形式的 API,二者協同為 Kubernetes 提供了儲存卷的自動供給能力。按照官方的 persistent-volume-provisioning 示列,這裡需要配置 Heketi 提供了一個管理 GlusterFS 叢集的 RESTTful 服務,提供 API 介面供 Kubernetes 呼叫 。
$ yum install epel-release $ yum install heketi heketi-client
檢視版本
$ heketi --version Heketi 7.0.0 $ heketi --help Heketi is a restful volume management server Usage: heketi [flags] heketi [command] Examples: heketi --config=/config/file/path/ Available Commands: db heketi db management help Help about any command Flags: --config string Configuration file -h, --help help for heketi -v, --version Show version Use "heketi [command] --help" for more information about a command.
修改 Heketi 配置檔案
vi /etc/heketi/heketi.json #修改埠,預設 8080(伺服器 8080 佔用了) "port": "8000", ...... # 允許認證 "use_auth": true, ...... # 修改admin使用者的key "key": "testtoken" ...... # 配置ssh的所需證書,對叢集中的機器免密登陸 "executor": "ssh", "sshexec": { "keyfile": "/etc/heketi/heketi_key", "user": "root", "port": "22", "fstab": "/etc/fstab" }, ...... # 定義heketi資料庫檔案位置 "db": "/var/lib/heketi/heketi.db" ...... #修改日誌級別 "loglevel" : "info"
配置 SSH 金鑰
#生成 rsa ssh-keygen -t rsa -q -f /etc/heketi/heketi_key -N '' chmod 700 /etc/heketi/heketi_key.pub # 複製 ssh 公鑰上傳到 GlusterFS 三臺伺服器(heketi 也可以單獨部署) ssh-copy-id -i /etc/heketi/heketi_key.pub [email protected]172.23.216.48 ssh-copy-id -i /etc/heketi/heketi_key.pub [email protected]172.23.216.49 ssh-copy-id -i /etc/heketi/heketi_key.pub [email protected]172.23.216.50 # 驗證是否能通過ssh金鑰正常連線到 glusterfs 節點 ssh -i /etc/heketi/heketi_key [email protected]172.23.216.49
啟動 Heketi
$ nohup heketi --config=/etc/heketi/heketi.json & nohup: ignoring input and appending output to ‘nohup.out’ $ cat nohup.out Heketi 7.0.0 [heketi] INFO 2018/11/09 15:50:36 Loaded ssh executor [heketi] INFO 2018/11/09 15:50:36 GlusterFS Application Loaded [heketi] INFO 2018/11/09 15:50:36 Started Node Health Cache Monitor Authorization loaded Listening on port 8000
測試 Heketi 服務端$ curl http://localhost:8000/hello Hello from Heketi
Heketi 要求在每個 GlusterFS 節點上配備裸磁碟 device,不支援檔案系統,一般如下配置,可以通過 fdisk –l 命令檢視。系統盤:/dev/vda 資料盤:/dev/vdb 雲硬碟:/dev/vdc
檢視磁碟$ fdisk -l Disk /dev/sda: 53.7 GB, 53687091200 bytes, 104857600 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk label type: dos Disk identifier: 0x000d3387 Device Boot Start End Blocks Id System /dev/sda1 * 2048 411647 204800 83 Linux /dev/sda2 411648 8800255 4194304 82 Linux swap / Solaris /dev/sda3 8800256 104857599 48028672 83 Linux $ df -lh Filesystem Size Used Avail Use% Mounted on /dev/sda3 46G 5.1G 41G 11% / devtmpfs 3.9G 0 3.9G 0% /dev tmpfs 3.9G 0 3.9G 0% /dev/shm tmpfs 3.9G 172M 3.7G 5% /run tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup /dev/sda1 197M 167M 30M 85% /boot overlay 46G 5.1G 41G 11% /var/lib/docker/containers/1c3c53802122a9ce7e3044e83f22934bb700baeda1bedc249558e9a068e892a7/mounts/shm overlay 46G 5.1G 41G 11% /var/lib/docker/overlay2/bbda116e3a230e59710afd2d9ec92817d65d71b82ccebf4d71bfc589c3605b75/merged tmpfs 3.9G 12K 3.9G 1% /var/lib/kubelet/pods/fb62839c-dc19-11e8-90ea-0050569f4a19/volumes/kubernetes.io~secret/coredns-token-v245h tmpfs 3.9G 12K 3.9G 1% /var/lib/kubelet/pods/fb638fce-dc19-11e8-90ea-0050569f4a19/volumes/kubernetes.io~secret/coredns-token-v245h overlay 46G 5.1G 41G 11% /var/lib/docker/overlay2/a85cbca8be37d9e00565d83350721091105b74e1609d399a0bb1bb91a2c56e09/merged shm 64M 0 64M 0% tmpfs 783M 0 783M 0% /run/user/0 vdb 3.9G 0 3.9G 0% /mnt/disks/vdb vdc 3.9G 0 3.9G 0% /mnt/disks/vdc
配置 topology-sample.json
{ "clusters": [ { "nodes": [ { "node": { "hostnames": { "manage": [ "172.23.216.48" ], "storage": [ "172.23.216.48" ] }, "zone": 1 }, "devices": [ "/dev/vdb" ] }, { "node": { "hostnames": { "manage": [ "172.23.216.49" ], "storage": [ "172.23.216.49" ] }, "zone": 1 }, "devices": [ "/dev/vdb" ] }, { "node": { "hostnames": { "manage": [ "172.23.216.50" ], "storage": [ "172.23.216.50" ] }, "zone": 1 }, "devices": [ "/dev/vdb" ] } ] } ] }
新增節點
$ heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" topology load --json=topology-sample.json Creating cluster ... ID: c2834ba9a3b5b6975150ad396b5ed7ca Allowing file volumes on cluster. Allowing block volumes on cluster. Creating node 172.23.216.48 ... ID: 8c5cbad748520b529ea20f5296921928 Adding device /dev/vdb ... Unable to add device: Device /dev/vdb not found. Found node 172.23.216.49 on cluster c13ecf0a70808a3dc8abcd8de908c1ea Adding device /dev/vdb ... Unable to add device: Device /dev/vdb not found. Found node 172.23.216.50 on cluster c13ecf0a70808a3dc8abcd8de908c1ea Adding device /dev/vdb ... Unable to add device: Device /dev/vdb not found.
其他命令#建立 cluster
heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" topology load --json=topology-sample.json
#建立 volume
heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" volume cre