Kubernetes中Pod间共享内存方案

Author: xidianwangtao@gmail.comnode

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

为何要将公共基础组件Agent进行DaemonSet部署

自研的公共基础组件,好比服务路由组件、安全组件等,一般以进程方式部署在Node上并同时为Node上全部的业务提供服务,微服务及容器化以后,服务数量成百上千的增加,若是以sidecar或者打包到业务Image中继续Per Pod Per Agent的方式部署, 那么基础组件的Server端的压力可能也会成百上千的增加,风险是很大的。所以,咱们但愿能以DaemonSet方式部署这些组件的Agents。api

先说说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数量要几十倍的增加,若是业务进行了微服务拆分,这个指数会更大,这些基础组件服务端是否能承受比以往高几十倍上百倍的通讯请求,这是未知的。app

  • 若是你要全网升级某个基础组件Agent,那你可能会疯掉,你须要从新打全部业务镜像,而后全网业务要进行灰度升级。由于一个Agent的升级,致使你不得不重建业务Pod。你可能会说,基础组件Agents都会有本身的热升级方案,咱们经过它们的方案升级就行了呀,那你将引入很大麻烦:Agents的热升级由于没法被Kubernetes感知,将引起Kubernetes中集群中的数据不一致问题,那就真的要回到虚拟机或者物理机部署的玩法了。固然,这样的需求,咱们也想过经过Operator也实现,但代价太大了,并且很不CloudNative!ide

将基础组件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的隔离也是没有的,因此这一点是能够接受的。

灰度上线

对于集群中的存量业务,以前都是将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/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: 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方式共享内存。

相关文章
相关标签/搜索