在本地宿主机能够正常运行的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)被占用过大,严重时会出现集群雪崩的问题。
因此,在生产环境中,在前期集群设计的过程中需要多多思考,好好设计,为了后期少填坑,少吃亏。