暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【eLakehouse】组件容器化Statefulset与Deployment的选型与实践

原创 苏嘉洛 2023-11-24
399

 本文作者为中国移动云能力中心大数据团队软件开发工程师李思渊,本篇文章从Pod域名通信和存储两个方面对Deployment和Statefulset进行展开,并结合移动云消息队列Pulsar和Kafka产品的实际应用案例,帮助读者进一步了解二者的差异,以便根据不同的场景选择更为恰当的资源管理方式。

01

前言

Kubernetes是一个开源的容器编排引擎,经常用于管理容器化应用,现在也被广泛用于大数据组件的容器化改造。由于组件的多样性,为了应对不同组件的不同需求,选择合适的容器管理方式就变得十分重要。通常情况下,k8s自带的Daemonset、Deployment和Statefulset就可以涵盖绝大部分的使用场景。其中Daemonset的使用场景很容易辨别,适合于每个K8s节点启动一个Pod副本的场景。而对于多Pod的集群类应用,在使用Deployment还是Statefulset上常常陷入犹豫,本文会从实际的角度列出二者在使用上常见的区别和实践经验。

02

一般区别

Deployment和Statefulset的一般的区别相信大家或多或少都有一定了解,这里也先梳理一下。

1、Deployment应用场景:无状态服务特点:

  • Pod名称随机,每次创建都会不一样
  • Pod无顺序指定
  • 所有Pod共享存储

2、Statefulset应用场景:有状态服务特点:

  • Pod按照固定规则命名,每次创建结果一样
  • Pod有顺序指定,涉及创建、删除等操作
  • Pod有稳定唯一的网络标识
  • 每个Pod可以有独自的存储

一般情况下,我们可以根据以上区别选择Deployment或Statefulset。但是在实际使用中,为了实现功能我们需要更加精细地比较二者在某些情况下的不同,包括通信、存储以及对每个Pod管理的细粒度等,甚至有时需要跳出既定的使用框架,来达到我们的需求。

03

不同场景下的区别

3.1 Pod域名和通信
3.1.1 Statefulset和Deployment的域名

Deployment的一个特点就是,Pod名会带有特殊的随机字符串,一般来说,Deployment的通信是通过Service进行的,先连接到Service,再往下分发,然后访问到每个Pod。由此带来的问题就是,无法精确访问到想要访问的那个特定的Pod上,对于需要相互通信的组件来说则一般不会选用Deployment。

Statefulset则不同,statefulset的每个Pod都具有独立的域名,所以可以通过Headless Service精确访问到每一个Pod,同时每个Pod之间也可以相互精确访问,我们只需要按照规则,拼写出每个Pod的特定的域名,就可以实现了。拼写规则为:[Hostname].[Domain].[namespace].svc.cluster.local。

  • [Hostname]:Pod名,可以由Statefulset名加序号得到,也可以在容器内通过hostname命令得到。
  • [Domain]:一般是Service名,Statefulset中spec.serviceName的值。
  • [Namespace]:命名空间的名字。
  • svc.cluster.local:固定后缀,也可以是其他值,这取决于k8s集群的配置。

其中[Domain].[namespace].svc.cluster.local可以在容器内通过hostname -d命令得到。

示例如下:

apiVersion: apps/v1kind: Statefulsetmetadata: name: sfs namespace: devspec: serviceName: sfs-svc

提交这个yaml后,会创建一个名为sfs-0的Pod,对于这个Pod,其域名为:sfs-0.sfs-svc.dev.svc.cluster.local。

3.1.2 改变Deployment随机Pod名为固定Pod名的方法

在搭建可以构成集群且Pod需要相互通信的组件时,一般都会选用Statefulset,不过,我们可以采用其他办法使得Deployment也可以实现这种通信方式。

Deployment中可以通过spec.template.spec.hostname指定Pod名,且spec.template.spec.subdomain要设置为Service名,这样Pod就会有一个固定的域名,同时在Pod内部执行hostname和hostname -d命令也可以分别获得组成域名的值,这有利于编写脚本。

例如:

apiVersion: apps/v1kind: Deploymentmetadata: name: pod-con-2 namespace: t1spec: replicas: 1 spec: hostname: pod-con-dem subdomain: dem-domain

可以查询其域名:

IMG_256

可以ping通:

IMG_257

不过对于所有Pod来说,都会有着同样的Pod名,这也体现使用对应域名通信的时候,流量也会负载均衡到不同的Pod上。所以,如果要使用Deployment精确通信,可以设置副本数spec.replicas为1,启动多个Deployment来实现。在Statefulset的使用存在不便的场景,可以采用这种方法解决,相对于Statefulset,这种方法可以更精确地管理每个Pod,尤其是在创建删除的过程中(Statefulset是根据顺序创建删除Pod的)。

3.1.3 Statefulset对外暴露端口的通信问题

现有一个Statefulset有多个Pod副本,通过Nodeport方式将端口映射暴露到外部(loadbalancer某些情况下也存在此类问题,此处不过多讨论),有一个Nodeport的Service绑定这个Statefulset,在外部连接端口的时候,会发现网络流量负载到不同的Pod上。

例如,一个Statefulset启动了两个Pod,其中一个监听了32570端口,映射到外部为32578端口,在外部使用telnet连接的时候,可能会出现连到另一个Pod副本的情况,这时会出现连接错误。如图:

IMG_258

       

造成这一问题的原因是由Service的负载均衡导致的,这对于需要精确访问某个Pod的组件来说非常致命。好在Statefulset对每个Pod都会生成一个标识其名字的标签,由于这个标签的值可以事先预知,所以可以在Statefulset部署前就可以创建绑定每个Pod的Service,即每个Pod有自己专用的Service,这样就可以解决上面遇到的问题。

3.2.存储3.2.1 Deployment和Statefulset的共享存储模式

Deployment和Statefulset最常用的共享存储方式有两种常用的挂载方式:(1).hostpath。(2).PV/PVC。这两种挂载方式的写法大致相同,都是在spec.template.spec.containers.volumeMounts下描述在容器内的存储,再在deployment.spec.template.spec.volumes编写相对应的volume类型。

以Deployment为例:

apiVersion: apps/v1kind: Deploymentspec: template: spec: containers: - name: con volumeMounts: - name: vol-1 mountPath: /data/index1 - name: vol-2 mountPath: /data/index2 volumes: - name: vol-1 hostPath: path: /tmp/hostpath-1 type: Directory - name: vol-2 persistentVolumeClaim: claimName: dep-pvc

使用这种写法的存储,所有Pod都会共同使用这个存储目录,也就是说,不同的Pod对存储目录的操作,对于其他Pod也是可以读写的,这就会导致Pod之间的操作可能会互相影响,存在一定的安全问题。如果不同的Pod读写的文件名称都有区分的话,理论上这种问题是可以容忍的,但还是建议不要这样使用。

3.2.2 Statefulset Pod独享存储模式——volumeClaimTemplates

采用volumeClaimTemplates这种存储方式是Statefulset特有的。使用这种方式,无需手动创建PV/PVC,只需要在spec.volumeClaimTemplates下描述存储即可。在提交Statefulset的时候,Statefulset会自动给每个Pod创建各自专用的PV/PVC,这样可以避免多Pod共用一个存储目录的问题,使每个Pod独自使用一个存储,管理和使用上对比Deployment有很大的优势。

示例如下:

apiVersion: apps/v1kind: StatefulSetspec: template: spec: containers: - name: con volumeMounts: - name: vol-1 mountPath: /tmp/pvc-path-1 volumes: - name: vol-1 persistentVolumeClaim: claimName: sfs-pvc volumeClaimTemplates: - metadata: name: vol-1 spec: accessModes: [ "ReadWriteOnce" ] storageClassName: ceph-storageclass resources: requests: storage: 10Gi

Statefulset使用PC/PVC是常用的存储挂载方式,但是有一个问题需要多加注意:现有一个Statefulset使用了volumeClaimTemplates存储且挂载了Ceph网盘,包含了多个分布在不同K8s Node上的Pod,如果某个Node出现了意外宕机,此时这个Node上的Pod就会处于一种无法删除的中间状态,这就会导致有一个Pod无法正常使用。

一般情况下,我们都会尽快修复宕机Node节点使集群恢复正常,但如果在短时间内无法使得宕机Node恢复正常,转而采取重建受影响的Pod以解决问题,这个时候就要采取人工手段使Statefulset在其他正常Node上重建Pod,但是会发现PV/PVC无法正常挂载。

报错举例: 

Warning FailedMount 2m34s kubelet, node4 Unable to mount volumes for pod "web-1_default(5a734d94-a78f-11ea-b33d-384c4fcc7060)": timeout expired waiting for volumes to attach or mount for pod "default"/"web-1". list of unmounted volumes=[www]. list of unattached volumes=[www default-token-b2b49]

解决的方法就是手动去ceph集群侧修改相关watch,但是这种方式不仅繁琐,而且存在一定风险,不建议在生产环境操作。

出现这种无法挂载的情况是由Statefulset设计所导致的,有一定的合理性,不能算是一个BUG,所以如果要部署的服务无法接受这个问题,建议不要使用Statefulset。

04

在移动云产品的应用举例

移动云Pulsar是基于开源Apache Pulsar构建的企业级消息中间件服务,可应用于金融、物联网、互联网等领域。其内部采用了计算存储分离的架构,主要包含三个部分,底层存储服务Bookkeeper集群、协调服务Zookeeper集群、计算服务Pulsar Broker集群,其中Bookkeeper与Zookeeper是分别只有一个的共享集群,Pulsar Broker是多个实例集群,Bookkeeper和Pulsar Broker都依赖于Zookeeper。在产品开发的过程中,根据服务的特性采取了不同的资源管理,如下图所示。

IMG_259

Bookkeeper集群作为底层存储采用了Daemonset部署,在每个K8s Node上运行一个Pod,使用本地存储,这样可以保证存储数据分布在所有节点上。

Zookeeper集群采用Statefulset部署,这样易于管理Pod之间相互通信。存储方面使用volumeClaimTemplates并挂载Ceph,使每个Pod都有自己的存储目录,至于存储挂载导致的Pod无法漂移问题,现阶段认为出现此问题并必须要手动修改Ceph侧Watch名单的概率很低,采用Statefulset并不会提高维护成本。为了保证Pod分布在不同的Node上,设置了节点反亲和性,由于Zookeeper集群的节点数少于K8s Node数量,故不采用Daemonset。

Pulsar Broker集群采用Statefulset部署,由于Pulsar Broker是计算节点,只需要考虑通信问题,使用Statefulset易于开发和管理。


05

总结及拓展

Deployment和Statefulset都是官方给用户提供的较为方便的管理多Pod副本的方法,但在不同场景下的使用还是存在一些差异。

Statefulset对Pod的管理在通信和存储上有很大方便性,但并不适合所有场景,其中按照顺序对创建的Pod副本编号有的时候就显得不太理想,管理精细度就是一个痛点。

这个时候可以采用一个Deployment管理一个Pod副本,启动多个Deployment副本的方式进行集群管控,从而做到精细管控每一个Pod副本,也不会出现Statefulset方式下使用PV/PVC存储,在节点宕机下的Pod无法漂移的问题。这种架构方式的优点是明显的,所以移动云Kafka开发的时候就采用了管理多Deployment的方式,这很适合计算存储一体的Kafka。但是这样也会增加开发和维护的成本,例如PV/PVC就需要自己创建删除,管理上也从管理一个Statefulset变成管理多个Deployment等。

IMG_260

-  END -

关于本周文章如果大家有疑问,或者有其他感兴趣的大数据内容,欢迎大家在评论区留言,后续会持续分享大数据领域的干货知识~

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论