# K8s 里如何优雅地使用 /dev/shm 实现容器间共享内存


## 1. 从 docker run 的 --shm-size 参数聊起

今天无意见看到算法同学写的一个 `docker run` 命令用到了 `--shm-size` 参数。没错，接着他想将这些容器跑到 K8s 里，于是我就得开始思考 `--shm-size` 在 K8s 里对应的特性是什么了。

我猜 K8s 里会有优雅的方式来实现一样的目的，最终我也找到了那个优雅的方法。不过这个过程还是稍稍坎坷，我在在网上看一圈，找到的前2种方案都有点“丑陋”，但是给人一种“只能这样”的错觉。最后看到第三条路的时候，我不禁拍了一下大腿：“我X！太帅了！”。于是乎，我想让你们也拍一下大腿，然后就有了此文。行，且往下看。

## 2. Linux 里的 /dev/shm

Linux 里有一个 `/dev/shm` 目录，shm 代表共享内存（Shared Memory）。如果这是你第一次听说 shm，可能会有点疑惑：“这东西到底是内存还是磁盘”。其实 `/dev/shm` 是一个临时文件系统，让你用操作磁盘中的文件一样的方式来将读写内存。换言之，你可以用文件读写的语法来操作“/dev/shm/a.txt”，但是这个文件本质是存在内存里的，所以读写速度会非常快。

有了 `/dev/shm`，你就可以让一个进程非常高效地存取一些临时文件，或者让多个进程非常高效的“共享内存”。这种“进程间快速共享内存”的能力对于很多机器学习框架来说非常有用。

顺口提一下，可能大伙平时接触最多的进程间通信（IPC）方式是 Socket 和 RPC，这类走网络通信的方式可以实现跨主机的 IPC，应用场景更广。不过上帝给你开了一扇门，就不会多给你开一堆窗，Socket 和 RPC 的通信效率肯定就没有“共享内存”来得快了。（可能你心里在想 http 协议，没错，http 用的更多，但是一般 http 不被归入进程间通信方式。）

## 3. Docker 对共享内存的支持

在 Linux 系统中，`/dev/shm` 的大小一般为物理内存的一半。Docker 在启动容器的时候，默认给容器设置的 `/dev/shm` 大小是 64M。（你品，你细品，是不是自定义容器内 shm 大小的需求就产生了？）

Docker 在 shm 大小的配置上提供了两种支持：

1. `docker run` 支持通过 `--shm-size=1g` 这种方式指定一个容器的 shm 大小；
2. Docker 配置 `daemon.json` 中可以通过 `"shared-memory-size": "1G"` 配置指定容器启动的默认 shm 大小。

不难猜到，`--shm-size` 参数的优先级会比 `shared-memory-size` 配置高。

## 4. K8s 里如何设置 `/dev/shm` 大小

K8s 里没有原生提供啥直观的 shm 配置方式。但是为了让应用能够尝到“共享内存”的滋味，YAML 工程师还是得给出方案。

### 4.1 方案一：修改节点的 Docker 配置

Sao 操作来了，因为 Docker 支持 `shared-memory-size` 配置，所以直接修改节点的 `daemon.json` 配置，那不就让所有 Pod 都能够用起来更大的 share memory 了？

我就不演示了。反正基本满足需求，但是冥冥之中又让你觉得哪里不太对劲，总之吧，不优雅。

### 4.2 方案二：通过 Volume 挂载一个大目录到 Pod 的 /dev/shm

不就是让 Pod 内的 `/dev/shm` 大一点嘛，那就挂卷呀：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: shared-memory-pod
spec:
  containers:
    - name: mycontainer
      image: your_image_name
      volumeMounts:
        - name: shared-memory-volume
          mountPath: /dev/shm
  volumes:
    - name: shared-memory-volume
      emptyDir: {}
```

如果用 PVC 的方式，后端存储系统支持限定卷大小的话，这里就能精准分配 shm 的大小了。总之，业务能跑起来。（你是不是已经忍不住要开喷了？忍忍。我知道，这种方式通过磁盘上的文件来实现“共享内存”，功能走通了，性能不达标。）

到这一步，我以为“方案一”挺合理的，改改 Docker 的启动参数，似乎影响不大，没啥大坏处，妥。

不过更进一步，K8s 真的不支持配置 shm？这个需求听起来合理呀，为什么没有被实现呢？继续搜一搜，我在 stackoverflow 上看到了第三种玩法。

### 4.3 方案三：用 K8s 的方式共享内存

且看这段配置：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: shared-memory-pod
spec:
  volumes:
    - name: dshm
      emptyDir:
        medium: Memory
        sizeLimit: "1Gi"
  containers:
    - name: mycontainer
      image: your_image_name
      volumeMounts:
        - name: dshm
          mountPath: /dev/shm

```

行吧，破案了。之前我也不知道 emptyDir 可以配置 `medium: Memory`。

## 5. 总结

散了吧，总结啥。大周五的，早点回家。

