最近几天在测试StatefulSet的使用时,遇到了接触Kubernetes以来最大的一个困难,即配置StorageClass动态生成PersistentVolume。考虑到NFS存储操作相对简洁,因此在刚接触StorageClass的情况下选择了NFS作为Provisioner,没想到却是一段噩梦的开始。。。前前后后花费了将近一个星期的时间,才初步实现了PV的动态创建,并完成了一个简单的StatefulSet的案例。现在将我踩过的坑记录如下:
一、自己配置NFS系统
主要参考了下面的这篇文章:
http://www.showerlee.com/archives/2280
注意,文章中配置NFS共享目录的"# echo -e "/srv/pv-demo kube-master(rw,sync)" > /etc/export"应为"/etc/exports"。
完成配置后,可以参考官方文档编写yaml文件:
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs/docs/demo
最后一直没有成功,花费了好多天调试都只能实现数据的本地挂载,无法跨机器远程挂载,所以不详细写了。
二、使用已配置好的NFS系统
因为公司刚好有已经配置好的NFS系统,在自己搭建不成功的情况下就使用了现成的系统。仍然是主要参考官方文档:
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client
1.创建测试的静态PV和PVC
首先,需要利用静态的PV和PVC来测试一下NFS系统能否正常工作:
pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: mypv1 spec: capacity: storage: 4Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle nfs: path: [已配置的NFS系统的路径] server: [已配置的NFS系统的IP地址]
pvc.yaml
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: mypvc1 spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi
创建二者后,如果能够自动绑定,说明NFS系统工作正常,这样才能执行下面的步骤。
2.运行nfs-client-provisioner
想要动态生成PV,需要运行一个NFS-Provisioner服务,将已配置好的NFS系统相关参数录入,并向用户提供创建PV的服务。官方推荐使用Deployment运行一个replica来实现,当然也可以使用Daemonset等其他方式,这些都在官方文档中提供了。
在创建Deployment之前,一定要按照官方文档中的Step 3部分配置相关的内容。
编写rbac.yaml文件如下:
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-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-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-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-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner # replace with namespace where provisioner is deployed namespace: default roleRef: kind: Role name: leader-locking-nfs-provisioner apiGroup: rbac.authorization.k8s.io
编写serviceaccount.yaml文件如下:
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner
注意,针对已配置好的NFS系统和自己搭建NFS系统,Deployment中使用的镜像不同!这里踩了一个大坑,在转为使用现成系统后没有修改原来的yaml文件中的镜像,导致持续报错,调试了好长时间才意识到问题。
编写deployment.yaml文件如下:
kind: Deployment apiVersion: extensions/v1beta1 metadata: name: nfs-provisioner spec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-provisioner spec: serviceAccount: nfs-provisioner containers: - name: nfs-provisioner image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: example.com/nfs - name: NFS_SERVER value: [已配置的NFS系统的IP地址] - name: NFS_PATH value: [已配置的NFS系统的挂载路径] volumes: - name: nfs-client-root nfs: server: [已配置的NFS系统的IP地址] path: [已配置的NFS系统的挂载路径]
注意,官方文档提供的镜像在国内无法正常下载,在网上找到了一个阿里云的镜像作为替代。参考https://www.centos.bz/2018/04/%E5%AE%9E%E6%88%98kubernetes%E5%8A%A8%E6%80%81%E5%8D%B7%E5%AD%98%E5%82%A8nfs/
这个镜像中volume的mountPath默认为/persistentvolumes,不能修改,否则运行时会报错。
创建后观察Pod能否正常运行。后面如果出现错误,可以用kubectl logs查看这个Pod的日志来查看错误,进行调试。
3.创建StorageClass
编写并创建storageclass.yaml如下:
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: nfs provisioner: example.com/nfs
4.创建测试claim
接下来要创建测试的claim,以检测StorageClass能否正常工作:
编写并创建test-claim.yaml如下,注意storageClassName应确保与上面创建的StorageClass名称一致。
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: test-claim1 spec: accessModes: - ReadWriteMany resources: requests: storage: 1Mi storageClassName: nfs
创建后,用kubectl get pvc查看,观察新创建的PVC能够自动绑定PV。
5.创建测试Pod
创建一个测试的Pod使用这个PVC,编写test-pod.yaml文件如下:
kind: Pod apiVersion: v1 metadata: name: test-pod spec: containers: - name: test-pod image: busybox 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-claim1
查看Pod状态是否变为Completed。如果是,则应该能在NFS系统的共享路径中看到一个SUCCESS文件。
这样,StorageClass动态创建PV的功能就成功实现了。
6.创建StatefulSet案例
本来做StorageClass测试的目的就是为了实现StatefulSet,结果越做越跑偏。。。现在回到原本的目标,来实现一个简单的StatefulSet。
编写statefulset.yaml文件如下:
apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx1" replicas: 2 volumeClaimTemplates: - metadata: name: test annotations: volume.beta.kubernetes.io/storage-class: "nfs" spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi template: metadata: labels: app: nginx1 spec: serviceAccount: nfs-provisioner containers: - name: nginx1 image: nginx imagePullPolicy: IfNotPresent volumeMounts: - mountPath: "/persistentvolumes" name: test
注意,这里的mountPath必须指定为/persistentvolumes,否则会出现表面上PV创建成功,其实在NFS系统中找不到的问题。。。
当发现两个Pod都正常运行,且在NFS系统中能够找到PV,说明试验成功。
进一步的,可以用kubectl exec -it 进入Pod,并创建一个文件,看看在PV中能否发现相同的文件已生成。