Python的virtual env和Anaconda的env可以用来做环境隔离防止不同的模型环境之间在安装时相互影响甚至因为支持包的版本冲突导致不能同时使用,但是不具备打包发布部署的功能,想要调试好一个用于训练或者模型运行调用的环境后直接可以打包发布部署到别的linux机器上,还是非docker莫属,Docker作为隔离运行和部署的工具利器,现在使用越来越多,要想在生产环境中使用同一打包文件简单高效快速部署于多个linux环境而不是一个个环境里一步一步去重复同样的步骤去安装支持软件和包、或者使用对硬盘做镜像与恢复这样的重型复制部署机制,一般还得使用docker。
Ubuntu上安装docker参考:Redirecting…, 注意针对X86和ARM64等不同平台上安装命令时不大一样的,在X86上:
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
(上面删除的是docker 19.3的安装步骤,下面是安装最新的20.10的步骤)
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
#Test
sudo docker run hello-world
安装docker后还需参考https://github.com/NVIDIA/nvidia-docker Installation Guide — NVIDIA Cloud Native Technologies documentation这里说明安装nvidia-docker2以便模型使用GPU:
#distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
# && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
# && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee # /etc/apt/sources.list.d/nvidia-docker.list
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
&& curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-docker2
sudo systemctl restart docker
#testing
#sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi
sudo docker run --rm --gpus all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi
上面的下载和添加gpgkey以及获取nvidia-docker.list数据并保存成文件的命令在网络不好时经常执行失败,可以把它拆成两步,更容易成功:
#curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
#curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu18.04/nvidia-docker.list | tee /etc/apt/sources.list.d/nvidia-docker.list
看到上面第二步有如下输出时才是成功的:
deb https://nvidia.github.io/libnvidia-container/stable/ubuntu18.04/$(ARCH) /
#deb https://nvidia.github.io/libnvidia-container/experimental/ubuntu18.04/$(ARCH) /
deb https://nvidia.github.io/nvidia-container-runtime/stable/ubuntu18.04/$(ARCH) /
#deb https://nvidia.github.io/nvidia-container-runtime/experimental/ubuntu18.04/$(ARCH) /
deb https://nvidia.github.io/nvidia-docker/ubuntu18.04/$(ARCH) /
也可以检查看/etc/apt/sources.list.d/nvidia-docker.list是否有内容了!!!然后再执行下面的apt-get update等步骤,否则会在安装nvidia-docker2时报错说找不到源。
以前还可以安装这个nvidia-container-toolkit来替代nvidia-docker2:https://github.com/NVIDIA/nvidia-container-toolkit
现在好像又退回到nvidia-docker2了
安装nvidia-docker2后,linux主机不需要安装cuda和cudnn,仅安装显卡驱动即可。
安装GPU driver参考NVIDIA Driver Installation Quickstart Guide :: NVIDIA Tesla Documentation
Jetson 序列板子上,直接使用命令sudo apt-get update && sudo apt-get install nvidia-container-toolkit安装即可,不要按照https://github.com/NVIDIA/nvidia-docker 这里的说明来安装!后者只适用于X86平台上,但是这个网页上没有任何说明,很坑人的,如果不知情,按照这个网页上的说明在Jetson上安装nvidia-container-toolkit的话,安装是可以成功的,但是创建docker容器时就会报错:
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"process_linux.go:432: running prestart hook 0 caused \\\"error running hook: exit status 1, stdout: , stderr: nvidia-container-cli: initialization error: driver error: failed to process request\\\\n\\\"\"": unknown.
ERRO[0000] error waiting for container: context canceled
使用命令
nvidia-container-cli -k -d /dev/tty info
查看错误具体的原因,可以看到:
driver.c:161] could not start driver service: load library failed: libnvidia-ml.so.1: cannot open shared object file: no such file or directory
这个 libnvidia-ml.so.1或者libnvidia-ml.so是NVML(管理方面用的库),是用于X86平台上的,jetson上没有(类似地,nvidia-smi也是在Jetson arm64平台上没有的),自然会报错!搞不清怎么回事的话,这个问题可能会坑你不少时间。
一般来说,jetson序列板子上用NVIDIA官网上下载的image烧写sd卡后里面就都安装好了docker和nvidia-container-toolkit(之前老版本的使用的是nvidia-docker2),不要自己再安装,如果不小心误删了,或者从l4t-base之类的最基础的image起进行安装,那么才需要自己安装nvidia-container-toolkit。
X86芯片的PC或Server上,nvidia container tool具体安装办法参考:https://github.com/NVIDIA/nvidia-docker (不知道为何 https://github.com/NVIDIA/nvidia-container-toolkit反而没有说明文档了,难道NVIDIA打算还是退回去在nvidia-docker2基础上继续升级?不过nvidia-container-toolkit还是可以安装的)
Installation Guide — NVIDIA Cloud Native Technologies documentation :
# Add the package repositories
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
#如果上面的步骤报错说no valid openPGP data found,可能是下载有问题,可以拆分成两步执行:
curl -O https://nvidia.github.io/nvidia-docker/gpgkey
sudo apt-key add gpgkey
再执行下面的docker源设置和安装:
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit ###或者 sudo apt-get install -y nvidia-docker2
sudo systemctl restart docker
#### Test nvidia-smi with the latest official CUDA image
docker run --gpus all nvidia/cuda:10.0-base nvidia-smi
列一下docker的常用命令和解说以备忘:
docker images #查看当前已导入或创建的所有docker image
docker ps #查看当前在运行的容器
docker ps -l #查看最近创建的容器
docker ps -a #查看所有状态的容器
docker port <container ID> #查看容器的端口映射配置
docker images | grep mmdetection #查看名字中含有mmdetection(内有mmdetection框架)的镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
mmdetection-defect-xsrt v2 e9f2895f9881 5 weeks ago 8.51GB
#创建mmdetection-defect-xsrt:v2的容器并启动运行它, -d 后台模式, --name指定容器的名字
#需要使用到GPU时,可以直接运行nvidia-docker或者docker --runtime=nvidia,或修改配置文件/etc/docker/daemon.json(不建议使用)
nvidia-docker run -itd --name mmdetection-defect25 mmdetection-defect-xsrt:v2 -v /data1/volumn/mmxsrt:/home/mmxsrt bash
或
docker run --runtime=nvidia -itd --name mmdetection-defect25 mmdetection-defect-xsrt:v2 -v /data1/volumn/mmxsrt:/home/mmxsrt bash
docker ps |grep mmdetection #查看容器
#假设容器的id为866e005aba10,可以使用下面的命令停止/启动/重启它:
nvidia-docker stop 866e005aba10
nvidia-docker start -i 866e005aba10
nvidia-docker restart 866e005aba10
#连到已经在运行的容器
docker exec -it 866e005aba10 bash
#批量停掉容器
docker stop $(docker ps -qa)
#批量删除容器(不建议使用,太危险)
docker rm $(docker ps -qa)
#批量删除image(不建议使用,太危险)
docker rmi -f $(docker images -q)
#单个删除image
docker images #查看image ID
docker rmi <image ID> #如果有image对应的container 不论在运行还是没在运行,如果不加-f参数,都会提示有对应的container存在而删不了,先用docker rm <container ID>删掉容器再删image
#查看docker images和container占用的空间
docker system df
#压缩已删除container和image的空间,以释放docker占用的空间
docker system prune
docker system prune -a #danger
#当修改了容器内容,想把修改内容保存下来时,可以先commit成一个新的镜像:
docker commit -a "xsrt.cfy" 866e005aba10 object_detection:v1
docker images |grep object_detection #查看刚才commit生成的镜像是否已生成
#docker inspect object_detection:v1 #检查一下镜像(非必须)
docker save -o /data1/volumes/object_detection.tar object_detection:v1 #将镜像保存成tar文件
#下面测试保存的镜像的tar文件是否可正常使用:
docker images |grep object_detection #假设查得object_detection镜像id是ea1e46456429
docker rmi ea1e46456429 #先删除这个镜像
docker load -i /data1/volumes/object_detection.tar #装载tar文件
docker images |grep object_detection #装载成功后查看
nvidia-docker run -itd -v /data1/volumes/mmxsrt:/home/mmxsrt --name object_detection object_detection.v1 bash #创建新镜像的容器并运行它
docker -p 可以指定要映射的IP和端口,在一个指定端口上只可以绑定一个容器,-p 支持的格式有:
hostPort:containerPort、hostIP:hostPort:containerPort、 hostIP::containerPort
要修改容器的端口映射,可以删掉容器后重新运行和创建容器时使用-p指定端口映射,例如 -p 9888:8888,
也可以修改既有容器的端口映射:
首先到dockerspace/container/下找到容器ID(例如0eaddfdda71d)对应的完整ID路径(例如dockerspace/container/0eaddfdda71d26b8bee80e561f995283ae5fe3165e1abea856829afa92c38265),然后修改里面的hostconfig.json文件里的PortBindings,例如,假若宿主机上已经有一个docker容器绑定了宿主机的8888端口,新启动一个容器也有8888端口需要映射,就不能使用默认的映射配置8888:8888,需要把容器的8888端口绑定到宿主机的其他端口,例如9888(否则启动容器时会报错,例如: Error response from daemon: Cannot restart container 0eaddfdda71d: driver failed programming external connectivity on endpoint tensorflow2.0.0-gpu-py3-jupyter
(cd0a7637dc6cb05d796c54e016ba48c61c53c188a8c51503b63162cfa558dbef): Bind for 0.0.0.0:8888 failed: port is already allocated):
"PortBindings":{"8888/tcp":[{"HostIp":"","HostPort":"9888"}]}
这里前一个端口8888是容器端口, 后一个端口9888是宿主机端口,也就是你跑docker服务所在的机器的端口
然后执行sudo systemctl restart docker 以让修改的配置生效即可。(最好先stop,修改完配置文件后再start)
修改或添加容器内外的路径映射:
需要修改容器对应的hostconfig.json文件(例如/var/lib/docker/containers/1e3572db321c6b0115f17404c0999797b0c47badd6a47661baac98f0508c4dd7/hostconfig.json):
"Binds":["/home/fychen/workspace/horizon:/open_explorer","/home/fychen/workspace/LD:/open_explorer/lddata"]
以及config.v2.json文件(例如/var/lib/docker/containers/1e3572db321c6b0115f17404c0999797b0c47badd6a47661baac98f0508c4dd7/config.v2.json):
"MountPoints":{"/open_explorer":{"Source":"/home/fychen/workspace/horizon","Destination":"/open_explorer","RW":true,"Name":"","Driver":"","Type":"bind","Propagation":"rprivate","Spec":{"Type":"bind","Source":"/home/fychen/workspace/horizon","Target":"/open_explorer"},"SkipMountpointCreation":false},"/open_explorer/lddata":{"Source":"/home/fychen/workspace/LD","Destination":"/open_explorer/lddata","RW":true,"Name":"","Driver":"","Type":"bind","Propagation":"rprivate","Spec":{"Type":"bind","Source":"/home/fychen/workspace/LD","Target":"/open_explorer/lddata"},"SkipMountpointCreation":false}}
修改完后sudo systemctl start docker启动docker服务,然后执行sudo docker start <容器id>启动容器并执行sudo docker exec -it <容器id> bash,即可看到容器内添加或修改了的路径
修改配置文件/etc/docker/daemon.json并生效:
cat /etc/docker/daemon.json
{
"default-runtime": "nvidia",
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
},
"exec-opts": ["native.cgroupdriver=systemd"]
}
在daemon.js内增加hub.docker.com的国内的镜像源:
{
"registry-mirrors": ["https://dockerhub.azk8s.cn",
"http://hub-mirror.c.163.com",
"https://puwd7yjy.mirror.aliyuncs.com",
"https://registry.docker-cn.com",
"https://docker.mirrors.ustc.edu.cn/",
"https://hub.docker.com"]
}
#修改daemon.json配置文件后要生效,重启docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
对于系统以docker image形式发布和部署时,docker镜像极其容器的更新维护可以使用这个很好的工具: Watchtower, github地址: https://github.com/containrrr/watchtower
使用这个工具的好处: With watchtower you can update the running version of your containerized app simply by pushing a new image to the Docker Hub or your own image registry. Watchtower will pull down your new image, gracefully shut down your existing container and restart it with the same options that were used when it was deployed initially.
附带记录一个不大相关的,docker里执行可视化程序的设置,可加入到~/.bashrc里以便不用每次手工设置:
apt install x11-xserver-utils
hostname # 例如: gpu-tesla-v100
export DISPLAY=:0.0
xhost +gpu-tesla-v100 #gpu-tesla-v100 being added to access control list
python demo_video.py
查看容器内控制台输出日志可以用docker logs <container_id>