Kubernetes / 运维笔记

理解kubernetes中的Storage

Einic Yeo · 1月27日 · 2019年 · · · ·

序、为何需要存储卷

容器部署过程中一般有以下三种数据:

  • 启动时需要的初始数据,可以是配置文件
  • 启动过程中产生的临时数据,该临时数据需要多个容器间共享
  • 启动过程中产生的持久化数据

以上三种数据都不希望在容器重启时就消失,存储卷由此而来,它可以根据不同场景提供不同类型的存储能力。
各类卷:

  • spec.volumes:通过此字段提供指定的存储卷
  • spec.containers.volumeMounts:通过此字段将存储卷挂接到容器中

一、普通存储卷的用法

容器启动时依赖数据

1. ConfigMap

上图也说了它是在容器启动的时候以来的数据来源

示例:

# 1.创建configmap预制数据卷
$ cat >> configmap.yaml << EOF
apiVersion: v1
data:
  guomaoqiu: hello-world
kind: ConfigMap
metadata:
  name: test
EOF
$ kubectl create -f configmap.yaml
configmap/test created
# 2. 创建pod来使用这个configmap
# 这里我创建了一个nginx pod 让他启动是挂载这个数据
$ cat nginx-deployment.yaml
......
......
    spec:
      containers:
      - image: nginx
        name: nginx
        resources: {}
        volumeMounts:
        - name: test
          mountPath: /tmp  # 挂载点
      volumes:
      - name: test
        configMap: 
          name: test       # ConfigMap name
          defaultMode: 420 # 指定挂载到pod文件的权限
......
......

# 3.检查数据是否挂载进pod
$kubectl exec  nginx-7d8bb9c4fc-bxxkt -- ls /tmp
guomaoqiu
$ kubectl exec  nginx-7d8bb9c4fc-bxxkt -- cat /tmp/guomaoqiu
hello-world                                                                                                                                                                                                                                 $
# 以上可以看到将configmap的key作为了文件名,将value作为了文件的内容。
# 他的作用也就是允许您将配置文件从容器镜像中解耦,从而增强容器应用的可移植性
# 更多可查看: https://k8smeetup.github.io/docs/tasks/configure-pod-container/configmap/

2. Serect

Secret 是一种包含少量敏感信息例如密码、token 或 key 的对象。这样的信息可能会被放在 Pod spec 中或者镜像中;将其放在一个 secret 对象中可以更好地控制它的用途,并降低意外暴露的风险。

用户可以创建 secret,同时系统也创建了一些 secret。
要使用 secret,pod 需要引用 secret。Pod 可以用两种方式使用 secret:作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里,或者当 kubelet 为 pod 拉取镜像时使用。

示例:

假设有些 pod 需要访问数据库。这些 pod 需要使用的用户名和密码在您本地机器的 ./username.txt 和 ./password.txt 文件里。

# Create files needed for rest of example.
$ echo -n "admin" > ./username.txt
$ echo -n "1f2d1e2e67df" > ./password.txt

kubectl create secret 命令将这些文件打包到一个 Secret 中并在 API server 中创建了一个对象。

$ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret/db-user-pass created

$ kubectl describe secret db-user-pass
Name:         db-user-pass
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password.txt:  12 bytes
username.txt:  5 bytes
# 默认情况下,get 和 describe 命令都不会显示文件的内容。
# 这是为了防止将 secret 中的内容被意外暴露给从终端日志记录中刻意寻找它们的人。

在 Pod 中使用 Secret 文件
在挂载的 secret volume 的容器内,secret key 将作为文件,并且 secret 的值使用 base-64 解码并存储在这些文件中。这是在上面的示例容器内执行的命令的结果:

$ kubectl exec -it mypod /bin/bash
[email protected]:/# cat /etc/foo/username.txt
admin
[email protected]:/# cat /etc/foo/password.txt
1f2d1e2e67df

临时数据存储

1. emptryDir

当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir 中的数据将被永久删除。

注意:容器崩溃不会从节点中移除 pod,因此 emptyDir 卷中的数据在容器崩溃时是安全的。

缺省情况下,EmptyDir 是使用主机磁盘进行存储的,也可以设置emp版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!tyDir.medium 字段的值为Memory,来提高运行速度,但是这种设置,对该卷的占用会消耗容器的内存份额。

emptyDir 的用法有:

  • 暂存空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • Web服务器容器提供数据时,保存内容管理器容器提取的文件
  • 可以在同一 Pod 内的不同容器之间共享工作过程中产生的文件。

示例:

apiVersion: v1
kind: Pod
metadata:
  name: test-emptydir-pod
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: test-emptydir-pod
    volumeMounts:
    - mountPath: /temp-data      # 挂载点
      name: temp-data
  volumes:
  - name: temp-data
    emptyDir: {}

创建pod

$ kubectl create -f test-emptydir-pod.yaml
pod/test-emptydir-pod created
$ kubectl get pods -o wide
NAME                READY     STATUS    RESTARTS   AGE       IP            NODE      NOMINATED NODE
test-emptydir-pod   1/1       Running   0          18s       10.244.0.23   k8s-m3    <none>

此时Emptydir已经创建成功,在宿主机上的访问路径为/var/lib/kubelet/pods//volumes/kubernetes.io~empty-dir/temp-data,如果在此目录中创建删除文件,都将对容器中的/data目录有影响,如果删除Pod,文件将全部删除,即使是在宿主机上创建的文件也是如此,在宿主机上删除容器则k8s会再自动创建一个容器,此时文件仍然存在。

# 获取 pod uid
$ kubectl get pod test-emptydir-pod -o yaml | grep -n uid
11:  uid: 3328c9e1-ff8f-11e8-b6d6-00505621dd5b

# 登录到节点k8s-m3 查看
$ ll /var/lib/kubelet/pods/3328c9e1-ff8f-11e8-b6d6-00505621dd5b/volumes/kubernetes.io~empty-dir/
total 0
drwxrwxrwx 2 root root 6 Dec 14 05:58 temp-data

2. hostPath

hostPath volume映射node文件系统中的文件或者目录到pod里。大多数Pod都不需要这个功能,但对于一些特定的场景,该特性还是很有作用的。这些场景包括:

  • 运行的容器需要访问Docker内部结构:使用hostPath映射/var/lib/docker
  • 在容器中运行cAdvisor,使用hostPath映射/dev/cgroups

不过,使用这种volume要小心,因为:

  • 配置相同的pod(如通过podTemplate创建),可能在不同的Node上表现不同,因为不同节点上映射的文件内容不同
  • 当Kubernetes增加了资源敏感的调度程序,hostPath使用的资源不会被计算在内
  • 宿主机下创建的目录只有root有写权限。你需要让你的程序运行在privileged container上,或者修改宿主机上的文件权限。  

示例:

假设我们在创建docker镜像的时候忘记了更改容器中的时区文件;这时候想把宿主机的/usr/share/zoneinfo/Asia/Shanghai挂到pod中的容器内(这里为了学习挂载方式,暂且不谈不同系统或不同宿主机的一些配置或路径)

apiVersion: v1
kind: Pod
metadata:
  name: test-emptydir-pod
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: test-emptydir-pod
    volumeMounts:
    - name: container-time
      mountPath: /etc/localtime  # 挂载点,container启动后直接挂载覆盖此文件
  volumes:
  - name: container-time
    hostPath: 
      path: /usr/share/zoneinfo/Asia/Shanghai  # 挂载时区文件到container中

通过创建pod后登录检查时区正常。

二、持久存储卷的用法

学习持久化存储之前需要学习一下以下概念:
Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足。
拿前面 AWS EBS 的例子来说,要使用 Volume,Pod 必须事先知道如下信息:

  • 当前 Volume 来自 AWS EBS。
  • EBS Volume 已经提前创建,并且知道确切的 volume-id。

Pod 通常是由应用的开发人员维护,而 Volume 则通常是由存储系统的管理员维护。开发人员要获得上面的信息:

  • 要么询问管理员。
  • 要么自己就是管理员。

这样就带来一个管理上的问题:应用开发人员和系统管理员的职责耦合在一起了。如果系统规模较小或者对于开发环境这样的情况还可以接受。但当集群规模变大,特别是对于生成环境,考虑到效率和安全性,这就成了必须要解决的问题。

Kubernetes 给出的解决方案是 PersistentVolume 和 PersistentVolumeClaim。

PersistentVolume (PV) 是外部存储系统中的一块存储空间,版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!由管理员创建和维护。与 Volume 一样,PV 具有持久性,生命周期独立于 Pod。

PersistentVolumeClaim (PVC) 是对 PV 的申请 (Claim)。PVC 通常由普通用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的 PV。

有了 PersistentVolumeClaim,用户只需要告诉 Kubernetes 需要什么样的存储资源,而不必关心真正的空间从哪里分配,如何访问等底层版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!细节信息。这些 Storage Provider 的底层信息交给管理员来处理,只有管理员才应该关心创建 PersistentVolume 的细节信息。

Kubernetes 支持多种类型的 PersistentVolume,比如 AWS EBS、Ceph、NFS 等,完整列表请参考 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes

主要包括:NFS/GlusterFS/CephFS/AWS/GCE等等
作为一个容器集群,支持网络存储自然是重中之重了,Kubernetes支持为数众多的云提供商和网络存储方案。
不同公司选择的方案也是不相同。

下节我用 NFS 来体会它存储的使用方法。

NFS

1. 在NFS存储的服务器创建存储目录:

# 这里先创建三个共享目录稍后会用到.
$ mkdir /{nfs_data_1,nfs_data_2,nfs_data_3}

2. 安装nfs:

$ yum -y install nfs-utils rpcbind

3. 写入配置

$ echo "/nfs_data_1 *(rw,sync,no_subtree_check,no_root_squash)" > /etc/exports
$ echo "/nfs_data_2 *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports
$ echo "/nfs_data_3 *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports

4. 重启nfs服务验证

$ systemctl restart nfs-server
$ showmount -e 192.168.56.113  # NFS Server
Export list for 192.168.56.113:
/nfs_data_3 *
/nfs_data_2 *
/nfs_data_1 *

# nfs安装并且共享目录已经创建完毕

NFS作为普通存储卷

在Kubernetes中,可以通过nfs类型的存储卷将现有的NFS(网络文件系统)到的挂接到Pod中。在移除Pod时,NFS存储卷中的内容被不会被删除,只是将存储卷卸载而已。这意味着在NFS存储卷总可以预先填充数据,并且可以在Pod之间共享数据。NFS可以被同时挂接到多个Pod中,并能同时进行写入。需要注意的是:在使用nfs存储卷之前,必须已正确部署和运行NFS服务器,并已经设置了共享目录。

创建一个pod,让其挂载上面创建的共享目录

$ cat >> nfs-busybox << EOF
apiVersion: v1
kind: Pod
metadata:
  name: nfs-busybox-pod
spec:
  containers:
  - name: nfs-busybox-pod
    image: busybox
    imagePullPolicy: IfNotPresent
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-busybox-storage
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-busybox-storage
      nfs:
        path: /nfs_data_1       # NFS 共享目录
        server: 192.168.56.113  # NFS Server
EOF

启动POD,一会儿POD就是completed状态,说明执行完毕。

$ kubectl apply -f nfs-busybox.yaml
pod/nfs-busybox-pod created
$ kubectl get pods
NAME              READY     STATUS      RESTARTS   AGE
nfs-busybox-pod   0/1       Completed   0          7s

我们去NFS Server共享目录查看有没有SUCCESS文件。

$ ls /nfs_data_1/
SUCCESS

说明当NFS作为普通存储卷时我们挂载到pod容器中产生数据将会保存到这个存储卷当中,并且删除pod不会影响数据。


如何基于NFS文件系统创建持久化存储?

Provisioning: PV的预制创建有两种模式:静态动态供给模式,他们的含义是:

静态供给模式: 需要先手动创建PV, 然后通过 PVC 申请 PV 并在 Pod 中使用,这种方式叫做静态供给(Static Provision)。
动态供给模式: 只需要创建PVC,系统根据PVC创建PV, 如果没有满足 PVC 条件的 PV,会动态创建 PV。相比静态供给,动态供给(Dynamical Provision)有明显的优势:不需要提前创建 PV,减少了管理员的工作量,效率高.

动态供给是通过 StorageClass 实现的,StorageClass 定义了如何创建 PV。

下面就分别实践这两种模式的创建跟使用:

NFS作为静态供给模式持久化存储卷

 手动创建PV--->手动创建PVC--->POD挂载使用 

1. 创建PV

$ cat >> nfs-test-pv.yaml << EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-test-pv
  namespace: default
  labels:
    app: nfs-test-pv
spec:
  capacity:
    storage: 5Gi   # 指定PV容量为5G
  accessModes:
    - ReadWriteOnce #  指定访问模式为 ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle # 指定当 PV 的回收策略为 Recycle
  storageClassName: nfs   # 定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。
  nfs:
    path: /nfs_data_2 # 指定 PV 在 NFS 服务器上对应的目录
    server: 192.168.56.113 # NFS Server地址
EOF

STATUS 为 Available,表示 nfs-test-pv 就绪,可以被 PVC 申请。
接下来创建 PVC nfs-test-pv-claim:

2. 创建PVC

$ cat >> nfs-test-pv-claim.yaml << EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-test-pv-claim
  namespace: default
spec:
  accessModes: # 存储访问模式,此能力依赖存储厂商能力
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi # 请求获得的pvc存储大小
  storageClassName: nfs 
EOF

从 kubectl g版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!et pvc 和 kubectl get pv 的输出可以看到 nfs-test-pv-claim 已经 Bound 到 nfs-test-pv,申请成功。
接下来就可以在 Pod 中使用存储了:

3. 创建Pod

$ cat >> nfs-test-pod.yaml << EOF
apiVersion: v1
kind: Pod
metadata:
  name:  nfs-test-pod
spec:
  containers:
  - name:  nfs-test-pod
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 30000
    volumeMounts:
    - mountPath: "/nfs-data"
      name: nfs-data
  volumes:
    - name: nfs-data
      persistentVolumeClaim:
        claimName:  nfs-test-pv-claim
EOF

与使用普通 Volume 的格式类似,在 volumes 中通过 persistentVolumeClaim 指定使用 nfs-test-pv-claim 申请的 Volume。

验证 PV 是否可用:

$ kubectl exec nfs-test-pod touch /nfs-data/SUCCESS

可见,在 Pod 中创建的文件 /nfs-data/SUCCESS 确实已经保存到了 NFS 服务器目录 /nfs_data_2/中。
如果不再需要使用 PV,可用删除 PVC 回收 PV:

4. 回收PVC

持久化卷声明的保护
PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失。
注意:当 pod 状态为 Pending 并且 pod 已经分配给节点或 pod 为 Running 状态时,PVC 处于活动状态。
当启用PVC保护功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用。
您可以看到,当我直接删除上面POD正在使用的PVC时命令直接hang住了,此时虽然为 Teminatiing,但PVC 受到保护,Finalizers 列表中包含 kubernetes.io/pvc-protection:

$ kubectl delete pvc nfs-test-pv-claim

等待 pod 状态变为 Terminated(删除 pod 或者等到它结束),然后检查,确认 PVC 被移除。

反之,如果一个PVC没有被pod使用则可以直接删除。

用户用完 volume 后,可以从允许回收资源的 API 中删除 PVC 对象。PersistentVolume 的回收策略告诉集群在存储卷声明释放后应如何处理该卷。目前,volume 的处理策略有:

  • Retain,不清理, 保留 Volume(需要手动清理)
  • Recycle,删除数据,即 rm -rf /thevolume/*(只有 NFS 和 HostPath 支持)
  • Delete,删除存储资源,比如删除 AWS EBS 卷(只有 AWS EBS, GCE PD, Azure Disk 和 Cinder 支持)

OK 以上是NFS创建静态模式创建PVC,以及PVC跟POD生命周期的一些实践,下面实践动态模式

NFS作为动态持久化存储卷

利用NFS client provisioner动态提供Kubernetes后端存储卷

想要动态生成PV,需要运行一个NFS-Provisioner服务,将已配置好的NFS系统相关参数录入,并向用户提供创建PV的服务。官方推荐使用Deployment运行一个replica来实现,当然也可以使用Daemonset等其他方式,这些都在官方文档中提供了。

前提条件是有已经安装好的NFS服务器,并且NFS服务器与Kubernetes的Slave节点都能网络连通。 所有下文用到的文件来自于git clone https://github.com/kubernetes-incubator/external-storage.git 的nfs-client目录

nfs-client-provisioner 是一个Kubernetes的简易NFS的外部provisioner,本身不提供NFS,需要现有的NFS服务器提供存储

  • PV以 ${namespace}-${pvcName}-${pvName}的命名格式提供(在NFS服务器上)
  • PV回收的时候以 archieved-${namespace}-${pvcName}-${pvName} 的命名格式(在NFS服务器上)

1. 获取nfs-client-provisioner配置文件

$ git clone https://github.com/kubernetes-incubator/external-storage.git

2. 安装部署:

1. 修改deployment版权声明:本文遵循 CC 4.0 BY-SA 版权协议,若要转载请务必附上原文出处链接及本声明,谢谢合作!文件并部署 deploy/deployment.yaml

需要修改的地方只有NFS服务器所在的IP地址(192.168.56.113),以及NFS服务器共享的路径(/nfs_data_3),两处都需要修改为你实际的NFS服务器和共享目录

$ cat deploy/deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 192.168.56.113 
            - name: NFS_PATH
              value: /nfs_data_3
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.56.113
            path: /nfs_data_3

$ kubectl apply -f deploy/deployment.yaml # 执行部署
serviceaccount/nfs-client-provisioner created
deployment.extensions/nfs-client-provisioner created
2. 修改StorageClass文件并部署 deploy/class.yaml

此处可以不修改,或者修改provisioner的名字,需要与上面的deployment的PROVISIONER_NAME名字一致。

$ cat deploy/class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  archiveOnDelete: "false"
  
# 执行部署 
$ kubectl apply -f deploy/class.yaml
storageclass.storage.k8s.io/managed-nfs-storage created

# 查看StorageClass
$ kubectl get sc
NAME                  PROVISIONER      AGE
managed-nfs-storage   fuseim.pri/ifs   16m

# 设置这个managed-nfs-storage名字的SC为Kubernetes的默认存储后端
$ kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
$ kubectl get sc
NAME                            PROVISIONER      AGE
managed-nfs-storage (default)   fuseim.pri/ifs   19m
3. 授权

如果您的集群启用了RBAC,或者您正在运行OpenShift,则必须授权provisioner。 如果你在非默认的“default”名称空间/项目之外部署,可以编辑deploy/rbac.yaml或编辑`oadm policy“指令。

如果启用了RBAC
需要执行如下的命令来授权。

$ cat deploy/rbac.yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

$ kubectl create -f deploy/rbac.yaml
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
4. 执行部署:

测试创建PVC

$ cat deploy/test-claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi

$ kubectl create -f deploy/test-claim.yaml
persistentvolumeclaim/test-claim created

$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                STORAGECLASS          REASON    AGE
persistentvolume/pvc-137f0450-0048-11e9-af7a-00505621dd5b   1Mi        RWX            Delete           Bound     default/test-claim   managed-nfs-storage             9m

NAME                               STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
persistentvolumeclaim/test-claim   Bound     pvc-137f0450-0048-11e9-af7a-00505621dd5b   1Mi        RWX            managed-nfs-storage   9m
# 以上可以看到我们的pvc已经申请成功,等待POD挂载使用
测试创建POD :

POD文件如下,作用就是在test-claim的PV里touch一个SUCCESS文件。

$ cat deploy/test-pod.yaml
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: gcr.io/google_containers/busybox:1.24
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim
        
$ kubectl create -f deploy/test-pod.yaml
pod/test-pod created


# 启动POD,一会儿POD就是completed状态,说明执行完毕。
$ kubectl get pod | grep test-pod
NAME                                      READY     STATUS      RESTARTS   AGE
test-pod                                  0/1       Completed   0          18s

在NFS服务器上的共享目录下的卷子目录中检查创建的NFS PV卷下是否有”SUCCESS” 文件。

以上,说明我们部署正常,并且可以通过动态分配NFS的持久共享卷 。

参考文献

https://k8smeetup.github.io/docs/concepts/storage/volumes/

https://kubernetes.io/docs/concepts/configuration/secret/
https://k8smeetup.github.io/docs/concepts/storage/persistent-volumes/#回收-1
https://k8smeetup.github.io/docs/tasks/administer-cluster/pvc-protection/#lichuqiang
https://jimmysong.io/kubernetes-handbook/practice/using-nfs-for-persistent-storage.html

1 条回应
  1. xxx2021-11-17 · 13:02

    赞,总结得非常好