kubernetes -- helm charts 开发: 14、编写statefulset

1 statefulset基础

1.1 引入原因

deployment, daemonset, job无状态,但是数据库集群的相关服务一般都是有状态的

1.2 statefulset特点

1) 每个pod有唯一网络标识,可用于发现集群内其他成员.
每个结点有固定id,集群中成员可通过该id互相发现并通信。集群规模固定。
2) statefulset控制的pod启停顺序是受控的,操作第n个pod时,
前n-1个pod已经是运行且准备好的状态
3) statefuleset中的pod采用持久化存储卷,通过PV/PVC来实现,
删除pod默认不会删除与statefulset相关的存储卷。
4) statefulset搭配使用headless service,在statefulset中要
声明其属于哪个headless service。
headless service没有Cluster IP,解析headless service的DNS域名,
返回的是该service对应的全部pod的endpoint列表。
5) 每个satefulset的pod的域名格式如下:
$(podname).(headless server name)   
FQDN: $(podname).(headless server name).namespace.svc.cluster.local

1.3 statefulset和deployment的区别

deployment的名字是随机的,无法产生固定身份id,并且pod的ip是运行才会产生的。

1.4 statefulset的存储

集群中的pod需要挂接共享存储。

1.5 statefulset应用

1) 持久化存储,pod重新调度后:     还能访问到相同的持久化数据,基于pvc实现
2) 稳定的网络标志,pod重新调度后:其podname和hostname不变,基于headless service(无cluster ip)
3) 有序部署,pod按照顺序启动,按照从0到N-1顺序
4) 逆序删除,删除pod,按照从N-1到0的顺序

1.6 statefulset组成

headless service + 用于创建PersistentVolumes的volumeClaimTemplates + 定语具体应用的statefulset

1.7 statefulset的每个pod的DNS格式

statefulsetName-{0...N-1}.serviceName.namespace.svc.cluster.local
解释:
serviceName: 是headless service名字
statefulsetName:是statefuleSet的名字
namespace: 是服务锁在的namespace, headless service和statefulset必须在同一个namespace下面
.cluster.local: 是cluster domain

注意:
1)StatefulSet 在节点丢失之后不迁移到其它节点,是 work as design
2)删除statefulset不会删除与该statefulset相关联的volume
3)给pod的存储是通过PersistentVolume Provisioner根据请求的storage class进行配置。


2 statefulset实战样例


下面的代码来自: kubernetes-handbook

2.1 先创建了一个nginx的headless service

apiVersion:    v1
kind:    Service
metadata:
    name:    nginx
    labels:
      app: nginx
spec:
    ports:
    -  port:80
       name: web
    clusterIP: None
    selector:
      app: nginx

分析:
headless service最重要的就是clusterIP为None

2.2 创建了一个叫web的statefulset,定义3个运行nginx的pod

---
apiVersion:    apps/v1beta1
kind:    StatefulSet
metadata:
    name:    web
spec:
    serviceName: "nginx"
    replicas: 3
    template:
      metadata:
        labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: gcr.io/google_containers/nginx-slim:0.8
        ports:
        -    containerPort: 80
        name:    web
        volumeMounts:
        -    name:    www
        mountPath: /usr/share/nginx/html
    volumeClaimTemplates:
    - metadata:
        name: www
        annotations:
        volume.beta.kubernetes.io/storage-class:    anything
      spec:
        accessModes:    [ "ReadWriteOnce"    ]
        resources:
        requests:
        storage: 1Gi

分析:
1) statefulset与deployment的最大区别就是:
多了
serviceName: "nginx"字段,
该字段作用就是与nginx这个headless service建立联系
2) statefulset必须提供pvc
上述例子中即为:
    volumeClaimTemplates:
    - metadata:
        name: www
        annotations:
        volume.beta.kubernetes.io/storage-class:    anything
      spec:
        accessModes:    [ "ReadWriteOnce"    ]
        resources:
        requests:
        storage: 1Gi


3 statefulset为什么使用headless service无头服务

pod的ip会在重建后发生变化,而statefulset需要能稳定标识pod的名称,
而headless service恰好可以给每个pod唯一的名称。

4 statefulset为什么使用volumeClaimTemplate

volumeClaimTemplate: 卷申请模板,为每个pod生成不同pvc,并绑定pv,从而实现每个pod自己的存储,
对于statefulset应用场景,例如mongodb集群这种分布式集群,数据不一样,各个结点
不能使用同一存储卷,之后再利用该集群本身机制去进行数据同步更新等。
如果是deployment中的pod定义的存储卷,所有副本集共用一个存储卷,因为基于模板。
无法实现有状态应用自身的特性(即有状态应用在每个结点上数据不一样,需要有各自的存储区域)。

5 为什么要给statefulset的每个pod设置域名.

原因是pod如果发生故障会飘移到其他结点上,pod ip发生变化。所以不能基于pod ip通信,
而是需要一个固定的标识来通信,而这种pod域名满足固定标示的特点,可以被用于服务之间通信。

headless service的域名为:
..svc.cluster.local
其中cluster.local指的是集群的域名

6 statefulset中pvc名称是什么

pvc的命名规则如下:
-,例如
上面volumeClaimTemplates.name>为www, pod名称=web-[0-2]
则pvc名称为:
www-web-0, www-web-1, www-web-2。

7 statefulset实战

7.1 编写headless service的chart

需要包含: api版本,类型,元数据(名称,标签),spec规格: 端口列表(每个端口,名称) , clusterIP, selector
apiVersion
内容如下:

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  selector:
    app: nginx
  clusterIP: None

执行命令:
kubectl create -f myservice.yaml

查看:
kubectl get svc -n openstack
输出结果:
[root@localhost statefulset_demo_code]# kubectl get svc -n default
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1            443/TCP   91d
nginx        ClusterIP   None                80/TCP    90s


7.2 编写statefulset

需要包含: api版本, 类型,元数据(statefulset的名称),
spec规格: 服务名称,副本数,
       模板: 元数据(标签列表),
      spec:容器列表: 容器名称,镜像,端口列表(容器端口), 挂载点列表(每个挂载名,挂载路径),
      卷申请模板列表: 元数据(名称,注释),spec规格(访问模式,资源列表:请求列表(存储))

7.2.1) 查询系统中已有的storageclass
[root@localhost statefulset_demo_code]# kubectl get storageclass
NAME                 PROVISIONER                AGE
standard (default)   k8s.io/minikube-hostpath   91d

7.2.2) 内容如下:

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"  #声明它属于哪个Headless Service.
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:   #可看作pvc的模板
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"  #存储类名,改为集群中已存在的
      resources:
        requests:
          storage: 1Gi

注意: 
将上述storageClassName后面的"standard"替换为步骤
7.2.1中查询到的系统已有的storageclass

7.2.3) 执行命令
kubectl create -f mystatefulset.yaml

输出结果如下
statefulset.apps/web created

[root@localhost statefulset_demo_code]# kubectl get statefulset -n default
NAME   READY   AGE
web    3/3     5m53s

[root@localhost statefulset_demo_code]# kubectl get pods -n default
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          6m12s
web-1   1/1     Running   0          5m6s
web-2   1/1     Running   0          5m1s

查看pvc
[root@localhost statefulset_demo_code]# kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-ac506022-91a8-11e9-9cb2-08002753dff7   1Gi        RWO            standard       8m3s
www-web-1   Bound    pvc-d40397e2-91a8-11e9-9cb2-08002753dff7   1Gi        RWO            standard       6m57s
www-web-2   Bound    pvc-d675f485-91a8-11e9-9cb2-08002753dff7   1Gi        RWO            standard       6m52s

其中pvc名称符合: -


查看endpoints
[root@localhost statefulset_demo_code]# kubectl get ep
NAME         ENDPOINTS                                   AGE
kubernetes   192.168.99.103:8443                         91d
nginx        172.17.0.7:80,172.17.0.8:80,172.17.0.9:80   35m

8 总结


statefulset用于有状态服务,例如数据库集群,pod启动有先后顺序。
需要挂载pv到pvc来做永久数据存储。

参考:
[1] kubernetes权威指南:从docker到kubernetes实践纪念版.pdf
[2] kubernetes-handbook
[3] https://www.cnblogs.com/hixiaowei/p/9783560.html
[4] https://blog.51cto.com/newfly/2140004

你可能感兴趣的:(kubernetes)