持久卷

持久卷

PersistentVolume 子系统为用户和管理员提供了一个 API,总结了 Pods 如何提供和消耗存储的细节。为了最好地控制这个系统,引入了两个 API 功能。PersistentVolumee PersistentVolumeClaim。

  • PersistentVolume(PV)是集群中的一种资源,就像节点一样。但在这种情况下,它是一种存储资源。PV 是集群中的一块存储资源,它已经被管理员配置好了。PV 有一个独立于任何与之相关联的 pod 的生命周期。这个 API 允许存储的类型。NFS、ISCSI 或来自特定云提供商的存储。

  • PersistentVolumeClaim(PVC)类似于 Pod。Pods 消耗来自节点的资源,PVC 消耗来自 PV 的资源。但什么是 PVC 呢?它不过是用户创建的一个存储请求。

Local Persistent Volumes

在 Kubernetes 1.14 版本中,Local Persistent Volumes(以下简称 LPV)已变为正式版本(GA),LPV 的概念在 1.7 中被首次提出(alpha),并在 1.10 版本中升级到 beat 版本。现在用户终于可以在生产环境中使用 LPV 的功能和 API 了。

Local Persistent Volumes 代表了直接绑定在计算节点上的一块本地磁盘。Kubernetes 提供了一套卷插件(volume plugin)标准,使得 K8s 集群的工作负载可以使用多种块存储和文件存储。大部分磁盘插件都使用了远程存储,这是为了让持久化的数据与计算节点彼此独立,但远程存储通常无法提供本地存储那么强的读写性能。有了 LPV 插件,Kubernetes 负载现在可以用同样的 volume api,在容器中使用本地磁盘。

  • 与 hostPath 的区别:hostPath 是一种 Volume,可以让 pod 挂载宿主机上的一个文件或目录(如果挂载路径不存在,则创建为目录或文件并挂载)。最大的不同在于调度器是否能理解磁盘和 node 的对应关系,一个使用 hostPath 的 pod,当他被重新调度时,很有可能被调度到与原先不同的 node 上,这就导致 pod 内数据丢失了。而使用 LPV 的 pod,总会被调度到同一个 node 上(否则就调度失败)。

NFS

我们将创建一个 NFS 类型的 PersistentVolume,为此我们将安装必要的软件包,在 GNU/Linux 上创建一个 NFS 服务器。

# Ubuntu
$ sudo apt-get install -y nfs-kernel-server
$ sudo apt-get install -y nfs-common

# CentOS
$ yum install -y nfs-utils

现在我们要在 elliot-01 节点上建立一个目录,并给予必要的权限来测试我们所说的一切。

$ sudo mkdir /opt/dados
$ sudo chmod 1777 /opt/dados/
$ sudo vim /etc/exports

# 添加如下行
/opt/dados *(rw,sync,no_root_squash,subtree_check)

# 将 NFS 配置应用于节点 elliot-01。
$ sudo exportfs -a

# 重启服务
$ sudo systemctl restart nfs-kernel-server
$ systemctl restart nfs

依然是在节点 elliot-01 中,我们将在这个目录下创建一个文件用于我们的测试。

$ sudo touch /opt/dados/FUNCIONA

还是在节点 elliot-01 中,我们将创建我们 PersistentVolume 的 manifest yaml。记得把服务器字段的 IP 地址改成节点 elliot-01 的 IP 地址。

sudo vim primeiro-pv.yaml

apiVersion : v1
kind : PersistentVolume
metadata :
   name : prime-pv
spec :
   capacity :
     storage : 1Gi
  accessModes :
  - ReadWriteMany
  persistentVolumeReclaimPolicy : Retain
  nfs :
     path : /opt/data
    server : 10.138.0.2
    readOnly : false

然后执行创建命令;

$ kubectl create -f primeiro-pv.yaml

persistentvolume/primeiro-pv created

$ kubectl get pv

NAME          CAPACITY   ACCESS MODES    RECLAIM POLICY   ...  AGE
primeiro-pv   1Gi        RWX             Retain           ...  22s

$ kubectl describe pv primeiro-pv

Name:            primeiro-pv
Labels:          <none>
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:
Reclaim Policy:  Retain
Access Modes:    RWX
Capacity:        1Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    10.138.0.2
    Path:      /opt/dados
    ReadOnly:  false
Events:        <none>

现在我们需要创建我们的 PersitentVolumeClaim,这样 Pod 就可以从我们的 PersistentVolume 中请求读写。

$ vim primeiro-pvc.yaml

apiVersion : v1
kind : PersistentVolumeClaim
metadata :
   name : prime-pvc
spec :
   accessModes :
  - ReadWriteMany
  resources :
     requests :
       storage : 800Mi

然后执行创建命令:

$ kubectl create -f primeiro-pvc.yaml

persistentvolumeclaim/primeiro-pvc created

$ kubectl get pv

NAME         CAPACITY  ACCESS MOD  ... CLAIM                ...  AGE
primeiro-pv  1Gi       RWX         ... default/primeiro-pvc ...  8m

$ kubectl get pvc

NAME          STATUS   VOLUME       CAPACITY   ACCESS MODES ...  AGE
primeiro-pvc  Bound    primeiro-pv  1Gi        RWX          ...  3m

现在我们有了一个 PersistentVolume 和一个 PersistentVolumeClaim,让我们创建一个消耗这个卷的部署。

$ vim nfs-pv.yaml

apiVersion : apps / v1
kind : Deployment
metadata :
   labels :
     run : nginx
  name : nginx
  namespace : default
spec :
   progressDeadlineSeconds : 600
  replicas : 1
  revisionHistoryLimit : 10
  selector :
     matchLabels :
       run : nginx
  strategy :
     rollingUpdate :
       maxSurge : 1
      maxUnavailable: 1
    type : RollingUpdate
  template :
     metadata :
       labels :
         run : nginx
    spec :
       containers :
      - image : nginx
        imagePullPolicy : Always
        name : nginx
        volumeMounts :
        - name : nfs-pv
          mountPath : / giropops
        resources : {}
        terminationMessagePath : / dev / termination-log
        terminationMessagePolicy : File
      volumes :
      - name : nfs-pv
        persistentVolumeClaim :
           claimName : prime-pvc
      dnsPolicy : ClusterFirst
      restartPolicy : Always
      schedulerName : default-scheduler
      securityContext : {}
      terminationGracePeriodSeconds : 30

执行创建操作:

$ kubectl create -f nfs-pv.yaml

deployment.apps/nginx created

$ kubectl describe deployment nginx

Name:                   nginx
Namespace:              default
CreationTimestamp:      Wed, 7 Jul 2018 22:01:49 +0000
Labels:                 run=nginx
Annotations:            deployment.kubernetes.io/revision=1
Selector:               run=nginx
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:  run=nginx
  Containers:
   nginx:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:
      /giropops from nfs-pv (rw)
  Volumes:
   nfs-pv:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  primeiro-pvc
    ReadOnly:   false
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-b4bd77674 (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  7s    deployment-controller  Scaled up replica set nginx-b4bd77674 to 1

正如我们可以看到详细介绍我们的 pod,它是使用值为 primeiro-pvc 的 ClaimName 创建的 NFS 卷。我们将详细检查我们的 pod 是否一切正常。

$ kubectl get pods -o wide

NAME           READY    STATUS   RESTARTS   AGE ...  NODE
nginx-b4b...   1/1      Running  0          28s      elliot-02

$ kubectl describe pod nginx-b4bd77674-gwc9k

Name:               nginx-b4bd77674-gwc9k
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               elliot-02/10.138.0.3
...
    Mounts:
      /giropops from nfs-pv (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-np77m (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  nfs-pv:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  primeiro-pvc
    ReadOnly:   false
  default-token-np77m:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-np77m
    Optional:    false
...

好吧,对吧?pod 实际上是使用卷 nfs 来挂载/giropops 这个卷。现在我们要用我们的例子列出容器中路径内的文件,在这个例子中,它位于 elliot-02 节点。

$ kubectl exec -ti nginx-b4bd77674-gwc9k -- ls /giropops/

FUNCIONA

我们可以看到一开始的文件都列在目录中。现在我们要用容器本身的命令 kubectl exec 和参数–touch 来创建另一个文件。

$ kubectl exec -ti nginx-b4bd77674-gwc9k -- touch /giropops/STRIGUS

$ kubectl exec -ti nginx-b4bd77674-gwc9k -- ls -la  /giropops/

total 4
drwxr-xr-x. 2 root root 4096 Jul  7 23:13 .
drwxr-xr-x. 1 root root   44 Jul  7 22:53 ..
-rw-r--r--. 1 root root    0 Jul  7 22:07 FUNCIONA
-rw-r--r--. 1 root root    0 Jul  7 23:13 STRIGUS

在容器里面列表,我们可以看到文件被创建了,但是在我们的 NFS 服务器里面呢?我们将 NSF 服务器目录列在 elliot-01。

$ ls -la /opt/dados/

-rw-r--r-- 1 root root    0 Jul  7 22:07 FUNCIONA
-rw-r--r-- 1 root root    0 Jul  7 23:13 STRIGUS

看到了吗?我们的 NFS 服务器工作正常,现在让我们删除部署,看看卷的情况。

$ kubectl get deployment

NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx               1         1         1            1           28m

$ kubectl delete deployment nginx

deployment.extensions "nginx" deleted

$ ls -la /opt/dados/

-rw-r--r-- 1 root root    0 Jul  7 22:07 FUNCIONA
-rw-r--r-- 1 root root    0 Jul  7 23:13 STRIGUS

正如预期的那样,文件还在,并没有在排除 Deployment 的情况下被删除,这是保留持久性文件供 Pods 消费的几种方法之一。

上一页