Shm On K8S—记一次Kubernetes解决Shm问题的前因后果

2,735 阅读5分钟

在本地宿主机能够正常运行的AI训练程序,使用Docker运行时却报错,最终定位问题是容器中Share Memory过小导致程序失败。

问题发现

启动容器,并进入

docker run -it  -v /mnt/mfs/traincodes/test-20200908/V0000001/PytorchSSD/:/app -v /mnt/mfs/data/:/dataset 0f3bd9e6a0c3 bash
  • 训练代码中将num_workers设置为4

运行代码,报错如下:

RuntimeError: DataLoader worker (pid 180) exited unexpectedly with exit code 1. Details are lost due to multiprocessing. Rerunning with num_workers=0 may give better error trace.

将num_workers设置为0,的确可以解决多进程的问题,但是这也大大减低了训练的速度,因此查阅资料,得知是因为共享内存(shm)设置的过小导致的。

在docker中默认的设置的shm大小为64M,如下面的/dev/shm

root@ac4af7598549:/app# df -h
Filesystem              Size  Used Avail Use% Mounted on
overlay                  17T  662G   15T   5% /
tmpfs                    64M     0   64M   0% /dev
tmpfs                    63G     0   63G   0% /sys/fs/cgroup
mfs#192.168.4.221:9421   87T  1.1T   86T   2% /app
/dev/sdb3                17T  662G   15T   5% /etc/hosts
shm                      64M     0   64M   0% /dev/shm
tmpfs                    63G   12K   63G   1% /proc/driver/nvidia
/dev/sda1               271G  162G   96G  63% /usr/bin/nvidia-smi
udev                     63G     0   63G   0% /dev/nvidia0
tmpfs                    63G     0   63G   0% /proc/acpi
tmpfs                    63G     0   63G   0% /proc/scsi
tmpfs                    63G     0   63G   0% /sys/firmware

问题定位:共享内存设置过小,num_workers>0,多进程共享的内存超出64M,导致训练程序因此报错。

定位到问题,解决问题就相对容易了

问题解决(Docker版)

放宽shm-size的大小,启动容器,并进入

docker run -it --shm-size 1024M -v /mnt/mfs/traincodes/test-20200908/V0000001/PytorchSSD/:/app -v /mnt/mfs/data/:/dataset 0f3bd9e6a0c3 bash
  • 训练代码中将num_workers仍然设置为4

继续运行,程序正常运行成功!!

查看shm的使用情况:

root@b43b495d728f:/# watch -n 1 df -h
Filesystem              Size  Used Avail Use% Mounted on
overlay                  17T  662G   15T   5% /
tmpfs                    64M     0   64M   0% /dev
tmpfs                    63G     0   63G   0% /sys/fs/cgroup
mfs#192.168.4.221:9421   87T  1.1T   86T   2% /app
/dev/sdb3                17T  662G   15T   5% /etc/hosts
shm                     1.0G  109M  916M  11% /dev/shm
tmpfs                    63G   12K   63G   1% /proc/driver/nvidia
/dev/sda1               271G  162G   96G  63% /usr/bin/nvidia-smi
udev                     63G     0   63G   0% /dev/nvidia0
tmpfs                    63G     0   63G   0% /proc/acpi
tmpfs                    63G     0   63G   0% /proc/scsi
tmpfs                    63G     0   63G   0% /sys/firmware
  • shm的使用明显超过了64M,最大的时候可以达到200多M

问题解决(Kubernetes版)

这个问题其实就是解决如何在pod中这是shm的大小?

测试1-不设置shm大小

[root@t34 volume]# vim pod-shm.yaml 

apiVersion: v1
kind: Pod
metadata:
  name: test-pd-shm
spec:
  containers:
  - image: centos
    name: centos
    command: [ "sleep", "1000000" ]
    imagePullPolicy: "IfNotPresent"
    volumeMounts:
      - mountPath: /dev/shm
        name: cache-volume
  volumes:
  - emptyDir:
      medium: Memory
    name: cache-volume
  • 设置/dev/shm挂载emptyDir目录
  • 进入pod,查看shm大小,发现shm的大小与pod所在宿主机上面shm的大小一致
[root@t34 volume]# kubectl exec -it test-pd-shm bash 
[root@test-pd-shm /]# df -h | grep shm
tmpfs                    126G     0  126G   0% /dev/shm
[root@test-pd-shm /]# 

测试2-设置shm大小

[root@t34 volume]# vim pod-shm.yaml 

apiVersion: v1
kind: Pod
metadata:
  name: test-pd-shm
spec:
  containers:
  - image: centos
    name: centos
    command: [ "sleep", "1000000" ]
    imagePullPolicy: "IfNotPresent"
    volumeMounts:
      - mountPath: /dev/shm
        name: cache-volume
  volumes:
  - emptyDir:
      medium: Memory
      sizeLimit: 128Mi
    name: cache-volume
  • 进入pod,查看shm大小,发现shm的大小与测试1的相同,即为pod所在宿主机上shm的大小.
[root@t34 volume]# kubectl exec -it test-pd-shm bash 
[root@test-pd-shm /]# df -h | grep shm
tmpfs                    126G     0  126G   0% /dev/shm
[root@test-pd-shm /]# 

难道sizeLimit设置没有生效?

带着疑问,又模拟向/dev/shm下面写数据,过程如下:

[root@t34 volume]# kubectl exec -it test-pd-shm bash 
[root@test-pd-shm /]# df -h | grep shm
tmpfs                    126G     0  126G   0% /dev/shm
## 向/dev/shm下写入100M < 128M                             
[root@test-pd-shm /]# dd if=/dev/zero  of=/dev/shm/test bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.0859482 s, 1.2 GB/s
[root@test-pd-shm /]# df -h | grep shm
tmpfs                    126G  100M  126G   1% /dev/shm
## 向/dev/shm下写入200M > 128M
[root@test-pd-shm /]# dd if=/dev/zero  of=/dev/shm/test bs=1M count=200
200+0 records in
200+0 records out
209715200 bytes (210 MB, 200 MiB) copied, 0.146763 s, 1.4 GB/s
[root@test-pd-shm /]# df -h | grep shm
tmpfs                    126G  200M  126G   1% /dev/shm

[root@test-pd-shm /]# command terminated with exit code 137
  • 向/dev/shm写入100M,容器正常运行
  • 向/dev/shm写入200M,容器等上几秒之后以137的退出码退出。(137表明容器是被kill掉的,多数是因为资源不足)
  • 查看pod状态,发现是因为shm超过设置的128Mi导致pod被驱逐,又重新调度。
[root@t34 volume]# kubectl describe pod test-pd-shm

...

Events:
  Type     Reason     Age    From               Message
  ----     ------     ----   ----               -------
  Normal   Pulled     10m    kubelet, t32       Container image "centos" already present on machine
  Normal   Created    10m    kubelet, t32       Created container centos
  Normal   Started    10m    kubelet, t32       Started container centos
  Warning  Evicted    9m3s   kubelet, t32       Usage of EmptyDir volume "cache-volume" exceeds the limit "128Mi".
  Normal   Killing    9m3s   kubelet, t32       Stopping container centos
  Normal   Scheduled  6m39s  default-scheduler  Successfully assigned default/test-pd-shm to t32

总结

在机器学习训练或需要高效率运行的其他应用场景中,应该根据实际情况调整shm的大小。设置太小,不能够满足高效率的要求,但是,一味地设置过大,容易导致宿主机内存(默认情况下,shm是宿主机内存的1/2)被占用过大,严重时会出现集群雪崩的问题。

因此,在生产环境中,在前期集群设计的过程中需要多多思考,好好设计,为了后期少填坑,少吃亏。