十六. Kubernetes 工作负载 之 StatefulSet

一. StatefulSet 基础解释

  1. Deployment部署的应用一般称为无状态应用,StatefulSet部署的应用一般称为有状态应用
  1. 无状态应用:可以简单理解为无状态应用不会记录当前状态,例如网络,存储等信息,如果应用宕机重启,重启后网络,存储,顺序可能等可能会变,例如Deployment部署的业务代码
  2. 有状态应用:可以记录当前状态,即使重启,重启后根据状态记录信息可以做到网络,存储,顺序等不变,例如中间件MySQL、Redis、MQ等等,以mysql为例,部署为有状态的服务,即使宕机重启访问地址不会变,其它服务还是可以正常访问的
  1. 并且使用StatefulSet部署的服务是有顺序的,创建pod时,会给创建的多个pod添加索引,部署,启动等都是按照这个索引顺序进行,停止或删除是按照索引逆序执行
  2. StatefulSet 使用场景总结:
  1. 稳定、唯一的网络标识(dnsname)必须配合Service使用,StatefulSet通过与其相关的无头服务Service为每个pod提供DNS解析条目,通过解析DNS解析条目访问pod
  2. 稳定的、持久的存储, 每个Pod始终对应各自的存储路径(PersistantVolumeClaimTemplate)
  3. 有序的、优雅的部署和缩放。按顺序地增加副本、减少副本,并在减少副本时执行清理
  4. 有序的、自动的滚动更新。按顺序自动地执行滚动更新
  1. StatefulSet注意点
  1. 给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。
  2. 删除或者收缩 StatefulSet 并不会 删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
  3. StatefulSet 当前需要无头服务 来负责 Pod 的网络标识。你需要负责创建此服务。
  4. 当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的Pod 可以有序地且体面地终止,可以在删除之前将 StatefulSet 缩放为 0。
  5. 在默认 Pod 管理策略( OrderedReady ) 时使用 滚动更新,可能进入需要人工干预 才能修复的损坏状态

二. StatefulSet 配合 Service 负载均衡网络(无头服务)的使用

  1. StatefulSe与Service负载均衡网络的配合,使用StatefulSet 部署的有状态应用网络,存储等信息不变,以访问地址不变为例,封装Service负载均衡网络时设置" clusterIP: None" 不要分配ClusterIP,headless service等,访问地址由Service内部代理的通过StatefulSe部署pod自己控制
  1. 该方式下k8s内部通过"podName.serviceName.namespace.svc.cluster.local" 访问指定pod
  2. 通过无头服务Service代理StatefulSet 部署的多个pod,也是可以直接访问service,通过Service负载均衡选择指定pod访问
  3. 此时外部无法访问这个无头服务Service的,如果想要外部访问需要使用ingress网络定义所有的访问逻辑
  1. 一个StatefulSet与Service示例:(下方解释)
  1. 下方使用StatefulSet部署mysql服务,部署3个副本,pod名称为none-mysql,并使用serviceName将当前部署的服务添加到none-service负载均衡网络中
  2. 封装Service负载均衡网络"none-service",通过"clusterIP: None"设置当前Service为无头服务,不分配ClusterIP,headless等
  3. 由于使用StatefulSet部署时,会给部署的多个pod添加索引,加上索引后每个pod名称都是唯一的,例如"none-mysql-0",“none-mysql-1”,“none-mysql-2”,内部管理的多个pod启停删除等都是由顺序的,访问ip也是固定的,即使pod重启也不会变
  4. 部署的服务添加到无头的负载均衡网络后,通过"podName.serviceName.namespace.svc.cluster.local"访问指定pod(后面的".svc.cluster.local"是固定格式),换算下来如果想要访问none-mysql第一个pod,请求"none-mysql-0.none-service.default.svc.cluster.local"即可
apiVersion: apps/v1
kind: StatefulSet  ### 有状态副本集
metadata:
  name: stateful-mysql
  namespace: default
spec:
  selector:
    matchLabels:
      app: none-mysql
  serviceName: "none-service"  ## 服务名,指定加到那个service里面
  replicas: 3 # 三个副本
  template: ## Pod模板
    metadata:
      labels:
        app: none-mysql
    spec:
      containers:
      - name: none-mysql
        image: mysql
---
#负载均衡网络
apiVersion: v1
kind: Service
metadata:
  name: none-service ## 和部署的服务serviceName值与此处相同表示加入到当前网络中
  namespace: default
spec:
  selector:
    app: none-mysql #选中哪些pod的标签
  clusterIP: None  ## 设置为None 表示当前是一个无头服务,不要分配ClusterIP,headless service,整个集群的Pod能直接访问,注意此时浏览器目前不能访问,如果想要外部访问时需要使用ingress网络定义所有的访问逻辑
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: 80

podManagementPolicy: pod的管理策略

  1. 在使用StatefulSet 部署时,提供了一个podManagementPolicy属性,用来控制Pod创建,升级以及扩缩容逻辑,可以设置为:
  1. OrderedReady: 按需(默认值,有序启动,StatefulSet部署时会给每个pod提供一个索引,会按照这个索引顺序启动,索引逆序停止)
  2. Parallel:并发(同时创建启动,一般不使用)

updateStrategy: pod更新策略

  1. 在使用StatefulSet 部署时,提供了一个updateStrategy属性,用来控制更新策略,是一个属性对象,内部有rollingUpdate和type两个属性,默认情况下使用RollingUpdate滚动升级作为更新策略,可以指定为partition按分区升级

使用StatefulSet时,会给部署的pod添加索引,在指定为partition更新策略,更新时只会更新索引大于等于partition的pod

apiVersion: apps/v1
kind: StatefulSet #有状态副本集
metadata:
  name: stateful-nginx
  namespace: default
spec:
  podManagementPolicy: OrderedReady #所有Pod一起创建,OrderedReady:有序创建
  updateStrategy: #升级策略
    rollingUpdate:
  	partition: 1 #更新大于等于这个索引的pod
  selector:
    matchLabels:
      app: ss-nginx #hastomatch.spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 #三个副本
  template: #Pod模板
    metadata:
      labels:
        app: ss-nginx
    spec:
      containers:
        - name: nginx
          image: nginx