<xmp id="63nn9"><video id="63nn9"></video></xmp>

<xmp id="63nn9"></xmp>

<wbr id="63nn9"><ins id="63nn9"></ins></wbr>

<wbr id="63nn9"></wbr><video id="63nn9"><ins id="63nn9"><table id="63nn9"></table></ins></video>

【Kubernetes】K8s筆記(十四):PersistentVolume 使用網絡共享存儲(NFS)

要想讓存儲卷真正能被 Pod 任意掛載,我們需要變更存儲的方式,不能限定在本地磁盤,而是要改成網絡存儲,這樣 Pod 無論在哪里運行,只要知道 IP 地址或者域名,就可以通過網絡通信訪問存儲設備。

網絡存儲是一個非常熱門的應用領域,有很多知名的產品,比如 AWS、Azure、Ceph,Kubernetes 還專門定義了 CSI(Container Storage Interface)規范,不過這些存儲類型的安裝、使用都比較復雜,在實驗環境里部署難度比較高。

所以我們以 NFS (Network File System)為例學習如何在 Kubernetes 里使用網絡存儲,以及靜態存儲卷和動態存儲卷的概念。

0. 安裝 NFS 服務器及客戶端

NFS 采用的是經典的 Client/Server 架構,需要選定一臺主機作為 Server,安裝 NFS 服務端;其他要使用存儲的主機作為 Client,安裝 NFS 客戶端工具。

我這里就再安裝一臺虛擬機作為 NFS Server:

虛擬機地址 功能
172.16.63.128 Kubernetes Control-Plane
172.16.63.129 Kubernetes Worker Node
172.16.63.131 NFS Server

要安裝 NFS Server 只需要執行下面的命令:

$ sudo apt -y install nfs-kernel-server

安裝好之后,需要給 NFS 指定一個存儲位置,也就是網絡共享目錄。一般來說,應該建立一個專門的 /data 目錄,在這里我使用 /data/nfs

$ sudo mkdir -p /data/nfs

接下配置 NFS 訪問共享目錄,修改 /etc/exports,指定目錄名、允許訪問的網段,還有權限等參數:

/data/nfs 172.16.63.1/24(rw,sync,no_subtree_check,no_root_squash,insecure)

改好之后,需要用 exportfs -ra 通知 NFS,讓配置生效,再用 exportfs -v 驗證效果:

$ sudo exportfs -ra

$ sudo exportfs -v
/data/nfs     	172.16.63.1/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,insecure,no_root_squash,no_all_squash)

最后使用 systemctl 啟動 NFS 服務:

$ sudo systemctl start nfs-server
$ sudo systemctl enable nfs-server
$ sudo systemctl status nfs-server
● nfs-server.service - NFS server and services
     Loaded: loaded (/lib/systemd/system/nfs-server.service; enabled; vendor preset: enabled)
    Drop-In: /run/systemd/generator/nfs-server.service.d
             └─order-with-mounts.conf
     Active: active (exited) since Fri 2022-10-28 08:39:24 UTC; 8min ago
   Main PID: 2677 (code=exited, status=0/SUCCESS)
        CPU: 8ms

然后使用下面的命令檢查 NFS 的網絡掛載情況:

$ showmount -e 127.0.0.1
Export list for 127.0.0.1:
/data/nfs 172.16.63.1/24

為了讓 Kubernetes 集群能夠訪問 NFS 存儲服務,我們還需要在每個節點上都安裝 NFS 客戶端:

$ sudo apt -y install nfs-common

同樣,在節點上可以用 showmount 檢查 NFS 能否正常掛載,注意 IP 地址要寫成 NFS 服務器的地址:

$ showmount -e 172.16.63.131
Export list for 172.16.63.131:
/data/nfs 172.16.63.1/24

手動測試掛載 NFS
首先在 Worker 節點上創建一個文件夾作為掛載點:

$ sudo mkdir -p /tmp/nfs-test

用命令 mount 把 NFS 服務器的共享目錄掛載到剛才創建的本地目錄上:

$ sudo -i 
# echo "hello, nfs!" > /tmp/nfs-test/hello
# cat /tmp/nfs-test/hello 
hello, nfs!

回到 NFS 服務器,檢查共享目錄,應該會看到也出現了一個同樣的文件。

1. 在 Kubernetes 中使用 NFS 存儲卷

現在我們已經為 Kubernetes 配置好了 NFS 存儲系統,就可以使用它來創建新的 PV 存儲對象了。

手工分配一個存儲卷,指定 storageClassNamenfs,accessMode 設置為 ReadWriteMany (因為 NFS 支持多個節點同時訪問一個共享目錄)。

因為這個存儲卷是 NFS 系統,所以我們還需要在 YAML 里添加 nfs 字段,指定 NFS 服務器的 IP 地址和共享目錄名。

下面我們在 NFS 服務器的 共享目錄中建立一個文件夾 1gib-pv 表示一個 1GiB 的 PersistentVolume,然后使用 YAML 文件描述這個 PV:

# nfs-1gib-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-1gib-pv

spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany
  capacity:
    storage: 1Gi

  nfs:
    path: /data/nfs/1gib-pv
    server: 172.16.63.131

然后我們創建這個 PV 對象,然后查看狀態:

$ kubectl apply -f nfs-1gib-pv.yaml 
persistentvolume/nfs-1gib-pv created

$ kubectl get pv -o wide 
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS   REASON   AGE    VOLUMEMODE
nfs-1gib-pv   1Gi        RWX            Retain           Available                           nfs                     22s    Filesystem

注意:spec.nfs 里的 IP 地址一定要正確,路徑一定要存在(事先創建好),否則 Kubernetes 按照 PV 的描述會無法掛載 NFS 共享目錄,PV 就會處于 Pending 狀態無法使用。

有了 PV,我們就可以定義申請存儲的 PVC 對象了,它的內容和 PV 差不多,但不涉及 NFS 存儲的細節,只需要用 resources.request 來表示希望要有多大的容量,這里寫成 1GB,和 PV 的容量相同:

# 1gib-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-static-pvc

spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany

  resources:
    requests:
      storage: 1Gi

創建 PVC 對象之后,Kubernetes 就會根據 PVC 的描述,找到最合適的 PV:

$ kubectl apply -f 1gib-pvc.yaml 
persistentvolumeclaim/nfs-static-pvc created

$ kubectl get pv -o wide 
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   REASON   AGE    VOLUMEMODE
nfs-1gib-pv   1Gi        RWX            Retain           Bound    default/nfs-static-pvc   nfs                     8m1s   Filesystem

$ kubectl get pvc -o wide 
NAME             STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS   AGE    VOLUMEMODE
nfs-static-pvc   Bound    nfs-1gib-pv   1Gi        RWX            nfs            17s    Filesystem

最后創建一個 Pod,把 PVC 掛載成它的一個 volume。在這一步我們只需要在 persistentVolumeClaim 中指定 PVC 的名稱就可以了:

# nfs-static-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfs-static-pod

spec:
  volumes:
  - name: nfs-pvc-vol
    persistentVolumeClaim:
      claimName: nfs-static-pvc

  containers:
    - name: nfs-pvc-test
      image: nginx:alpine
      ports:
      - containerPort: 80

      volumeMounts:
        - name: nfs-pvc-vol
          mountPath: /tmp

創建完畢 Pod 后我們使用 describe 命令查看 Volumes:

$ kubectl apply -f nfs-static-pod.yaml 
pod/nfs-static-pod created

$ kubectl describe pod nfs-static-pod 
Name:             nfs-static-pod
Namespace:        default
Priority:         0
Service Account:  default
Node:             worker1/172.16.63.129
...
Volumes:
  nfs-pvc-vol:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  nfs-static-pvc
    ReadOnly:   false
...

Pod、PVC、PV 和 NFS 存儲的關系可以用下圖來形象地表示:

因為我們在 PV/PVC 里指定了 storageClassNamenfs,節點上也安裝了 NFS 客戶端,所以 Kubernetes 就會自動執行 NFS 掛載動作,把 NFS 的共享目錄 /tmp/nfs/1g-pv 掛載到 Pod 里的 /tmp,完全不需要我們去手動管理。

現在測試一下掛載的正確性,首先我們使用命令進入 Pod:

$ kubectl exec -it pods/nfs-static-pod -- sh

進入 Pod 后我們在掛載目錄建立一個文件:

/ # cd tmp 
/tmp # echo Hello! This is a file created on a pod. > hello.text
/tmp #

然后在 NFS 服務器查看該文件:

$ ls
hello.text
$ cat hello.text 
Hello! This is a file created on a pod.

發現 Pod 里創建的文件確實寫入了共享目錄。

而且因為 NFS 是一個網絡服務,不會受 Pod 調度位置的影響,所以只要網絡通暢,這個 PV 對象就會一直可用,數據也就實現了真正的持久化存儲。

2. 動態存儲卷 Provisioner

現在網絡存儲系統確實能夠讓集群里的 Pod 任意訪問,數據在 Pod 銷毀后仍然存在,新創建的 Pod 可以再次掛載,然后讀取之前寫入的數據。但是,PV 之類的對象還是需要運維人員手工管理,而且 PV 的大小也很難提前知曉、精確控制,容易出現空間不足或者空間浪費等情況。

在一個大集群里,每天可能會有幾百幾千個應用需要 PV 存儲,如果仍然用人力來管理分配存儲,管理員很可能會忙得焦頭爛額,導致分配存儲的工作大量積壓。

為了實現 PV 創建自動化和卷分配自動化,Kubernetes 提出“動態存儲卷”的概念:它可以用 StorageClass 綁定一個 Provisioner 對象,而這個 Provisioner 就是一個能夠自動管理存儲、創建 PV 的應用,代替了原來系統管理員的手工勞動。

目前,Kubernetes 里每類存儲設備都有相應的 Provisioner 對象,對于 NFS 來說,它的 Provisioner 就是 NFS subdir external provisioner

NFS Provisioner 也是以 Pod 的形式運行在 Kubernetes 里的,在 GitHub 的 deploy 目錄里是部署它所需的 YAML 文件,一共有三個,分別是 rbac.yaml class.yaml deployment.yaml。

這里我將部署文件放在 nfs/provisioner 目錄下。

要想在集群內運行 Provisioner,我們還要對其中兩個文件進行修改:

第一個要修改的是 rbac.yaml,它使用的是默認的 default 名字空間,應該把它改成其他的名字空間,避免與普通應用混在一起,可以用“查找替換”的方式把它統一改成 kube-system。

然后修改 deployment.yaml,首先要把名字空間改成和 rbac.yaml 一樣,比如是 kube-system,然后重點要修改 volumesenv 里的 IP 地址和共享目錄名,必須和集群里的 NFS 服務器配置一樣。

# nfs/provisoner/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: kube-system
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: docker.io/chronolaw/nfs-subdir-external-provisioner:v4.0.2 # 改一下鏡像地址
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 172.16.63.131
            - name: NFS_PATH
              value: /data/nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: 172.16.63.131
            path: /data/nfs

還有一件事就是 gcr.io 上的鏡像拉取困難,羅劍鋒老師把它的鏡像轉存到了 Docker Hub 上。我們只需要更改一下鏡像地址即可 image: docker.io/chronolaw/nfs-subdir-external-provisioner:v4.0.2。

把這兩個 YAML 修改好之后,我們就可以在 Kubernetes 里創建 NFS Provisioner 了。

$ kubectl apply -f rbac.yaml -f class.yaml -f deployment.yaml 
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
storageclass.storage.k8s.io/nfs-client created
deployment.apps/nfs-client-provisioner created

使用命令 kubectl get,再加上名字空間限定 -n kube-system,就可以看到 NFS Provisioner 在 Kubernetes 里運行起來了。

$ kubectl get deploy -n kube-system -o wide 
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS               IMAGES                                                       SELECTOR
coredns                  2/2     2            2           13d   coredns                  registry.aliyuncs.com/google_containers/coredns:v1.9.3       k8s-app=kube-dns
nfs-client-provisioner   1/1     1            1           61s   nfs-client-provisioner   docker.io/chronolaw/nfs-subdir-external-provisioner:v4.0.2   app=nfs-client-provisioner

$ kubectl get pods -n kube-system -l app=nfs-client-provisioner
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-7f58779d49-k78m2   1/1     Running   0          2m22s

3. 使用 NFS 動態存儲卷

因為有了 Provisioner,我們就不再需要手工定義 PV 對象了,只需要在 PVC 里指定 StorageClass 對象,它再關聯到 Provisioner。

我們來看一下 NFS 默認的 StorageClass 定義:

# nfs/provisioner/class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client

provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"

YAML 里的關鍵字段是 provisioner,它指定了應該使用哪個 Provisioner。另一個字段 parameters 是調節 Provisioner 運行的參數,需要參考文檔來確定具體值,在這里的 archiveOnDelete: "false" 就是自動回收存儲空間。

理解了 StorageClass 的 YAML 之后,你也可以不使用默認的 StorageClass,而是根據自己的需求,任意定制具有不同存儲特性的 StorageClass,比如添加字段 onDelete: "retain" 暫時保留分配的存儲,之后再手動刪除。

現在我們定義一個 PVC,向系統申請 10MB 的存儲空間,使用的 StorageClass 是默認的 nfs-client

# nfs/test/nfs-provisioner-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-dyn-10mib-pvc

spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany

  resources:
    requests:
      storage: 10Mi

寫好了 PVC,我們還是在 Pod 里用 volumesvolumeMounts 掛載,然后 Kubernetes 就會自動找到 NFS Provisioner,在 NFS 的共享目錄上創建出合適的 PV 對象:

# nfs/test/nfs-provisioner-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfs-dyn-pod

spec:
  volumes:
  - name: nfs-dyn-10mib-vol
    persistentVolumeClaim:
      claimName: nfs-dyn-10mib-pvc

  containers:
    - name: nfs-dyn-test
      image: nginx:alpine
      ports:
      - containerPort: 80

      volumeMounts:
        - name: nfs-dyn-10mib-vol
          mountPath: /tmp

創建 PVC 和 Pod,然后查看集群狀態:

$ kubectl apply -f nfs-provisioner-pvc.yaml -f nfs-provisioner-pod.yaml 
persistentvolumeclaim/nfs-dyn-10mib-pvc created
pod/nfs-dyn-pod created

$ kubectl get pv -o wide 
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                       STORAGECLASS   REASON   AGE     VOLUMEMODE
pvc-4a7bc325-ca6e-46ca-9f96-8d0217647019   10Mi       RWX            Delete           Bound    default/nfs-dyn-10mib-pvc   nfs-client              35s     Filesystem

$ kubectl get pvc -o wide 
NAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE     VOLUMEMODE
nfs-dyn-10mib-pvc   Bound    pvc-4a7bc325-ca6e-46ca-9f96-8d0217647019   10Mi       RWX            nfs-client     23s     Filesystem

雖然我們沒有直接定義 PV 對象,但由于有 NFS Provisioner,它就自動創建一個 PV,大小剛好是在 PVC 里申請的 10MiB。

如果這個時候再去 NFS 服務器上查看共享目錄,也會發現多出了一個目錄,名字與這個自動創建的 PV 一樣,但加上了名字空間和 PVC 的前綴:

nfs-server:/data/nfs$ ls
1gib-pv  default-nfs-dyn-10mib-pvc-pvc-4a7bc325-ca6e-46ca-9f96-8d0217647019  hello
posted @ 2022-10-31 16:40  joexu01  閱讀(333)  評論(0編輯  收藏  舉報
人碰人摸人爱免费视频播放

<xmp id="63nn9"><video id="63nn9"></video></xmp>

<xmp id="63nn9"></xmp>

<wbr id="63nn9"><ins id="63nn9"></ins></wbr>

<wbr id="63nn9"></wbr><video id="63nn9"><ins id="63nn9"><table id="63nn9"></table></ins></video>