zoukankan      html  css  js  c++  java
  • 第9关 k8s架构师课程之有状态服务StatefulSet

    文件转载自博客:https://www.bilibili.com/video/BV1kK4y1U7PQ?spm_id_from=333.999.0.0

     https://www.toutiao.com/a6941713886601085471/?log_from=2e24852b1d691_1630849929667

    这里一定要看视频

    接下来先看这篇文章,再来看视频里面的文章

    https://zhuanlan.zhihu.com/p/164771138

    Statefulset

    StatefulSet是为了解决有状态服务的问题,对应的Deployment和ReplicaSet是为了无状态服务而设计,其应用场景包括:

    1. 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
    2. 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
    3. 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
    4. 有序收缩,有序删除(即从N-1到0)

    因为statefulset要求Pod的名称是有顺序的,每一个Pod都不能被随意取代,也就是即使Pod重建之后,名称依然不变。为后端的每一个Pod去命名。

    从上面的应用场景可以发现,StatefulSet由以下几部分组成: 1. 用于定义网络标志的Headless Service(headless-svc:无头服务。因为没有IP地址,所以它不具备负载均衡的功能了。) 2. 用于创建PersistentVolumes的volumeClaimTemplates 3. 定义具体应用的StatefulSet

    StatefulSet:Pod控制器。RC、RS、Deployment、DS。 无状态的服务。

    template(模板):根据模板创建出来的Pod,它们的状态都是一模一样的(除了名称、IP、域名之外)可以理解为:任何一个Pod,都可以被删除,然后用新生成的Pod进行替换。

    有状态的服务:需要记录前一次或者多次通信中的相关时间,以作为下一次通信的分类标准。比如:MySQL等数据库服务。(Pod的名称,不能随意变化。数据持久化的目录也是不一样,每一个Pod都有自己独有的数据持久化存储目录。)

    每一个Pod-----对应一个PVC-----每一个PVC对应一个PV。

    以自己的名称创建一个名称空间,以下所有资源都运行在此空间中。

    用statefuset资源运行一个httpd web服务,要求3个Pod,但是每个Pod的主界面内容不一样,并且都要做专有的数据持久化,尝试删除其中一个Pod,查看新生成的Pod,是否数据与之前一致。

    基于NFS服务,创建NFS服务。


    [root@master ~]# yum -y install nfs-utils rpcbind  br/>2.[root@master ~]# mkdir /nfsdata  
    [root@master ~]# vim /etc/exports  br/>4./nfsdata  *(rw,sync,no_root_squash)  
    [root@master ~]# systemctl start nfs-server.service   
    [root@master ~]# systemctl start rpcbind  br/>7.[root@master ~]# showmount -e  
    Export list for master:  
    ./nfsdata *

    2.创建RBAC权限

    vim rbac-rolebind.yaml

    apiVersion: v1
    kind: Namespace
    metadata: 
      name: lbs-test
    apiVersion: v1
        kind: ServiceAccount  创建rbac授权用户。及定义权限
    metadata:
      name: nfs-provisioner
      name:lbs-test
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: nfs-provisioner-runner
      name:lbs-test
    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: ["watch", "create", "update", "patch"]
       -  apiGroups: [""]
          resources: ["services", "endpoints"]
          verbs: ["get","create","list", "watch","update"]
       -  apiGroups: ["extensions"]
          resources: ["podsecuritypolicies"]
          resourceNames: ["nfs-provisioner
          verbs: ["use"]
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: run-nfs-provisioner
    subjects:
      - kind: ServiceAccount
        name: nfs-provisioner
        namespace:  lbs-test            如没有名称空间需要添加这个default默认否则报错
    roleRef:
      kind: ClusterRole
      name: nfs-provisioner-runner
      apiGroup: rbac.authorization.k8s.io


    执行yaml文件:

    [root@master yaml]# kubectl apply -f rbac-rolebind.yaml   
    namespace/lbh-test created  
    serviceaccount/nfs-provisioner created  
    clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created  
    clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created


    3.创建Deployment资源对象

    [root@master yaml]# vim nfs-deployment.yaml 
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: nfs-client-provisioner
      name:lbs-test
    spec:
      replicas: 1#副本数量为1
      strategy:
        type: Recreate#重置
      template:
        metadata:
          labels:
            app: nfs-client-provisioner
        spec:
          serviceAccount: nfs-provisioner#指定账户
          containers:
            - name: nfs-client-provisioner
              image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner使用的是这个镜像。
              volumeMounts:
                - name: nfs-client-root
                  mountPath:  /persistentvolumes#指定容器内的挂载目录
              env:
                - name: PROVISIONER_NAME#容器内置变量
                  value: bdqn#这是变量的名字
                - name: NFS_SERVER
                  value: 192.168.1.1
                - name: NFS_PATH#指定Nfs的共享目录
                  value: /nfsdata
          volumes:#指定挂载到容器内的nfs路径与IP
            - name: nfs-client-root
              nfs:
                server: 192.168.1.1
                path: /nfsdata

    执行yaml文件,查看Pod

    4.创建Storageclass资源对象(sc):

    root@master yaml]# vim sc.yaml 
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: sc-nfs
      namespace:lbs-test #名称空间 名
    provisioner: lbs-test#与deployment资源的env环境变量value值相同
    reclaimPolicy: Retain #回收策略

    执行yaml文件,查看SC

    [root@master yaml]# kubectl apply -f sc.yaml   
    storageclass.storage.k8s.io/sc-nfs created  
    [root@master yaml]# kubectl get sc -n lbs-test   
    NAME     PROVISIONER   AGE  
    sc-nfs   lbs-test      8s

    上面不清楚的请查看视频

    https://space.bilibili.com/314330254/video?tid=0&page=1&keyword=&order=pubdate

    接下来我们就创建stateful服务,不用收到创建pv和pvc,使用storegeclass帮我们自动创建pv和pvc

    5.创建StatefulSet资源对象,自动创建PVC:

    vim statefulset.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: headless-svc
      namespace: lbs-test
      labels:
        app: headless-svc
    spec:
      ports:
      - port: 80
        name: myweb
      selector:
        app: headless-pod
      clusterIP: None
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: statefulset-test
      namespace: lbs-test
    spec:
      serviceName: headless-svc
      replicas: 3
      selector:
        matchLabels:
          app: headless-pod
      template:
        metadata:
          labels:
            app: headless-pod
        spec:
          containers:
          - image: httpd
            name: myhttpd
            ports:
            - containerPort: 80
              name: httpd
            volumeMounts:
            - mountPath: /mnt
              name: test
      volumeClaimTemplates:     这个字段:自动创建PVC
      - metadata:
          name: test
          annotations:   //这是指定storageclass,名称要一致
            volume.beta.kubernetes.io/storage-class: sc-nfs
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 100Mi

    上面标注成黄色的都是必不可少的

    无头服务创建的service的集群IP是不存在的,这里设置为null

    clusterIP: None
    第二个deployment编排文件的类型设置为kind: StatefulSet
    第三个在创建的pod中必须收到指定serviceName:
      serviceName: headless-svc
    第四个我们要收到创建pvc和pv,需要使用
    volumeClaimTemplates,在
    volumeClaimTemplates中storage-class: sc-nfs必须和上面创建的名称保证一样
    kind: StorageClass
    metadata:
      name: sc-nfs

    执行yaml文件,查看Pod:

    [root@master yaml]# kubectl apply -f statefulset.yaml   
    service/headless-svc created
    statefulset.apps/statefulset-test created  
    [root@master yaml]# kubectl get pod -n lbs-test   
    NAME                                      READY   STATUS    RESTARTS   AGE  
    nfs-client-provisioner-5d88975f6d-wdbnc   1/1     Running   0          22m  
    statefulset-test-0                        1/1     Running   0          8m59s  
    statefulset-test-1                        1/1     Running   0          2m30s  
    statefulset-test-2                        1/1     Running   0          109s
    这里我们看到创建的pod都是有规则的命名的,如
    statefulset-test-0、
    statefulset-test-1、
    statefulset-test-2
     

    有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现

    并且每个pod都固定访问对应的pvc,pvc是固定不变的,当pod重启之后也是访问相同的pvc,稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现

    查看是否自动创建PV及PVC

    PV:

    [root@master yaml]# kubectl get pv -n lbs-test   
    NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                              STORAGECLASS   REASON   AGE  
    pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5   100Mi      RWO            Delete           Bound    lbh-test/test-statefulset-test-2   sc-nfs                  4m23s  
    pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5   100Mi      RWO            Delete           Bound    lbh-test/test-statefulset-test-0   sc-nfs                  11m  
    pvc-99137753-ccd0-4524-bf40-f3576fc97eba   100Mi      RWO            Delete           Bound    lbh-test/test-statefulset-test-1   sc-nfs                  5m4s

    PVC:

    [root@master yaml]# kubectl get pvc -n lbs-test   
    NAME                      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE  
    test-statefulset-test-0   Bound    pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5   100Mi      RWO            sc-nfs         13m  
    test-statefulset-test-1   Bound    pvc-99137753-ccd0-4524-bf40-f3576fc97eba   100Mi      RWO            sc-nfs         6m42s  
    test-statefulset-test-2   Bound    pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5   100Mi      RWO            sc-nfs         6m1s

    这里我们可以看到pvc的命名规则:test是volumeClaimTemplates的名称

    statefulset-test是statefulset的名称,这个名称是固定的,如果要手动创建pvc必须按照这个方式命名
    0 1 2是对应的pod启动的顺序

    查看是否创建持久化目录:

    [root@master yaml]# ls /nfsdata/  
    lbh-test-test-statefulset-test-0-pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5  
    lbh-test-test-statefulset-test-1-pvc-99137753-ccd0-4524-bf40-f3576fc97eba  
    lbh-test-test-statefulset-test-2-pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5

    6.在pod资源内创建数据。并访问测试。

    [root@master yaml]# cd /nfsdata/  
    [root@master nfsdata]# echo 111 > lbs-test-test-statefulset-test-0-pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5/index.html  
    [root@master nfsdata]# echo 222 > lbs-test-test-statefulset-test-1-pvc-99137753-ccd0-4524-bf40-f3576fc97eba/index.html  
    [root@master nfsdata]# echo 333 > lbs-test-test-statefulset-test-2-pvc-0454e9ad-892f-4e39-8dcb-79664f65d1e5/index.html  
    [root@master nfsdata]# kubectl get pod -o wide -n lbs-test   
    NAME                                      READY   STATUS    RESTARTS   AGE     IP           NODE     NOMINATED NODE   READINESS GATES  
    nfs-client-provisioner-5d88975f6d-wdbnc   1/1     Running   0          30m     10.244.2.2   node02   <none>           <none>  
    statefulset-test-0                        1/1     Running   0          17m     10.244.1.2   node01   <none>           <none>  
    statefulset-test-1                        1/1     Running   0          10m     10.244.2.3   node02   <none>           <none>  
    statefulset-test-2                        1/1     Running   0          9m57s   10.244.1.3   node01   <none>           <none>  
    [root@master nfsdata]# curl 10.244.1.2  
    111  
    [root@master nfsdata]# curl 10.244.2.3  
    222  
    [root@master nfsdata]# curl 10.244.1.3  
    333

    7.删除其中一个pod,查看该pod资源的数据是否会重新创建并存在。

    [root@master ~]# kubectl get pod -n lbs-test   
    NAME                                      READY   STATUS    RESTARTS   AGE  
    nfs-client-provisioner-5d88975f6d-wdbnc   1/1     Running   0          33m  
    statefulset-test-0                        1/1     Running   0          20m  
    statefulset-test-1                        1/1     Running   0          13m  
    statefulset-test-2                        1/1     Running   0          13m  
    [root@master ~]# kubectl delete pod -n lbs-test statefulset-test-0   
    pod "statefulset-test-0" deleted

    删除后会重新创建pod资源

    [root@master ~]# kubectl get pod -n lbs-test -o wide  
    NAME                                      READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES  
    nfs-client-provisioner-5d88975f6d-wdbnc   1/1     Running   0          35m   10.244.2.2   node02   <none>           <none>  
    statefulset-test-0                        1/1     Running   0          51s   10.244.1.4   node01   <none>           <none>  
    statefulset-test-1                        1/1     Running   0          15m   10.244.2.3   node02   <none>           <none>  
    statefulset-test-2                        1/1     Running   0          14m   10.244.1.3   node01   <none>           <none>

    数据依旧存在

    [root@master ~]# curl 10.244.1.4  
    111  
    [root@master ~]# cat /nfsdata/lbs-test-test-statefulset-test-0-pvc-2cb98c60-977f-4f3b-ba97-b84275f3b9e5/index.html   
    111

    StatefulSet资源对象,针对有状态的服务的数据持久化测试完成。 通过测试,即使删除Pod,重新生成调度后,依旧能访问到之前的持久化数据

    相当的经典呀。

    如果我们不使用storageClass自动创建pv和pvc,如果要收到的创建pv和pvc,那么pvc的命名必须满足规范,我们来看下下面的要求

    不清楚的可以看博客

    https://www.toutiao.com/a6941713886601085471/?log_from=2e24852b1d691_1630849929667

     1、创建两个pod、创建两个pv、创建两个pvc

    1、创建pv
    -------------------------------------------
    
    root@node1:~# cat web-pv.yaml 
    # mkdir -p /nfs_dir/{web-pv0,web-pv1}
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: web-pv0
      labels:
        type: web-pv0
    spec:
      capacity:
        storage: 1Gi
      accessModes:
        - ReadWriteOnce
      persistentVolumeReclaimPolicy: Retain
      storageClassName: my-storage-class
      nfs:
        path: /nfs_dir/web-pv0
        server: 10.0.1.201
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: web-pv1
      labels:
        type: web-pv1
    spec:
      capacity:
        storage: 1Gi
      accessModes:
        - ReadWriteOnce
      persistentVolumeReclaimPolicy: Retain
      storageClassName: my-storage-class
      nfs:
        path: /nfs_dir/web-pv1
        server: 10.0.1.201
    
    2、创建pvc(这一步可以省去让其自动创建,这里手动创建是为了让大家能更清楚在sts里面pvc的创建过程)
    -------------------------------------------
    这一步非常非常的关键,因为如果创建的PVC的名称和StatefulSet中的名称没有对应上,
    那么StatefulSet中的Pod就肯定创建不成功.
    
    我们在这里创建了一个叫做www-web-0和www-web-1的PVC,这个名字是不是很奇怪,
    而且在这个yaml里并没有提到PV的名字,所以PV和PVC是怎么bound起来的呢?
    是通过labels标签下的key:value键值对来进行匹配的,
    我们在创建PV时指定了label的键值对,在PVC里通过selector可以指定label。
    
    然后再回到这个PVC的名称定义:www-web-0,为什么叫这样一个看似有规律的名字呢,
    这里需要看看下面创建StatefulSet中的yaml,
    首先我们看到StatefulSet的name叫web,设置的replicas为2个,
    volumeMounts和volumeClaimTemplates的name必须相同,为www,
    所以StatefulSet创建的第一个Pod的name应该为web-0,第二个为web-1。
    这里StatefulSet中的Pod与PVC之间的绑定关系是通过名称来匹配的,即:
    
    PVC_name  =  volumeClaimTemplates_name + "-" + pod_name
    www-web-0     =       www               + "-" +   web-0
    www-web-1     =       www               + "-" +   web-1
    
    
    root@node1:~# cat web-pvc.yaml 
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: www-web-0
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
      storageClassName: my-storage-class
      selector:
        matchLabels:
          type: web-pv0
    ---
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: www-web-1
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
      storageClassName: my-storage-class
      selector:
        matchLabels:
          type: web-pv1
    3、创建Service  StatefulSet
    -------------------------------------------
    在上一步中我们已经创建了名为www-web-0的PVC了,接下来创建一个service和statefulset,
    service的名称可以随意取,但是statefulset的名称已经定死了,为web,
    并且statefulset中的volumeClaimTemplates_name必须为www,volumeMounts_name也必须为www。
    只有这样,statefulset中的pod才能通过命名来匹配到PVC,否则会创建失败。
    root@node1:~# cat web.yaml 
    apiVersion: v1
    kind: Service
    metadata:
      name: web-headless
      labels:
        app: nginx
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: nginx
    
    
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: nginx # has to match .spec.template.metadata.labels
      serviceName: "web-headless"
      replicas: 2 # by default is 1
      template:
        metadata:
          labels:
            app: nginx # has to match .spec.selector.matchLabels
        spec:
          terminationGracePeriodSeconds: 10
          containers:
          - name: nginx
            image: nginx
            imagePullPolicy: IfNotPresent
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "my-storage-class"
          resources:
            requests:
              storage: 1Gi
    我们在做测试的时候,可以启动一个busybox的容器,在容器内部使用域名来访问无头服务
    上面的创建成功之后会启动两个web容器

     可以启动一个busybox的容器,在容器内部使用域名来访问无头服务下对应的pod:k8s为service资源分配了DNS名称,通过DNS名称可以访问到service对用的pod。而通过statefulset创建的pod,并依赖service headless能实现给statefulset管理的pod提供固定的DNS名称pod-name.service-headless-name.namespace.svc.cluster-domain.example,具体样例:

    域名构成如下web-0是访问的pod的名称,web-headless是对应的service-headless-name的名称,web是对应的namespace,剩余的svc.cluster-domain.example默认可以不写

     


    statefulset
  • 相关阅读:
    Java基础-3y
    对线面试官面试系列-3y
    从零单排学Redis【青铜】
    mock官方文档
    route路由组件传递参数
    axios拦截器与vue代理设置
    Sass使用方法
    Less使用方法
    Vue-cli
    Vue-组件注册
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/15231667.html
Copyright © 2011-2022 走看看