Kubernetes之(十四)StatefulSet控制器

释放双眼,带上耳机,听听看~!

目录

  • Kubernetes之(十四)StatefulSet控制器

  • 简介

    • StatefulSet使用
  • 滚动更新
    * 扩展伸缩
    * 更新策略和版本升级

Kubernetes之(十四)StatefulSet控制器

简介

StatefulSet 作为 Controller 为 Pod 提供唯⼀的标识。 它可以保证部署和 scale 的顺序。
StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为⽆状态服务⽽设计) , 其应⽤场景包括:

  • 稳定的持久化存储, 即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格式为 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

部署Scale保证:

  • 对于有N个副本的StatefulSet,Pod将按照{0..N-1}的顺序被创建和部署。
  • 当删除Pod的时候,将按照逆序来终结,从{N-1..0}
  • 对Pod执⾏scale操作之前,它所有的前任必须处于Running和Ready状态。
  • 在终⽌Pod前,它所有的继任者必须处于完全关闭状态。

Headless Service:
在deployment中,每一个pod是没有名称,是随机字符串,是无序的。而statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。
为了实现标识符的稳定,这时候就需要一个headless service 解析直达到pod,还需要给pod配置一个唯一的名称。

volumeClainTemplate:
大部分有状态副本集都会用到持久存储,比如分布式系统来说,由于数据是不一样的,每个节点都需要自己专用的存储节点。而在deployment中pod模板中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,而statefulset定义中的每一个pod都不能使用同一个存储卷,由此基于pod模板创建pod是不适应的,这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷。Pod名称、PVC和PV关系图如下:

StatefulSet使用

使用之前配置的NFS服务器,PV


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1#nfs服务器
2[root@nfs ~]# showmount -e
3Export list for nfs:
4/data/volumes/v5 10.0.0.0/24
5/data/volumes/v4 10.0.0.0/24
6/data/volumes/v3 10.0.0.0/24
7/data/volumes/v2 10.0.0.0/24
8/data/volumes/v1 10.0.0.0/24
9
10
11#PV
12[root@master volume]# kubectl get pv
13NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
14pv001   1Gi        RWO,RWX        Retain           Available                                   3s
15pv002   2Gi        RWO            Retain           Available                                   3s
16pv003   2Gi        RWO,RWX        Retain           Available                                   3s
17pv004   4Gi        RWO,RWX        Retain           Available                                   3s
18pv005   4Gi        RWO,RWX        Retain           Available                                   3s                         23h
19

创建statefulSet


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
1[root@master statefulset]# vim statefulset-v1.yaml
2#定义Headless服务
3apiVersion: v1
4kind: Service
5metadata:
6  name: myapp
7  labels:
8    app: myapp
9spec:
10  ports:
11  - port: 80
12    name: web
13  clusterIP: None
14  selector:
15    app: myapp-pod
16---
17#配置statefulset
18apiVersion: apps/v1
19kind: StatefulSet
20metadata:
21  name: myapp
22spec:
23  serviceName: myapp-svc
24  replicas: 2
25  selector:
26    matchLabels:
27      app: myapp-pod
28  template:
29    metadata:
30      labels:
31        app: myapp-pod
32    spec:
33      containers:
34      - name: myapp
35        image: ikubernetes/myapp:v1
36        ports:
37        - name: web
38          containerPort: 80
39        volumeMounts:
40        - name: myappdata
41          mountPath: /usr/share/nginx/html/
42  volumeClaimTemplates:
43  - metadata:
44      name: myappdata
45    spec:
46      accessModes: [ "ReadWriteOnce" ]
47      resources:
48        requests:
49          storage: 1.5Gi
50[root@master statefulset]# kubectl apply -f statefulset-v1.yaml
51service/myapp created
52statefulset.apps/myapp created
53
54[root@master statefulset]# kubectl get pods
55NAME      READY   STATUS    RESTARTS   AGE
56myapp-0   1/1     Running   0          5s
57myapp-1   1/1     Running   0          4s
58myapp-2   1/1     Running   0          3s
59#此时Pod名称不是乱码是从0~N-1
60[root@master statefulset]# kubectl get svc
61NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
62kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   7d
63myapp        ClusterIP   None         <none>        80/TCP    31s
64
65[root@master statefulset]# kubectl get sts
66NAME    READY   AGE
67myapp   3/3     48s
68

查看pvc pv资源


1
2
3
4
5
6
7
8
9
10
11
12
13
1[root@master statefulset]# kubectl get pvc
2NAME                STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
3myappdata-myapp-0   Bound    pv002    2Gi        RWO                           3m56s
4myappdata-myapp-1   Bound    pv003    2Gi        RWO,RWX                       2m49s
5myappdata-myapp-2   Bound    pv004    4Gi        RWO,RWX                       88s
6[root@master statefulset]# kubectl get pv
7NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                       STORAGECLASS   REASON   AGE
8pv001   1Gi        RWO,RWX        Retain           Available                                                       22m
9pv002   2Gi        RWO            Retain           Bound       default/myappdata-myapp-0                           22m
10pv003   2Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-1                           22m
11pv004   4Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-2                           22m
12pv005   4Gi        RWO,RWX        Retain           Available                                                       22m
13

查看Pod使用的存储卷


1
2
3
4
5
6
7
8
1[root@master statefulset]# kubectl describe pods myapp-2
2......
3Volumes:
4  myappdata:
5    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
6    ClaimName:  myappdata-myapp-2
7......
8

就算删除Pod后重建的Podi依然绑定在myappdata-myapp-2这个PVC上

滚动更新


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
1[root@master statefulset]# vim statefulset-v1.yaml
2
3        image: ikubernetes/myapp:v3
4
5[root@master statefulset]# kubectl apply -f statefulset-v1.yaml
6service/myapp unchanged
7statefulset.apps/myapp configured
8
9[root@master ~]# kubectl get pods -w
10NAME      READY   STATUS    RESTARTS   AGE
11myapp-0   1/1     Running   0          9m18s
12myapp-1   1/1     Running   0          9m17s
13myapp-2   1/1     Running   0          9m16s
14myapp-2   1/1   Terminating   0     9m22s
15myapp-2   0/1   Terminating   0     9m23s
16myapp-2   0/1   Terminating   0     9m26s
17myapp-2   0/1   Terminating   0     9m26s
18myapp-2   0/1   Pending   0     0s
19myapp-2   0/1   Pending   0     0s
20myapp-2   0/1   ContainerCreating   0     0s
21myapp-2   1/1   Running   0     2s
22myapp-1   1/1   Terminating   0     9m29s
23myapp-1   0/1   Terminating   0     9m30s
24myapp-1   0/1   Terminating   0     9m31s
25myapp-1   0/1   Terminating   0     9m31s
26myapp-1   0/1   Pending   0     0s
27myapp-1   0/1   Pending   0     0s
28myapp-1   0/1   ContainerCreating   0     0s
29myapp-1   1/1   Running   0     1s
30myapp-0   1/1   Terminating   0     9m33s
31myapp-0   0/1   Terminating   0     9m34s
32myapp-0   0/1   Terminating   0     9m35s
33myapp-0   0/1   Terminating   0     9m35s
34myapp-0   0/1   Pending   0     0s
35myapp-0   0/1   Pending   0     0s
36myapp-0   0/1   ContainerCreating   0     0s
37myapp-0   1/1   Running   0     1s
38

在创建的每一个Pod中,每一个pod自己的名称都是可以被解析的,如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
1[root@master statefulset]# kubectl get pods -o wide
2NAME      READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
3myapp-0   1/1     Running   0          39s   10.244.2.66   node02   <none>           <none>
4myapp-1   1/1     Running   0          42s   10.244.1.56   node01   <none>           <none>
5myapp-2   1/1     Running   0          46s   10.244.2.65   node02   <none>           <none>
6
7[root@master statefulset]# kubectl exec -it myapp-1 -- /bin/sh
8/ # nslookup myapp-1.myapp-svc.default.svc.cluster.local
9nslookup: can't resolve '(null)': Name does not resolve
10
11Name:      myapp-1.myapp-svc.default.svc.cluster.local
12Address 1: 10.244.1.56 myapp-1.myapp-svc.default.svc.cluster.local
13

从上面的解析,我们可以看到在容器当中可以通过对Pod的名称进行解析到ip。其解析的域名格式如下:
pod_name.service_name.ns_name.svc.cluster.local
eg: myapp-0.myapp.default.svc.cluster.local

扩展伸缩


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
1#扩展副本到4个
2[root@master statefulset]# kubectl scale sts myapp --replicas=4
3statefulset.apps/myapp scaled
4#查看结果
5[root@master ~]# kubectl get pods -w
6NAME      READY   STATUS    RESTARTS   AGE
7myapp-0   1/1     Running   0          6m17s
8myapp-1   1/1     Running   0          6m20s
9myapp-2   1/1     Running   0          6m24s
10myapp-3   0/1   Pending   0     0s
11myapp-3   0/1   Pending   0     0s
12myapp-3   0/1   Pending   0     0s
13myapp-3   0/1   ContainerCreating   0     0s
14myapp-3   1/1   Running   0     2s
15
16#查看PV绑定
17[root@master statefulset]# kubectl get pv
18NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                       STORAGECLASS   REASON   AGE
19pv001   1Gi        RWO,RWX        Retain           Available                                                       38m
20pv002   2Gi        RWO            Retain           Bound       default/myappdata-myapp-0                           38m
21pv003   2Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-1                           38m
22pv004   4Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-2                           38m
23pv005   4Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-3  
24
25 #打补丁方式缩容
26 [root@master statefulset]# kubectl patch sts myapp -p '{"spec":{"replicas":2}}'
27statefulset.apps/myapp patched
28
29[root@master ~]# kubectl get pods -w
30NAME      READY   STATUS    RESTARTS   AGE
31myapp-0   1/1     Running   0          9m26s
32myapp-1   1/1     Running   0          9m29s
33myapp-2   1/1     Running   0          13s
34myapp-3   1/1     Running   0          11s
35myapp-3   1/1   Terminating   0     16s
36myapp-3   0/1   Terminating   0     18s
37myapp-3   0/1   Terminating   0     22s
38myapp-3   0/1   Terminating   0     22s
39myapp-2   1/1   Terminating   0     24s
40myapp-2   0/1   Terminating   0     24s
41myapp-2   0/1   Terminating   0     30s
42myapp-2   0/1   Terminating   0     30s
43
44

更新策略和版本升级

修改更新策略,以partition方式进行更新,更新值为2,只有myapp编号大于等于2的才会进行更新。类似于金丝雀部署方式


1
2
3
4
5
6
7
8
9
10
11
12
13
1[root@master statefulset]# kubectl patch sts myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
2statefulset.apps/myapp patched
3
4[root@master statefulset]# kubectl describe sts myapp
5Name:               myapp
6Namespace:          default
7CreationTimestamp:  Wed, 03 Apr 2019 16:53:24 +0800
8Selector:           app=myapp-pod
9Labels:             <none>
10Annotations:        kubectl.kubernetes.io/last-applied-configuration:
11                      {"apiVersion":"apps/v1","kind":"StatefulSet","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"replicas":3,"sele...
12
13

版本升级,将image的版本升级为v5,升级后对比myapp-2和myapp-1的image版本是不同的。这样就实现了金丝雀发布的效果。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1[root@master statefulset]# kubectl get sts -o wide
2NAME    READY   AGE   CONTAINERS   IMAGES
3myapp   4/4     23m   myapp        ikubernetes/myapp:v5
4
5[root@master statefulset]# kubectl get pods myapp-2 -o yaml |grep image
6  - image: ikubernetes/myapp:v5
7    imagePullPolicy: IfNotPresent
8    image: ikubernetes/myapp:v5
9    imageID: docker-pullable://ikubernetes/myapp@sha256:85d1005d172aa8b97d7f1aa67519132cd450f59d01a607d4b4eaf5bcf402ce52
10    
11[root@master statefulset]# kubectl get pods myapp-0 -o yaml |grep image
12  - image: ikubernetes/myapp:v3
13    imagePullPolicy: IfNotPresent
14    image: ikubernetes/myapp:v3
15    imageID: docker-pullable://ikubernetes/myapp@sha256:b8d74db2515d3c1391c78c5768272b9344428035ef6d72158fd9f6c4239b2c69
16
17

将剩余的Pod也更新版本,只需要将更新策略的partition值改为0即可,如下:


1
2
3
4
5
6
7
8
1[root@master statefulset]# kubectl patch sts myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'
2statefulset.apps/myapp patched
3
4[root@master statefulset]# kubectl get pods myapp-0 -o yaml |grep image                                             - image: ikubernetes/myapp:v5
5    imagePullPolicy: IfNotPresent
6    image: ikubernetes/myapp:v5
7    imageID: docker-pullable://ikubernetes/myapp@sha256:85d1005d172aa8b97d7f1aa67519132cd450f59d01a607d4b4eaf5bcf402ce52
8

生产中还是不建议把重要应用使用statefulset,如mysql redis 等 。

给TA打赏
共{{data.count}}人
人已打赏
安全运维

故障复盘的简洁框架-黄金三问

2021-9-30 19:18:23

安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索