一、背景
为了追求更好的性能,一些公共服务组件与业务之间的耦合过紧,导致在制作基础镜像时,这些基础组件都被打包进去。这样一来,当业务镜像启动后,容器内部会存在大量进程,这使得 Kubernetes 对 Pod 的管理存在很大的隐患。为了让业务容器瘦身,同时也为了更方便管理和维护基础组件,我们需要将基础组件从业务镜像中剥离,并通过 DaemonSet 将其容器化部署。然而,一些基础组件 Agent 与业务 Pod 之间需要通过共享内存的方式进行通信,因此在同一Node中跨Pod的共享内存方案成为了一个需要解决的问题。
二、测试环境
为了说明物理机上共享内存通信和Kubernetes的pod共享内存之间的区别,我们在物理机和kubernetes集群中简单进行了验证和比较,概述并验证一个多Pod间共享内存通信方案,同时提供相关测试步骤和部署文件。
三、共享内存通信
在物理机上共享内存通信是指两个进程在同一台机器上通过共享内存交换数据。通常情况下,需要使用专用的API(如System V、POSIX共享内存)来创建和管理共享内存区域。物理机上的共享内存通信需要考虑诸如内存管理、权限管理和进程间同步等问题。
3.1 测试过程
以下是一个使用System V机制进行linux之间进程通信的演示过程,分为两个进程,一个是reader进程,一个writer进程。writer进程负责初始化创建一段共享内存,reader进程启动后不断轮询将数据读取出来。
1、首先,我们需要在Linux系统上创建一个tmpfs目录来模拟共享内存。可以使用以下命令:
sudo mkdir /tmp/myshmsudo mount -t tmpfs -o size=64M myshm /tmp/myshm
此处我们可以限制 myshm 的大小
2、接下来,我们将创建两个Bash脚本文件:一个循环写入共享内存的脚本和一个循环读取共享内存的脚本。在本例中,我们将使用System V机制。
写入脚本:
#!/bin/bashkey=1234size=1024 # 创建共享内存段id=$(sudo ipcs -m | grep "0x${key}" | awk '{print $2}')if [ -z "$id" ]; then echo "Creating shared memory segment..." id=$(sudo ipcmk -M ${size})fiecho "Shared memory segment id: ${id}" # 挂载共享内存段addr=$(sudo ipcs -m | grep "0x${key}" | awk '{print $6}')if [ "$addr" == "0x00000000" ]; then echo "Attaching shared memory segment..." sudo ipcrm -m ${id} &> /dev/null sudo mount -t tmpfs -o size=${size} tmpfs /tmp/myshm addr=$(sudo shmat -v ${id} | grep "attached at" | awk '{print $5}')fiecho "Shared memory segment attached at address: ${addr}" # 写入数据count=0while true; do echo "Writing data to shared memory: ${count}" echo "Data ${count}" > /tmp/myshm/mydata count=$((count+1)) sleep 1done
读取脚本:
#!/bin/bashkey=1234 # 获取共享内存段地址addr=$(sudo ipcs -m | grep "0x${key}" | awk '{print $6}')if [ "$addr" == "0x00000000" ]; then echo "Shared memory segment not found." exit 1fiecho "Shared memory segment attached at address: ${addr}" # 读取数据while true; do data=$(sudo cat /tmp/myshm/mydata 2> /dev/null) if [ -n "$data" ]; then echo "Read data from shared memory: ${data}" sudo rm /tmp/myshm/mydata fi sleep 1done
3、运行写入脚本
chmod +x write.sh./write.sh
4、运行读取脚本:
chmod +x read.sh./read.sh3.2 结果说明
我们可以用如下命令来查看结果:
ipcs -m
POSIX和System V共享内存都是基于tmpfs来实现的。
从这里可以看到tmpfs主要有两个作用:
用于SYSV共享内存,还有匿名内存映射;这部分由内核管理,用户不可见;
用于POSIX共享内存,由用户负责mount,而且一般mount到/dev/shm ;依赖于CONFIG_TMPFS;
与之相比,Kubernetes 抽象了这些细节,提供了一个高级容器编排系统来管理容器间的通信和资源分配。它使用内置的Pod模型来管理容器集合,并自动处理网络和存储等方面的问题。
四、多 Pod 间共享内存通信
下面我们对上面的两个进程进行简单的容器化,再由k8s调度起来,并启动多个reader pod和一个writer pod,来看看是否多个reader pod可以读到writer写的数据,以达到每一个Node上数据只有一份,多pod共享的目的。
构建reader:latest镜像
# Dockerfile for writer imageFROM centos:latest # Copy the writer script into the containerCOPY write.sh /usr/local/bin # Set the writer script as the entrypointENTRYPOINT ["/usr/local/bin/write.sh"]
构建reader:latest镜像
# Dockerfile for reader imageFROM centos:latest # Copy the reader script into the containerCOPY read.sh /usr/local/bin # Set the reader script as the entrypointENTRYPOINT ["/usr/local/bin/read.sh"]
同时部署两个deployment分别部署多个reader和一个writer pod,并且在容器中挂载我们自定义的tmpfs路径/tmp/myshm。
Reader 的编排文件:
apiVersion: apps/v1kind: Deploymentmetadata: name: myshm-readerspec: replicas: 1 selector: matchLabels: app: myshm-reader template: metadata: labels: app: myshm-reader spec: hostIPC: true hostNetwork: true nodeName: grissom-03 containers: - name: reader image: reader:latest imagePullPolicy: Never env: - name: KEY value: "1234" volumeMounts: - mountPath: /tmp/myshm name: shm volumes: - name: shm hostPath: path: /tmp/myshm type: Directory
Writer 的编排文件:
apiVersion: apps/v1kind: Deploymentmetadata: name: myshm-writerspec: replicas: 1 selector: matchLabels: app: myshm-writer template: metadata: labels: app: myshm-writer spec: hostIPC: true hostNetwork: true nodeName: grissom-03 containers: - name: writer image: writer:latest imagePullPolicy: Never env: - name: KEY value: "1234" - name: SIZE value: "1024" volumeMounts: - mountPath: /tmp/myshm name: shm volumes: - name: shm hostPath: path: /tmp/myshm type: Directory
我们观察到对应的 pod 的运行情况以及 writer pod 和 reader pod日志。
五、结论
在单节点上使用 tmpfs 模拟共享内存的方式,多个 Pod 之间可以实现共享内存通信:
在 Pod 内挂载相同的 tmpfs 目录,可以将该目录作为共享内存区域,实现不同Pod之间的数据共享;
通过在一个Pod中写入数据,并在多个 Pod 中读取该数据,可以验证多个 Pod 之间可以共享数据。
这种方式可以实现在同一节点上的多个Pod之间的高速数据共享,避免了使用网络传输带来的延迟和带宽瓶颈问题。但是需要注意的是,使用tmpfs模拟的共享内存仅仅存在于内存中,并不会写入到磁盘中。因此,如果发生系统重启或容器删除等情况,数据将会丢失。此外,需要特别关注内存使用情况,防止内存占用过高导致系统性能下降或容器崩溃。
为了追求更好的性能,一些公共服务组件与业务之间的耦合过紧,导致在制作基础镜像时,这些基础组件都被打包进去。这样一来,当业务镜像启动后,容器内部会存在大量进程,这使得 Kubernetes 对 Pod 的管理存在很大的隐患。为了让业务容器瘦身,同时也为了更方便管理和维护基础组件,我们需要将基础组件从业务镜像中剥离,并通过 DaemonSet 将其容器化部署。然而,一些基础组件 Agent 与业务 Pod 之间需要通过共享内存的方式进行通信,因此在同一Node中跨Pod的共享内存方案成为了一个需要解决的问题。
二、测试环境
为了说明物理机上共享内存通信和Kubernetes的pod共享内存之间的区别,我们在物理机和kubernetes集群中简单进行了验证和比较,概述并验证一个多Pod间共享内存通信方案,同时提供相关测试步骤和部署文件。
三、共享内存通信
在物理机上共享内存通信是指两个进程在同一台机器上通过共享内存交换数据。通常情况下,需要使用专用的API(如System V、POSIX共享内存)来创建和管理共享内存区域。物理机上的共享内存通信需要考虑诸如内存管理、权限管理和进程间同步等问题。
3.1 测试过程
以下是一个使用System V机制进行linux之间进程通信的演示过程,分为两个进程,一个是reader进程,一个writer进程。writer进程负责初始化创建一段共享内存,reader进程启动后不断轮询将数据读取出来。
1、首先,我们需要在Linux系统上创建一个tmpfs目录来模拟共享内存。可以使用以下命令:
sudo mkdir /tmp/myshmsudo mount -t tmpfs -o size=64M myshm /tmp/myshm
此处我们可以限制 myshm 的大小
2、接下来,我们将创建两个Bash脚本文件:一个循环写入共享内存的脚本和一个循环读取共享内存的脚本。在本例中,我们将使用System V机制。
写入脚本:
#!/bin/bashkey=1234size=1024 # 创建共享内存段id=$(sudo ipcs -m | grep "0x${key}" | awk '{print $2}')if [ -z "$id" ]; then echo "Creating shared memory segment..." id=$(sudo ipcmk -M ${size})fiecho "Shared memory segment id: ${id}" # 挂载共享内存段addr=$(sudo ipcs -m | grep "0x${key}" | awk '{print $6}')if [ "$addr" == "0x00000000" ]; then echo "Attaching shared memory segment..." sudo ipcrm -m ${id} &> /dev/null sudo mount -t tmpfs -o size=${size} tmpfs /tmp/myshm addr=$(sudo shmat -v ${id} | grep "attached at" | awk '{print $5}')fiecho "Shared memory segment attached at address: ${addr}" # 写入数据count=0while true; do echo "Writing data to shared memory: ${count}" echo "Data ${count}" > /tmp/myshm/mydata count=$((count+1)) sleep 1done
读取脚本:
#!/bin/bashkey=1234 # 获取共享内存段地址addr=$(sudo ipcs -m | grep "0x${key}" | awk '{print $6}')if [ "$addr" == "0x00000000" ]; then echo "Shared memory segment not found." exit 1fiecho "Shared memory segment attached at address: ${addr}" # 读取数据while true; do data=$(sudo cat /tmp/myshm/mydata 2> /dev/null) if [ -n "$data" ]; then echo "Read data from shared memory: ${data}" sudo rm /tmp/myshm/mydata fi sleep 1done
3、运行写入脚本
chmod +x write.sh./write.sh
4、运行读取脚本:
chmod +x read.sh./read.sh3.2 结果说明
我们可以用如下命令来查看结果:
ipcs -m
POSIX和System V共享内存都是基于tmpfs来实现的。
从这里可以看到tmpfs主要有两个作用:
用于SYSV共享内存,还有匿名内存映射;这部分由内核管理,用户不可见;
用于POSIX共享内存,由用户负责mount,而且一般mount到/dev/shm ;依赖于CONFIG_TMPFS;
与之相比,Kubernetes 抽象了这些细节,提供了一个高级容器编排系统来管理容器间的通信和资源分配。它使用内置的Pod模型来管理容器集合,并自动处理网络和存储等方面的问题。
四、多 Pod 间共享内存通信
下面我们对上面的两个进程进行简单的容器化,再由k8s调度起来,并启动多个reader pod和一个writer pod,来看看是否多个reader pod可以读到writer写的数据,以达到每一个Node上数据只有一份,多pod共享的目的。
构建reader:latest镜像
# Dockerfile for writer imageFROM centos:latest # Copy the writer script into the containerCOPY write.sh /usr/local/bin # Set the writer script as the entrypointENTRYPOINT ["/usr/local/bin/write.sh"]
构建reader:latest镜像
# Dockerfile for reader imageFROM centos:latest # Copy the reader script into the containerCOPY read.sh /usr/local/bin # Set the reader script as the entrypointENTRYPOINT ["/usr/local/bin/read.sh"]
同时部署两个deployment分别部署多个reader和一个writer pod,并且在容器中挂载我们自定义的tmpfs路径/tmp/myshm。
Reader 的编排文件:
apiVersion: apps/v1kind: Deploymentmetadata: name: myshm-readerspec: replicas: 1 selector: matchLabels: app: myshm-reader template: metadata: labels: app: myshm-reader spec: hostIPC: true hostNetwork: true nodeName: grissom-03 containers: - name: reader image: reader:latest imagePullPolicy: Never env: - name: KEY value: "1234" volumeMounts: - mountPath: /tmp/myshm name: shm volumes: - name: shm hostPath: path: /tmp/myshm type: Directory
Writer 的编排文件:
apiVersion: apps/v1kind: Deploymentmetadata: name: myshm-writerspec: replicas: 1 selector: matchLabels: app: myshm-writer template: metadata: labels: app: myshm-writer spec: hostIPC: true hostNetwork: true nodeName: grissom-03 containers: - name: writer image: writer:latest imagePullPolicy: Never env: - name: KEY value: "1234" - name: SIZE value: "1024" volumeMounts: - mountPath: /tmp/myshm name: shm volumes: - name: shm hostPath: path: /tmp/myshm type: Directory
我们观察到对应的 pod 的运行情况以及 writer pod 和 reader pod日志。
五、结论
在单节点上使用 tmpfs 模拟共享内存的方式,多个 Pod 之间可以实现共享内存通信:
在 Pod 内挂载相同的 tmpfs 目录,可以将该目录作为共享内存区域,实现不同Pod之间的数据共享;
通过在一个Pod中写入数据,并在多个 Pod 中读取该数据,可以验证多个 Pod 之间可以共享数据。
这种方式可以实现在同一节点上的多个Pod之间的高速数据共享,避免了使用网络传输带来的延迟和带宽瓶颈问题。但是需要注意的是,使用tmpfs模拟的共享内存仅仅存在于内存中,并不会写入到磁盘中。因此,如果发生系统重启或容器删除等情况,数据将会丢失。此外,需要特别关注内存使用情况,防止内存占用过高导致系统性能下降或容器崩溃。