Deployment 和 StatefulSet 是 Kubernetes 里最常用的两种工作负载控制器,长得像但脾气完全不同。这篇从适用场景、存储和命名几个角度,把两者的区别梳理清楚。

适用场景

Deployment适用场景

无状态的应用

1.pod之间没有顺序

2.所有pod共享存储

3.pod名字包含随机数字

4.service都有ClusterIP,可以负载均衡

StatefulSet适用场景

有状态的应用

1.部署、扩展、更新、删除都要有顺序

2.每个pod都有自己存储,所以都用volumeClaimTemplates,为每个pod都生成一个自己的存储,保存自己的状态

3.pod名字始终是固定的

4.service没有ClusterIP,是headlessservice,所以无法负载均衡,返回的都是pod名,所以pod名字都必须固定,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名:$(podname).(headless server name).namespace.svc.cluster.local

部署StatefulSet和Deployment

部署StatefulSet

首先需要创建一个headlessService,其yaml文件如下:

apiVersion: v1
kind: Service
metadata:
  name: my-sts
  namespace: shield
  labels:
    app: my-sts
spec:
  selector:
    app: my-sts
  clusterIP: None
  ports:
  - name: my-sts
    port: 80

然后创建storageClass和PVC

storageClass的yaml文件如下(集群中已有,不用自己创建):

allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"
  creationTimestamp: "2023-05-29T05:32:23Z"
  name: alibabacloud-cnfs-nas
  resourceVersion: "1152865259"
  uid: c0fafd32-a72c-497f-b89c-cc9e01d0865f
mountOptions:
- nolock,tcp,noresvport
- vers=3
parameters:
  archiveOnDelete: "false"
  containerNetworkFileSystem: cnfs-nas-c5f3de5c4331f4649bd8ac9b8d6f71431
  path: /
  volumeAs: subpath
provisioner: nasplugin.csi.alibabacloud.com
reclaimPolicy: Delete
volumeBindingMode: Immediate

创建PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-sts
  namespace: shield
  labels:
    app: my-sts
spec:
  storageClassName: alibabacloud-cnfs-nas
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

创建StatefulSet

StatefulSet的yaml文件如下:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: my-sts
  namespace: shield
spec:
  selector:
    matchLabels:
      app: my-sts # has to match .spec.template.metadata.labels
  serviceName: "my-sts"
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: my-sts # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: my-sts
        image: nginx
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "alibabacloud-cnfs-nas"
      resources:
        requests:
          storage: 1Gi

成功部署一个含有3个副本的StatefulSet

[zenghui_wang@aprd4-master task]$ kubectl get sts
NAME     READY   AGE
my-sts   3/3     55m

部署Deployment

yaml文件如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deploy
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels: 
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx

成功部署一个含有3个副本的deployment

[zenghui_wang@aprd4-master task]$ kubectl get deploy
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
my-deploy   3/3     3            3           11s

Pods的命名区别

查看本命名空间下的所有Pods

[zenghui_wang@aprd4-master task]$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
my-deploy-748c667d99-k4tnh   1/1     Running   0          11m
my-deploy-748c667d99-nhz89   1/1     Running   0          11m
my-deploy-748c667d99-pkm4s   1/1     Running   0          11m
my-sts-0                     1/1     Running   0          73m
my-sts-1                     1/1     Running   0          72m
my-sts-2                     1/1     Running   0          72m

可以看到明显区别,StatefulSet创建的Pods是有序的,以StatefulSet的名字作为前缀,从序号0开始命名,而Deployment的名字为随机的。

扩容缩容和滚动更新的区别

扩容

修改StatefulSet和Deployment的yaml文件,replicas修改为10,执行kubectl get pods命令

StatefulSet的扩容结果

[zenghui_wang@aprd4-master task]$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
my-sts-0                     1/1     Running   0          92m
my-sts-1                     1/1     Running   0          92m
my-sts-2                     1/1     Running   0          92m
my-sts-3                     1/1     Running   0          21s
my-sts-4                     1/1     Running   0          18s
my-sts-5                     1/1     Running   0          15s
my-sts-6                     1/1     Running   0          12s
my-sts-7                     1/1     Running   0          10s
my-sts-8                     1/1     Running   0          7s
my-sts-9                     1/1     Running   0          4s

Deployment的扩容结果

[zenghui_wang@aprd4-master task]$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
my-deploy-748c667d99-7zzm8   1/1     Running   0          7s
my-deploy-748c667d99-94ppc   1/1     Running   0          7s
my-deploy-748c667d99-bw8fv   1/1     Running   0          7s
my-deploy-748c667d99-dqmzk   1/1     Running   0          7s
my-deploy-748c667d99-hg8bd   1/1     Running   0          7s
my-deploy-748c667d99-kn72g   1/1     Running   0          7s
my-deploy-748c667d99-nchjk   1/1     Running   0          12m
my-deploy-748c667d99-nhz89   1/1     Running   0          34m
my-deploy-748c667d99-q5klt   1/1     Running   0          12m
my-deploy-748c667d99-z2zqg   1/1     Running   0          7s

从各个Pods的创建时间可以看出,StatefulSet创建Pods是有序的,从编号从小到大开始创建,而Deployment创建Pods顺序是随机的。

缩容

对StatefulSet执行缩容操作,使用kubectl describe查看记录

Normal  SuccessfulDelete  30s (x2 over 11m)    statefulset-controller  delete Pod my-sts-9 in StatefulSet my-sts successful
Normal  SuccessfulDelete  29s (x2 over 11m)    statefulset-controller  delete Pod my-sts-8 in StatefulSet my-sts successful
Normal  SuccessfulDelete  28s (x2 over 11m)    statefulset-controller  delete Pod my-sts-7 in StatefulSet my-sts successful
Normal  SuccessfulDelete  27s (x2 over 11m)    statefulset-controller  delete Pod my-sts-6 in StatefulSet my-sts successful
Normal  SuccessfulDelete  27s (x2 over 11m)    statefulset-controller  delete Pod my-sts-5 in StatefulSet my-sts successful

可以看出StatefulSet删除Pods也是有序的,从编号从大到小开始删除。Deployment为随机删除。

滚动更新

修改StatefulSet的容器的镜像版本,使用kubectl describe查看记录

Events:
  Type    Reason            Age                From                    Message
  ----    ------            ----               ----                    -------
  Normal  SuccessfulDelete  11m                statefulset-controller  delete Pod my-sts-2 in StatefulSet my-sts successful
  Normal  SuccessfulCreate  11m (x2 over 21h)  statefulset-controller  create Pod my-sts-2 in StatefulSet my-sts successful
  Normal  SuccessfulDelete  11m                statefulset-controller  delete Pod my-sts-1 in StatefulSet my-sts successful
  Normal  SuccessfulCreate  11m (x2 over 21h)  statefulset-controller  create Pod my-sts-1 in StatefulSet my-sts successful
  Normal  SuccessfulCreate  11m (x3 over 21h)  statefulset-controller  create Pod my-sts-0 in StatefulSet my-sts successful
  Normal  SuccessfulDelete  11m                statefulset-controller  delete Pod my-sts-0 in StatefulSet my-sts successful

可以发现从序号最大的 Pod 开始,逐个删除和更新每一个 Pod,直到序号最小的 Pod 被更新。

对Deployment执行相同的操作:

Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  58s   deployment-controller  Scaled up replica set my-deploy-69849c999b to 1
  Normal  ScalingReplicaSet  53s   deployment-controller  Scaled down replica set my-deploy-748c667d99 to 2 from 3
  Normal  ScalingReplicaSet  53s   deployment-controller  Scaled up replica set my-deploy-69849c999b to 2 from 1
  Normal  ScalingReplicaSet  49s   deployment-controller  Scaled down replica set my-deploy-748c667d99 to 1 from 2
  Normal  ScalingReplicaSet  49s   deployment-controller  Scaled up replica set my-deploy-69849c999b to 3 from 2
  Normal  ScalingReplicaSet  44s   deployment-controller  Scaled down replica set my-deploy-748c667d99 to 0 from 1

可以看出Deployment的滚动更新策略是:

更新 Deployment 时,Deployment Controller 先创建一个新的 ReplicaSet (nginx-deployment-1564180365) 并将其 scale up 到 1 个副本,然后 scale down 旧的 ReplicaSet 到 2。Deployment Controller 继续 scale up 新的 ReplicaSet 并 scale down 旧的 ReplicaSet,直到最后,新旧两个 ReplicaSet,一个副本数为 3,另一个副本数为 0。

StatefulSet的特点

稳定、唯一的网络标识(dnsname)

每个Pod始终对应各自的存储路径(PersistantVolumeClaimTemplate)

按顺序地增加副本、减少副本,并在减少副本时执行清理

按顺序自动地执行滚动更新