加入公众号一起发现美好技术
一、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插件,观察日志,直到日志正常为止