实战:k8s之NFS存储-2022.2.22

image-20220221202654541

目录

实验环境

实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
   k8s version:v1.22.2
   containerd://1.5.5

实验软件

链接:https://pan.baidu.com/s/166m8lMseErl0wbuQDOUA6w?pwd=y4yi
提取码:y4yi
2022.2.22-44.NFS存储-实验代码

image-20220222130547985

1、NFS 共享存储

前面我们学习了 hostPath 与 Local PV 两种本地存储方式,但是平时我们的应用更多的是无状态服务,可能会同时发布在不同的节点上,这个时候本地存储就不适用了,往往就需要使用到共享存储了,比如最简单常用的网络共享存储 NFS,本节课我们就来介绍下如何在 Kubernetes 下面使用 NFS 共享存储。

2、安装

📍 案例演示:nfs服务安装(测试成功)

1️⃣ nfs server端配置

我们这里为了演示方便,先使用相对简单的 NFS 这种存储资源,接下来我们在master1节点 172.29.9.51 上来安装 NFS 服务,数据目录:/var/lib/k8s/data/

🍀 关闭防火墙

[root@master1 ~]#systemctl stop firewalld.service
[root@master1 ~]#systemctl disable firewalld.service

🍀 安装配置 nfs

[root@master1 ~]#yum -y install nfs-utils rpcbind

🍀 共享目录设置权限:

[root@master1 ~]#mkdir -p /var/lib/k8s/data
[root@master1 ~]#chmod 755 /var/lib/k8s/data/

🍀 配置 nfs,nfs 的默认配置文件在 /etc/exports 文件下,在该文件中添加下面的配置信息:

[root@master1 ~]#vim /etc/exports
/var/lib/k8s/data  *(rw,sync,no_root_squash)

配置说明:

  • /var/lib/k8s/data:是共享的数据目录
  • *:表示任何人都有权限连接,当然也可以是一个网段,一个 IP,也可以是域名
  • rw:读写的权限
  • sync:表示文件同时写入硬盘和内存
  • no_root_squash:当登录 NFS 主机使用共享目录的使用者是 root 时,其权限将被转换成为匿名使用者,通常它的 UID 与 GID,都会变成 nobody 身份

当然 nfs 的配置还有很多,感兴趣的同学可以在网上去查找一下。

🍀 启动服务 nfs 需要向 rpc 注册,rpc 一旦重启了,注册的文件都会丢失,向他注册的服务都需要重启 注意启动顺序,先启动 rpcbind

[root@master1 ~]#systemctl start rpcbind.service
[root@master1 ~]#systemctl enable rpcbind.service
[root@master1 ~]#systemctl status rpcbind.service
● rpcbind.service - RPC bind service
   Loaded: loaded (/usr/lib/systemd/system/rpcbind.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2022-01-19 20:30:33 CST; 1 months 2 days ago
 Main PID: 5571 (rpcbind)
   CGroup: /system.slice/rpcbind.service
           └─5571 /sbin/rpcbind -w

Jan 19 20:30:33 master1 systemd[1]: Starting RPC bind service...
Jan 19 20:30:33 master1 systemd[1]: Started RPC bind service.

看到上面的 Started 证明启动成功了。

🍀 然后启动 nfs 服务:

[root@master1 ~]#systemctl start nfs.service
[root@master1 ~]#systemctl enable nfs.service
[root@master1 ~]#systemctl status nfs.service
● nfs-server.service - NFS server and services
   Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; enabled; vendor preset: disabled)
  Drop-In: /run/systemd/generator/nfs-server.service.d
           └─order-with-mounts.conf
   Active: active (exited) since Mon 2022-02-21 20:42:05 CST; 17s ago
 Main PID: 27590 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/nfs-server.service

Feb 21 20:42:05 master1 systemd[1]: Starting NFS server and services...
Feb 21 20:42:05 master1 systemd[1]: Started NFS server and services.

同样看到 Started 则证明 NFS Server 启动成功了。

🍀 另外我们还可以通过下面的命令确认下:

[root@master1 ~]#rpcinfo -p|grep nfs
    100003    3   tcp   2049  nfs
    100003    4   tcp   2049  nfs
    100227    3   tcp   2049  nfs_acl
    100003    3   udp   2049  nfs
    100003    4   udp   2049  nfs
    100227    3   udp   2049  nfs_acl

🍀 查看具体目录挂载权限:

[root@master1 ~]#cat /var/lib/nfs/etab
/var/lib/k8s/data       *(rw,sync,wdelay,hide,nocrossmnt,secure,no_root_squash,no_all_squash,no_subtree_check,secure_locks,acl,no_pnfs,anonuid=65534,anongid=65534,sec=sys,rw,secure,no_root_squash,no_all_sq

到这里我们就把 nfs server 给安装成功了。

2️⃣ nfs client端配置

然后就是前往节点安装 nfs 的客户端来验证,安装 nfs 当前也需要先关闭防火墙:

systemctl stop firewalld.service
systemctl disable firewalld.service

然后安装 nfs

yum -y install nfs-utils rpcbind

安装完成后,和上面的方法一样,先启动 rpc、然后启动 nfs:

systemctl start rpcbind.service
systemctl enable rpcbind.service
systemctl start nfs.service
systemctl enable nfs.service

🍀 挂载数据目录 客户端启动完成后,我们在客户端来挂载下 nfs 测试下,首先检查下 nfs 是否有共享目录:

[root@node1 ~]#showmount -e 172.29.9.51
Export list for 172.29.9.51:
/var/lib/k8s/data *

🍀 然后我们在客户端上新建目录:

[root@node1 ~]#mkdir -p /root/course/kubeadm/data

将 nfs 共享目录挂载到上面的目录:

[root@node1 ~]#mount -t nfs 172.29.9.51:/var/lib/k8s/data /root/course/kubeadm/data

🍀 挂载成功后,在客户端上面的目录中新建一个文件,然后我们观察下 nfs 服务端的共享目录下面是否也会出现该文件:

[root@node1 ~]#touch /root/course/kubeadm/data/test.txt

🍀 然后在 nfs 服务端查看:

[root@master1 ~]#ls -ls /var/lib/k8s/data/
total 0
0 -rw-r--r-- 1 root root 0 Feb 21 20:46 test.txt

如果上面出现了 test.txt 的文件,那么证明我们的 nfs 挂载成功了。

⚠️ 注意:以上只是用node1来作为nfs client来测试nfs共享存储的可用性而已,实际nfs作为k8s集群共享存储使用时,只要保证nfs server可用,k8s node节点都可以正常访问到nfs服务就可以(不需要在节点上挂载nfs配置,因为在pv里面已经有指定了nfs路径新信息)

实验结束,完美。😘

3、使用

📍案例演示1:nfs共享存储测试(测试成功)

上面已经安装好了nfs服务,也测试过k8s节点是可以正常访问nfs服务的;

前面我们已经了解到了 PV、PVC、StorgeClass 的使用,那么我们的 NFS 又应该如何在 Kubernetes 中使用呢?

🍀 同样创建一个如下所示 nfs 类型的 PV 资源对象:

# 01-nfs-volume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /var/lib/k8s/data/  # 指定nfs的挂载点
    server: 172.29.9.51  # 指定nfs服务地址
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  storageClassName: manual
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

🍀 我们知道用户真正使用的是 PVC,而要使用 PVC 的前提就是必须要先和某个符合条件的 PV 进行一一绑定,比如存储容器、访问模式,以及 PV 和 PVC 的 storageClassName 字段必须一样,这样才能够进行绑定,当 PVC 和 PV 绑定成功后就可以直接使用这个 PVC 对象了:

# 02-nfs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-volumes
spec:
  volumes:
  - name: nfs
    persistentVolumeClaim:
      claimName: nfs-pvc
  containers:
  - name: web
    image: nginx
    ports:
    - name: web
      containerPort: 80
    volumeMounts:
    - name: nfs
      subPath: test-volumes #注意:这个就是在nfs目录会创建有一个子目录`test-volumes`
      mountPath: "/usr/share/nginx/html"

🍀 直接创建上面的资源对象即可:

$kubectl apply -f 01-nfs-volume.yaml 
persistentvolume/nfs-pv created
persistentvolumeclaim/nfs-pvc created
$kubectl apply -f 02-nfs-pod.yaml 
pod/test-volumes created
$kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
nfs-pv   1Gi        RWO            Retain           Bound    default/nfs-pvc   manual                  54s
$kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc   Bound    nfs-pv   1Gi        RWO            manual         62s
$kubectl get po test-volumes -owide
NAME           READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-volumes   1/1     Running   0          66s   10.244.2.60   node2   <none>           <none>

🍀 由于我们这里 PV 中的数据为空,所以挂载后会将 nginx 容器中的 /usr/share/nginx/html 目录覆盖,那么访问应用的时候就没有内容了(就会报403错误):

[root@master1 ~]#curl 10.244.2.60:80 
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>

我们可以在 PV 目录中添加一些内容:

# 在 nfs 服务器上面执行
[root@master1 ~]#echo "nfs pv content" > /var/lib/k8s/data/test-volumes/index.html
[root@master1 ~]#curl 10.244.2.60:80
nfs pv content

然后重新访问就有数据了,而且当我们的 Pod 应用挂掉或者被删掉重新启动后数据还是存在的,因为数据已经持久化了:

$ kubectl delete -f 02-nfs-pod.yaml 
pod "test-volumes" deleted
$ kubectl apply -f 02-nfs-pod.yaml
pod/test-volumes created
$ kubectl get po test-volumes -owide
NAME           READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
test-volumes   1/1     Running   0          17s   10.244.2.88   node2   <none>           <none>
# curl 10.244.2.88:80
nfs pv content

实验结束,完美。😘

📍案例演示2:nfs共享存储,自动存储类测试(测试成功)

🍀 上面的示例中需要我们手动去创建 PV 来和 PVC 进行绑定,有的场景下面需要自动创建 PV,这个时候就需要使用到 StorageClass 了,并且需要一个对应的 provisioner 来自动创建 PV,比如这里我们使用的 NFS 存储,则可以使用 nfs-subdir-external-provisioner 这个 Provisioner,它使用现有的和已配置的NFS 服务器来支持通过 PVC 动态配置 PV,持久卷配置为 ${namespace}-${pvcName}-${pvName},首先我们使用 Helm Chart 来安装:

[root@master1 ~]#helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
[root@master1 ~]#helm upgrade --install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --set nfs.server=172.29.9.51 --set nfs.path=/var/lib/k8s/data --set image.repository=cnych/nfs-subdir-external-provisioner --set storageClass.defaultClass=true -n kube-system

上面的命令会在 kube-system 命名空间下安装 nfs-subdir-external-provisioner,并且会创建一个名为 nfs-client 默认的 StorageClass:

[root@master1 ~]#kubectl get sc
NAME                   PROVISIONER                                     RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-storage          kubernetes.io/no-provisioner                    Delete          WaitForFirstConsumer   false                  17h        
nfs-client (default)   cluster.local/nfs-subdir-external-provisioner   Delete          Immediate              true                   3m1s                       38d
➜ kubectl get sc nfs-client -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  ......
  name: nfs-client
parameters:
  archiveOnDelete: "true"
provisioner: cluster.local/nfs-subdir-external-provisioner
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true

这样当以后我们创建的 PVC 中如果没有指定具体的 StorageClass 的时候,则会使用上面的 SC 自动创建一个 PV。

🍀 比如我们创建一个如下所示的 PVC:

# 03-nfs-sc-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-sc-pvc
spec:
  # storageClassName: nfs-client  # 不指定则使用默认的 SC
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

直接创建上面的 PVC 资源对象后就会自动创建一个 PV 与其进行绑定:

$ kubectl apply -f 03-nfs-sc-pvc.yaml 
persistentvolumeclaim/nfs-sc-pvc created
[root@master1 ~]#kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE 
nfs-pvc      Bound    nfs-pv                                     1Gi        RWO            manual         124m
nfs-sc-pvc   Bound    pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92   1Gi        RWO            nfs-client     11s 
[root@master1 ~]#kubectl get pv 
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE 
nfs-pv                                     1Gi        RWO            Retain           Bound    default/nfs-pvc      manual                  125mpvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92   1Gi        RWO            Delete           Bound    default/nfs-sc-pvc   nfs-client              29s 

image-20220222075346549

对应自动创建的 PV 如下所示:

[root@master1 ~]#kubectl get pv pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92 -oyaml
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: cluster.local/nfs-subdir-external-provisioner
  creationTimestamp: "2022-02-21T23:52:37Z"
  finalizers:
  - kubernetes.io/pv-protection
  name: pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92
  resourceVersion: "1339652"
  uid: 536ff7a4-7eac-49f8-bb08-64b2719a483f
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 1Gi
  claimRef:
    apiVersion: v1
    kind: PersistentVolumeClaim
    name: nfs-sc-pvc
    namespace: default
    resourceVersion: "1339647"
    uid: af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92
  nfs:
    path: /var/lib/k8s/data/default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92
    server: 172.29.9.51
  persistentVolumeReclaimPolicy: Delete
  storageClassName: nfs-client
  volumeMode: Filesystem
status:
  phase: Bound

挂载的 nfs 目录为 /var/lib/k8s/data/default-nfs-sc-pvc-pvc-ed8e2fb7-897d-465f-8735-81d52c91d074,和上面的 ${namespace}-${pvcName}-${pvName} 规范一致的。

我们可以到这个nfs共享路径下面看看:

[root@master1 ~]#cd /var/lib/k8s/data/                                                           
[root@master1 data]#ls
default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92  test-volumes  test.txt
[root@master1 data]#cd default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/
[root@master1 default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92]#ls

🍀 我们再次测试一个pod应用

# 04-nfs-sc-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nfs-sc-pod
spec:
  volumes:
  - name: nfs
    persistentVolumeClaim:
      claimName: nfs-sc-pvc
  containers:
  - name: web
    image: nginx
    ports:
    - name: web
      containerPort: 80
    volumeMounts:
    - name: nfs
      # subPath: test-volumes #注意:这个就是在nfs目录会创建有一个子目录`test-volumes`,在storageclass里面,这边就不需要这个参数了。
      mountPath: "/usr/share/nginx/html"

直接部署上面资源:

$ kubectl apply -f 04-nfs-sc-pod.yaml 
pod/nfs-sc-pod created

查看:

[root@master1 ~]#kubectl get po nfs-sc-pod -owide
NAME         READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
nfs-sc-pod   1/1     Running   0          33s   10.244.1.78   node1   <none>           <none>
[root@master1 ~]#kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc      Bound    nfs-pv                                     1Gi        RWO            manual         133m
nfs-sc-pvc   Bound    pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92   1Gi        RWO            nfs-client     9m3s
[root@master1 ~]#kubectl get pv 
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
nfs-pv                                     1Gi        RWO            Retain           Bound    default/nfs-pvc      manual                  134mpvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92   1Gi        RWO            Delete           Bound    default/nfs-sc-pvc   nfs-client              9m14s

测试:

[root@master1 ~]#cd /var/lib/k8s/data/
[root@master1 data]#ls
default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92  test-volumes  test.txt
[root@master1 data]#cd default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/
[root@master1 default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92]#ls
[root@master1 default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92]#echo "nfs sc  pv content" > /var/lib/k8s/data/default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/index.html
[root@master1 default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92]#curl  10.244.1.78
nfs sc  pv content

实验结束,完美。😘

📍 案例演示3:k8s持久化存储pv和pvc实验完整模拟(测试成功)

来源于:simmon老师。

0.搭建一个nfs存储

注意:本次复用k8s-master节点作为nfs server端来提供nfs服务。

(1)在nfs server端安装nfs服务

# 在nfs server服务端节点(k8s-master)安装
[root@k8s-master ~]#yum install nfs-utils -y
[root@k8s-master ~]#mkdir /nfs
[root@k8s-master ~]#echo "/nfs *(rw,insecure,sync,no_root_squash)" > /etc/exports
[root@k8s-master ~]#systemctl enable rpcbind nfs-utils nfs-server --now

# 检测一下
[root@k8s-master ~]#showmount -e 172.29.9.31
Export list for 172.29.9.31:
/nfs *
[root@k8s-master ~]#

(2)nfs server端配置完成后,可以在其他的机器上挂在检测一下

#本次在k8s-node1上验证下nfs server提供的nfs服务是否正常
用下面这个命令验证下:
mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 172.29.9.31:/nfs /opt

#如果是报下面这个错误的话,是因为nfs client也是需要安装nfs-utils包的,要不这个测试命令用不了的。
#最后,特别注意:无论nfs客户端还是服务端均要安装nfs-utils软件包的,否则后期pod创建会报错的!!!
[root@k8s-node1 ~]#mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 172.29.9.31:/nfs /opt
mount: wrong fs type, bad option, bad superblock on 172.29.9.31:/nfs,
       missing codepage or helper program, or other error
       (for several filesystems (e.g. nfs, cifs) you might
       need a /sbin/mount.<type> helper program)

       In some cases useful info is found in syslog - try
       dmesg | tail or so.
[root@k8s-master ~]#

#安装nfs-utils
[root@k8s-node1 ~]#yum install -y nfs-utils

#再次用命令测试nfs 服务是否正常
[root@k8s-node1 ~]#mount -t nfs -o vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 172.29.9.31:/nfs /opt
#可以看到nfs服务一切正常
[root@k8s-node1 ~]#df|grep nfs 
172.29.9.31:/nfs         17811456 6279424  11532032  36% /opt
[root@k8s-node1 ~]#mount|grep nfs
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
172.29.9.31:/nfs on /opt type nfs (rw,relatime,vers=3,rsize=262144,wsize=262144,namlen=255,hard,nolock,noresvport,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.29.9.31,mountvers=3,mountport=20048,mountproto=tcp,local_lock=all,addr=172.29.9.31)
[root@k8s-node1 ~]#


#我们可以顺便写几个文件测测下效果:
[root@k8s-node1 ~]#df -hT|grep nfs
172.29.9.31:/nfs        nfs        17G  6.0G   11G  36% /opt
[root@k8s-node1 ~]#cd /opt/
[root@k8s-node1 opt]#ls
[root@k8s-node1 opt]#echo node2 > node2.txt #在node2 nfs client的nfs目录下写一个文件
[root@k8s-node1 opt]#
[root@k8s-node1 opt]#exit
logout
Connection to k8s-node1 closed.
[root@k8s-master ~]#cd /nfs/ #然后再nfs serrver端的nfs目录下就可以看见这个文件了,说明我们的nfs服务时没有问题的
[root@k8s-master nfs]#ls
node2.txt
[root@k8s-master nfs]#cat node2.txt
node2
[root@k8s-master nfs]#


#测试nfs正常后,我们这里把刚挂载的nfs给卸载掉
[root@k8s-node1 ~]#df -hT|grep nfs
172.29.9.31:/nfs        nfs        17G  6.0G   11G  36% /opt
[root@k8s-node1 ~]#umount /opt/
[root@k8s-node1 ~]#df -hT|grep nfs
[root@k8s-node1 ~]#

nfs服务搭建成功后,我们就可以来测试pv、pvc的实验了。

⚠️ 问题:测试pv,pvc实验时,需要把刚才测试nfs服务可用性时挂载的目录给卸载吗?
答案:要去掉,一般情况是需要去掉的,当时我们只是测试下nfs服务的可用性而已。并且我们只要保证k8s节点均能使用nfs服务即可。记得,节点上要安装nfs-utils软件包哦。

1.创建基于NFS的PV

我们先来查看下当前k8s集群环境,保证本次实验环境纯净:

[root@k8s-master ~]#kubectl get po,deploy,svc,pv,pvc #可以看到,当前k8s集群环境是ok的
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   166d
[root@k8s-master ~]#

1、创建pv的yaml文件

运维工程师准备好PV的配置文件,如下所示:

vim pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
  labels:
    type: k8s-claim # <----------  0	
spec:
  capacity:
    storage: 1Gi # <----------  1
  accessModes: ["ReadWriteOnce"] # <----------  2
  persistentVolumeReclaimPolicy: Recycle # <----------  3
  storageClassName: nfs # <----------  4
  nfs:
    path: /nfs/pv1 # <----------  5
    server: 172.29.9.31    

对上面的yaml文件我们做个说明:

0、labels:
      type: k8s-claim    # 这里建议打上一个独有的标签,在多个pv的时候方便提供pvc选择挂载,工作中一般上打标签格式是:项目组-团队名-pv名
1、capacity是指定pv的容量是1G
2、accessModes是指定的访问模式,支持的访问模式有: 
	ReadWriteOnce表示PV能以read-write模式mount到单个节点
	ReadOnlyMany表示pv有read-only只读模式mount到多个节点
	ReadWriteMany表示pv以read-write模式mount到多个节点

3、persistentVolumeReclaimPolicy 指定当PV的回收策略,有三种方式:#研发使用完后,就会删除pvc,删除pvc后就会有3个动作
	Retain: 删除容器之后,但是pv由管理员手工回收,安全级别比较高 #工作环境中一般常用的是`retain`
	Recycle:删除容器之后,清除PV中的数据,效果相当于执行了rm -rf /nfs/pv1/*
	Delete:删除容器之后,删除整个PV对应的存储资源,不推荐!!!

4、storageClassName是指定PV的class为我们这里定义的nfs,相当于为PV设置了一个分类,`pvc可以指定class申请相应class的pv`。举个例子:开发去申请2G大小的pvc,那么符合pvc条件的pv有两种。一种是高速存储Gclass,一种是低速存储Lclass,开发申请的2G是用于数据库,那么需要高速存储,此时定义storageClassName为Gclass,所以pvc会自动去找Gclass。

这里有个坑,`就是PVC里面的storageClassName必须和PV里面的storageClassName保持一致`#我们可以看到,pvC的限制非常多,都要符合pv的条件才行;

另外:这里需要先指定PV在NFS服务器上对应的目录,这里注意,需要手动提前创建好这个目录并授权,否则后面挂载会提示目录不存在。授权的用户及用户组是nobody #这里需要注意下。

[root@k8s-master ~]#cat /etc/passwd|grep nobody
nobody:x:99:99:Nobody:/:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
[root@k8s-master ~]#mkdir /nfs/pv1
[root@k8s-master ~]#chown -R nobody.nobody /nfs/pv1/
[root@k8s-master ~]#chown -R nobody.nobody /nfs/
[root@k8s-master ~]#ll -d /nfs/ /nfs/pv1/
drwxr-xr-x 3 nobody nobody 34 Nov 16 14:29 /nfs/
drwxr-xr-x 2 nobody nobody  6 Nov 16 14:29 /nfs/pv1/
[root@k8s-master ~]#

2、创建PV

# 部署pv
[root@k8s-master ~]#kubectl apply -f pv.yaml
persistentvolume/pv1 created

# 查看pv的状态
[root@k8s-master ~]#kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv1    1Gi        RWO            Recycle          Available           nfs                     5s
[root@k8s-master ~]#

# 注意:查看STATUS字段,只有为Available字段才可以正常使用,被pvc申请
2.创建基于NFS的PVC

1、接下来创建pvc

vim pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1	#  <----------4

spec:
  accessModes: ["ReadWriteOnce"]	# <----------0
  resources:
    requests:
      storage: 1Gi	# <----------1
  storageClassName: nfs	# <----------2
  selector:
    matchLabels:
      type: k8s-claim    # <----------3   

接下来我们对上面的参数做一个说明

0、accessModes是指定的访问模式,跟我们上面解释的一个道理,需要注意,`pv里面是什么模式,这里也要写对应的模式`
1、storage存储大小,与pv里面保持一致
2、storageClassName名字,至少和某个pv的storageClassName保持一致,否则找不到对应的分类
3、matchLabels: 这是匹配的pv的标签,标签在k8s里面非常重要的一个概念
4、metadata:pvc的标签,后面挂载成功后会用到

2、创建pvc

# 应用pvc.yaml配置文件
[root@k8s-master ~]#kubectl apply -f pvc.yaml
persistentvolumeclaim/pvc1 created

# 获取pvc状态
[root@k8s-master ~]#kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv1      1Gi        RWO            nfs            53s
[root@k8s-master ~]#
# 主要是看STATUS字段,当表示Bound代表成功找到pv并成功绑定

# 此时再去看pv的状态
[root@k8s-master ~]#kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM          STORAGECLASS   REASON   AGE
pv1    1Gi        RWO            Recycle          Bound    default/pvc1   nfs                     12m
[root@k8s-master ~]#
# 可以看到pv的STATUS字段显示Bound,而且CLAIM字段提示被default/pvc所消费
3.创建Pod应用挂载pvc

pvc与pv绑定成功后就可以使用pvc了,使用pvc其实就是挂载pvc,当目录挂载了pvc之后,应用就可以正常工作了,比如将pvc挂载到nginx的web根目录/usr/share/www/html上,如下例子所示:

vim nginx.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts: # 我们这里将nginx容器默认的页面目录挂载
        - name: html-files # 与下面的volumes的name保持一致
          mountPath: "usr/share/nginx/html"

      volumes:
      - name: html-files
        persistentVolumeClaim:  # 卷类型使用pvc,同时下面名称处填先创建好的pvc1
          claimName: pvc1
4.创建对应存储上面的资源

在实际的工作环境当中,创建一个相对应的web服务之前已经准备好了访问的资源,等容器创建完毕,就可以立马访问了。

# 先在对应的存储上创建访问的资源
[root@k8s-master ~]#cd /nfs/pv1/
[root@k8s-master pv1]#echo 'HelloWorld' > index.html
5.创建nginx的pod和service
[root@k8s-master ~]#kubectl apply -f nginx.yaml
service/nginx created
deployment.apps/nginx created
[root@k8s-master ~]#

等待这个pod资源创建完毕

[root@k8s-master ~]#kubectl get po,deploy,svc
NAME                       READY   STATUS    RESTARTS   AGE
pod/nginx-ffc5548d-48zwj   1/1     Running   0          30s

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           30s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   167d
service/nginx        ClusterIP   10.97.70.55   <none>        80/TCP    30s
[root@k8s-master ~]#

访问一下

~]# curl http://svc地址

[root@k8s-master ~]#curl 10.97.70.55
HelloWorld
[root@k8s-master ~]#

进入容器内部修改index.html文件(不建议),再次访问查看效果:

[root@k8s-master ~]#kubectl get po
NAME                   READY   STATUS    RESTARTS   AGE
nginx-ffc5548d-48zwj   1/1     Running   0          3m13s
[root@k8s-master ~]#kubectl exec -it nginx-ffc5548d-48zwj -- bash
root@nginx-ffc5548d-48zwj:/# echo 222222 >> /usr/share/nginx/html/index.html
root@nginx-ffc5548d-48zwj:/# exit
exit
[root@k8s-master ~]#cat /nfs/pv1/index.html
HelloWorld
222222
[root@k8s-master ~]#
#以上符合预期效果。

看的出来容器里面的修改都会保存到nfs存储上。

有的同学访问403的原因和解决方法

没有在nfs存储创建相应的资源导致的。主要是容器里面的/usr/share/nginx/html目录下的内容挂载后被覆盖了,所以没有index.html文件

在nginx的pod所在的节点执行下面的命令可以查看相关的挂载信息

#查看次pod被调度在了k8s-node1节点上了
[root@k8s-master ~]#kubectl get po -owide
NAME                   READY   STATUS    RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
nginx-ffc5548d-48zwj   1/1     Running   0          4m56s   10.244.36.122   k8s-node1   <none>           <none>
[root@k8s-master ~]#

#到k8s-node1节点上可以看到次pod的挂载信息的
[root@k8s-node1 ~]#mount -l|grep nfs
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
172.29.9.31:/nfs/pv1 on /var/lib/kubelet/pods/91246cf2-5a69-4e93-999b-5166be2a12b6/volumes/kubernetes.io~nfs/pv1 type nfs4 (rw,relatime,vers=4.1,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=172.29.9.32,local_lock=none,addr=172.29.9.31)
[root@k8s-node1 ~]#

#是可以看到index.html文件的内容的
[root@k8s-node1 ~]#ls /var/lib/kubelet/pods/91246cf2-5a69-4e93-999b-5166be2a12b6/volumes/kubernetes.io~nfs/pv1/
index.html
[root@k8s-node1 ~]#cat  /var/lib/kubelet/pods/91246cf2-5a69-4e93-999b-5166be2a12b6/volumes/kubernetes.io~nfs/pv1/index.html
HelloWorld
222222
[root@k8s-node1 ~]#

# 在nfs-server端执行,也是可以查看存储目录相关信息的
fuser -mv  /nfs/pv1 #注意:这个fuser命令需要安装psmisc-22.20-17.el7.x86_64软件包
[root@k8s-master ~]#yum install -y psmisc-22.20-17.el7.x86_64
[root@k8s-master ~]#fuser -mv  /nfs/pv1|grep nfs
                     USER        PID ACCESS COMMAND
/nfs/pv1:            root      mount /
                     root      knfsd /nfs
                     root      .rce. systemd
                     root      .rc.. kthreadd
                     root      .rc.. ksoftirqd/0
                     root      .rc.. kworker/0:0H
                     root      .rc.. migration/0
                     root      .rc.. rcu_bh
                     root      .rc.. rcu_sched
                     root      .rc.. lru-add-drain
                     root      .rc.. watchdog/0
                     root      .rc.. watchdog/1
                     root      .rc.. migration/1
                     root      .rc.. ksoftirqd/1
                     root      .rc.. kworker/1:0H
                     root      .rc.. netns
                     root      .rc.. khungtaskd
                     root      .rc.. writeback
                     root      .rc.. kintegrityd
                     root      .rc.. bioset
                     root      .rc.. bioset
                     root      .rc.. bioset
                     root      .rc.. kblockd
                     root      .rc.. md
                     root      .rc.. edac-poller
                     root      .rc.. watchdogd
                     root      .rc.. kswapd0
                     root      .rc.. ksmd
                     root      .rc.. khugepaged
                     root      .rc.. crypto
                     root      .rc.. kthrotld
                     root      .rc.. kmpath_rdacd
                     root      .rc.. kaluad
                     root      .rc.. kpsmoused
                     root      .rc.. ipv6_addrconf
                     root      .rc.. deferwq
                     root      .rc.. kauditd
                     root      .rc.. nfit
                     root      .rc.. ata_sff
                     root      .rc.. mpt_poll_0
                     root      .rc.. mpt/0
                     root      .rc.. scsi_eh_0
                     root      .rc.. scsi_tmf_0
                     root      .rc.. scsi_eh_1
                     root      .rc.. scsi_tmf_1
                     root      .rc.. scsi_eh_2
                     root      .rc.. scsi_tmf_2
                     root      .rc.. ttm_swap
                     root      .rc.. irq/16-vmwgfx
                     root      .rc.. kworker/1:0
                     root      .rc.. kdmflush
                     root      .rc.. bioset
                     root      .rc.. kdmflush
                     root      .rc.. bioset
                     root      .rc.. bioset
                     root      .rc.. xfsalloc
                     root      .rc.. xfs_mru_cache
                     root      .rc.. xfs-buf/dm-0
                     root      .rc.. xfs-data/dm-0
                     root      .rc.. xfs-conv/dm-0
                     root      .rc.. xfs-cil/dm-0
                     root      .rc.. xfs-reclaim/dm-
                     root      .rc.. xfs-log/dm-0
                     root      .rc.. xfs-eofblocks/d
                     root      .rc.. xfsaild/dm-0
                     root      .rc.. kworker/1:1H
                     root      .rce. systemd-journal
                     root      .rce. lvmetad
                     root      frce. systemd-udevd
                     root      .rc.. kworker/0:1H
                     root      .rc.. xfs-buf/sda1
                     root      .rc.. xfs-data/sda1
                     root      .rc.. xfs-conv/sda1
                     root      .rc.. xfs-cil/sda1
                     root      .rc.. xfs-reclaim/sda
                     root      .rc.. xfs-log/sda1
                     root      .rc.. xfs-eofblocks/s
                     root      .rc.. xfsaild/sda1
                     root      .rc.. rpciod
                     root      .rc.. xprtiod
                     root      Frce. auditd
                     root      .rce. systemd-logind
                     root      .rce. irqbalance
                     root      Frce. VGAuthService
                     root      Frce. vmtoolsd
                     dbus      .rce. dbus-daemon
                     root      Frce. gssproxy
                     polkitd   .rce. polkitd
                     root      .rce. crond
                     root      .rce. login
                     root      .rc.. kworker/1:2
                     root      .rce. sshd
                     root      Frce. tuned
                     root      Frce. rsyslogd
                     root      Frce. kubelet
                     root      Frce. containerd
                     root      Frce. dockerd
                     root      Frce. master
                     postfix   .rce. qmgr
                     root      .rce. ssh
                     root      .rc.. kworker/1:1
                     root      .r.e. containerd-shim
                     root      .r.e. containerd-shim
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      ....m pause
                     root      ....m pause
                     root      .rce. grep
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      .r.e. containerd-shim
                     root      F...m etcd
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      .r.e. containerd-shim
                     root      ....m kube-proxy
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      .r.e. containerd-shim
                     polkitd   ....m kube-controller
                     root      .r.e. containerd-shim
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      ....m pause
                     root      .r.e. containerd-shim
                     root      .r.e. containerd-shim
                     root      ....m pause
                     root      ....m pause
                     root      .rc.. kworker/0:1
                     rpc       .rce. rpcbind
                     rpcuser   .rce. rpc.statd
                     root      .rce. rpc.idmapd
                     root      frce. rpc.mountd
                     root      .rc.. nfsd4_callbacks
                     root      .rc.. lockd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. nfsd
                     root      .rc.. kworker/u256:0
                     root      .r.e. containerd-shim
                     root      ....m kube-controller
                     root      .r.e. containerd-shim
                     root      .r.e. containerd-shim
                     (unknown)  F...m metrics-sidecar
                     root      ....m kube-scheduler
                     root      .r.e. containerd-shim
                     root      .r.e. containerd-shim
                     root      ....m coredns
                     root      ....m coredns
                     root      .r.e. containerd-shim
                     root      ....m kube-apiserver
                     root      .r.e. containerd-shim
                     (unknown)  F...m dashboard
                     root      .r.e. containerd-shim
                     root      ....m runsvdir
                     root      ....m runsv
                     root      ....m runsv
                     root      ....m runsv
                     root      ....m runsv
                     root      ....m runsv
                     root      ....m runsv
                     root      ....m calico-node
                     root      ....m calico-node
                     root      ....m calico-node
                     root      ....m calico-node
                     root      ....m bird6
                     root      ....m bird
                     root      .rc.. kworker/u256:1
                     root      .rc.. kworker/0:0
                     root      .rc.. kworker/u257:0
                     root      .rc.. hci0
                     root      .rc.. hci0
                     root      .rc.. kworker/u257:2
                     root      .rce. sshd
                     root      .rce. sshd
                     root      .rce. sftp-server
                     root      .rce. bash
                     postfix   .rce. pickup
                     root      .rce. sshd
                     root      .rce. sshd
                     root      .rce. sftp-server
                     root      .rce. bash
                     root      .rc.. nfsiod
                     root      .rce. bash
[root@k8s-master ~]#
6.模拟容器意外退出

手动删除pod,查看容器里面的修改是否持久化了

kubectl delete pod xxxxx
curl http://svc地址

#测试过程
[root@k8s-master ~]#kubectl get po,svc
NAME                       READY   STATUS    RESTARTS   AGE
pod/nginx-ffc5548d-dj9t7   1/1     Running   0          2m8s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   167d
service/nginx        ClusterIP   10.97.70.55   <none>        80/TCP    16m
[root@k8s-master ~]#kubectl delete pod nginx-ffc5548d-dj9t7
pod "nginx-ffc5548d-dj9t7" deleted
[root@k8s-master ~]#kubectl get po,svc
NAME                       READY   STATUS    RESTARTS   AGE
pod/nginx-ffc5548d-f78lf   1/1     Running   0          31s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1     <none>        443/TCP   167d
service/nginx        ClusterIP   10.97.70.55   <none>        80/TCP    17m
[root@k8s-master ~]#curl 10.97.70.55
HelloWorld
222222
[root@k8s-master ~]#

此时会发现我们之前修改的内容依然保存着,这就是容器由于某些原因意外退出后,数据依然持久化的目的。

7.回收PVC和PV
[root@k8s-master ~]#kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv1      1Gi        RWO            nfs            56m
[root@k8s-master ~]#kubectl delete pvc pvc1
persistentvolumeclaim "pvc1" deleted # 一直卡在这里

# 卡着的原因就是因为我们的pod应用正在使用pvc,所以删除不了
# 看下pvc发现STATUS是Terminating删除中的状态,这是因为服务pod还在占用这个pvc使用中
[root@k8s-master ~]#kubectl get pvc
NAME   STATUS        VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Terminating   pv1      1Gi        RWO            nfs            57m
[root@k8s-master ~]#

接下来我们先删除pod

# 先访问一把
[root@k8s-master ~]#curl 10.97.70.55
HelloWorld
222222

# 删除nginx资源
[root@k8s-master ~]#kubectl delete -f nginx.yaml
service "nginx" deleted
deployment.apps "nginx" deleted

#再次查看pvc
[root@k8s-master ~]#kubectl get pvc
No resources found in default namespace.
[root@k8s-master ~]#
# 会发现,删除了nginx资源,pvc这个资源立马就消失了,

# 根据先前创建pv时的数据回收策略为Recycle – 清除 PV 中的数据,这时果然先创建的index.html已经被删除了,在生产中要尤其注意这里的模式,注意及时备份数据
[root@k8s-master ~]#ls /nfs/pv1/
[root@k8s-master ~]#

# 注意:虽然此时pv是可以再次被pvc来消费的,但根据生产的经验,建议在删除pvc时,也同时把它消费的pv一并删除,然后再重启创建都是可以的

至此,以上实验结束,感谢阅读。😘

4、原理

我们只是在 volumes 中指定了我们上面创建的 PVC 对象,当这个 Pod 被创建之后, kubelet 就会把这个 PVC 对应的这个 NFS 类型的 Volume(PV)挂载到这个 Pod 容器中的目录中去。前面我们也提到了这样的话对于普通用户来说完全就不用关心后面的具体存储在 NFS 还是 Ceph 或者其他了,只需要直接使用 PVC 就可以了,因为真正的存储是需要很多相关的专业知识的,这样就完全职责分离解耦了。

普通用户直接使用 PVC 没有问题,但是也会出现一个问题**,那就是当普通用户创建一个 PVC 对象的时候,这个时候系统里面并没有合适的 PV 来(或者没有默认的stora)和它进行绑定,因为 PV 大多数情况下是管理员给我们创建的,这个时候启动 Pod 肯定就会失败了**,如果现在管理员如果去创建一个对应的 PV 的话,PVC 和 PV 当然就可以绑定了,然后 Pod 也会自动的启动成功,这是因为在 Kubernetes 中有一个专门处理持久化存储的控制器 Volume Controller,这个控制器下面有很多个控制循环,其中一个就是用于 PV 和 PVC 绑定的 PersistentVolumeController

PersistentVolumeController 会不断地循环去查看每一个 PVC,是不是已经处于 Bound(已绑定)状态。如果不是,那它就会遍历所有的、可用的 PV,并尝试将其与未绑定的 PVC 进行绑定,这样,Kubernetes 就可以保证用户提交的每一个 PVC,只要有合适的 PV 出现,它就能够很快进入绑定状态。而所谓将一个 PV 与 PVC 进行绑定其实就是将这个 PV 对象的名字,填在了 PVC 对象的 spec.volumeName 字段上。

PV 和 PVC 绑定上了,那么又是如何将容器里面的数据进行持久化的呢,我们知道 Docker 的 Volume 挂载其实就是将一个宿主机上的目录和一个容器里的目录绑定挂载在了一起,具有持久化功能当然就是指的宿主机上面的这个目录了,当容器被删除或者在其他节点上重建出来以后,这个目录里面的内容依然存在,所以一般情况下实现持久化是需要一个远程存储的,比如 NFS、Ceph 或者云厂商提供的磁盘等等。所以接下来需要做的就是持久化宿主机目录这个过程

当 Pod 被调度到一个节点上后,节点上的 kubelet 组件就会为这个 Pod 创建它的 Volume 目录,默认情况下 kubelet 为 Volume 创建的目录在 kubelet 工作目录下面:

/var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>

比如上面我们创建的 Pod 对应的 Volume 目录完整路径为:

/var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/

提示

要获取 Pod 的唯一标识 uid:
方法1:可通过命令 `kubectl get pod pod名 -o jsonpath={.metadata.uid}` 获取。
	[root@master1 pods]#kubectl get po nfs-sc-pod -o jsonpath={.metadata.uid}
	effdc8a1-1981-4a26-924a-c62a246a5865
方法2:可直接导出pod的yaml并用grep筛选。
	[root@master1 pods]#kubectl get po nfs-sc-pod -oyaml|grep uid
  	uid: effdc8a1-1981-4a26-924a-c62a246a5865

然后就需要根据我们的 Volume 类型来决定需要做什么操作了,假如后端存储使用的 Ceph RBD,那么 kubelet 就需要先将 Ceph 提供的 RBD 挂载到 Pod 所在的宿主机上面,这个阶段在 Kubernetes 中被称为 Attach 阶段。Attach 阶段完成后,为了能够使用这个块设备,kubelet 还要进行第二个操作,即:格式化这个块设备,然后将它挂载到宿主机指定的挂载点上。这个挂载点,也就是上面我们提到的 Volume 的宿主机的目录。将块设备格式化并挂载到 Volume 宿主机目录的操作,在 Kubernetes 中被称为 Mount 阶段。但是对于我们这里使用的 NFS 就更加简单了, 因为 NFS 存储并没有一个设备需要挂载到宿主机上面,所以这个时候 kubelet 就会直接进入第二个 Mount 阶段,相当于直接在宿主机上面执行如下的命令:

mount -t nfs 172.29.9.51:/var/lib/k8s/data/ /var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/

同样可以在测试的 Pod 所在节点查看 Volume 的挂载信息:

[root@node1 ~]#findmnt /var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92/
TARGET                                                                         SOURCE                                      FSTYPE OPTIONS
/var/lib/kubelet/pods/effdc8a1-1981-4a26-924a-c62a246a5865/volumes/kubernetes.io~nfs/pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92
                                                                               172.29.9.51:/var/lib/k8s/data/default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92

我们可以看到这个 Volume 被挂载到了 NFS(172.29.9.51:/var/lib/k8s/data/)下面,以后我们在这个目录里写入的所有文件,都会被保存在远程 NFS 服务器上。

这样在经过了上面的阶段过后,我们就得到了一个持久化的宿主机上面的 Volume 目录了,接下来 kubelet 只需要把这个 Volume 目录挂载到容器中对应的目录即可,这样就可以为 Pod 里的容器挂载这个持久化的 Volume 了,这一步其实也就相当于执行了如下所示的命令:

# docker 或者 nerdctl
docker run -v /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>:/<容器内的目标目录> 我的镜像 ...

整个存储的架构可以用下图来说明: 存储架构

  • PV Controller:负责 PV/PVC 的绑定,并根据需求进行数据卷的 Provision/Delete 操作
  • AD Controller:负责存储设备的 Attach/Detach 操作,将设备挂载到目标节点
  • Volume Manager:管理卷的 Mount/Unmount 操作、卷设备的格式化等操作
  • Volume Plugin:扩展各种存储类型的卷管理能力,实现第三方存储的各种操作能力和 Kubernetes 存储系统结合

我们上面使用的 NFS 就属于 In-Tree 这种方式,In-Tree 就是在 Kubernetes 源码内部实现的,和 Kubernetes 一起发布、管理的,但是更新迭代慢、灵活性比较差,另外一种方式 Out-Of-Tree 是独立于 Kubernetes 的,目前主要有 CSIFlexVolume 两种机制,开发者可以根据自己的存储类型实现不同的存储插件接入到 Kubernetes 中去,其中 CSI 是现在也是以后主流的方式,接下来我们会主要介绍 CSI 这种存储插件的使用。

QA

📍 特别注意:nfs网络共享存储只需提供nfs服务就好

实际nfs作为k8s集群共享存储使用时(不管是nfs作为共享存储或者是使用storageClass),只要保证nfs server可用,k8s node节点都可以正常访问到nfs服务就可以(不需要在节点上挂载nfs配置,因为在pv里面已经有指定了nfs路径新信息;或者storageclass里面已经配置了nfs server相关信息了;)

📍 需要注意:nfs默认不支持delete回收策略的

nfs storage classpv回收策略虽然默认为delete,但是nfs不支持,依然是用retain这个策略;(只是声明而已,支不支持,关键还是要看后端存储)

测试过程如下:

01.查看当前环境
[root@master1 ~]#kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS   REASON   AGE
nfs-pv                                     1Gi        RWO            Retain           Bound    default/nfs-pvc      manual                  5h  
pvc-3f576ba3-deeb-4161-af24-5588e96f20c7   1Gi        RWO            Delete           Bound    default/nfs-sc-pvc   nfs-client              97s 
[root@master1 ~]#kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc      Bound    nfs-pv                                     1Gi        RWO            manual         5h
nfs-sc-pvc   Bound    pvc-3f576ba3-deeb-4161-af24-5588e96f20c7   1Gi        RWO            nfs-client     99s

[root@master1 ~]#kubectl get po -owide
NAME           READY   STATUS    RESTARTS   AGE     IP             NODE    NOMINATED NODE   READINESS GATES
nfs-sc-pod     1/1     Running   0          2m32s   10.244.1.123   node1   <none>           <none>
test-volumes   1/1     Running   0          3h8m    10.244.2.88    node2   <none>           <none>
[root@master1 ~]#kubectl get pv pvc-3f576ba3-deeb-4161-af24-5588e96f20c7 -oyaml|grep persistentVolumeReclaimPolicy
  persistentVolumeReclaimPolicy: Delete #可以看到这个nfs storage class默认的回收策略是delete
  
#查看pod所在宿主机volume存放情况
[root@master1 ~]#kubectl get po nfs-sc-pod -owide
NAME         READY   STATUS    RESTARTS   AGE     IP             NODE    NOMINATED NODE   READINESS GATES
nfs-sc-pod   1/1     Running   0          3m40s   10.244.1.123   node1   <none>           <none>
[root@master1 ~]#kubectl get po nfs-sc-pod -owide -oyaml|grep uid
  uid: 17b601d3-afcf-4b89-9cb5-0bc654cacfc0
[root@master1 ~]#ssh node1  
[root@node1 ~]#ls /var/lib/kubelet/pods/17b601d3-afcf-4b89-9cb5-0bc654cacfc0/volumes/kubernetes.io~nfs/
pvc-3f576ba3-deeb-4161-af24-5588e96f20c7

02.我们删除pvc(注意这里要按正常流程删除pod),并再次观察
$ kubectl delete -f 04-nfs-sc-pod.yaml 
pod "nfs-sc-pod" deleted
[root@master1 ~]#kubectl delete pvc nfs-sc-pvc 
persistentvolumeclaim "nfs-sc-pvc" deleted
[root@master1 ~]#ls /var/lib/k8s/data/ #可以看到nfs server端,pv数据还是会被archived的;
archived-default-nfs-sc-pvc-pvc-3f576ba3-deeb-4161-af24-5588e96f20c7  test-volumes
archived-default-nfs-sc-pvc-pvc-af1b2b91-38b6-4bf0-99f1-cc2fa4fb2d92  test.txt

📍 面试题:k8s挂载pv、pvc的流程

1. 调度器把根据各种调度算法把该POD分配到某个节点,比如node01
2. Node01上的kubelet等待Volume Manager准备存储设备
3. PV控制器调用存储插件创建PV并与PVC进行绑定
4、Kubelet被告知卷已经准备好,开始启动POD,通过映射方式挂载到容器中

关于我

我的博客主旨:我希望每一个人拿着我的博客都可以做出实验现象,先把实验做出来,然后再结合理论知识更深层次去理解技术点,这样学习起来才有乐趣和动力。并且,我的博客内容步骤是很完整的,也分享源码和实验用到的软件,希望能和大家一起共同进步!

各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人免费帮您解决问题:

  1. 个人微信二维码:x2675263825 (舍得), qq:2675263825。

    image-20211002091450217

  2. 个人博客地址:www.onlyonexl.cn

    image-20211002092057988

  3. 个人微信公众号:云原生架构师实战

    image-20211002141739664

  4. 个人csdn

    https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

    image-20211002092344616

  5. 个人已开源干货😘

    名称链接
    01 实战:打造一款王者云笔记:typora+坚果云+阿里云osshttps://www.jianguoyun.com/p/DXS6qiIQvPWVCRiS0qoE
    02 实战:定制宇宙中最美的typora主题皮肤https://www.jianguoyun.com/p/DeUK9u0QvPWVCRib0qoE
    03 玩转vscodehttps://www.jianguoyun.com/p/DZe8gmsQvPWVCRid0qoE
    04 陈果的幸福哲学课https://www.jianguoyun.com/p/Db0kM7gQvPWVCRj2q6YE

最后

​ 好了,关于nfs存储实验就到这里了,感谢大家阅读,最后贴上我女神的photo,祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20220222103003687

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个基本的 Kubernetes YAML 文件示例,可用于部署容器镜像 `registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0`: ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: nfs-subdir-external-provisioner --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: nfs-subdir-external-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"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: run-nfs-subdir-external-provisioner roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: nfs-subdir-external-provisioner-runner subjects: - kind: ServiceAccount name: nfs-subdir-external-provisioner namespace: default --- apiVersion: apps/v1 kind: Deployment metadata: name: nfs-subdir-external-provisioner spec: replicas: 1 selector: matchLabels: app: nfs-subdir-external-provisioner template: metadata: labels: app: nfs-subdir-external-provisioner spec: serviceAccountName: nfs-subdir-external-provisioner containers: - name: nfs-subdir-external-provisioner image: registry.cn-beijing.aliyuncs.com/mydlq/nfs-subdir-external-provisioner:v4.0.0 env: - name: PROVISIONER_NAME value: nfs-subdir-external-provisioner - name: NFS_SERVER value: your.nfs.server.ip - name: NFS_PATH value: /path/to/nfs/share volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes volumes: - name: nfs-client-root nfs: server: your.nfs.server.ip path: /path/to/nfs/share ``` 注意替换 `NFS_SERVER` 和 `NFS_PATH` 的值为你的 NFS 服务器的 IP 地址和共享路径。同时,需要根据实际情况修改应用的名称、命名空间和其他配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值