K8S集群中部署NFS之四:使用StorageClass创建PV

在K8S中对于无状态的应用我们可以比较容易的扩展应用程序、滚动升级和重新启动崩溃的应用程序,但是对于一些无状态的应用,分为需要维护服务的拓扑结构和需要持久化数据两种情况,对于前者,我们可以使用headless service来实现,对于后者我们需要在集群中使用持久存储来持久化数据。而持久化存储,NFS(Network File System)是一个使用比较常用的分布式存储技术,在本系列中,我将详细演进如何在K8S集群中部署NFS。为了使得大家比较容易理解,将文章分成4个部分。
第一部分:配置 NFS 服务器
第二部分:将 NFS 服务器文件夹挂载到每个节点的本地文件夹
第三部分:将Persistent Volume配置直接连接到 NFS 服务器
第四部分:创建Storage Class声明来自动创建Persistent Volume(本文)

写在前面

这一系列文章主要译自下面的文章,但由于k8s版本的原因导致一些功能不正常,我在部署过程中也遇到了一些问题,并得以解决。此外作者主要是在树莓派上完成部署,是ARM架构,因此一些脚本不能在在PC上正常工作,我也在文中加以修改,并给出了详细的解决办法。

https://levelup.gitconnected.com/how-to-use-nfs-in-kubernetes-cluster-configuring-the-nfs-server-1bf4116641d4

本文的源代码都是来自作者的github主页

https://github.com/fabiofernandesx/k8s-volumes

实现方法

谈到自动化,每次需要k8s集群对外做一些事情的时候,我们都需要一些特殊的配置来处理安全部分。
第一件事是设置服务帐户和角色绑定。 我们将使用 RBAC(基于角色的访问控制)来进行此配置。
我们需要 5 个配置来完成安全部分的工作:

  • Service Account(授予权限)
  • Cluster Role
  • Cluster Role Binding
  • Role
  • Role Binding
    它看起来太多了,但你最终可以在一个 60 行“ish”行的文件中进行所有配置。 您可以将所有内容放在同一个文件中,用 3 个破折号分隔每个配置,为了便于发现问题,下面详细执行每一步来执行安全相关的脚本

创建Service Account

脚本如下:

kind: ServiceAccount
apiVersion: v1
metadata:
  name: nfs-client-provisioner
  namespace: pv-demo

cd到脚本目录yml/StorageClass文件夹,执行创建脚本

kubectl apply -f service-account.yml
deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ kubectl get sa -n pv-demo
NAME                     SECRETS   AGE
default                  1         14d
nfs-client-provisioner   1         13d

查看service account,nfs-client-provisioner创建成功

创建cluster role

脚本如下:

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: ["create", "update", "patch"]

执行脚本,并查看执行结果

kubectl apply -f  cluster-role.yml
deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ kubectl get clusterroles|grep nfs-client
nfs-client-provisioner-runner                                          2021-06-10T03:45:34Z

创建Cluster Role Binding

脚本如下:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: pv-demo
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io

执行脚本,并查看结果:

kubectl apply -f  cluster-role-binding.yml 
deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ kubectl get clusterrolebindings|grep nfs-client
run-nfs-client-provisioner                             ClusterRole/nfs-client-provisioner-runner                                          13d

创建role

脚本如下:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: pv-demo
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]

执行脚本,并查看结果:

kubectl apply -f role.yml
deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ kubectl get roles -n pv-demo
NAME                                    CREATED AT
leader-locking-nfs-client-provisioner   2021-06-10T03:47:14Z

创建role binding

脚本如下:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: pv-demo
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: pv-demo
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

执行脚本,并查看结果:

kubectl apply -f role-binding.yml
deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ kubectl get rolebindings -n pv-demo
NAME                                    ROLE                                         AGE
leader-locking-nfs-client-provisioner   Role/leader-locking-nfs-client-provisioner   13d

创建StorageClass

完成安全相关的脚本创建后,再来创建StorageClass。如果我们使用的是privisioner插件,我们可以将它添加到配置中的配置参数中,比如 kubernetes.io/aws-ebs 或 kubernetes.io/azure-file 但我们没有 NFS 插件。 因此,请改用 mysite.com/nfs 之类的任何内容。
下面是storage-class的配置文件

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd-nfs-storage
provisioner: ff1.dev/nfs
parameters:
  archiveOnDelete: "false"

执行脚本,并查看结果:

kubectl apply -f storage-class.yml
deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ kubectl get storageclass
NAME              PROVISIONER   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
ssd-nfs-storage   ff1.dev/nfs   Delete          Immediate           false                  13d

创建provisioner deployment

我们需要部署provisioner,worker节点将与 NFS 服务器通信,并在NFS server上创建文件夹,然后将新 PV 配置到集群中。
作者的实例中是的arm镜像,我们需要换成x86镜像。
如果需要更加详细的了解provisioner镜像,参考开源网址:

[https://github.com/kubernetes-retired/external-storage/tree/master/nfs-client](https://github.com/kubernetes-retired/external-storage/tree/master/nfs-client)

provisioner deployment脚本如下:

kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-client-provisioner
  namespace: pv-demo
spec:
  selector:
    matchLabels:
      app: nfs-client-provisioner
  replicas: 1
  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
          resources:
            limits:
              cpu: 1
              memory: 1Gi
          volumeMounts:
            - name: nfs-client-ssd
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: ff1.dev/nfs
            - name: NFS_SERVER
              value: 159.99.100.88
            - name: NFS_PATH
              value: /home/deploy/ssd/dynamic
      volumes:
        - name: nfs-client-ssd
          nfs:
            server: 159.99.100.88
            path: /home/deploy/ssd/dynamic

执行创建脚本

kubectl apply -f provisioner-deploy.yml

创建pvc

PVC脚本如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ssd-nfs-pvc-1
  namespace: pv-demo
spec:
  storageClassName: ssd-nfs-storage
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Mi

执行脚本并查看结果

kubectl apply -f volume-claim.yml
kubectl get pvc,pv -n pv-demo

发现pvc的状态一直是pending,说明没有创建pv,也未能与pv进行绑定。

排查原因
  • 通过kubectl describe命令查看错误提示信息,信息中有:waiting for a volume to be created, either by external provisioner “ff1.dev/nfs” or manually created by system administrator。
  • 通过kubectl logs命令查看pod(nfs-client-provisioner)日志,日志中有:unexpected error getting claim reference: selfLink was empty, can’t make reference。
  • 使用第二步骤的信息去网上查找,原来是1.20版本(我的是1.20.2)默认禁止使用selfLink。
解决办法

如果是通过yaml文件部署kube-apiserver的,在kube-apiserver.yaml中添加- --feature-gates=RemoveSelfLink=false参数。通过命令可以查找kube-apiserver.yaml文件位置

find / -name kube-apiserver.yaml 

添加- --feature-gates=RemoveSelfLink=false行

sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml 

systemctl daemon-reload 加载一下,如果不生效,需要重启机器。
如果一切正常,发现pvc已经与pv进行了绑定,并在nfs server目录下自动创建了一个文件夹,比如我机器上多了一个如下文件夹

deploy@master-node:~/k8s-volumes-main/yml/StorageClass$ ls /home/deploy/ssd/dynamic/
pv-demo-ssd-nfs-pvc-1-pvc-5c2cbadd-fb7d-4faa-9269-2d4e1dff92bd

部署网站

下面创建deployment和service,同之前的两篇文章中的方法基本类似,就不一步步执行了,脚本合在一个文件中。

kind: Deployment
apiVersion: apps/v1
metadata:
  labels:
    app: ssd-storage-class-site
  namespace: pv-demo
  name: ssd-storage-class-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ssd-storage-class-site
  template:
    metadata:
      labels:
        app: ssd-storage-class-site
    spec:
      volumes:
        - name: ssd-storage-class-volume
          persistentVolumeClaim:
            claimName: ssd-nfs-pvc-1
      containers:
        - image: nginx
          name: ssd-storage-class-site
          resources:
            limits:
              cpu: '1'
              memory: 100Mi
          volumeMounts:
            - name: ssd-storage-class-volume
              mountPath: /usr/share/nginx/html
---
kind: Service
apiVersion: v1
metadata:
  name: ssd-storage-class-service
  namespace: pv-demo
spec:
  selector:
    app: ssd-storage-class-site
  ports:
  - protocol: TCP
    port: 80
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  namespace: pv-demo
  name: ssd-storage-class-ingress
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: ssd-storage-class.192.168.2.101.nip.io
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: ssd-storage-class-service
            port:
              number: 80

拷贝网站程序到NFS server

使用scp命令远程拷贝index.html到NFS server的ssd/dynamic/pv-demo-ssd-nfs-pvc-1-pvc-5c2cbadd-fb7d-4faa-9269-2d4e1dff92bd文件夹

sudo scp index.html 192.168.2.30:ssd/dynamic/pv-demo-ssd-nfs-pvc-1-pvc-5c2cbadd-fb7d-4faa-9269-2d4e1dff92bd

访问服务

使用clusterip的方式访问此服务,我们的url如下:

 curl http://10.196.26.24

可以查看该网站内容.也可以进入到容器内使用dns来查看网站内容.

kubectl exec -it -n pv-demo ssd-storage-class-nginx-6994764775-f4cbw -- curl http://ssd-storage-class-service.pv-demo.svc.cluster.local

写在最后

经过四篇文章,一步步的演绎了如何在K8S中部署NFS集群,虽然k8s中也有很多开源的比如rook-ceph,用的比较多,但NFS也有不少人在项目中使用,因此经过这一系列文章,相信大家完全可以在K8S中部署一个NFS集群。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容