前言: K8S 是一个成熟的容器业务集群管理方案, 但是目前来讲, 在存储方面显得过于复杂和繁琐, 更不要说中间穿插着资源访问权限和认证的管理, 尽管 google 在开源它之前有很多年的实战经验, 但是从目前官方的发布文档来看, 依然显得学习成本略高, 我们来看看这个学习流程:
1, 最初, 学习 k8s 发现它罗列了一系列存储方案, 无非就是对一系列本地和云存储的广泛支持:
看到这么多的存储卷类型的确很唬人, 但是没有办法, 在目前存储界没有达到统一 (也不可能统一) 之前, 为了保持自己的强大的生态兼容性, K8S 作出了无奈之举.
2, 好在我们不需要学习每一种存储类型, 只要要针对需要的存储卷类型进行了解和学习, 接着, 为了让 Pod 和存储资源管理解耦, K8S 又引入了 PV 和 PVC 的概念, 于是集群管理员和研发人员各司其职, 但是带来了额外的 PV 和 PVC 的创建, 删除以及存储卷的绑定和回收以及读写控制等资源管理工作.
3, 当集群规模增长后, 发现 PV 和 PVC 的管理开销惊人的时候, K8S 又引入 StorageClass 的动态卷以及基于 storageClass 的 CSI 的概念, 这是目前 K8S 存储灵活分配的终极方案.
4, 就在我们还没有完全理解 StorageClass 和 CSI 的时候, 我们在官网又看到相关的 Clone 和 Snapshot 已经出来了.
说明一个问题, 业务需求场景不断升级, 业务需求量不断变大, 解决方案也不断更新和变更, 仅仅一个存储就前后涉及诸多概念, 实在让人有点无所适从, 弱水三千, 取一瓢饮, 今天这个实验是针对 NFS 的共享存储展开的.
为什么围绕 NFS 展开实验呢? 因为从企业来讲, NFS 共享在企业私有云内部非常容易取得, 并且成本较低, 一台普通的四盘位的企业级 NAS 服务器出厂即携带稳定可靠的 NFS 服务, 无需自己部署, 和企业的文件共享服务器集成, 无需另外的硬件和部署成本.
首先, 我们验证 NAS 服务器的 NFS 服务是否就位:
1, 启动 NFS 客户端:
挂载前, 请确保系统中已经安装了 nfs-utils 或 nfs-common, 安装方法如下:
- CentOS:
- yum install nfs-utils -y
Ubuntu 或 Debian:
apt-get install nfs-common
创建挂载路径:
mkdir /nfs
确认远端 NAS 服务器上面存在的 NFS 服务和共享路径:
挂载 NFS 共享路径:
mount -t nfs NAS 服务器的 IP:/NAS-NFS
我们可以进入挂载路径尝试创建文件夹, 看是否挂载成功并且读写正常;
容易出问题的是, 挂载成功但是无法创建文件夹, 提示文件系统只读, 但是 ls 命令显示 777, 这是因为 NAS 服务器的 NFS 服务权限没有开放匿名访问的读写权限:
以上全部成功后进一步测试 NFS 共享存储在 PV,PVC 中是否工作正常:
2, 创建测试的静态 PV 和 PVC
pv.YAML
- apiVersion: v1
- kind: PersistentVolume
- metadata:
- name: mypv1
- spec:
- capacity:
- storage: 4Gi
- accessModes:
- - ReadWriteOnce
- persistentVolumeReclaimPolicy: Recycle
- nfs:
- path: /NAS-NFS
server: [NAS 服务器 IP 或域名]
pvc.YAML
- kind: PersistentVolumeClaim
- apiVersion: v1
- metadata:
- name: mypvc1
- spec:
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: 100Mi
开始创建资源对象(为了简化实验, 所有资源对象都是默认的命名空间 default 展开):
并且 dashboard 里面显示, 绑定成功:
以上实验充分证明 nfs 系统工作正常, 删除以上测试的内容, 进行下一步.
注意:
如果不删除 PVC 来解除资源的占用, 是无法直接删除 PV 并释放资源,
具体操作表现在三个方面:
1,kubectl delete 命令一直停留在执行状态不显示结果;
2,Dashboard 资源对象一直正常显示存在, 无视删除操作;
3, 命令行强制退出删除命令, 但是并不代表撤销删除操作, 而是挂起, 一直等待 PVC 手工清除, 随即立即删除 PV.
想要动态生成 PV, 需要运行一个 NFS-Provisioner 服务, 将已配置好的 NFS 系统相关参数录入, 向用户提供创建 PV 的服务.
官方推荐使用 Deployment 运行一个副本集来实现, 当然也可以使用 Daemonset 等其他方式, 这些都在官方文档中提供了.
创建持久卷的服务访问角色, 角色绑定, 权限分配供 Provisioner 角色后台调用:
编写 serviceaccount.YAML 文件如下:
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: nfs-provisioner
编写 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
创建 NFS 服务的 provisioner 角色, 部署结果是得到一个 Pod 来响应各种 cliet 提交的存储资源请求:
编写 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
- #官方镜像: quay.io/external_storage/nfs-client-provisioner:latest 无法下载, 使用上面阿里云镜像代替
- volumeMounts:
- - name: nfs-client-root
- mountPath: /persistentvolumes
- env:
- - name: PROVISIONER_NAME
- value: example.com/nfs
- - name: NFS_SERVER
value: [NAS 服务器的 IP 地址]
- - name: NFS_PATH
- value: /NAS-NFS #NFS 系统的挂载路径
- volumes:
- - name: nfs-client-root
- nfs:
server: [NAS 服务器的 IP 地址]
path: /NAS-NFS #NFS 系统的挂载路径
将以上文件通过 kubectl 命令创建完毕后, 我们查看 K8S 部署结果:
我们得到一个副本集和这个副本集启动一个 Pod, 来充当 nfs-client-provisioner 角色, 这个角色用来响应各种存储资源请求, 但是并不能直接使用, 需要用 StorageClass 去触发.
创建存储类 StorageClass
编写并创建 storageclass.YAML 如下:
- kind: StorageClass
- apiVersion: storage.k8s.io/v1
- metadata:
- name: nfs
- provisioner: example.com/nfs
接下来要创建测试的 claim, 以检测 StorageClass 能否正常工作:
编写并创建 test-claim.YAML 如下, 注意 storageClassName 应确保与上面创建的 StorageClass 名称一致.
- kind: PersistentVolumeClaim
- apiVersion: v1
- metadata:
- name: test-claim1
- spec:
- accessModes:
- - ReadWriteMany
- resources:
- requests:
- storage: 1Mi
- storageClassName: nfs #这里和创建的 SC 名称保持一致
创建后, 用 kubectl get pvc 查看, 观察新创建的 PVC 能够自动绑定 PV.
创建一个测试的 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 的功能就成功实现了.
普通的 pod 是在编排文件里面通过声明预先手工创建 PVC 去请求 storageclass 来自动获取动态卷, 实际上这种自动由于 PVC 的存在也属于半自动, 还是不够灵活, 所以, 官方推荐使用 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" # nfs 和前面定义的 storageclass 的名称保持一致
- 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: "/my_dir" #此挂载路径可以任意指定
- name: test
查看执行结果:
查看存储资源创建结果:
打开 nfs 的文件系统目录, 发现和 PV 对应的资源文件夹自动生成:
文件夹的自动命名规则为 pod 的各种资源名称的集合.
进一步的, 可以用 kubectl exec -it 进入其中一个 Pod 中验证:
至此, 实验基本完成, 注意, 使用 statefulset 的方式去创建 pod, 删除 statefulset 会自动销毁 pod, 但是并不销毁 pod 中的提交创建的 pv 和 pvc, 因为违反数据持久化原则, 所以需要手工删除.
来源: http://blog.51cto.com/kingda/2440315