docker日常使用指南
前言
在对外提供应用程序时,常常碰到运行环境与开发环境不一致的情况,或虽然操作系统一样,但部署时可能碰到一些系统库版本不一样导致运行问题。或对外提供库时,没有客户需要的环境,需要花时间搭建虚拟机。这一些坑,大部分情况下都能通过docker这个工具帮我们方便的解决。因此学习docker是比较有价值的,同时,docker的使用也非常的简单,核心的命令比较少。本文对自己使用中用到的一些方式进行总结,一些理解可能不完全正确,更详细的可以去看官方文档。
1.基础知识
1.1 docker是什么
docker是一种容器技术,是一种沙盒技术。它提供了一种非常遍历的打包机制,这种机制直接打包了应用运行所需要的整个操作系统,从而能够保证本地环境(开发环境)和生产环境(运行环境)的高度一致。
想了解docker更底层技术的,可以去扩展阅读:Cgroups和Namespace技术
扩展阅读2:daemon和client
1.2 与虚拟机(VM)的区别
图的左边,画出了虚拟机的工作原理。其中,名为 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: https://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 https://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] 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
验证安装结果:
sudo docker run --rm hello-world
如果宿主机没有显卡或者我们不使用显卡,到这一步就结束了安装,如果要使用显卡,还需要进一步安装 nvidia-docker2:
https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#install-guide
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
sudo apt-get update && sudo apt-get install -y nvidia-docker2
sudo systemctl restart docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://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 离线安装
对于宿主机不让联网的时候,可以进行离线安装,也比较方便。
可以参考这篇总结:https://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": ["https://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=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://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": "http://192.168.1.12:3128",
"httpsProxy": "http://192.168.1.12:3128",
"noProxy": "*.test.example.com,.example2.com,127.0.0.0/8"
}
}
}
还有一种方式是在Dockerfile或启动容器的时候设置环境变量:
4.docker的使用
4.1 基础使用
4.1.1 启动容器(docker run)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run有比较多的参数可以设置,完整的参见:https://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
root@d8324be2d956:/#
也就是第一次运行时,会去拉取镜像,然后启动容器并进入容器终端(也可以通过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 -a ###列出当前容器(包括已经停止的)
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 <hannibal@a-team.com>")
-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者也可能混用。它们之间的差异参考:https://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去连接,获得图形化的开发环境。
一键快捷安装脚本:
链接:https://pan.baidu.com/s/1tb9tWEMzs6Ms1WDCzf1fqQ
提取码:58q3
若以上链接失效,关注 老司机的视觉屋,回复dockertool即可获取链接