摘要:
一些公共服务组件在追求性能过程中,与业务耦合太紧,造成在制作基础镜像时,都会把这些基础组件都打包进去,因此当业务镜像启动后,容器里面一大堆进程,这让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该如何支持这两种类型的共享内存机制呢?
当然,安全性上做出了牺牲,但在非容器化之前IPC的隔离也是没有的,所以这一点是可以接受的。