• 第12章:有状态应用部署


    1.StatefulSet控制器概述

    StatefulSet:

    • 部署有状态应用

    • 解决Pod独立生命周期,保持Pod启动顺序和唯一性

    1. 稳定,唯一的网络标识符,持久存储

    2. 有序,优雅的部署和扩展、删除和终止

    3. 有序,滚动更新

    应用场景:数据库

    2.稳定的网络ID

    说起StatefulSet稳定的网络标识符,不得不从Headless说起了。

    标准Service:

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      selector:
        app: nginx
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376

    无头Service(Headless Service):

    apiVersion: v1
    kind: Service
    metadata:
      name: my-service
    spec:
      clusterIP: None
      selector:
        app: nginx
      ports:
        - protocol: TCP
          port: 80
          targetPort: 9376

    标准Service与无头Service区别是clusterIP: None,这表示创建Service不要为我(Headless Service)分配Cluster IP,因为我不需要。

    为什么无头Service需要?

    这就是无状态和有状态的控制器设计理念了,无状态的应用Pod是完全对等的,提供相同的服务,可以在飘移在任意节点,例如Web。而像一些分布式应用程序,例如zookeeper集群、etcd集群、mysql主从,每个实例都会维护着一种状态,每个实例都有各自的数据,并且每个实例之间必须有固定的访问地址(组建集群),这就是有状态应用。所以有状态应用是不能像无状态应用那样,创建一个标准Service,然后访问ClusterIP负载均衡到一组Pod上。这也是为什么无头Service不需要ClusterIP的原因,它要的是能为每个Pod固定一个”身份“。

    举例说明:

    # cat headless-svc.yaml

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: nginx
      serviceName: "headless-svc"
      replicas: 3
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
              name: web
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: headless-svc
    spec:
      clusterIP: None
      selector:
        app: nginx
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80

    相比之前讲的yaml,这次多了一个serviceName: “headless-svc”字段,这就告诉StatefulSet控制器要使用headless-svc这个headless service来保证Pod的身份。

    # kubectl get statefulset,pod,svc,ep -o wide
    NAME                   READY   AGE     CONTAINERS   IMAGES
    statefulset.apps/web   3/3     2m32s   nginx       nginx

    NAME       READY   STATUS   RESTARTS   AGE     IP           NODE
    pod/web-0   1/1     Running   0         2m32s   10.244.2.8   k8s-node2
    pod/web-1   1/1     Running   0         2m12s   10.244.1.10   k8s-node1
    pod/web-2   1/1     Running   0         113s   10.244.2.9   k8s-node2

    NAME                   TYPE       CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE     SELECTOR
    service/headless-svc   ClusterIP   None         <none>       80/TCP   2m32s   app=nginx
    service/kubernetes     ClusterIP   10.96.0.1   <none>       443/TCP   22d     <none>

    NAME                     ENDPOINTS                                   AGE
    endpoints/headless-svc   10.244.1.10:80,10.244.2.8:80,10.244.2.9:80   2m32s
    endpoints/kubernetes     172.16.1.70:6443                             22d

    临时创建一个Pod,测试DNS解析:

    # kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
    If you don't see a command prompt, try pressing enter.
    / # nslookup headless-svc.default.svc.cluster.local
    Server:   10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

    Name:     headless-svc.default.svc.cluster.local
    Address 1: 10.244.2.9 web-2.headless-svc.default.svc.cluster.local
    Address 2: 10.244.2.8 web-0.headless-svc.default.svc.cluster.local
    Address 3: 10.244.1.10 web-1.headless-svc.default.svc.cluster.local

    结果得出该Headless Service代理的所有Pod的IP地址和Pod 的DNS A记录。

    通过访问web-0.headless-svc的Pod的DNS名称时,可以解析到对应Pod的IP地址,其他 Pod 的DNS名称也是如此,这个DNS名称就是固定身份,在生命周期不会再变化:

    / # nslookup web-0.headless-svc.default.svc.cluster.local
    Server:   10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

    Name:     web-0.headless-svc.default.svc.cluster.local
    Address 1: 10.244.2.8 web-0.headless-svc.default.svc.cluster.local

    进入容器查看它们的主机名:

    [root@k8s-admin ~]# kubectl exec web-0 hostname
    web-0
    [root@k8s-admin ~]# kubectl exec web-1 hostname
    web-1
    [root@k8s-admin ~]# kubectl exec web-2 hostname
    web-2

    可以看到,每个Pod都从StatefulSet的名称和Pod的序号中获取主机名的。

    不过,相信你也已经注意到了,尽管web-0.headless-svc这条记录本身不会变,但它解析到的 Pod 的 IP 地址,并不是固定的。这就意味着,对于“有状态应用”实例的访问,你必须使用 DNS 记录或者 hostname 的方式,而绝不应该直接访问这些 Pod 的 IP 地址。

    以下是Cluster Domain,Service name,StatefulSet名称以及它们如何影响StatefulSet的Pod的DNS名称的一些选择示例。

    Cluster Domain Service (ns/name) StatefulSet (ns/name) StatefulSet Domain Pod DNS Pod Hostname
    cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
    cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}
    kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1}

    3.稳定的存储

    pod - pvc - pv

    StatefulSet 的存储卷使用 VolumeClaimTemplate 创建,称为卷申请模板,当 StatefulSet 使用 VolumeClaimTemplate 创建一个PersistentVolume 时,同样也会为每个Pod分配并创建一个编号的PVC。Deployment无状态服务PV自动供给,多个pod公用一个pvc和pv。

    示例:

    参考上篇文章安装nfs动态供给插件。

    # cat headless-pvc.yaml

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      selector:
        matchLabels:
          app: nginx
      serviceName: "headless-svc"
      replicas: 3
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: [ "ReadWriteMany" ]
          storageClassName: "managed-nfs-storage"
          resources:
            requests:
              storage: 10Gi
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: headless-svc
    spec:
      clusterIP: None
      selector:
        app: nginx
      ports:
        - protocol: TCP
          port: 80
          targetPort: 80

    # kubectl get pod,pvc,pv

    image-20210517003312544

    (1) StatefulSet为每个Pod分配专属的PVC及编号。每个PVC绑定对应的 PV,从而保证每一个 Pod 都拥有一个独立的 Volume (2) 删除Pods或StatefulSet时(或kubectl delete -f headless-pvc.yaml方式),它所对应的PVC和PV不会被删除。所以,当这个Pod被重新创建 出现之后,Kubernetes会为它找到同样编号的PVC,挂载这个PVC对应的Volume,从而获取到以前保存在 Volume 里的数据。

    小结

    StatefulSet与Deployment区别: (1) StatefulSet有身份的,身份三要素:域名,主机名,存储(PVC) (2) 有状态应用:在k8s一般指的分布式应用程序,mysql主从、zookeeper集群、etcd集群 1)数据持久化 2)IP地址,名称(为每个Pod分配一个固定DNS名称) 3)启动顺序

    https://kubernetes.io/zh/docs/tutorials/stateful-application/zookeeper/

    (3) 无有状态应用:nginx、api、微服务jar

  • 相关阅读:
    [BOST] 博赞有机的学习技巧
    [BOST] 你的大脑比你想象的更优秀
    Markdown学习笔记
    nodejs原生态模块,写个聊天室
    【2】自定义WindowsForm分页控件使用【共两篇】
    【1】自定义WindowsForm分页控件使用【共两篇】
    《Log4net写出适合自己的日志类》第三篇【终】【怎样让它适合你自己需求】
    《Log4net写出适合自己的日志类》第二篇【没有理论的实践是盲目】
    《Log4net写出适合自己的日志类》第一篇【上来就是干,先实践后理论】
    记忆留住深刻过往,博客写出平淡事迹【博客首篇】
  • 原文地址:https://www.cnblogs.com/LiuChang-blog/p/14776611.html
Copyright © 2020-2023  润新知