Kubernetes入门 八、StatefulSet控制器

目录

  • 概述
  • 创建StatefulSet
  • Pod 的管理策略
    • 扩容缩容
  • 镜像更新
    • 滚动更新
    • 分区更新
    • 删除更新
  • 删除

概述

Stateful 翻译为 有状态的 ,也是pod控制器的一种。

StatefulSet 是为了解决有状态应用的问题,Deployment是为无状态应用设计的。

  • 无状态应用:网络可能会变(IP 地址)、存储可能会变(卷)、顺序可能会变(启动的顺序)。应用场景:业务代码,如:使用 SpringBoot 开发的商城应用的业务代码。

  • 有状态应用:网络不变、存储不变、顺序不变。应用场景:中间件,如:MySQL 集群、Zookeeper 集群、Redis 集群、MQ 集群。

Stateful 具有以下特点:

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

StatefulSet 由以下几个部分组成:

  • 用于定义网络标志(DNS domain)的 Headless Service
  • 用于创建 PersistentVolumes 的 volumeClaimTemplates
  • 定义具体应用的 StatefulSet

StatefulSet 通过和其相关的无头服务为每个 Pod 提供 DNS 解析条目,并且每个 Pod 都是唯一的。

StatefulSet 中每个 Pod 的 DNS 条目为 statefulSetName-{0…N-1}.serviceName.namespace.svc.cluster.local

  • serviceName 为 Headless Service 的名字
  • 0…N-1 为 Pod 所在的序号,从 0 开始到 N-1
  • statefulSetName 为 StatefulSet 的名字
  • namespace 为服务所在的 namespace,Headless Servic 和 StatefulSet 必须在相同的 namespace
  • .cluster.local 为 Cluster Domain

我们可以通过这个dns域名直接访问,也可以省略的使用statefulSetName-{0…N-1}.serviceName就可以访问,后面我们会验证。

注意事项:

  • kubernetes v1.5 版本以上才支持
  • 给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。
  • 删除或者收缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
  • StatefulSet 需要一个 Headless Service 来定义 DNS domain,需要在 StatefulSet 之前创建好。
  • 当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序地且体面地终止,可以在删除之前将 StatefulSet 缩放为 0。
  • 在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新,可能进入需要人工干预才能修复的损坏状态。

创建StatefulSet

一般我们还是通过yaml文件来创建StatefulSet。

创建nginx-sts.yaml文件,内容如下:

---
# 在yaml中,---中间的内容,属于嵌套配置,这里用于将 StatefulSet 加入网络
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None  # 不分配 ClusterIP ,形成无头服务,整个集群的 Pod 能直接访问,但是浏览器不可以访问。
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: nginx  # 使用哪个service来管理dns,,这个字段需要和 service 的 metadata.name 相同
  replicas: 2
  selector:  # 选择器,用于定义管理的指定资源的标签(Service和Pod)
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:  # 容器内部暴漏的端口
        - containerPort: 80  # 具体的端口
          name: web  # 端口的名字
        volumeMounts:  # 数据卷挂载
        - name: www  # 数据卷名字
          mountPath: /usr/share/nginx/html  # 加载到容器内的哪个目录
  volumeClaimTemplates:  # 数据卷模板
  - metadata:
      name: www  # 数据卷名称
      annotations:  # 注解信息
        volume.alpha.kubernetes.io/storage-class: anything
    spec:  # 数据卷配置
      accessModes: [ "ReadWriteOnce" ]  # 访问模式
      resources:  # 资源配置
        requests:
          storage: 1Gi

然后执行创建命令:

kubectl create -f nginx-sts.yaml
# 结果如下,service和statefulset都创建出来了
service/nginx created
statefulset.apps/web created

查看service:

kubectl get svc
# 结果如下,第一个是默认的,不用管,第二个ngin使我们创建出来的
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   9d
nginx        ClusterIP   None         <none>        80/TCP    103s

查看statefulset:

kubectl get sts
# 结果如下
NAME   READY   AGE
web    2/2     3m5s

查看存储卷PersistentVolumes:

kubectl get pvc
# 结果如下
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-bdd3c8f0-8a9d-4c3c-ac3e-2fe6701d6c01   1Gi        RWO            hostpath       3m59s
www-web-1   Bound    pvc-54a1dab2-59ea-4611-9d73-312789c3d5f7   1Gi        RWO            hostpath       3m56s

查看po:

kubectl get po
# 结果如下
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          7m29s
web-1   1/1     Running   0          7m26s

可以看到pod的名字后缀和deployment不一样,这里是数字,deployment是一串字符串。

接着我们新创建一个容器,看看能否在新容器中通过name访问到这个容器。

# busybox是一个linux的工具包镜像;--restart=Never表示不重启, --rm:运行完的时候删除
kubectl run -it --image busybox dns-test --restart=Never --rm /bin/sh
# 成功后进入容器的终端控制台
If you don't see a command prompt, try pressing enter.
/ #

我们在容器内ping下刚刚创建的web-0 pod:

# web-0是pod的名字,nginx是svc的名字
ping web-0.nginx
# 结果如下
PING web-0.nginx (10.1.0.80): 56 data bytes
64 bytes from 10.1.0.80: seq=0 ttl=64 time=0.601 ms
64 bytes from 10.1.0.80: seq=1 ttl=64 time=0.190 ms
64 bytes from 10.1.0.80: seq=2 ttl=64 time=0.325 ms
64 bytes from 10.1.0.80: seq=3 ttl=64 time=0.294 ms

同样的,pod web-1也是可以ping通的。

这样就证明了service为pod提供了稳定网络,固定的dns,可以通过dns直接访问。

Pod 的管理策略

StatefulSet 的 Pod 的管理策略(podManagementPolicy)分为:

  • OrderedReady(有序启动,默认值):也就是pod的创建是按照顺序来的,启动后,我们看到后缀数字就是其顺序。
  • Parallel(并发一起启动):pod的创建没有顺序。

配置如下:

...
spec:
  podManagementPolicy: OrderedReady # 控制 Pod 创建、升级以及扩缩容逻辑。Parallel(并发一起启动) 和 
...

一般我们不用去修改。

扩容缩容

与deployment一样,statefulSet也可以实现扩容缩容。也是通过修改我们的副本数即可。

方式也是一样,这里我们再回顾一下:

  1. 使用 Kubectl edit 命令,修改 spec:replicas:n 即可
kubectl edit sts web
  1. 使用 scale 命令实现扩缩容
kubectl scale sts web --replicas=5
  1. 修改 yaml 文件中 spec.replicas 字段,随后使用 kubectl apply -f xxx.yaml 命令即可
vim nginx-sts.yaml
kubectl apply -f nginx-sts.yaml
  1. 使用patch命令
kubectl patch statefulset web -p '{"spec":{"replicas":3}}'

示例:

使用下面命令扩容:

kubectl scale sts web --replicas=5
# statefulset.apps/web scaled

查看statefulset:

kubectl get sts
# 结果如下
NAME   READY   AGE
web    5/5     37m

可以看到结果变成了5个。

通过describe查看下Events信息:

kubectl describe sts web

结果如下:

Events:
  Type    Reason            Age   From                    Message
  ----    ------            ----  ----                    -------
  Normal  SuccessfulCreate  38m   statefulset-controller  create Claim www-web-0 Pod web-0 in StatefulSet web success
  Normal  SuccessfulCreate  38m   statefulset-controller  create Pod web-0 in StatefulSet web successful
  Normal  SuccessfulCreate  37m   statefulset-controller  create Claim www-web-1 Pod web-1 in StatefulSet web success
  Normal  SuccessfulCreate  37m   statefulset-controller  create Pod web-1 in StatefulSet web successful
  Normal  SuccessfulCreate  91s   statefulset-controller  create Claim www-web-2 Pod web-2 in StatefulSet web success
  Normal  SuccessfulCreate  91s   statefulset-controller  create Pod web-2 in StatefulSet web successful
  Normal  SuccessfulCreate  88s   statefulset-controller  create Claim www-web-3 Pod web-3 in StatefulSet web success
  Normal  SuccessfulCreate  88s   statefulset-controller  create Pod web-3 in StatefulSet web successful
  Normal  SuccessfulCreate  85s   statefulset-controller  create Claim www-web-4 Pod web-4 in StatefulSet web success
  Normal  SuccessfulCreate  85s   statefulset-controller  create Pod web-4 in StatefulSet web successful

可以看到pod后缀依次从0到4,共5个。

再进行缩容看下:

kubectl scale sts web --replicas=3
# statefulset.apps/web scaled

直接查看Events:

Events:
  Type    Reason            Age    From                    Message
  ----    ------            ----   ----                    -------
  Normal  SuccessfulCreate  41m    statefulset-controller  create Claim www-web-0 Pod web-0 in StatefulSet web success
  Normal  SuccessfulCreate  41m    statefulset-controller  create Pod web-0 in StatefulSet web successful
  Normal  SuccessfulCreate  41m    statefulset-controller  create Claim www-web-1 Pod web-1 in StatefulSet web success
  Normal  SuccessfulCreate  41m    statefulset-controller  create Pod web-1 in StatefulSet web successful
  Normal  SuccessfulCreate  5m13s  statefulset-controller  create Claim www-web-2 Pod web-2 in StatefulSet web success
  Normal  SuccessfulCreate  5m13s  statefulset-controller  create Pod web-2 in StatefulSet web successful
  Normal  SuccessfulCreate  5m10s  statefulset-controller  create Claim www-web-3 Pod web-3 in StatefulSet web success
  Normal  SuccessfulCreate  5m10s  statefulset-controller  create Pod web-3 in StatefulSet web successful
  Normal  SuccessfulCreate  5m7s   statefulset-controller  create Claim www-web-4 Pod web-4 in StatefulSet web success
  Normal  SuccessfulCreate  5m7s   statefulset-controller  create Pod web-4 in StatefulSet web successful
  Normal  SuccessfulDelete  104s   statefulset-controller  delete Pod web-4 in StatefulSet web successful
  Normal  SuccessfulDelete  103s   statefulset-controller  delete Pod web-3 in StatefulSet web successful

可以看到最后两行,依次把web-4和web-3删除了。也就是删除也是从后往前删除。

镜像更新

StatefulSet支持两种镜像更新策略:滚动更新(RollingUpdate)删除更新(OnDelete)。默认是RollingUpdate。

其配置如下:

...
spec:
  updateStrategy: # 更新策略
    type: RollingUpdate # OnDelete 删除之后才更新;RollingUpdate 滚动更新
    rollingUpdate:
      partition: 0 # 更新索引 >= partition 的 Pod ,默认为 0
...

滚动更新

StatefulSet的镜像更新目前还不支持set命令直接更新 image,需要 patch 来间接实现,如下:

kubectl patch sts web --type='json' -p='[{"op": "replace", "path":"/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]'
# statefulset.apps/web patched

执行完命令后看下我们StatefulSet:

kubectl get sts
# 结果如下
NAME   READY   AGE
web    3/3     56m

查看版本:

kubectl rollout history sts web
statefulset.apps/web
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

可以看到已经有两个版本了。

查看版本2的信息:

kubectl rollout history sts web --revision=2
# 结果如下:看到更新到版本1.9.1了
statefulset.apps/web with revision #2
Pod Template:
  Labels:	app=nginx
  Containers:
   nginx:
    Image:	nginx:1.9.1
    Port:	80/TCP
    Host Port:	0/TCP
    Environment:	<none>
    Mounts:
      /usr/share/nginx/html from www (rw)
  Volumes:	<none>

然后查看Events:

Events:
  Type    Reason            Age                From                    Message
  ----    ------            ----               ----                    -------
 # 只看最后6行即可
  Normal  SuccessfulDelete  39s                statefulset-controller  delete Pod web-2 in StatefulSet web successful
  Normal  SuccessfulCreate  38s (x2 over 19m)  statefulset-controller  create Pod web-2 in StatefulSet web successful
  Normal  SuccessfulDelete  37s                statefulset-controller  delete Pod web-1 in StatefulSet web successful
  Normal  SuccessfulCreate  36s (x2 over 56m)  statefulset-controller  create Pod web-1 in StatefulSet web successful
  Normal  SuccessfulDelete  35s                statefulset-controller  delete Pod web-0 in StatefulSet web successful
  Normal  SuccessfulCreate  34s (x2 over 56m)  statefulset-controller  create Pod web-0 in StatefulSet web successful

可以看到滚定更新全过程,是删除一个,再创建一个新的,依次一个一个地更新。

由 默认的,pod 是有序的,在 StatefulSet 中更新时也是基于 pod 的顺序倒序更新的。

分区更新

上面我们知道在配置更新策略时,还有一个partition的概念。

例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod。 partition 默认为0,也就是全部更新。

利用该机制,我们可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再主键增大更新的 pod 数量,最终实现全部 pod 更新。

利用 滚动更新的partition 可以实现类似灰度发布/金丝雀发布的效果。

灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

接下来看下如何实现灰度发布。

首先还使用我们前面创建的,修改副本数为5:

kubectl scale sts web --replicas=5
# statefulset.apps/web scaled

然后通过edit命令修改sts:

kubectl edit sts web

修改我们的 partition值为3,即只有序号大于等于3的才会更新;修改镜像版本为1.7.9(原来为1.9.1):

Kubernetes入门 八、StatefulSet控制器_第1张图片

编辑好后保存并退出。

查看pod:

kubectl get po
# 结果如下
NAME       READY   STATUS    RESTARTS   AGE
dns-test   1/1     Running   0          79m
web-0      1/1     Running   0          37m
web-1      1/1     Running   0          37m
web-2      1/1     Running   0          37m
web-3      1/1     Running   0          15s
web-4      1/1     Running   0          17s

这样看不出效果,再查看具体pod web-3的信息:

kubectl describe po web-3

结果如下,看到版本已经变为1.7.9。

Kubernetes入门 八、StatefulSet控制器_第2张图片

再查看具体pod web-2的信息:

kubectl describe po web-2

结果如下,看到版本还是1.9.1:

Kubernetes入门 八、StatefulSet控制器_第3张图片

同理,可以查看其他pod。得出结论:pod后面序号为0,1,2的没有更新,序号为3和4的更新了。也就验证了partition的作用。

接着如果发现序号为3和4的使用没问题,我们可以把partition改为0,实现全部更新。这里就不在演示了。

以上步骤就可以实现灰度发布的效果了。

删除更新

当使用删除更新的策略时,我们修改了镜像版本,并不会马上去更新。当我们手动删除某个pod时,就会吃席创建出一样的pod,这个时候,就会更新成最新的镜像了。

删除更新不常用,就不再演示了。

删除

创建StatefulSet时,会创建出三个资源:StatefulSet、Service、Pod。

当我们需要删除时,

执行如下命令:

kubectl delete statefulset web

注意:当我们删除statefulset时,会级联删除下面所有的Pod。

但是Service不会被级联删除,需要单独删除,如下:

kubectl delete service nginx

如果不想级联删除Pod,可以使用如下命令:

kubectl deelte sts web --cascade=false

你可能感兴趣的:(K8S入门与实战,kubernetes,容器,云原生)