Kubernetes中Pod间共享内存方案

Stella981
• 阅读 373

Kubernetes中Pod间共享内存方案

转载本文需注明出处:微信公众号EAWorld,违者必究。

摘要:

一些公共服务组件在追求性能过程中,与业务耦合太紧,造成在制作基础镜像时,都会把这些基础组件都打包进去,因此当业务镜像启动后,容器里面一大堆进程,这让Kubernetes对Pod的管理存在很大隐患。为了让业务容器瘦身,更是为了基础组件自身的管理更独立和方便,将基础组件从业务镜像中剥离并DaemonSet容器化部署。然而一些基础组件Agent与业务Pod之间通过共享内存的方式进行通信,同一Node中跨Pod的共享内存方案是首先要解决的问题。

目录:

一、为什么要将公共基础组件Agent进行DaemonSet部署

二、Linux共享内存机制

三、同一Node上夸Pod的共享内存方案

四、灰度上线

一、为什么要将公共基础组件Agent进行DaemonSet部署

自研的公共基础组件,比如服务路由组件、安全组件等,通常以进程方式部署在Node上并同时为Node上所有的业务提供服务,微服务及容器化之后,服务数量成百上千的增长,如果以sidecar或者打包到业务Image中继续Per Pod Per Agent的方式部署, 那么基础组件的Server端的压力可能也会成百上千的增长,风险是很大的。因此,我们希望能以DaemonSet方式部署这些组件的Agents。

先说说Kubernetes大行其道的今天,如果不将这些基础组件从业务Pod中剥离,存在哪些问题:

  • 业务容器中存在一大堆进程,我们在为Pod申请资源(cpu/mem request and limit)时,不仅要考虑业务应用本身的资源消耗,还要考虑这些基础组件的资源消耗。而且一旦某些Agent有Bug,比如内存泄漏,这将导致Pod牵连被重建,甚至Cgroup OOM在kill进程时,可能将业务进程kill了。

  • 违背了Kubernetes&微服务的部署最佳实践:Per Process Per Contaienr,并且业务进程在前台运行,使其与容器共生死,不然这将导致Kubernetes无法根据业务进程状态关联到容器状态,进而进行高可用管理。

  • 一个Node上运行10个Pod,那么就会有x10的基础组件数量在Node上。没有容器化之前,一个Node只要部署一个组件进程即可,容器化之后,集群中组件Agents数量要几十倍的增长,如果业务进行了微服务拆分,这个指数会更大,这些基础组件服务端是否能承受比以往高几十倍上百倍的通信请求,这是未知的。

  • 如果你要全网升级某个基础组件Agent,那你可能会疯掉,你需要重新打所有业务镜像,然后全网业务要进行灰度升级。因为一个Agent的升级,导致你不得不重建业务Pod。你可能会说,基础组件Agents都会有自己的热升级方案,我们通过它们的方案升级就好了呀,那你将引入很大麻烦:Agents的热升级因为无法被Kubernetes感知,将引发Kubernetes中集群中的数据不一致问题,那就真的要回到虚拟机或者物理机部署的玩法了。当然,这样的需求,我们也想过通过Operator也实现,但代价太大了,而且很不CloudNative!

将基础组件Agents从业务Pod中剥离,以上的问题都能解决了,架构上的解耦带来的好处无需多言。而且我们可以通过Kubernetes管理这些基础组件Agents了,享受其自愈、滚动升级等好处。

二、Linux共享内存机制

然而,理想很美好,现实很残酷。首先要解决的问题是,有些组件Agent与业务Pod之间是通过共享内存通信的,这跟Kubernetes&微服务的最佳实践背道而驰。

大家都知道,Kubernetes单个Pod内是共享IPC的,并且可以通过挂载Medium为Memory的EmptyDir Volume共享同一块内存Volume。

首先我们来了解一下Linux共享内存的两种机制:

  • POSIX共享内存(shm_open()、shm_unlink())

  • System V共享内存(shmget()、shmat()、shmdt())

其中,System V共享内存历史悠久,一般的UNIX系统上都有这套机制;而POSIX共享内存机制接口更加方便易用,一般是结合内存映射mmap使用。

mmap和System V共享内存的主要区别在于:

  • sysv shm是持久化的,除非被一个进程明确的删除,否则它始终存在于内存里,直到系统关机

  • mmap映射的内存在不是持久化的,如果进程关闭,映射随即失效,除非事先已经映射到了一个文件上

  • /dev/shm 是Linux下sysv共享内存的默认挂载点

POSIX共享内存是基于tmpfs来实现的。实际上,更进一步,不仅PSM(POSIX shared memory),而且SSM(System V shared memory)在内核也是基于tmpfs实现的。

从这里可以看到tmpfs主要有两个作用:

  • 用于SYSV共享内存,还有匿名内存映射;这部分由内核管理,用户不可见

  • 用于POSIX共享内存,由用户负责mount,而且一般mount到/dev/shm ;依赖于CONFIG_TMPFS

虽然System V与POSIX共享内存都是通过tmpfs实现,但是受的限制却不相同。也就是说 /proc/sys/kernel/shmmax只会影响SYS V共享内存,/dev/shm只会影响Posix共享内存 。实际上,System V与Posix共享内存本来就是使用的两个不同的tmpfs实例(instance)。

SYS V共享内存能够使用的内存空间只受/proc/sys/kernel/shmmax限制;而用户通过挂载的/dev/shm,默认为物理内存的1/2。

概括一下:

  • POSIX共享内存与SYS V共享内存在内核都是通过tmpfs实现,但对应两个不同的tmpfs实例,相互独立。

  • 通过/proc/sys/kernel/shmmax可以限制SYS V共享内存的最大值,通过/dev/shm可以限制POSIX共享内存的最大值(所有之和)。

三、同一Node上夸Pod的共享内存方案

基础组件Agents DaemonSet部署后,Agents和业务Pod分别在同一个Node上不同的Pod,那么Kubernetes该如何支持这两种类型的共享内存机制呢?

Kubernetes中Pod间共享内存方案

当然,安全性上做出了牺牲,但在非容器化之前IPC的隔离也是没有的,所以这一点是可以接受的。

四、灰度上线

对于集群中的存量业务,之前都是将Agents与业务打包在同一个docker image,因此需要有灰度上线方案,以保证存量业务不受影响。

  • 首先创建好对应的Kubernetes ClusterRole, SA, ClusterRoleBinding, PSP Object。关于PSP 的内容,请参考官方文档介绍pod-security-policy。

  • 在集群中任意选择部分Node,给Node打上Label(AgentsDaemonSet:YES)和Taint(AgentsDaemonSet=YES:NoSchedule)。

    $ kubectl label node $nodeName AgentsDaemonSet=YES$ kubectl taint node $nodeName AgentsDaemonSet=YES:NoSchedule

(安卓系统可左右滑动查看全部代码)

  • 部署Agent对应的DaemonSet(注意DaemonSet需要加上对应的NodeSelector和Toleration, Critical Pod Annotations), Sample as follows:

    apiVersion: apps/v1kind: DaemonSetmetadata:  name: demo-agent  namespace: kube-system  labels:    k8s-app: demo-agentspec:  selector:    matchLabels:      name: demo-agent  template:    metadata:      annotations:        scheduler.alpha.kubernetes.io/critical-pod: ""      labels:        name: demo-agent    spec:      tolerations:      - key: "AgentsDaemonSet"        operator: "Equal"        value: "YES"        effect: "NoSchedule"      hostNetwork: true      hostIPC: true      nodeSelector:        AgentsDaemonSet: "YES"      containers:      - name: demo-agent        image: demo_agent:1.0        volumeMounts:        - mountPath: /dev/shm          name: shm        resources:          limits:            cpu: 200m            memory: 200Mi          requests:            cpu: 100m            memory: 100Mi      volumes:      - name: shm        hostPath:          path: /dev/shm          type: Directory

  • 在该Node上部署不包含基础组件Agent的业务Pod,检查所有基础组件和业务是否正常工作,如果正常,再分批次选择剩余Nodes,加上Label(AgentsDaemonSet:YES)和Taint(AgentsDaemonSet=YES:NoSchedule),DaemonSet Controller会自动在这些Nodes创建这些DaemonSet Agents Pod。如此逐批次完成集群中基础组件Agents的灰度上线。

总结:


在高并发业务下,尤其还是以C/C++代码实现的基础组件,经常会使用共享内存通信机制来追求高性能,本文给出了Kubernetes Pod间Posix/SystemV共享内存方式的折中方案,以牺牲一定的安全性为代价,请知悉。当然,如果微服务/容器化改造后,基础服务的Server端确定不会有压力,那么建议以SideCar Container方式将基础服务的Agents与业务Container部署在同一Pod中,利用Pod的共享IPC特性及Memory Medium EmptyDir Volume方式共享内存。

推荐阅读

说说K8S是怎么来的,又是怎么没的

容器云之K8s自动化安装方式的选择

Docker 与 K8s 在企业基础设施服务的应用

Kubernetes中Pod间共享内存方案 关于作者:王涛,腾讯云高级工程师,西安电子科大硕士毕业,持续深耕云计算领域七年,目前在腾讯基于TKE(Tencent Kubernetes Engine)构建DevOps平台,帮助集团自研业务上云,曾在华为、唯品会、vivo从事企业私有云平台的建设工作,2014年开始专注于Kubernetes/Docker等容器技术栈在DevOps、AI Platform方向的应用,积累了大量生产经验。

Kubernetes中Pod间共享内存方案 关于EAWorld:微服务,DevOps,数据治理,移动架构原创技术分享。长按二维码关注!

本文分享自微信公众号 - EAWorld(eaworld)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这