有关Docker安装、使用的部分相关问题

@Last Update: 2022/10/31

注: 如需在容器内使用显卡,需要安装好NVIDIA显卡驱动、cuda、nvidia-docker[方法见下文]。

0.运行环境

Docker 引擎支持在x86_64 (或amd64), armhf, and arm64 上运行.

1.卸载旧版本docker

sudo apt-get remove docker docker-engine docker.io containerd runc

执行以上命令将会卸载docker,但是docker本地保存的images, containers, volumes, and networks将会保存。

使用一下命令将清除本地保存的内容:

sudo apt-get purge docker-ce docker-ce-cli containerd.io
sudo rm -rf /var/lib/docker

2.安装docker

参考官方教程:https://docs.docker.com/engine/install/ubuntu/#uninstall-docker-engine

设置仓库

  1. 安装环境:

    sudo apt-get update
    
    sudo apt-get install \
       apt-transport-https \
       ca-certificates \
       curl \
       gnupg-agent \
       software-properties-common
    
  2. 添加Docker官方GPG key:

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    
    sudo apt-key fingerprint 0EBFCD88
    
  3. 设置稳定版仓库.

sudo add-apt-repository \
     "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
     $(lsb_release -cs) \
     stable"

3.安装docker引擎

  1. 使用以下命令安装最新版docker。

    sudo apt-get update
    sudo apt-get install docker-ce docker-ce-cli containerd.io
    
  2. 以下是安装指定版本docker的方法:

    a. 试用一下命令查看可安装的版本:

    apt-cache madison docker-ce
    

    docker-ce | 5:18.09.13-0ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    docker-ce | 5:18.09.03-0ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    docker-ce | 18.06.1ce3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    docker-ce | 18.06.0ce3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    ...

    b. 使用以下命令,安装指定版本的docker 5:18.09.1~3-0~ubuntu-xenial.

    sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io
    
  3. 运行 hello-world.

    sudo docker run hello-world
    

4.[可选]授权多用户使用docker服务

1.创建docker组:sudo groupadd docker

2.将当前用户加入docker组:sudo gpasswd -a [授权的用户名] docker

3.重启服务:sudo service docker restart

4.刷新docker成员:newgrp - docker

5.[可选]安装nvidia-docker

参考来源:https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#getting-started

执行以下命令添加仓库:

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

curl -s -L https://nvidia.github.io/nvidia-container-runtime/experimental/$distribution/nvidia-container-runtime.list | sudo tee /etc/apt/sources.list.d/nvidia-container-runtime.list

安装 nvidia-docker2 :

sudo apt-get update
sudo apt-get install -y nvidia-docker2

重启docker守护进程

sudo systemctl restart docker

测试:

sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi

6.[可选]修改docker文件存储位置

1、停止docker服务运行

2、在Ubuntu中,需要修改以下文件:/lib/systemd/system/docker.service

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

后面新加

--graph=/data/docker

/data/docker 是指新的存储位置

3、转移原先的docker的文件到新的文件夹

4、重新载入配置并重启:

sudo systemctl daemon-reload
sudo systemctl restart docker.service

7.[可选]添加国内加速源

修改/etc/docker/daemon.json(没安装过nvidia-docker,可能需要创建本文件),并加入registry-mirrors项。

一定要注意格式!!!!简单方法请参考这里!

示例(安装过nvidia-docker的情况下):

{
   "runtimes": {
       "nvidia": {
           "path": "nvidia-container-runtime",
           "runtimeArgs": []
       }
   },
   "registry-mirrors": [
    "http://hub-mirror.c.163.com",
    "https://docker.mirrors.ustc.edu.cn"]
}

重新载入配置并重启:

sudo systemctl daemon-reload
sudo systemctl restart docker.service

8.[可选]Docker容器日志查看与清理

docker容器日志导致主机磁盘空间占用严重。很占用空间,不用的日志可以清理掉了。

解决方法

8.1 找出Docker容器日志

在linux上,容器日志一般存放在/var/lib/docker/containers/container_id/下面, 以json.log结尾的文件(业务日志)很大,查看各个日志文件大小的脚本docker_log_size.sh,内容如下:

#!/bin/sh
echo "======== docker containers logs file size ========"  
logs=$(find /var/lib/docker/containers/ -name *-json.log)  
for log in $logs  
        do  
             ls -lh $log   
        done  
chmod +x docker_log_size.sh
./docker_log_size.sh

8.2 清理Docker容器日志(治标)

如果docker容器正在运行,那么使用rm -rf方式删除日志后,通过df -h会发现磁盘空间并没有释放。原因是在Linux或者Unix系统中,通过rm -rf或者文件管理器删除文件,将会从文件系统的目录结构上解除链接(unlink)。如果文件是被打开的(有一个进程正在使用),那么进程将仍然可以读取该文件,磁盘空间也一直被占用。正确姿势是cat /dev/null > *-json.log,当然你也可以通过rm -rf删除后重启docker。接下来,提供一个日志清理脚本clean_docker_log.sh,内容如下:

#!/bin/sh 
echo "======== start clean docker containers logs ========"  
logs=$(find /var/lib/docker/containers/ -name *-json.log)  
for log in $logs  
        do  
                echo "clean logs : $log"  
                cat /dev/null > $log  
        done  
echo "======== end clean docker containers logs ========"  

chmod +x clean_docker_log.sh
./clean_docker_log.sh

但是,这样清理之后,随着时间的推移,容器日志会像杂草一样,卷土重来。

8.3 设置Docker容器日志大小

  • 设置一个容器服务的日志大小上限

上述方法,日志文件迟早又会涨回来。要从根本上解决问题,需要限制容器服务的日志大小上限。这个通过配置容器docker-compose的max-size选项来实现

nginx: 
  image: nginx:1.12.1 
  restart: always 
  logging: 
    driver: “json-file” 
    options: 
      max-size: “5g” 

重启nginx容器之后,其日志文件的大小就被限制在5GB,再也不用担心了。

  • 全局设置

新建/etc/docker/daemon.json,若有就不用新建了。添加log-dirver和log-opts参数,样例如下:

vim /etc/docker/daemon.json
{
  "log-opts": {"max-size":"500m", "max-file":"3"}
}

max-size=500m,意味着一个容器日志大小上限是500M,
max-file=3,意味着一个容器有三个日志,分别是id+.json、id+1.json、id+2.json。

// 重启docker守护进程
# systemctl daemon-reload
# systemctl restart docker

注意:设置的日志大小,只对新建的容器有效。

8.4 本节参考文章

https://blog.csdn.net/xunzhaoyao/article/details/72959917
https://www.cnblogs.com/testzcy/p/7904829.html

9.[可选]在容器内安装指定版本tf/torch环境

部分低版本 TensorFlow/Pytorch无法在最新的cuda版本下运行【尤其是tf1.15以下版本,无法使用cuda11】,并且最新的显卡并没有低版本的cuda支持,因此,如需要使用显卡运行低版本的TensorFlow/Pytorch,可借助nvidia官方容器。

使用方法:

  1. 打开英伟达NGC NVIDIA NGC
  2. 搜索TensorFlow(以TensorFlow为例)
  3. 打开搜索的结果,如TensorFlow | NVIDIA NGC
  4. 打开页面中Container Release Notes(可以在页面中搜索下release note)
  5. 点击左侧,选择对应的Container Release 版本,每个页面中都会包含该版本所包含的系统版本、cuda版本、tf版本等内容。
  6. 选择适合的版本,并记录其容器版本,如18.02-py2
  7. 使用以下命令下载并启动容器
docker run --gpus all -it --rm nvcr.io/nvidia/tensorflow:xx.xx-tfx-py3

其中xx.xx-tfx-py3为以上版本号,如18.02-py2

  1. 将代码移入docker内运行

10. Dataloader中的num_workers设置与docker的shared memory相关问题

错误一

RuntimeError: DataLoader worker (pid 53617) is killed by signal: Bus error. It is possible that dataloader's workers are out of shared memory. Please try to raise your shared memory limit.

错误二

ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm).

产生错误的原因

由于在docker镜像中默认限制了shm(shared memory),然而数据处理时pythorch则使用了shm。这就导致了在运行多线程时会将超出限制的DataLoader并直接被kill掉。dataloader从RAM中找本轮迭代要用的batch,如果找到了就使用。如果没找到,就要num_worker个worker继续加载batch到内存,直到dataloader在RAM中找到目标batch。num_worker设置得大,好处是寻batch速度快,因为下一轮迭代的batch很可能在上一轮/上上一轮迭代时已经加载好了。坏处是内存开销大,也加重了CPU负担(worker加载数据到RAM的进程是CPU复制的)。

num_workers的经验设置值是自己电脑/服务器的CPU核心数,如果CPU很强、RAM也很充足,就可以设置得更大些。

解决办法

  1. 在Dataloader中将num_worker设置为0。意味着每一轮迭代时,dataloader不再有自主加载数据到RAM这一步骤(因为没有worker了),而是在RAM中找batch,找不到时再加载相应的batch。
  2. 在起Docker容器时,设置 --ipc=host 或 --shm-size 或者
docker run --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=2,3 --shm-size 8G  -it --rm dev:v1 /bin/bash

11.使用Docker训练模型挂载数据集相关的问题

Docker容器启动的时候,如果要挂载宿主机的一个目录,可以用-v参数指定。

比如启动一个centos容器,宿主机的/test目录挂载到容器的/soft目录,可通过以下方式指定:# docker run -it -v /test:/soft centos /bin/bash

这样在容器启动后,容器内会自动创建/soft的目录。通过这种方式,可以明确一点,即-v参数中,冒号":"前面的目录是宿主机目录,后面的目录是容器内目录。

一、容器目录不可以为相对路径

[root@localhost ~]# docker run -it -v /test:soft centos /bin/bash
invalid value "/test:soft" for flag -v: soft is not an absolute path
See 'docker run --help'.

直接报错,提示soft不是一个绝对路径,所谓的绝对路径,必须以下斜线“/”开头。

二、宿主机目录如果不存在,则会自动生成

如果宿主机中存在/test目录,首先删除它

[root@localhost ~]# rm -rf /test
[root@localhost ~]# ls /
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

启动容器

[root@localhost ~]# docker run -it -v /test:/soft centos /bin/bash
[root@a487a3ca7997 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  soft  srv  sys  tmp  usr  var

查看宿主机,发现新增了一个/test目录

[root@localhost ~]# ls /
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test  tmp  usr  var

三、宿主机的目录如果为相对路径时

# docker run -it -v test1:/soft centos /bin/bash

再到宿主机上查看是否新增了一个/test1目录,结果没有,查看test1目录是否在当前目录下,结果发现还是没有。

通过docker inspect命令,查看容器“Mounts”那一部分,可以看出,容器内的/soft目录挂载的是宿主机上的/var/lib/docker/volumes/test1/_data目录

    "Mounts": [
        {
            "Name": "test1",
            "Source": "/var/lib/docker/volumes/test1/_data",
            "Destination": "/soft",
            "Driver": "local",
            "Mode": "z",
            "RW": true
        }
    ],

所谓的相对路径指的是/var/lib/docker/volumes/,与宿主机的当前目录无关。

四、如果只是-v指定一个目录

启动一个容器

[root@localhost ~]# docker run -it -v /test2 centos /bin/bash
[root@ea24067bc902 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  test2  tmp  usr  var

同样使用docker inspect命令查看宿主机的挂载目录

   "Mounts": [
        {
            "Name": "96256232eb74edb139d652746f0fe426e57fbacdf73376963e3acdb411b3d73a",
            "Source": "/var/lib/docker/volumes/96256232eb74edb139d652746f0fe426e57fbacdf73376963e3acdb411b3d73a/_data",
            "Destination": "/test2",
            "Driver": "local",
            "Mode": "",
            "RW": true
        }
    ],

可以看出,同3中的结果类似,只不过,它不是相对路径的目录名,而是随机生成的一个目录名。

五、如果在容器内修改了目录的属主和属组,那么对应的挂载点也会修改

在容器内修改/soft的属主和属组,与UID有关系,UID,即“用户标识号”,是一个整数,系统内部用它来标识用户。一般情况下它与用户名是一一对应的。

六、容器销毁了,在宿主机上新建的挂载目录是否会消失?

在这里,主要验证两种情况:一、指定了宿主机目录,即 -v /test:/soft。二、没有指定宿主机目录,即-v /soft

第一种情况:

[root@localhost ~]# rm -rf /test    --首先删除宿主机的/test目录
[root@localhost ~]# ls /    --可以看到,宿主机上无/test目录
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
[root@localhost ~]# docker run -it --name=centos_test -v /test:/soft centos /bin/bash  --启动容器,为了删除方便,我用--name参数指定了容器的名字
[root@82ad7f3a779a /]# exit
exit
[root@localhost ~]# docker rm centos_test   --删除容器
centos_test
[root@localhost ~]# ls /   --发现 /test目录依旧存在
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test  tmp  usr  var

可以看出,即便容器销毁了,新建的挂载目录不会消失。进一步也可验证,如果宿主机目录的属主和属组发生了变化,容器销毁后,宿主机目录的属主和属组不会恢复到挂载之前的状态。

第二种情况,通过上面的验证知道,如果没有指定宿主机的目录,则容器会在/var/lib/docker/volumes/随机配置一个目录,那么我们看看这种情况下的容器销毁是否会导致相应目录的删除

七、挂载宿主机已存在目录后,在容器内对其进行操作,报“Permission denied”。

可通过两种方式解决:

1> 关闭selinux。

临时关闭:# setenforce 0

永久关闭:修改/etc/sysconfig/selinux文件,将SELINUX的值设置为disabled。

2> 以特权方式启动容器

指定--privileged参数

如:# docker run -it --privileged=true -v /test:/soft centos /bin/bash

posted @ 2023-06-19 14:38  Yeahchen  阅读(13)  评论(0编辑  收藏  举报