3.16 StatefulSet 部署有状态应用

一、认识StatefulSet

StatefulSet可以与ReplicaSet对应来看,ReplicaSet通过模板可以创建多个pod副本,多个pod名字和IP地址不同外,其它都相同,他们共享持久卷,pod重新调度后依然继续共享持久卷,重新调度的pod可以代替之前的pod,因此ReplicaSet创建的pod是无状态的,可以随意对pod进行重新调度。
而StatefulSet创建的每个pod都是专门定制的,每一个pod实例都是不可替代的个体,每个pod实例都有稳定的名字和IP地址。

二、StatefulSet原理

1、提供稳定的pod网络标识

稳定的网络标识指pod的名字和域名是稳定的。首先说一下稳定的名字,ReplicaSet创建的pod的名称都是随机的,而StatefulSet创建的则是有规律的。StatefulSet创建的pod都是从0开始的顺序索引,pod的名称都是可以预知的,如下图所示即使名为A-2的pod消亡后,会立即重启一个相同的A-2的pod,如下图所示。
在这里插入图片描述
再说一下稳定的域名,ReplicaSet控制的Pod副本都是一样的,无状态的,客户端请求时,通过负载每一个pod副本,或者随意选择一个pod进行请求,结果都是一致的。但是StarefulSet管理的pod则不可以随意选择一个Pod,因为他们是有状态的,每一个pod都是不相同的,StatefulSet需要创建一个用来记录每个pod的headless service,通过这个service,每个pod拥有独立的DNS记录,每个pod的域名是不同的,客户端就是通过域名来选择后端服务pod的。比如default名称空间下有个myheadlessservice无头服务,它的一个pod名称为A-2,则客户端访问后端pod时,可以通过a-2.myheadlessservice.default.svc.cluster.local域名方式进行访问。ReplicaSet管理的pod则不支持此种方式。

StatefulSet保持Pod名称不变的两种场景应用:

1.1. 替换现有的pod
当StatefulSet管理的pod中有一个由于某种原因,或者手动删除时,StatefulSet会保证重启一个新的pod实例,与ReplicaSet类似,但与ReplicaSet不同的是,新创建的pod与消亡的pod会保持一致的名字,比如上图中名为A-1的pod消亡后,会保证重建一个一样的名为A-1的pod。

1.2. 扩缩容StatefulSet
扩容的情况下,StatefulSet会使用一个还没用到的新的顺序索引值创建一个pod实例,比如上面已经创建StatefulSet含有3个副本,名分别为A-0、A-1、A-2,现在要扩容到4个,会创建一个名为A-4的pod,如果还要继续扩容,名字顺序号依次进行递增。
缩容的情况下,必须从索引值最大的pod开始缩容,比如A-0、A-1、A-2,要先把A-2的pod取消掉。需要注意的是,StatefulSet管理的pod有不健康的情况下,是不允许进行缩容的,因为StatefulSet缩容时不一定会消亡不健康的pod,而是从索引值最大的pod开始消亡,比如A-0、A-1、A-2,其中A-0、A-1是处于不健康状态,此时如果缩容1个pod,就会把A-2的pod消亡掉,StatefulSet管理的pod服务则不能正常对外响应,因此StatefulSet在pod不健康的情况下是不允许缩容的。基于以上原因,StatefulSet扩容和缩容是比较慢的,需要一个接着一个的进行扩容和缩容,而ReplicaSet则是批量进行扩容和缩容。

2、为每个pod提供稳定的存储

StatefulSet是通过持久卷和持久卷声明方式为pod提供稳定存储的,在创建StatefulSet之前先由管理员创建出多个持久卷,StatefulSet可以通过一个或多个持久卷声明模板创建持久卷声明,绑定到每一个pod示例上。后续会通过示例进行演示。
StatefulSet在进行缩容时,会删除掉pod,但是不会删除pod的持久卷声明,因为一旦删除掉持久卷声明,与之绑定的持久卷就会回收,数据丢失,当对StatefulSet再进行扩容时,就丢失了原持久化的内容;
因StatefulSet缩容会保留持久卷声明,在StatefulSet随后的扩容中,新创建的pod实例会使用缩容时保留的持久卷声明以及其上的数据,新创建的pod实例与之前状态一致。

三、StatefulSet应用

1、部署StatefulSet

为部署StatefulSet服务,只需如下三步:

  • 创建持久卷;
  • 创建headless service;
  • 创建StatefulSet
    首先由管理员创建好持久卷,然后创建Headless Sercie,如下所示
apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
spec:
  clusterIP: None   #clusterIP一定要设置None,表示headless service
  selector:
    app: nginx
  ports:
  - name: http
    port: 80

然后创建StatefulSet,如下所示

apiVersion: app/v1betal
kind: StatefulSet  			#资源类型
  name: nginx-statefulset
spec:
  serviceName: nginx 	#StatefuleSet服务的名字,在解析pod域名时用
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: mynginx:0.1
        pors:
        - name: http
          port: 80
        volumeMounts:
        - name: data
          mountPath: /data
  volumeClaimTemplates:
  - matadata:
      name: data
    spec:
      resources:
        requests:
          storage: 50Mi
      accessModes:
      - ReadWriteOnce

通过使用volumeClaimTemplates,为每一个pod创建一个持久卷声明,并绑定到pod上,创建的持久化声明名字规则:按照volumeClaimTemplates定义的名字加上pod定义的名字,再加上顺序(从0开始)。例如本例创建的持久化声明分别为data-nginx-0、data-nginx-1、data-nginx-2。
其中定义serviceName: nginx,该服务名字在解析pod域名时起作用,pod域名规则:podName + serviceName + nameSpace + svc + cluster + local。例如本例第一个pod域名为:nginx-0.nginx.default.svc.cluster.local。
另外与ReplicaSet不同的是,StatefulSet每次创建成功后一个pod,才会去创建下一个pod,保证pod是顺序创建的。

2、可以为StatefulSet管理的pod创建一个ClusterIP service

如下所示,为StatefulSet管理的pod创建ClusterIP service

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

如果想对StatefulSet管理的pod通过集群进行负载访问,可以为这些pod再额外创建一个ClusterIP service,ClusterIP service不对外暴露,外部客户端不能直接访问ClusterIP service,进而访问StatefulSet管理的pod,可以通过下面两种方式进行访问(已在k8s的安全机制中介绍过):

  1. 在k8s中创建一个pod,客户端访问pod,然后通过pod的ServiceAccount方式访问ClusterIP service,进而访问StatefulSet管理的po;
  2. 对客户端进行RBAC鉴权和认证,通过Restful接口进行访问
curl API服务IP:端口/api/v1/namespaces/default/services/nginx-service/

3、StatefulSet处理时效节点

StatefulSet在不确定一个pod的状态时,是不会去创建新的pod代替旧的pod的。
例如当集群中一个节点失效或断网后,集群中该节点显示NotReady状态,由StatefulSet管理并调度在该节点上的pod在集群中显示unknown状态,如果节点短时间恢复,pod会重新标记为Running状态,如果几分钟后(时间可以配置)仍未恢复,控制器就会标记pod为删除状态。
当pod为删除状态时,此时如果手动delete该pod,StatefulSet会重新在正常节点上再调度一个名字一样的pod,但pod会显示unknown状态,过会就显示Terminating状态了,是因为在手动删除该未知的pod时,控制器已经标记pod为删除状态,只要失效节点上的kubelete没有通知API服务器pod已经被终止,即使在集群中手动删除失效的pod,控制器依然会标记为删除状态。

强制删除pod
如果确定失联的pod已不可用,可以强制删除该pod,集群中就可以重新调度一个pod了,强制删除pod执行如下命令:

kubectl delete pod nginx-0 --force --grace-period 0

参考《k8s in action》

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值