3

statefulset详解及为何结合headless service部署有状态应用 - 渡边灬

 1 year ago
source link: https://www.cnblogs.com/zhangpeiyao/p/17099494.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.1 有状态应用管理statefulset

StatefulSet(有状态集,缩写为sts)常用于部署有状态的且需要有序启动的应用程序,比如在进行SpringCloud项目容器化时,Eureka的部署是比较适合用StatefulSet部署方式的,可以给每个Eureka实例创建一个唯一且固定的标识符,并且每个Eureka实例无需配置多余的Service,其余Spring Boot应用可以直接通过Eureka的Headless Service即可进行注册。

1.1.1 statefulset基本概念

- StatefulSet主要用于管理有状态应用程序的工作负载API对象。比如在生产环境中,可以部署ElasticSearch集群、MongoDB集群或者需要持久化的RabbitMQ集群、Redis集群、Kafka集群和ZooKeeper集群等。
- 和Deployment类似,一个StatefulSet也同样管理着基于相同容器规范的Pod。不同的是,StatefulSet为每个Pod维护了一个粘性标识。这些Pod是根据相同的规范创建的,但是不可互换,每个Pod都有一个持久的标识符,在重新调度时也会保留,一般格式为StatefulSetName-Number。
- 比如定义一个名字是Redis-Sentinel的StatefulSet,指定创建三个Pod,那么创建出来的Pod名字就为Redis-Sentinel-0、Redis-Sentinel-1、Redis-Sentinel-2。
- 而StatefulSet创建的Pod一般使用Headless Service(无头服务)进行通信,和普通的Service的区别在于Headless Service没有ClusterIP,它使用的是Endpoint进行互相通信,Headless一般的格式为:
- statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local。

说明:
serviceName为Headless Service的名字,创建StatefulSet时,必须指定Headless Service名称;
0..N-1为Pod所在的序号,从0开始到N-1;
statefulSetName为StatefulSet的名字;
namespace为服务所在的命名空间;
.cluster.local为Cluster Domain(集群域)

假如公司某个项目需要在Kubernetes中部署一个主从模式的Redis,此时使用StatefulSet部署就极为合适,因为StatefulSet启动时,只有当前一个容器完全启动时,后一个容器才会被调度,并且每个容器的标识符是固定的,那么就可以通过标识符来断定当前Pod的角色

image
image

1.1.2 StatefulSet注意事项

一般StatefulSet用于有以下一个或者多个需求的应用程序:

  1. 需要稳定的独一无二的网络标识符。
    
  2. 需要持久化数据。
    
  3. 需要有序的、优雅的部署和扩展。
    
  4. 需要有序的自动滚动更新。
    
    如果应用程序不需要任何稳定的标识符或者有序的部署、删除或者扩展,应该使用无状态的控制器部署应用程序,比如Deployment或者ReplicaSet。
    StatefulSet是Kubernetes 1.9版本之前的beta资源,在1.5版本之前的任何Kubernetes版本都没有。
    Pod所用的存储必须由PersistentVolume Provisioner(持久化卷配置器)根据请求配置StorageClass,或者由管理员预先配置,当然也可以不配置存储。
    为了确保数据安全,删除和缩放StatefulSet不会删除与StatefulSet关联的卷,可以手动选择性地删除PVC和PV)。
    StatefulSet目前使用Headless Service(无头服务)负责Pod的网络身份和通信,需要提前创建此服务。
    删除一个StatefulSet时,不保证对Pod的终止,要在StatefulSet中实现Pod的有序和正常终止,可以在删除之前将StatefulSet的副本缩减为0

为什么要用headless service+statefulSet部署有状态应用?

Headless Services介绍

Headless Services是一种特殊的service,其spec:clusterIP表示为None,这样在实际运行时就不会被分配ClusterIP。也被称为无头服务。

1、headless Service和普通Service的区别

headless不分配clusterIP

headless service可以通过解析service的DNS,返回所有Pod的地址和域名(statefulSet部署的Pod才有域名)

headless service会为关联的Pod分配一个域:
service-name.namespace-name.svc.cluster.local

普通的service,只能通过解析service的DNS返回service的ClusterIP

2、statefulSet和Deployment控制器的区别

statefulSet下的Pod有DNS地址,通过解析Pod的DNS可以返回Pod的IP

StatefulSet会为关联的Pod保持一个不变的Pod Name
statefulset中Pod的hostname格式为
statefulsetname-(pod序号)

而deployment下的Pod没有具体的域名,想访问Pod都是通过普通service来负载均衡到后端pod,无法指定访问具体哪个Pod

3、普通Service解析service的DNS结果

Service的ClusterIP工作原理:一个service可能对应一组endpoints(所有pod的地址+端口),client访问ClusterIP,通过iptables或者ipvs转发到Real Server(Pod)。

StatefulSet+headless service会为关联的每个Pod都分配一个具体的域名:
Pod-Name.service-name.namespace-name.svc.cluster.local

1.1.3 定义一个StatefulSet资源文件

image

点击查看代码

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None  #这里可以有IP,也可以无IP,推荐无IP,也就是 无头service
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.15.2
        ports:
        - containerPort: 80
          name: web
image
image

创建个Busybox演示如何通过无头service名字来进行网络访问

image
image

通过nslookup来解析web-0.nginx(sts名称+无头service名称),可以得到这个Pod的ip地址:10.244.32.152

image

image
进入集群busybox容器对web-0.nginx进行Ping和wget发现都是通的

image
image

1.1.4 缩容扩容StatefulSet
查看sts lable标签
image
通过标签对指定sts进行监听

image

对sts进行扩容操作
image
可以看到sts启动的顺序是严格按照序号来进行启动的,同理缩容的话会严格按照pod的序号倒序进行删除

image
image

1.1.4 StatefulSet更新策略

1. RollingUpdate 更新策略

  • 默认更新策略RollingUpdate

点击查看代码

updateStrategy:
     rollingUpdate:
       partition: 0       #不更新小于 N 的副本
     type: RollingUpdate

更新镜像版本

image

image

开启另一个窗口,观察Pod更新过程,可以看到他的更新顺序是倒序的

image

2. Ondelete 更新策略 [适用于灰度发布]

更改更新策略为Ondelete

点击查看代码

 修改:
   updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate
 
 改为:
   updateStrategy:
    type: OnDelete   #修改为OnDelete更新模式并保存,该更新策略是,删除时才会进行更新

image
更新镜像版本,并观察更新过程

image

只有进行delete pod删除操作的时候,pod才会被更新

image
image

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK