docker日常使用指南

docker日常使用指南

前言

在对外提供应用程序时,常常碰到运行环境与开发环境不一致的情况,或虽然操作系统一样,但部署时可能碰到一些系统库版本不一样导致运行问题。或对外提供库时,没有客户需要的环境,需要花时间搭建虚拟机。这一些坑,大部分情况下都能通过docker这个工具帮我们方便的解决。因此学习docker是比较有价值的,同时,docker的使用也非常的简单,核心的命令比较少。本文对自己使用中用到的一些方式进行总结,一些理解可能不完全正确,更详细的可以去看官方文档。

1.基础知识

1.1 docker是什么

docker是一种容器技术,是一种沙盒技术。它提供了一种非常遍历的打包机制,这种机制直接打包了应用运行所需要的整个操作系统,从而能够保证本地环境(开发环境)和生产环境(运行环境)的高度一致。
想了解docker更底层技术的,可以去扩展阅读:Cgroups和Namespace技术
扩展阅读2:daemon和client
image

1.2 与虚拟机(VM)的区别

image
图的左边,画出了虚拟机的工作原理。其中,名为 Hypervisor 的软件是虚拟机最主要的部分。它通过硬件虚拟化功能,模拟出了运行一个操作系统需要的各种硬件,比如 CPU、内存、I/O 设备等等。然后,它在这些虚拟的硬件上安装了一个新的操作系统,即 Guest OS。

跟真实存在的虚拟机不同,在使用 Docker 的时候,并没有一个真正的“Docker 容器”运行在宿主机里面。Docker 项目帮助用户启动的,还是原来的应用进程,只不过在创建这些进程时,Docker 为它们加上了各种各样的 Namespace 参数。

总而言之:Docker其实共享了宿主机操作系统的内核,因此内核不同的情况下,不能很好的使用docker, 比如windows上想用linux的docker就比较麻烦。Docker相对于VM比较轻量。

1.3 镜像与容器

镜像与容器的关系有点像类与对象的关系,镜像是一个静态概念,容器是一个运行时概念,容器是镜像的实例。通俗的讲,镜像就是放在硬盘上的,而容器是基于镜像跑起来后的东西。

2.安装

以下步骤默认宿主机为Ubuntu, 其它操作系统可以参考官方文档。
快捷安装脚本可通过文末的方式去获得。

2.1 在线安装

安装docker: //docs.docker.com/engine/install/ubuntu/

sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
curl -fsSL //download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] //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

验证安装结果:

sudo docker run --rm hello-world

如果宿主机没有显卡或者我们不使用显卡,到这一步就结束了安装,如果要使用显卡,还需要进一步安装 nvidia-docker2:
//docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#install-guide

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L //nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L //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-docker2
sudo systemctl restart docker
tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": ["//6kx4zyno.mirror.aliyuncs.com"],
    "exec-opts": ["native.cgroupdriver=systemd"],
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
EOF
 
sudo systemctl restart docker
sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi 可用于验证能否访问GPU

2.2 离线安装

对于宿主机不让联网的时候,可以进行离线安装,也比较方便。
可以参考这篇总结://fanfuhan.github.io/2019/11/22/docker_based_use/

3.配置

此节总结几个常见的可能修改的配置。大部分的配置都可以通过修改/etc/docker/daemon.json完成,也有例外。

3.1 镜像存储位置设置

docker镜像默认占用根目录空间,有些时候根目录空间不足,需要指定其他位置。
此时通过修改/etc/docker/daemon.json完成:

{
    "data_root":"myownpath", ### 存储位置
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

执行命令使配置生效(修改daemon.json后都需要执行):

systemctl daemon-reload
systemctl restart docker

3.2 设置镜像源

镜像源是来获取镜像的地方,类似pip源,有时候官方源速度不行或根本访问不了。这时候可以通过设置代理(下节)或设置镜像源的方式去改善。

{
    "data_root":"myownpath", ### 存储位置
     "registry-mirrors": ["//6kx4zyno.mirror.aliyuncs.com"], ### 镜像源,可以设置多个
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}

3.3 代理设置

有些时候需要挂代理去下载代码或者镜像。代理的设置分为对daemon的设置(主要影响镜像下载)和client(影响的是容器运行中的)。

3.3.1 daemon代理

docker是一个C/S架构,我们执行的docker命令实际是一种客户端,它会发起REST API到daemon(Server端),由daemon去拉取需要的镜像。此节设置的就是daemon的代理。几乎所有的daemon相关设置都可以在daemon.json中完成,但代理是个例外,这个设置需要创建:
/etc/systemd/system/docker.service.d/http-proxy.conf 文件。

[Service]
Environment="HTTP_PROXY=//proxy.example.com:80"
Environment="HTTPS_PROXY=//proxy.example.com:443"
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"  ### 设置一些ip跳过代理

3.3.2 容器代理

创建~/.docker/config.json:

{
 "proxies":
 {
   "default":
   {
     "httpProxy": "//192.168.1.12:3128",
     "httpsProxy": "//192.168.1.12:3128",
     "noProxy": "*.test.example.com,.example2.com,127.0.0.0/8"
   }
 }
}

还有一种方式是在Dockerfile或启动容器的时候设置环境变量:
image

4.docker的使用

4.1 基础使用

4.1.1 启动容器(docker run)

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

docker run有比较多的参数可以设置,完整的参见://docs.docker.com/engine/reference/commandline/run/
比较常用的是下面一些:

-d: 后台运行容器,并返回容器ID;
-i: 以交互模式运行容器,通常与 -t 同时使用;
-P: 随机端口映射,容器内部端口随机映射到主机的端口
-p: 指定端口映射,格式为:主机(宿主)端口:容器端口
-t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
--name="nginx-lb": 为容器指定一个名称;
--cpuset="0-2" or --cpuset="0,1,2": 绑定容器到指定CPU运行;
-m :设置容器使用内存最大值;
--net=bridge: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
--expose=[]: 开放一个端口或一组端口;
--volume , -v: 绑定一个卷
--rm ,退出容器后删除名字 
--restart ,重启选项,有no/always/on-failure/unless-stopped
--entrypoint ,重写容器进程的入口

比如我们执行sudo docker run -it --name test ubuntu:16.04就是以前台交互形式,以ubuntu:16.04镜像启动一个容器,第一次运行大概会输出下面这些内容:

Unable to find image 'ubuntu:16.04' locally
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete 
b51569e7c507: Pull complete 
da8ef40b9eca: Pull complete 
fb15d46c38dc: Pull complete 
Digest: sha256:454054f5bbd571b088db25b662099c6c7b3f0cb78536a2077d54adc48f00cd68
Status: Downloaded newer image for ubuntu:16.04
[email protected]:/# 

也就是第一次运行时,会去拉取镜像,然后启动容器并进入容器终端(也可以通过docker pull 去自己拉取)。进入容器终端,我们就可以像普通终端一样去安装工具,编译代码等等了。
而如果以后台形式运行,则是:

sudo docker run -d --name test ubuntu:16.04
a64fea9800522c5347eaa9feb87ee6c9d67762d81519cc58dbdeec5a8a786066

容器将以后台形式运行。那么我们怎么进入在后台运行的容器呢?

4.1.2 进入容器

进入容器有2种方式,一种是attach, 一种是

docker exec -it 容器ID/名称 bash

推荐使用这种。进入后,可输入exit退出容器终端(只是退出终端,容器并不停止)

4.1.3 停止/删除/启动/重启容器

docker stop 容器ID/名称
docker rm 容器ID/名称    ### 当容器发生重名时,我们就得删除以前的或者把新的改名
docker start 容器ID/名称
docker restart 容器ID/名称

还是非常好理解的,基本上就是英文加容器即可。
还有一个docker update可用于修改docker run时指定的参数。
基本上学会以上操作,就能跑起来一个现场的容器,创建一个隔离的环境了。但仅仅有这些还不够,因为一旦删除容器了,我们在容器里创建的内容都不在了。

4.2 进阶使用

4.2.1 持久化(挂载主机硬盘)

启动时通过-v 主机目录:容器目录选项即可将主机的目录挂载到容器中。

sudo docker run -d --name test -v /home/xxx:/root/xxx ubuntu:16.04

4.2.2 端口映射

有时候容器内启动的是一个网络服务,这个服务去监听一个接口。但它监听的实际上是容器的内部端口,直接去访问是不行的,需要映射一个主机端口到容器的端口。
通过-p 主机端口:容器端口或直接使用主机网络--net=host

docker run -d -p 5000:5000 ubuntu:16.04 
docker run -d --net=host ubuntu:16.04 

4.2.3 自定义启动命令

截止到目前,我们都没有指定过容器启动后运行什么命令,其实run的最后一个参数可以用于在启动容器后运行的命令:

docker run -d --name test ubuntu:16.04  /bin/bash
docker run -d --name test ubuntu:16.04  sh -c “/run.sh && /bin/bash” ### 多条命令拼接

4.2.4 容器状态/日志查看

> docker ps ###列出当前容器(包括已经停止的)
CONTAINER ID   IMAGE                     COMMAND                  CREATED       STATUS       PORTS                                            NAMES
b8862fc5d600   6e4bffa46d70              "kube-controller-man…"   13 days ago   Up 13 days                                                    k8s_kube-controller-manager_kube-controller-manager-alg-dev01_kube-system_5af433af822e5ae2fb8825ecf24ac394_11
8ed05c4c9210   ebac1ae204a2              "kube-scheduler --au…"   13 days ago   Up 13 days                                                    k8s_kube-scheduler_kube-scheduler-alg-dev01_kube-system_8d0b3537ceaac4d2c6bbcb377f490c26_10
f36f7818738b   66f781e54201              "nvidia-device-plugin"   2 weeks ago   Up 2 weeks                                                    k8s_nvidia-device-plugin-ctr_nvidia-device-plugin-daemonset-d2ct6_kube-system_09c990d8-83a5-4bc5-b8ad-c7bf00079f59_158850
c60e540d83d6   k8s.gcr.io/pause:3.1      "/pause"                 2 weeks ago   Up 2 weeks                          

> docker logs [-f等选项] 容器名/ID

4.2.5 对容器修改的提交

很多时候我们基于一个镜像启动了容器,在容器中我们安装了我们需要的软件,想在容器删除后也能够使用,而不是再装一次。这时就需要我们能够提交这个修改。和git类似,也是通过commit指令去提交。

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

Options:
  -a, --author string    Author (e.g., "John Hannibal Smith <[email protected]>")
  -c, --change list      Apply Dockerfile instruction to the created image
  -m, --message string   Commit message
  -p, --pause            Pause container during commit (default true)
  

例如在我们上述启动的ubuntu16.04的容器基础上,我们装了一些软件:

docker commit test ubuntu:my16.04

即可生成一个新的镜像(注意不是容器)。那么下次我们启动时直接用我们生成的这个镜像,启动的容器就包含了我们已经安装过的环境了。

docker run -d --name test ubuntu:my16.04  /bin/bash

4.3 制作镜像(Dockerfile)

4.3.1 Dockerfile编写

通过在容器内修改再提交的方式虽然能够生成镜像,但手动操作太多,而且不便于自动化。更常用的制作镜像的方式是Dockerfile。Dockerfile的基本使用比较简单,只需要掌握几个关键字:

FROM ubuntu:16.04 ### FROM: 基础镜像
ENV LANG C.UTF-8 
ENV TZ=Asia/Shanghai ### 设置容器的时区, ENV用于设置环境变量

RUN mkdir /opt/alg ### RUN: 执行一条命令,多个命令可以通过&&

ADD config/ /opt/alg/config/ ### ADD: 除有COPY的功能外,还能通过URL下载文件,并且会自动解压缩
COPY Dependency/ /opt/alg/Dependency/ ### COPY: 拷贝宿主机的文件或文件夹到镜像
COPY bin/ /opt/alg/bin/
COPY models/ /opt/alg/models/

ENTRYPOINT ["/opt/alg/config/start_service.sh" ]  ### 设置容器启动的入口,类似于main函数,在docker run中可以通过 --entrypoint=XXX 覆盖,如果有这个,那么docker run时设置的command就会被当作它的参数

除了用ENTRYPOINT去指定入口,还可以用CMD去指定,这2者也可能混用。它们之间的差异参考://blog.csdn.net/wuce_bai/article/details/88997725

4.3.2 镜像生成

有点类似于我们编译代码,docker提供的生成镜像的命令也是build:
在Dockerfile所在目录执行:

docker build . -t 镜像名:标签
例如:
docker build . -t myapp:v1

镜像生成后,我们就可以使用前文的方式去启动容器了。

4.4 镜像的保存、载入

镜像既可以上传至官方的DockerHub供人pull,也可以自行搭建私有化的镜像仓库(如harbor)。但对于普通人或日常使用,更多的可能是想将镜像保存成一个可传输的文件,然后放到其他机器,再载入。这个docker也是有对应命令支持的。

docker save [OPTIONS] IMAGE [IMAGE...]
> docker save -o my_ubuntu_v3.tar runoob/ubuntu:v3 ###将镜像runoob/ubuntu:v3 保存成my_ubuntu_v3.tar
docker load
--input , -i : 指定导入的文件,代替 STDIN。
--quiet , -q : 精简输出信息。
> docker load -i my_ubuntu_v3.tar

也可以结合其他压缩软件的命令,直接保存出压缩包:

docker save <myimage>:<tag> | gzip > <myimage>_<tag>.tar.gz
gunzip -c <myimage>_<tag>.tar.gz | docker load

4.5 显卡的使用

对于深度学习部署,很多可能需要显卡,使用docker时,需要保证显卡驱动安装,同时按上述步骤安装了nvidia-docker2。
启动容器时,增加--gpus选项即可:

sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi ### all: 所有显卡都可用
sudo docker run --rm --gpus device=0,2 nvidia/cuda:11.0-base nvidia-smi ### 0,2 卡可用
也可以用下列方式:
sudo docker run --rm --gpus '"device=0"' nvidia/cuda:11.0-base nvidia-smi ### 0卡可用

4.6 其他常用指令

docker images 列出所有镜像
docker rmi 删除镜像
docker cp 宿主机和容器间拷贝文件

还有一个不错的工具叫runlike,可以用来查看容器启动时的参数,可自行安装。

5. 使用场景

docker即可以用来发布应用,服务,这可能也是它的一种主要使用场景,也可以用来方便的创建不同的开发编译环境,比如在我们的ubuntu16.04机器上,去开发centos SDK, 甚至它也可以通过vnc去连接,获得图形化的开发环境。

一键快捷安装脚本:

链接://pan.baidu.com/s/1tb9tWEMzs6Ms1WDCzf1fqQ
提取码:58q3
若以上链接失效,关注 老司机的视觉屋,回复dockertool即可获取链接