kubernetes学习之持久化存储StorageClass(4---nfs存储服务)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: kubernetes学习之持久化存储StorageClass(4---nfs存储服务)

前言

持久化存储是容器无法绕开的一个问题,其主要原因是我们的容器或者pod有些是有状态服务,有些是无状态服务的,而容器或者pod的生命周期是短暂的,例如,一个MySQL容器或者pod,容器或者pod启动后,对该服务做了一些修改,比如修改密码,添加插件,删除无用数据等等操作后,如果容器或者pod在这些操作后被销毁了或者重启了,那么,前面做的这些改动就将消失了(容器或者pod规定的,只要重新启动将会恢复初始状态)。因此,数据的持久化对于有状态服务来说就是非常必要的了。

这里简单提一句,有状态服务和无状态服务到底是指的什么?

无状态服务
     -认为所有pod都是一样的,不具备与其他实例有不同的关系。
    - 没有顺序的要求。
    - 不用考虑再哪个Node运行。
    - 随意扩容缩容。

  • Deployment控制器设计原则:管理的所有Pod一模一样,提供同一个服务,也不考虑在哪台Node运行,可随意扩容和缩容。这种应用称为“无状态”,例如Web服务

有状态服务
  - 集群节点之间的关系。
  - 数据不完全一致。
  - 实例之间不对等的关系。
  - 依靠外部存储的应用。
  - 通过dns维持身份

  • 在实际的场景中,并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往有依赖关系,例如主从关系、主备关系,这种应用称为“有状态”,例如MySQL主从、Etcd集群

如何理解呢?http协议大家应该都清楚是无状态的协议服务,而电子商务内的购物车等等是有事务,有状态的,带session了嘛,比如,电子商务里的已付款商品和未付款商品,电子商务的后台对每种状态情况做了标识了,这样就算有状态了。举一个常见的例子,在商城里购买一件商品。需要经过放入购物车、确认订单、付款等多个步骤。由于HTTP协议本身是无状态的,所以为了实现有状态服务,就需要通过一些额外的方案。比如最常见的session,将用户挑选的商品(购物车),保存到session中,当付款的时候,再从购物车里取出商品信息 。




实验目标

前面说了那么多,做了N多铺垫,也就是想使用一下在k8s中应用最简单的nfs网络存储来持久化存储一个MySQL5.7.23的单实例,并且该MySQL单实例能通过navicat连接上,并进行一些初步的管理工作。

实验环境

(1)一个可正常运行的k8s环境,使用的服务器是centos7版本,三台虚拟机服务器,IP地址为:192.168.217.16/17/18 ,其中16服务器是主master服务器。17,18为node节点。

[root@master ~]# k get nodes 
NAME      STATUS   ROLES    AGE   VERSION
c7n.cnn   Ready    master   32d   v1.19.4
slave1    Ready    <none>   32d   v1.19.4
slave2    Ready    <none>   32d   v1.19.4

(2)nfs服务

nfs服务搭建在master节点,也就是16服务器上。配置文件内容如下:

[root@master ~]# cat /etc/exports
/data/nfs-sc  10.244.0.0/16(rw,no_root_squash,no_subtree_check) 192.168.217.16(rw,no_root_squash,no_subtree_check) 192.168.217.0/24(rw,no_root_squash,no_subtree_check)

(3)

nfs-client-provisioner镜像使用的是这个:

[root@master ~]# docker images
REPOSITORY                                                                    TAG                     IMAGE ID            CREATED             SIZE
registry.cn-shanghai.aliyuncs.com/c7n/nfs-client-provisioner                  v3.1.0-k8s1.11          e47e31bbe424        19 months ago       49.8MB

下载链接:https://pan.baidu.com/s/1ytRwIlMnF_FDzT_V9MQj0A?pwd=k8ss
提取码:k8ss

三个服务器都导入该镜像,因为不知道pod会调度到哪个节点。

实验步骤

一,

rbac角色权限建立,文件内容如下:

[root@master ~]# cat serviceacount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: kube-system
---
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: ["get", "list", "watch","create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: kube-system
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: kube-system
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: kube-system
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

这里需要注意一点,都是使用的namespace kube-system ,下面的文件也需要指定到kube-system命名空

执行该文件输出应该如下():

[root@master ~]# k apply -f serviceacount.yaml 
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created

   执行命令:kubectl apply -f serviceacount.yaml

(二)

部署pod

[root@master ~]# cat deploy-nfs.yaml 
apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: nfs-client-provisioner 
--- 
kind: Deployment 
apiVersion: apps/v1  
metadata: 
  name: nfs-client-provisioner 
  namespace: kube-system
spec: 
  replicas: 1 
  strategy: 
    type: Recreate 
  selector: 
    matchLabels: 
      app: nfs-client-provisioner 
  template: 
    metadata: 
      labels: 
        app: nfs-client-provisioner 
    spec: 
      serviceAccountName: nfs-client-provisioner 
      containers: 
        - name: nfs-client-provisioner 
          image: registry.cn-shanghai.aliyuncs.com/c7n/nfs-client-provisioner:v3.1.0-k8s1.11
          imagePullPolicy: IfNotPresent
          volumeMounts: 
            - name: nfs-client-root 
              mountPath: /persistentvolumes 
          env: 
            - name: PROVISIONER_NAME 
              value: fuseim.pri/ifs 
            - name: NFS_SERVER 
              value: 192.168.217.16
            - name: NFS_PATH 
              value: /data/nfs-sc 
      volumes: 
        - name: nfs-client-root 
          nfs: 
            server: 192.168.217.16
            path: /data/nfs-sc

nfs-client-provisioner这个镜像的部署,其中的env 需要根据自己的实际情况填写,也就是这些:

 - name: PROVISIONER_NAME 
              value: fuseim.pri/ifs  #可以自己定义一个任意的名字
            - name: NFS_SERVER         #这里不修改
              value: 192.168.217.16   #nfs服务所在的服务器IP
            - name: NFS_PATH        #这里不修改
              value: /data/nfs-sc    #nfs服务配置文件里写的路径,也就是/etc/exports文件内定义的路径
      volumes: 
        - name: nfs-client-root #这不修改
          nfs: 
            server: 192.168.217.16#nfs服务所在的服务器IP
            path: /data/nfs-sc  #nfs服务配置文件里写的路径,也就是/etc/exports文件内定义的路径

除了需要根据实际情况修改的地方,还有一个地方需要注意, serviceAccountName: nfs-client-provisioner,这个账号由于

metadata:
 name: nfs-client-provisioner
 namespace: kube-system

这一段是指定的命名空间kube-system,因此,上面的账号生成文件也必须是使用namespace 为kube-system总的来说,也就是两者使用的命名空间要保持一致

执行命令:kubectl apply -f deploy-nfs.yaml

这两步完成后,应该就能在命名空间kube-system 里看到一个正常运行的pod:

[root@master ~]# k get po -n kube-system|grep nfs
nfs-client-provisioner-6fc484bd4f-7vrb8   1/1     Running   0          135m

(三)

建立StorageClasses

vim  storageclass-nfs.yaml

此sc建立的时候就设定为了default class

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
  annotations:
    storageclass.beta.kubernetes.io/is-default-class: "true"
provisioner: fuseim.pri/ifs
reclaimPolicy: Delete
allowVolumeExpansion: True  #允许pvc创建后扩容

这个StorageClass是没有和命名空间namespace有依赖的,提供者provisioner: fuseim.pri/ifs这个是建立pvc时候必须指定的 哦,这个是非常重要的一个键值对,它的值由文件deploy-nfs.yaml内定义。

执行命令:kubectl apply -f storageclass-nfs.yaml

(四)

查询sc是否正常

[root@master nfs-sc]# k get sc
NAME                            PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage (default)   fuseim.pri/ifs   Delete          Immediate           true                   9d

这四步完成后,基本nfs持久化数据卷就已经认为可用了,可以算作一个阶段了。剩下的就是建立pvc,然后查看pvc是否自动生成pv。

为什么要说这四步完成就是一个阶段了呢?因为,后面的pvc pv的建立是实际的应用阶段了,现有的nfs持久卷挂载服务已经可用了。后面的步骤都是测试的啦!!!!

(五)

建立pvc并测试挂载pv是否正常,这个pvc名称是test-claim:

[root@master nfs-sc]# cat test-claim.yaml 
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations: 
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
---
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox:1.24
    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-claim

检测功能是否正常:

[root@master nfs-sc]# k get pods -A
NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
database      mysql2-7b77d5f496-8r5tr                   1/1     Running     1          7h36m
default       nfs-client-provisioner-6fc484bd4f-pjxm7   1/1     Running     14         8d
default       nginx-7b54d48599-x2zc5                    1/1     Running     10         5d13h
default       read-pod                                  0/1     Error       0          9h
default       test-pod                                  0/1     Completed   0          30s

有一个名字叫test-pod 的pod 是completed状态,在/data/nfs-sc/目录下:

[root@master nfs-sc]# pwd
/data/nfs-sc
[root@master nfs-sc]# ls
default-test-claim-pvc-a17177ba-d1df-452e-84ec-12499f453b88

可以看到一个SUCCESS文件,表示功能正常

[root@master nfs-sc]# cd default-test-claim-pvc-a17177ba-d1df-452e-84ec-12499f453b88/
[root@master default-test-claim-pvc-a17177ba-d1df-452e-84ec-12499f453b88]# ls
SUCCESS

可以看到pvc名称是test-claim,所在命名空间是default,这说明其它的命名空间可以自由使用kube-system下的pod    nfs-client-provisioner :

1. [root@master ~]# k get pvc -A
2. NAMESPACE     NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
3. default       test-claim    Bound    pvc-9e72b552-f3dd-432e-af7d-4d0a848447bf   1Mi        RWX            managed-nfs-storage   77m

可以看到pv的名称是pvc-9e72b552-f3dd-432e-af7d-4d0a848447bf,capacity,也就是申请的存储最大容量是1M,这个数值可以和前面的pvc定义文件相符:

[root@master ~]# k get pv -A
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS          REASON   AGE
pvc-9e72b552-f3dd-432e-af7d-4d0a848447bf   1Mi        RWX            Delete           Bound    default/test-claim        managed-nfs-storage            81m

(六)

部署一个单实例MySQL并挂载

建立一个pvc ,名称为test-claim1:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim1
  namespace: database
spec:
  accessModes:  ["ReadWriteOnce"]
  resources:
    requests:
      storage: 1Gi
  storageClassName:  managed-nfs-storage
[root@master mysql]# cat deploy_mysql.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql2
  namespace: database
spec:
  selector:
    matchLabels:
      app: mysql2
  template:
    metadata:
      labels:
        app: mysql2
    spec:
      containers:
      - name: mysql2
        image: mysql:5.7.23
        env:                        
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: nfs-pvc-test
          mountPath: /var/lib/mysql         
          subPath: mysql
      volumes:
      - name: nfs-pvc-test
        persistentVolumeClaim:
          claimName: test-claim1

(七)

mysql2这个pod 服务发布:

[root@master mysql]# cat svc_mysql.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql2
  namespace: database
spec:
  type: NodePort
  ports:
  - port: 3306
    targetPort: 3306
    nodePort: 32222
  selector:
    app: mysql2
  selector:
    app: mysql2

执行此文件即可: kubectl apply -f svc_mysql.yaml

(八)

Navicat连接数据库,IP地址填写任意一个集群内IP,比如,192.168.217.16,端口号是32222

(九)

单元测试

查看pod:

[root@master ~]# k get po -A
NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
database      mysql2-7b77d5f496-8r5tr                   1/1     Running     2          15h

查看pod的详细信息:

[root@master ~]# k describe pod  mysql2-7b77d5f496-2p6bl -n database
Name:         mysql2-7b77d5f496-2p6bl
Namespace:    database
Priority:     0
Node:         slave1/192.168.217.17
Start Time:   Sat, 16 Jul 2022 12:48:05 +0800
Labels:       app=mysql2
              pod-template-hash=7b77d5f496
Annotations:  <none>
Status:       Running
IP:           10.244.1.6
IPs:
  IP:           10.244.1.6
Controlled By:  ReplicaSet/mysql2-7b77d5f496
Containers:
  mysql2:
    Container ID:   docker://95c44eb4ed328ea8993190450c753bd8bfaae573929f81f25c1adf3d8f3d7ab9
    Image:          mysql:5.7.23
    Image ID:       docker://sha256:1b30b36ae96ace2d29cd9c7a724cbb9d1ce59424a79cad4d117175d273d1689b
    Port:           3306/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 16 Jul 2022 12:48:06 +0800
    Ready:          True
    Restart Count:  0
    Environment:
      MYSQL_ROOT_PASSWORD:  123456
    Mounts:
      /var/lib/mysql from nfs-pvc-test (rw,path="mysql")
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-v2llp (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  nfs-pvc-test:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  test-claim1
    ReadOnly:   false
  default-token-v2llp:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-v2llp
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:          <none>

这里可以看到,使用的pvc名称是test-claim1,并没有手动建立pv,总体功能是完全正常的。

总结

一二三四步骤是第一个阶段,五以及后面的步骤都是pvc的创建以及pv的自动创建测试。PVC创建的时候使用的namespace和后面部署pod的namespace要一致,第六步有详细的演示,两个文件内定义的namespace必须要一致哦,和最开始的rbac角色创建和nfs-client-provisioner的pod的部署的时候的namespace没有关系了。PROVISIONER_NAME 这个需要两个文件相互呼应,storageclass-nfs.yaml 这个文件会引用到这个值。pvc创建的时候只需要storageClassName这个名称了,因为StorageClass里已经定义过了。这里提到的两个值可以通过命令快速查出:

[root@master ~]# k get sc -A
NAME              PROVISIONER                           RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storager   fuseim.pri/ifs   Delete          Immediate           true                  4h6m

 

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
2月前
|
存储 Kubernetes 持续交付
k8s学习
【10月更文挑战第1天】
110 4
|
2月前
|
Prometheus Kubernetes 监控
k8s部署针对外部服务器的prometheus服务
通过上述步骤,您不仅成功地在Kubernetes集群内部署了Prometheus,还实现了对集群外服务器的有效监控。理解并实施网络配置是关键,确保监控数据的准确无误传输。随着监控需求的增长,您还可以进一步探索Prometheus生态中的其他组件,如Alertmanager、Grafana等,以构建完整的监控与报警体系。
141 60
|
2月前
|
Prometheus Kubernetes 监控
k8s部署针对外部服务器的prometheus服务
通过上述步骤,您不仅成功地在Kubernetes集群内部署了Prometheus,还实现了对集群外服务器的有效监控。理解并实施网络配置是关键,确保监控数据的准确无误传输。随着监控需求的增长,您还可以进一步探索Prometheus生态中的其他组件,如Alertmanager、Grafana等,以构建完整的监控与报警体系。
271 62
|
1月前
|
存储 Kubernetes 网络协议
k8s的无头服务
Headless Service 是一种特殊的 Kubernetes 服务,其 `spec:clusterIP` 设置为 `None`,不会分配 ClusterIP,通过 DNS 解析提供服务发现。与普通服务不同,Headless Service 不提供负载均衡功能,每个 Pod 都有唯一的 DNS 记录,直接映射到其 IP 地址,适用于有状态应用的场景,如与 StatefulSet 一起部署数据库。示例中通过创建 Nginx 的 StatefulSet 和 Headless Service,展示了如何直接访问单个 Pod 并进行内容修改。
53 3
|
2月前
|
Kubernetes 应用服务中间件 nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
|
2月前
|
Kubernetes 监控 测试技术
k8s学习--基于Ingress-nginx实现灰度发布系统
k8s学习--基于Ingress-nginx实现灰度发布系统
131 2
k8s学习--基于Ingress-nginx实现灰度发布系统
|
1月前
|
存储 Kubernetes Devops
Kubernetes集群管理和服务部署实战
Kubernetes集群管理和服务部署实战
56 0
|
2月前
|
存储 Kubernetes 调度
|
2月前
|
负载均衡 Kubernetes 区块链
随机密码生成器+阿里k8s负载均衡型服务加证书方法+移动终端设计+ico生成器等
随机密码生成器+阿里k8s负载均衡型服务加证书方法+移动终端设计+ico生成器等
64 1
|
2月前
|
Kubernetes API 调度
k8s学习--pod的所有状态详解(图例展示)
k8s学习--pod的所有状态详解(图例展示)
243 1