kubernetes支持local volume

目錄html

  • local volume
  • 建立一個storage class
  • 靜態建立PV
  • 使用local volume PV
  • 動態建立PV

local volume

kubernetes從1.10版本開始支持local volume(本地卷),workload(不只是statefulsets類型)能夠充分利用本地快速SSD,從而獲取比remote volume(如cephfs、RBD)更好的性能。node

在local volume出現以前,statefulsets也能夠利用本地SSD,方法是配置hostPath,並經過nodeSelector或者nodeAffinity綁定到具體node上。但hostPath的問題是,管理員須要手動管理集羣各個node的目錄,不太方便。nginx

下面兩種類型應用適合使用local volume。git

  • 數據緩存,應用能夠就近訪問數據,快速處理。
  • 分佈式存儲系統,如分佈式數據庫Cassandra ,分佈式文件系統ceph/gluster

下面會先以手動方式建立PV、PVC、Pod的方式,介紹如何使用local volume,而後再介紹external storage提供的半自動方式,最後介紹社區的一些發展。github

建立一個storage class

首先須要有一個名爲local-volume的sc。shell

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-volume
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

 

sc的provisioner是 kubernetes.io/no-provisioner數據庫

WaitForFirstConsumer表示PV不要當即綁定PVC,而是直到有Pod須要用PVC的時候才綁定。調度器會在調度時綜合考慮選擇合適的local PV,這樣就不會致使跟Pod資源設置,selectors,affinity and anti-affinity策略等產生衝突。很明顯:若是PVC先跟local PV綁定了,因爲local PV是跟node綁定的,這樣selectors,affinity等等就基本沒用了,因此更好的作法是先根據調度策略選擇node,而後再綁定local PV。ubuntu

靜態建立PV

經過kubectl命令,靜態建立一個5GiB的PV;該PV使用node ubuntu-1的 /data/local/vol1 目錄;該PV的sc爲local-volume。api

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-local-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-volume
  local:
    path: /data/local/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - ubuntu-1

 

Retain(保留)是指,PV跟PVC釋放後,管理員須要手工清理,從新設置該卷。緩存

須要指定PV對應的sc;目錄/data/local/vol1也須要建立。

kubectl get pv example-local-pv
NAME               CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
example-local-pv   5Gi        RWO            Retain           Available           local-volume            8d

 

使用local volume PV

接下來建立一個關聯 sc:local-volume的PVC,而後將該PVC掛到nginx容器裏。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: local-volume
---
kind: Pod
apiVersion: v1
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/usr/share/nginx/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

 

進入到容器裏,會看到掛載的目錄,大小其實就是上面建立的PV所在磁盤的size。

/dev/sdb         503G  235M  478G   1% /usr/share/nginx/html

 

在宿主機的/data/local/vol1目錄下建立一個index.html文件:

echo "hello world" > /data/local/vol1/index.html

 

而後再去curl容器的IP地址,就能夠獲得剛寫入的字符串了。

刪除Pod/PVC,以後PV狀態改成Released,該PV不會再被綁定PVC了。

動態建立PV

手工管理local PV顯然是很費勁的,社區提供了external storage能夠動態的建立PV(實際仍然不夠自動化)。

local volume provisioner的官方編排在local-volume/provisioner/deployment/kubernetes/example/default_example_provisioner_generated.yaml目錄裏,不過官方文檔一會fast-disk,一會local-storage,有點混亂。我這裏統一都用local-volume

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: local-provisioner-config
  namespace: default
data:
  storageClassMap: |
    local-volume:
       hostDir: /data/local
       mountDir:  /data/local
       blockCleanerCommand:
         - "/scripts/shred.sh"
         - "2"
       volumeMode: Filesystem
       fsType: ext4
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: local-volume-provisioner
  namespace: default
  labels:
    app: local-volume-provisioner
spec:
  selector:
    matchLabels:
      app: local-volume-provisioner
  template:
    metadata:
      labels:
        app: local-volume-provisioner
    spec:
      serviceAccountName: local-volume-admin
      containers:
        - image: "silenceshell/local-volume-provisioner:v2.1.0"
          imagePullPolicy: "Always"
          name: provisioner
          securityContext:
            privileged: true
          env:
          - name: MY_NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          volumeMounts:
            - mountPath: /etc/provisioner/config
              name: provisioner-config
              readOnly: true
            - mountPath:  /data/local
              name: local
              mountPropagation: "HostToContainer"
      volumes:
        - name: provisioner-config
          configMap:
            name: local-provisioner-config
        - name: local
          hostPath:
            path: /data/local
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: local-volume-admin
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: local-volume-provisioner-pv-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: local-volume-admin
  namespace: default
roleRef:
  kind: ClusterRole
  name: system:persistent-volume-provisioner
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: local-volume-provisioner-node-clusterrole
  namespace: default
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: local-volume-provisioner-node-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: local-volume-admin
  namespace: default
roleRef:
  kind: ClusterRole
  name: local-volume-provisioner-node-clusterrole
  apiGroup: rbac.authorization.k8s.io

 

kubectl建立後,因爲是daemonset類型,每一個節點上都會啓動一個provisioner。該provisioner會監視 「discovery directory」,即上面配置的/data/local

$ kubectl get pods -o wide|grep local-volume
local-volume-provisioner-rrsjp            1/1     Running   0          5m    10.244.1.141   ubuntu-2   <none>
local-volume-provisioner-v87b7            1/1     Running   0          5m    10.244.2.69    ubuntu-3   <none>
local-volume-provisioner-x65k9            1/1     Running   0          5m    10.244.0.174   ubuntu-1   <none>

 

前面mypod/myclaim已經刪除了,咱們從新建立一個,此時pvc myclaim是Pending狀態,provisoner並無自動供給存儲。爲何呢?

原來external-storage的邏輯是這樣的:其Provisioner自己其並不提供local volume,但它在各個節點上的provisioner會去動態的「發現」掛載點(discovery directory),當某node的provisioner在/data/local/目錄下發現有掛載點時,會建立PV,該PV的local.path就是掛載點,並設置nodeAffinity爲該node。

那麼如何得到掛載點呢?

直接去建立目錄是行不通的,由於provsioner但願PV是隔離的,例如capacity,io等。試着在ubuntu-2上的/data/local/下建立一個xxx目錄,會獲得這樣的告警。

discovery.go:201] Path "/data/local/xxx" is not an actual mountpoint

目錄不是掛載點,不能用。

該目錄必須是真材實料的mount才行。一個辦法是加硬盤、格式化、mount,比較麻煩,實際能夠經過本地文件格式化(loopfs)後掛載來「欺騙」provisioner,讓它覺得是一個mount的盤,從而自動建立PV,並與PVC綁定。

以下。

將下面的代碼保存爲文件 loopmount,加執行權限並拷貝到/bin目錄下,就可使用該命令來建立掛載點了。

#!/bin/bash
  
# Usage: sudo loopmount file size mount-point

touch $1
truncate -s $2 $1
mke2fs -t ext4 -F $1 1> /dev/null 2> /dev/null
if [[ ! -d $3 ]]; then
        echo $3 " not exist, creating..."
        mkdir $3
fi
mount $1 $3
df -h |grep $3

 

使用腳本建立一個6G的文件,並掛載到/data/local下。之因此要6G,是由於前面PVC須要的是5GB,而格式化後剩餘空間會小一點,因此設置文件更大一些,後面纔好綁定PVC。

# loopmount xxx 6G /data/local/xxx
/data/local/xxx  not exist, creating...
/dev/loop0     5.9G   24M  5.6G   1% /data/local/x1

 

查看PV,可見Provisioner自動建立了PV,而kubernetes會將該PV供給給前面的PVC myclam,mypod也run起來了。

# kubectl get pv
NAME              CAPACITY  ACCESS MODES   RECLAIM POLICY   STATUS  CLAIM            STORAGECLASS          REASON   AGE
local-pv-600377f7 5983Mi    RWO            Delete           Bound   default/myclaim  local-volume                   1s

 

可見,目前版本的local volume還沒法作到像cephfs/RBD同樣的全自動化,仍然須要管理員干涉,顯然這不是一個好的實現。

社區有人提交了基於LVM作local volume動態供給的Proposal,不過進展很緩慢。做者是huawei的員工,應該huawei已經實現了。

除了基於LVM,也能夠基於 ext4 project quota 來實現LV的動態供給。

除了使用磁盤,還能夠考慮使用內存文件系統,從而獲取更高的io性能,只是容量就沒那麼理想了。一些特殊的應用能夠考慮。

mount -t tmpfs -o size=1G,nr_inodes=10k,mode=700 tmpfs /data/local/tmpfs

 

總的來講,local volume本地卷目前不支持動態供給,還沒法真正推廣使用,但能夠用來解決一些特定問題。

Ref:

 

參考文檔:

https://kubernetes.io/blog/2019/04/04/kubernetes-1.14-local-persistent-volumes-ga/

https://ieevee.com/tech/2019/01/17/local-volume.html

相關文章
相關標籤/搜索