你是否经常会遇到这样的困难:处理不同进程的应用程序时,需求方会要求包含所有进程以实现更多隔离。在这种情况下,一个常见的问题是:如何在同一 Node 中的 Pod 间实现共享内存。王涛是腾讯云的高级工程师,在本文中,他将阐述一种在 Pod 间利用 Posix/SystemV 来实现共享内存的解决方案,一起来看看吧。
一些公共服务组件在追求性能的过程中,大多会出现与业务耦合过紧的情况。同时,工程师们在制作基础镜像时,会把这些基础组件都打包进去。因此当业务镜像启动后,容器内部就会存在大量进程,使得 Kubernetes 对 Pod 的管理产生隐患。
为了实现业务容器瘦身,更是为了基础组件自身的管理更加独立。工程师们可以将基础组件从业务镜像中剥离并进行 DaemonSet 容器化部署。但是,一些基础组件 Agent 与业务 Pod 之间是通过共享内存的方式进行通信的,所以整个部署的首要问题是:在同一 Node 中,Pod 之间如何去实现共享内存?
通过阅读本文你将了解:
为什么要将公共基础组件 Agent 进行 DaemonSet 部署
工程师们自研的公共基础组件,比如服务路由组件、安全组件等,通常以进程的方式部署在 Node 上,并为所有的业务提供服务。但是在上微服务和容器化之后,如果工程师还是惯用 sidecar 或者将组件打包到业务 Image 中,继续以 Per Pod Per Agent 的方式部署,基础组件 Server 端的压力将会成百上千的增长。
这样做的结果对于整个部署来说风险很大。因此,工程师们会更希望能以 DaemonSet 方式部署这些组件的 Agents。
先说说如果不将这些基础组件从业务的 Pod 中剥离,业务会存在哪些问题:
如果将基础组件 Agent 从业务 Pod 中剥离,那么以上的问题就都迎刃而解,并且架构上的解耦会带来很多好处。工程师们也可以通过 Kubernetes 管理这些基础组件的 Agents,并享受其自愈、滚动升级等功能。
Linux 共享内存机制
理想很美好,现实很残酷!在整个业务中,工程师们首先要解决的问题是:有些组件 Agent 与业务 Pod 之间是通过共享内存通信的,这跟 Kubernetes&微服务的最佳实践方案背道而驰。
众所周知,Kubernetes 单个 Pod 内是共享 IPC 的,并且它们可以通过挂载 Medium,与 Memory 中的 EmptyDir Volume 共享同一块内存 Volume。
Linux 共享内存的两种机制:
其中,System V 共享内存历史悠久,一般的 UNIX 系统上都有这套机制;而 POSIX 共享内存机制接口更加方便易用,一般是结合内存映射 mmap 使用。
mmap 和 System V 共享内存的主要区别在于:
POSIX 共享内存是基于 tmpfs 实现的。在内核中,不仅 PSM(POSIX shared memory),SSM(System V shared memory)也是基于 tmpfs 来实现的。
tmpfs 主要有两个作用:
虽然 System V 与 POSIX 共享内存都是通过 tmpfs 来实现的,但是它们所受的限制却不相同。/proc/sys/kernel/shmmax 只会影响 SystemV 的共享内存。/dev/shm 只会影响 POSIX 的共享内存 。也就是说,System V 与 POSIX 的共享内存使用的是两个不同的 tmpfs 实例(instance)。SystemV 共享内存能够使用的内存空间只受 /proc/sys/kernel/shmmax 的限制;而用户通过挂载的 /dev/shm,默认为物理内存的 1/2。
概括一下:
同一 Node 上跨 Pod 的共享内存方案
当基础组件 Agents 通过 DaemonSet 部署后,Agents 和业务进程就在 Node 上的不同 Pod 中。此时,Kubernetes 该如何支持跨 Pod 的共享内存场景呢?
如上图所示,在整个方案中,业务对 POSIX Type IPC 的共享支持是通过挂载 /dev/shm 来实现的;对 SystemV Type IPC 的共享支持是通过 Share HostIPC 来实现的。但是这样的做法会使存于共享内存中的信息被其他 Pod 误操作。在业务安全性上,它们没有被完全隔离。但其实在非容器化之前,各个业务共享内存也存在同样的风险,所以这一点对于用户来说是可以接受的。
如果工程师们确实发现有些业务存在共享内存的使用冲突,也可以再通过以下规则进行隔离部署:
requiredDuringSchedulingIgnoredDuringExecution PodAntiAffinity
灰度上线
对于集群中的存量业务,之前都是将 Agents 与业务打包在同一 docker image 中,因此需要有灰度上线方案,以保证存量业务不受影响。
$ kubectl label node $nodeName AgentsDaemonSet=YES
$ kubectl taint node $nodeName AgentsDaemonSet=YES:NoSchedule
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: demo-agent
namespace: kube-system
labels:
k8s-app: demo-agent
spec:
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: truehostIPC: truenodeSelector:
AgentsDaemonSet: "YES"containers:
- name: demo-agent
image: demo_agent:1.0volumeMounts:
- 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
总结
在高并发业务下,尤其还是以 C/C++ 代码实现的基础组件,工程师们经常会使用共享内存通信机制来追求高性能的标准。本文给出了 Kubernetes Pod 间 Posix/SystemV 共享内存方式的折中方案(以牺牲一定的安全性为代价)。
当然,业务在微服务/容器化部署后,基础服务的 Server 端是不会有压力的。在此,我建议以 SideCar Container 方式将基础服务的 Agents 与业务 Container 部署在同一 Pod 中,利用 Pod 的共享 IPC 特性及 Memory Medium EmptyDir Volume 方式共享内存。
作者:王涛(腾讯云) 来源:EAWorld
本文分享自 kubernetes中文社区 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!