加入公众号一起发现美好技术

在这里插入图片描述

一、Kubernetes配置StorageClass

1、安装nfs服务

yum -y install nfs-utils rpcbind

2、创建共享目录

mkdir /nfsdata

3、配置共享目录

[root@nfs-server ~]# cat > /etc/exports  << EOF
/nfsdata *(rw,sync)
EOF

4、使配置生效

exportfs -rv

5、启动nfs服务

systemctl start rpcbind
systemctl start nfs

6、检查rpcbind服务是否正常

[root@nfs-server ~]# rpcinfo -p localhost
   program vers proto   port  service
    100000    4   tcp    111  portmapper
    100000    3   tcp    111  portmapper
    100000    2   tcp    111  portmapper
    100000    4   udp    111  portmapper
    100000    3   udp    111  portmapper
    100000    2   udp    111  portmapper
    100024    1   udp  57106  status
    100024    1   tcp  35125  status
    100005    1   udp  20048  mountd
    100005    1   tcp  20048  mountd
    100005    2   udp  20048  mountd
    100005    2   tcp  20048  mountd
    100005    3   udp  20048  mountd
    100005    3   tcp  20048  mountd
    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
    100021    1   udp  38899  nlockmgr
    100021    3   udp  38899  nlockmgr
    100021    4   udp  38899  nlockmgr
    100021    1   tcp  59997  nlockmgr
    100021    3   tcp  59997  nlockmgr
    100021    4   tcp  59997  nlockmgr

7、检查nfs服务是否正常

[root@nfs-server ~]# showmount -e localhost
Export list for localhost:
/nfs-data *

8、客户端配置

yum -y install nfs-utils rpcbind

9、客户端测试

mount -t nfs 192.168.33.142:/nfsdata /mnt/

二、StorageClass

前面的课程中我们学习了 PV 和 PVC 的使用方法,但是前面的 PV 都是静态的,什么意思?就是我要使用的一个 PVC 的话就必须手动去创建一个 PV,我们也说过这种方式在很大程度上并不能满足我们的需求,比如我们有一个应用需要对存储的并发度要求比较高,而另外一个应用对读写速度又要求比较高,特别是对于 StatefulSet 类型的应用简单的来使用静态的 PV 就很不合适了,这种情况下我们就需要用到动态 PV,也就是我们今天要讲解的 StorageClass。

三、创建

要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner,这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。

自动创建的 PV 以${namespace}-${pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中
而当这个 PV 被回收后会以archieved-${namespace}-${pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。

当然在部署nfs-client之前,我们需要先成功安装上 nfs 服务器,上面我们已经配置过了,服务地址是:192.168.33.142,共享数据目录是/nfsdata,然后接下来我们部署 nfs-client 即可

四、配置nfs

4.1、配置 Deployment,将里面的对应的参数替换成我们自己的 nfs 配置(nfs-client.yaml)

[root@k8s-master1 ~]# cat nfs-client.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  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.33.142
            - name: NFS_PATH
              value: /nfsdata
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.33.142
            path: /nfsdata

4.2、将环境变量 NFS_SERVER 和 NFS_PATH 替换为自己的配置,当然也包括下面的 nfs 配置,我们可以看到这里使用了一个名为 nfs-client-provisioner 的serviceAccount,所以我们也需要创建一个 sa,然后绑定上对应的权限:(nfs-client-sa.yaml)

[root@k8s-master1 ~]# cat nfs-client-sa.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---
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: ["list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]

---
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

我们这里新建的一个名为 nfs-client-provisioner 的ServiceAccount,然后绑定了一个名为 nfs-client-provisioner-runner 的ClusterRole,而该ClusterRole声明了一些权限,其中就包括对persistentvolumes的增、删、改、查等权限,所以我们可以利用该ServiceAccount来自动创建 PV。
4.3、nfs-client 的 Deployment 声明完成后,我们就可以来创建一个StorageClass对象了:(nfs-client-class.yaml)

[root@k8s-master1 ~]# cat nfs-client-class.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: course-nfs-storage
provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'

我们声明了一个名为 course-nfs-storage 的StorageClass对象,注意下面的provisioner对应的值一定要和上面的Deployment下面的 PROVISIONER_NAME 这个环境变量的值一样。
4.4、应用以上yaml文件

[root@k8s-master1 ~]#  kubectl apply -f nfs-client.yaml
[root@k8s-master1 ~]#  kubectl apply -f nfs-client-sa.yaml
 [root@k8s-master1 ~]# kubectl apply -f nfs-client-class.yaml

4.5、查看资源状态

[root@k8s-master1 ~]# kubectl get pod 
NAME                                      READY   STATUS             RESTARTS   AGE
nfs-client-provisioner-69cc554596-mcxvg   1/1     Running            1          69m

[root@k8s-master1 ~]# kubectl get storageclass
NAME                 PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
course-nfs-storage   fuseim.pri/ifs   Delete          Immediate           false                  2d18h

4.6、我们这里有两种方法可以来利用上面我们创建的 StorageClass 对象来自动帮我们创建一个合适的 PV
4.6.1、第一种方法:在这个 PVC 对象中添加一个声明 StorageClass 对象的标识,这里我们可以利用一个 annotations 属性来标识,如下:

[root@k8s-master1 ~]# cat test-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
  annotations:
    volume.beta.kubernetes.io/storage-class: "course-nfs-storage"
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

4.6.2、第二种方法:我们可以设置这个 course-nfs-storage 的 StorageClass 为 Kubernetes 的默认存储后端,我们可以用 kubectl patch 命令来更新:

[root@k8s-master1 ~]# kubectl patch storageclass course-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

4.7、上面这两种方法都是可以的,当然为了不影响系统的默认行为,我们这里还是采用第一种方法,直接创建即可:

[root@k8s-master1 ~]# kubectl apply -f test-pvc.yaml
persistentvolumeclaim "test-pvc" created

[root@k8s-master1 ~]# kubectl get pvc
NAME                            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS         AGE
test-pvc                        Bound    pvc-8ba2adf7-e34b-4aa1-9d18-c21650cb1ef2   1Mi        RWX            course-nfs-storage   2d18h

我们可以看到一个名为 test-pvc 的 PVC 对象创建成功了,状态已经是 Bound 了,是不是也产生了一个对应的 VOLUME 对象,最重要的一栏是 STORAGECLASS,现在是不是也有值了,就是我们刚刚创建的 StorageClass 对象 course-nfs-storage。
4.8、查看pv状态

[root@k8s-master1 ~]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                   STORAGECLASS         REASON   AGE
pvc-8ba2adf7-e34b-4aa1-9d18-c21650cb1ef2   1Mi        RWX            Delete           Bound    default/test-pvc                        course-nfs-storage            145m

可以看到是不是自动生成了一个关联的 PV 对象,访问模式是 RWX,回收策略是 Delete,这个 PV 对象并不是我们手动创建的吧,这是通过我们上面的 StorageClass 对象自动创建的。这就是 StorageClass 的创建方法。

五、测试StorageClass

5.1、我们用一个简单的示例来测试下我们上面用 StorageClass 方式声明的 PVC 对象:(pod-test.yaml )

[root@k8s-master1 ~]# cat pod-test.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nfs-web
spec:
  serviceName: "nginx"  #声明它属于哪个Headless Service.
  replicas: 3
  selector:
    matchLabels:
      app: nfs-web
  template:
    metadata:
      labels:
        app: nfs-web
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.beta.kubernetes.io/storage-class: course-nfs-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 5Gi

上面这个 Pod 非常简单,就是用一个 busybox 容器,在 /mnt 目录下面新建一个 SUCCESS 的文件,然后把 /mnt 目录挂载到上面我们新建的 test-pvc 这个资源对象上面了,要验证很简单,只需要去查看下我们 nfs 服务器上面的共享数据目录下面是否有 SUCCESS 这个文件即可:
5.2、应用pod

kubectl apply -f pod-test.yaml
pod "pod-test" created

5.3、我们可以在 nfs 服务器的共享数据目录下面查看下数据

[root@nfs-server ~]# ls /nfsdata/
default-test-pvc-pvc-8ba2adf7-e34b-4aa1-9d18-c21650cb1ef2

5.4、我们可以看到下面有名字很长的文件夹,这个文件夹的命名方式是不是和我们上面的规则:namespace−{namespace}-namespace−{pvcName}-${pvName}是一样的,再看下这个文件夹下面是否有其他文件:

[root@nfs-server ~]# ls /nfsdata/default-test-pvc-pvc-8ba2adf7-e34b-4aa1-9d18-c21650cb1ef2/
SUCCESS

5.5、我们看到下面有一个 SUCCESS 的文件,是不是就证明我们上面的验证是成功的啊。

5.6、另外我们可以看到这里是手动创建的一个 PVC 对象,在实际工作中,使用 StorageClass 更多的是 StatefulSet 类型的服务,StatefulSet 类型的服务我们也可以通过一个 volumeClaimTemplates 属性来直接使用 StorageClass,如下:(test-statefulset-nfs.yaml)

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: nfs-web
spec:
  serviceName: "nginx"  #声明它属于哪个Headless Service.
  replicas: 3
  template:
    metadata:
      labels:
        app: nfs-web
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.beta.kubernetes.io/storage-class: course-nfs-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 5Gi

5.7、实际上 volumeClaimTemplates 下面就是一个 PVC 对象的模板,就类似于我们这里 StatefulSet 下面的 template,实际上就是一个 Pod 的模板,我们不单独创建成 PVC 对象,而用这种模板就可以动态的去创建了对象了,这种方式在 StatefulSet 类型的服务下面使用得非常多
Headless Service:名为nginx,用来定义Pod网络标识( DNS domain)。
StatefulSet:定义具体应用,名为Nginx,有三个Pod副本,并为每个Pod定义了一个域名。
volumeClaimTemplates: 存储卷申请模板,创建PVC,指定pvc名称大小,将自动创建pvc,且pvc必须由存储类供应。它会为每个Pod生成不同的pvc,并绑定pv, 从而实现各pod有专用存储。这就是为什么要用volumeClaimTemplate的原因
直接创建上面的对象:

[root@k8s-master1 ~]#  kubectl apply -f test-statefulset-nfs.yaml
statefulset.apps "nfs-web" created
[root@k8s-master1 ~]#  kubectl get pods
NAME                                             READY     STATUS              RESTARTS   AGE
...
nfs-web-0                                        1/1       Running             0          1m
nfs-web-1                                        1/1       Running             0          1m
nfs-web-2                                        1/1       Running             0          33s
...

5.8、创建完成后可以看到上面的3个 Pod 已经运行成功,然后查看下 PVC 对象:

[root@k8s-master1 ~]#  kubectl get pvc
NAME            STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
...
www-nfs-web-0   Bound     pvc-cc36b3ce-8b50-11e8-b585-525400db4df7   5Gi        RWO            course-nfs-storage    2m
www-nfs-web-1   Bound     pvc-d38285f9-8b50-11e8-b585-525400db4df7   5Gi        RWO            course-nfs-storage    2m
www-nfs-web-2   Bound     pvc-e348250b-8b50-11e8-b585-525400db4df7   5Gi        RWO            course-nfs-storage    1m
...

我们可以看到是不是也生成了3个 PVC 对象,名称由模板名称 name 加上 Pod 的名称组合而成,这3个 PVC 对象也都是 绑定状态了,很显然我们查看 PV 也可以看到对应的3个 PV 对象:

[root@k8s-master1 ~]#  kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                   STORAGECLASS          REASON    AGE
...                                                        1d
pvc-cc36b3ce-8b50-11e8-b585-525400db4df7   5Gi        RWO            Delete           Bound       default/www-nfs-web-0   course-nfs-storage              4m
pvc-d38285f9-8b50-11e8-b585-525400db4df7   5Gi        RWO            Delete           Bound       default/www-nfs-web-1   course-nfs-storage              4m
pvc-e348250b-8b50-11e8-b585-525400db4df7   5Gi        RWO            Delete           Bound       default/www-nfs-web-2   course-nfs-storage              4m
...

5.9、查看 nfs 服务器上面的共享数据目录:

[root@nfs-server ~]# ls /nfsdata/
default-www-nfs-web-0-pvc-9016bd73-f9b1-4f32-8582-193258037e83
default-www-nfs-web-1-pvc-ade65eb0-b017-4b35-afc6-d594b7da9e1a
default-www-nfs-web-2-pvc-7d8fe083-c72b-4676-97ae-0fc6d9d5a19a

六、排错,由于Kubernetes 1.20禁用了selfLink,创建的时候会报错,报错信息如下:

pvc error getting claim reference: selfLink was empty, can‘t make reference

6.1、解决方法1:
当前的解决方法是编辑/etc/kubernetes/manifests/kube-apiserver.yaml文件,添加如下内容

spec:
  containers:
  - command:
    - kube-apiserver
      添加这一行:
- --feature-gates=RemoveSelfLink=false

6.1.1、然后应用,即可

kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml

6.2、解决方法2:
如果是二进制安装的kubernetes集群,可以直接修改kube-apiserver文件,添加 --feature-gates=RemoveSelfLink=false,然后重启即可

[root@k8s-master1 ~]# cat /opt/kubernetes/cfg/kube-apiserver.conf 
KUBE_APISERVER_OPTS="--logtostderr=false \
--v=2 \
--log-dir=/opt/kubernetes/logs \
--etcd-servers=https://192.168.33.138:2379,https://192.168.33.139:2379,https://192.168.33.140:2379 \
--bind-address=192.168.33.138 \
--secure-port=6443 \
--advertise-address=192.168.33.138 \
--allow-privileged=true \
--service-cluster-ip-range=10.0.0.0/24 \
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
--authorization-mode=RBAC,Node \
--enable-bootstrap-token-auth=true \
--token-auth-file=/opt/kubernetes/cfg/token.csv \
--service-node-port-range=30000-32767 \
--kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \
--kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \
--tls-cert-file=/opt/kubernetes/ssl/server.pem  \
--tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \
--client-ca-file=/opt/kubernetes/ssl/ca.pem \
--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \
--service-account-issuer=api \
--service-account-signing-key-file=/opt/kubernetes/ssl/server-key.pem \
--etcd-cafile=/opt/etcd/ssl/ca.pem \
--etcd-certfile=/opt/etcd/ssl/server.pem \
--etcd-keyfile=/opt/etcd/ssl/server-key.pem \
--requestheader-client-ca-file=/opt/kubernetes/ssl/ca.pem \
--proxy-client-cert-file=/opt/kubernetes/ssl/server.pem \
--proxy-client-key-file=/opt/kubernetes/ssl/server-key.pem \
--requestheader-allowed-names=kubernetes \
--requestheader-extra-headers-prefix=X-Remote-Extra- \
--requestheader-group-headers=X-Remote-Group \
--requestheader-username-headers=X-Remote-User \
--enable-aggregator-routing=true \
--audit-log-maxage=30 \
--audit-log-maxbackup=3 \
--audit-log-maxsize=100 \
**--feature-gates=RemoveSelfLink=false \**
--audit-log-path=/opt/kubernetes/logs/k8s-audit.log"

6.2.1、重新加载kube-apiserver.service

systemctl daemon-reload && systemctl restart kube-apiserver

6.2.2、重新删除一下nfs插件,观察日志,直到日志正常为止

Logo

开源、云原生的融合云平台

更多推荐