一、statefulset简介 
从前面的学习我们知道使用Deployment创建的pod是无状态的,当挂载了Volume之后,如果使用“kubectl delete -f”删除了Deployment资源后,在使用“kubectl apply -f” 新创建的Pod无法在挂载到以前的Volume上。为了解决这一问题,就引入了StatefulSet用于保留Pod的状态信息。
 
StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括:  
1、稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现 
2、稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现 
3、有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现 
4、有序收缩,有序删除(即从N-1到0) 
5、有序的滚动更新 
 
从上面的应用场景可以发现,StatefulSet由以下几个部分组成:  
用于定义网络标志(DNS domain)的Headless Service(无头服务) 
用于创建PersistentVolumes的volumeClaimTemplates (存储卷申请模板) 
定义具体应用的StatefulSet 
 
二、为什么要有headless?? 
在deployment中,每一个pod是没有名称,是随机字符串,是无序的。而statefulset中是要求有序的,每一个pod的名称必须是固定的。当节点挂了,重建之后的标识符是不变的,每一个节点的节点名称是不能改变的。pod名称是作为pod识别的唯一标识符,必须保证其标识符的稳定并且唯一。
 
三、为什么要 有volumeClainTemplate?? 
大部分有状态副本集都会用到持久存储,比如分布式系统来说,由于数据是不一样的,每个节点都需要自己专用的存储节点。而在deployment中pod模板中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,而statefulset定义中的每一个pod都不能使用同一个存储卷,由此基于pod模板创建pod是不适应的,这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,从而有自己专用的存储卷。
 
四、statefulSet使用演示 
在创建StatefulSet之前需要准备的东西,值得注意的是创建顺序非常关键,创建顺序如下:
 
(1)查看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 [root@k8s-master ~]# kubectl explain statefulsetKIND:      StatefulSetVERSION:   apps/v1DESCRIPTION: set  of  pods with  consistent identities. Identitiesas : - Network: A single  stable DNS and  hostname. - Storage: As as  requested. The StatefulSet guarantees that a givento  the same storage identity.FIELDS: string >string >Object >Object >Object >KIND:      StatefulSetVERSION:   apps/v1RESOURCE:  spec <Object >DESCRIPTION: of  pods in  this set .is  the specification of  a StatefulSet.FIELDS: string >  #Pod管理策略integer >    #副本数量integer >   #历史版本限制Object > -required-    #选择器,必选项string > -required-  #服务名称,必选项Object > -required-    #模板,必选项Object >       #更新策略Object >   #存储卷申请模板,列表对象形式
(2)清单定义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 [root@k8s-master  mainfests ]apiVersion:  v1 kind:  Service metadata: name:  myapp-svc labels: app:  myapp-svc spec: ports: -  port:  80 name:  web clusterIP:  None selector: app:  myapp-pod --- apiVersion:  apps/v1 kind:  StatefulSet metadata: name:  myapp spec: serviceName:  myapp-svc replicas:  3 selector: matchLabels: app:  myapp-pod template: metadata: labels: app:  myapp-pod spec: containers: -  name:  myapp image:  nginx:latest imagePullPolicy:  IfNotPresent  ports: -  containerPort:  80 name:  web volumeMounts: -  name:  myappdata mountPath:  /usr/share/nginx/html volumeClaimTemplates: -  metadata: name:  myappdata spec: accessModes:  ["ReadWriteOnce" ]resources: requests: storage:  2Gi 
(3)修改pv的大小为2Gi 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@k8s-master  ~]# cd  mainf ests/volumesmaster  volumes ]master  volumes ]master  volumes ]1 Gi        RWO,RWX        Retain           Available                                      5s 2 Gi        RWO            Retain           Available                                      5s 2 Gi        RWO,RWX        Retain           Available                                      5s 2 Gi        RWO,RWX        Retain           Available                                      5s 2 Gi        RWO,RWX        Retain           Available                                      5s 
(4)创建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 [root@k8s-master  mainfests ]master  mainfests ]TYPE         CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE10.96 .0.1         <none>         443 /TCP             50 d<none>         80 /TCP              38s master  mainfests ]3          3          55s master  mainfests ]0    Bound     pv002     2 Gi        RWO                           1m 1    Bound     pv003     2 Gi        RWO,RWX                       1m 2    Bound     pv004     2 Gi        RWO,RWX                       1m master  mainfests ]1 Gi        RWO,RWX        Retain           Available                                                        6m 2 Gi        RWO            Retain           Bound       default/myappdata-myapp-0                             6m 2 Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-1                             6m 2 Gi        RWO,RWX        Retain           Bound       default/myappdata-myapp-2                             6m 2 Gi        RWO,RWX        Retain           Available                                                        6m master  mainfests ]0                   1 /1        Running   0           2m 1                   1 /1        Running   0           2m 2                   1 /1        Running   0           2m 2 /2        Running   0           1 d5 b5d6fbbbd-q8ppz   1 /1        Running   1           2 d
当删除的时候是从myapp-2开始进行删除的,关闭是逆向关闭
 
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 [root@k8s-master mainfests] 1           33d 2           33d 0           3m 0           3m 0           3m 0           1d 1           2d 0          3m 0          3m 0          3m 0          3m 0          3m 0          3m 0          3m 0          3m 0          4m 0          4m 0          3m 0          3m
五、滚动更新、扩展伸缩、版本升级、修改更新策略 1、滚动更新 
RollingUpdate 更新策略在 StatefulSet 中实现 Pod 的自动滚动更新。 当StatefulSet的 .spec.updateStrategy.type 设置为 RollingUpdate 时,默认为:RollingUpdate。StatefulSet 控制器将在 StatefulSet 中删除并重新创建每个 Pod。 它将以与 Pod 终止相同的顺序进行(从最大的序数到最小的序数),每次更新一个 Pod。 在更新其前身之前,它将等待正在更新的 Pod 状态变成正在运行并就绪。如下操作的滚动更新是有2-0的顺序更新。
 
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 [root@k8s-master mainfests] 0           36m 0           36m 0           36m 0          36m 0          36m 0          36m 0          36m 0          0s 0          0s 0          0s 0          2s 0          36m 0          36m 0          36m 0          36m 0          0s 0          0s 0          0s 0          1s 0          37m 0          37m 0          37m 0          37m
在创建的每一个Pod中,每一个pod自己的名称都是可以被解析的,如下:
 
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 [root@k8s-master ~]# kubectl get  pods -o wideNAME                      READY     STATUS    RESTARTS   AGE       IP            NODE-0                   1 /1        Running   0           8 m        10.244 .1 .62    k8s-node01-1                   1 /1        Running   0           8 m        10.244 .2 .49    k8s-node02-2                   1 /1        Running   0           8 m        10.244 .1 .61    k8s-node01-0  -0. myapp-svc.default .svc.cluster .local 't resolve ' (null )': Name does not resolve Name:      myapp-0.myapp-svc.default.svc.cluster.local Address 1: 10.244.1.62 myapp-0.myapp-svc.default.svc.cluster.local / # nslookup myapp-1.myapp-svc.default.svc.cluster.local nslookup: can' t resolve '(null)' : Name  does not  resolveName :      myapp-1. myapp-svc.default .svc.cluster .local 1 : 10.244 .2 .49  myapp-1. myapp-svc.default .svc.cluster .local -2. myapp-svc.default .svc.cluster .local 't resolve ' (null )': Name does not resolve Name:      myapp-2.myapp-svc.default.svc.cluster.local Address 1: 10.244.1.61 myapp-2.myapp-svc.default.svc.cluster.local 从上面的解析,我们可以看到在容器当中可以通过对Pod的名称进行解析到ip。其解析的域名格式如下: pod_name.service_name.ns_name.svc.cluster.local eg: myapp-0.myapp.default.svc.cluster.local 
2、扩展伸缩 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 [root@k8s-master mainfests] 0           23m 0           23m 0           23m 0          0s 0          0s 0          0s 0          1s 0           25m 0           25m 0           25m 0           1m 0          2m 0          2m 0          2m 0          2m 0          26m 0          26m 0          27m 0          27m
3、更新策略和版本升级 
修改更新策略,以partition方式进行更新,更新值为2,只有myapp编号大于等于2的才会进行更新。类似于金丝雀部署方式。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@k8s-master mainfests]4          4          1 h10  Oct 2018  21 :58 :24  -0400 /last-applied-configuration={"apiVersion":"apps/ v1"," kind":" StatefulSet"," metadata":{" annotations":{}," name":" myapp"," namespace":" default"}," spec":{" replicas":3," selector":{" match...4  desired | 4  total2 
版本升级,将image的版本升级为v3,升级后对比myapp-2和myapp-1的image版本是不同的。这样就实现了金丝雀发布的效果。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@k8s -master mainfests]@k8s -master ~]NAME       DESIRED    CURRENT    AGE        CONTAINERS    IMAGES 4          4          1h        myapp        ikubernetes/myapp: v3@k8s -master ~]image:  ikubernetes/myapp: v3imagePullPolicy:  IfNotPresent image:  ikubernetes/myapp: v3imageID:  docker-pullable: //ikubernetes/myapp @sha256 :b8d74db2515d3c1391c78c5768272b9344428035ef6d72158fd9f6c4239b2c69 @k8s -master ~]image:  ikubernetes/myapp: v2imagePullPolicy:  IfNotPresent image:  ikubernetes/myapp: v2imageID:  docker-pullable: //ikubernetes/myapp @sha256 : 85a2b81a62f09a414ea33b74fb8aa686ed9b168294b26b4c819df0be0712d358
将剩余的Pod也更新版本,只需要将更新策略的partition值改为0即可,如下:
 
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 [root@k8s-master mainfests] 0           58m 0           58m 0           13m 0           13m 0          58m 0          58m 0          58m 0          58m 0          0s 0          0s 0          0s 0          2s 0          58m 0          58m 0          58m 0          58m 0          0s 0          0s 0          0s 0          2s