最近在参加天池比赛,由于比赛需要使用阿里云容器镜像服务完成线上预测任务,所以花费了3-4天的时间了解并使用Docker完成相关镜像操作,在此分享下我学习的内容,以下是本文的目录结构:
- 介绍
- 镜像
- 容器
- 仓库
- 阿里镜像服务使用流程
- 开通镜像服务
- 构建镜像
- 检查镜像
- 推送镜像
- 其他
一、介绍
首先我们要知道为什么要使用Docker(可实现容器和镜像)?Docker是一种虚拟化方式,它与虚拟机不同之处在于:Docker不像虚拟机要提前下载好一个操作系统(Operating System, OS)才能实现虚拟化,很方便让用户把一个用户程序从一个平台直接迁移到另一个平台。举个简单的例子:你用一台电脑,从Github上fork的人脸识别项目代码要求使用环境得在cuda10.1和Pytorch1.4,然后你又要搞另外一个有关垃圾分类的算法项目,但这个项目又要求你的环境配置是cuda10。不同项目要求不同环境,这就比较让人为难,因此Docker的出现能让你本机安装环境不变的情况,虚拟化出不同的环境,且不同容器内放着不同代码,这样你能更好的进行项目迁移和扩展。云服务提供商也是用这种办法让用户更高效地调用他们平台资源。
那具体有哪些应用场景呢?例如:
- 公司要你同时进行多个不同环境配置下不同的项目。
- 算法比赛要对你代码预测时间进行考评。
- 云服务提供商要更好地利用服务器资源。
Docker包括三个概念:镜像(Image),容器(Container)和仓库(Repository)。
1. 镜像(Image)
镜像包含一个完整的ubuntu操作系统环境,相当于一个root文件系统,采用分层储存法(前一层是后一层基础,每一层的改变只会发生在自己这一层。基于基础镜像,自己还能定制化加入其它层)。
2. 容器(Container)
如果说镜像是基础层,那么容器就是它最上层的存储层。熟悉类和实例的朋友,可以把镜像看作类,容器看作实例。任何保存与容器存储层的信息都会被容器删除而丢失,因此不建议向其写入任何数据,用数据卷Volume比较好,这样数据不会随容器消失而丢失。容器的好处就是,你有镜像的环境,你再构建个容器关联到该镜像环境下,你就能把容器当作是一个简易版的Linux环境,在其中放入你的代码和数据,进入容器运行它们。
3. 仓库(Repository)
仓库就是集中存放镜像文件的场所,一个仓库可以包含同一软件不同版本的镜像,而标签tag就对应不同版本,例如:[仓库名] : [版本标签], ubuntu:16.04和ubuntu:18.04就是个很好的例子。总的来说:仓库注册服务器上往往存放多个仓库,每个仓库又包含多个镜像,每个镜像有不同的标签。 需要注意的是,如果我们使用官方Docker Hub的需要FQ,但像阿里云就提供了一些基础镜像(存放在阿里的服务器上),因此阿里云提供镜像的仓库也可以叫做是加速器,方便我们从国内下载合适的镜像。
*以上部分内容摘自:《Docker - 从入门到实践》
二、 阿里镜像服务使用流程
阿里虽然官方提供了一个简单的入门介绍 [1],但是有些简略,这里我完整地分享下我参加天池比赛提交镜像的使用流程。
1. 开通镜像服务
首先我们要建立个私有仓库去存放我们的构建的镜像文件。打开阿里云容器镜像服务,在网页右上角进入控制台,登录后可免费开通镜像托管,根据需求选择仓库地址,设置私有仓库,之后将代码源设为本地仓库,灵活度大。
创建好仓库后,一定要设置固定密码(在左侧菜单栏:默认实例 - 访问凭证 - 设置固定密码),用于拉取或上传镜像的凭证信息(避免其他人窃取你仓库的镜像)。
2. 构建镜像
现在仓库有了,我们需要构建符合我们项目代码运行环境的镜像了。
有两种构建镜像的方式,如下:
- 使用 docker pull 从仓库拉取我们需要的基础镜像到本地,再在本地上使用该镜像启动容器( docker run ),进入到容器后便可安装其他额外的依赖项或放入相关文件,最后使用 docker commit 提交更新后的镜像即可。
- 利用Dockerfile来创建镜像,其包含如何创建和修改基础镜像的指令。
个人比较偏好第二种,虽然第一种构建方式,对个人来说挺方便的,但构建完就容易忘记之前修改镜像的整个过程。而第二种方法适合在团队中有,有利于团队中互相了解镜像搭建过程,而且回头修改之前构建镜像过程也比较方便。我下面也讲下如何使用第Dockerfile来创建镜像。
首先,创建并进入到一个目录,假设这里是/taobao/,那么该目录下要有以下三类文件:
- Dockerfile:Docker镜像配置文件,内含构建镜像的各自指令。
- 项目代码文件:存放了项目代码,我这里就是上图的mmdetection_ahf,其用于目标检测任务。
- run.sh:运行代码的脚本文件。简单来说就是Linux Terminal下的要执行的命令集。
上面的解释可能会有点难理解,通过了解下面举的例子就能很好地明白上面三类文件的角色是什么了。首先我们看下Dockerfile的具体内容:
# 拉取阿里云公开的镜像,内含mmdetection文件 FROM registry.cn-shanghai.aliyuncs.com/tcc-public/mmdetection:pytorch1.3-cuda10.1-py3 # 加入本地文件 ADD . / # 到mmdetection下 WORKDIR /mmdetection_ahf # 编译mmdetection ENV FORCE_CUDA="1" RUN pip install --no-cache-dir -e . # 回到根目录 WORKDIR / # 运行sh CMD ["sh", "run.sh"]
上面这个例子是我要拉取阿里提供mmdetection的基础镜像(具体请见:阿里基础镜像地址集)。然后加入本地文件是指我把taobao/下的三类文件加入到镜像里了,之后进入到mmdetection_ahf目录下进行编译,之后回到根目录运行run.sh脚本文件。(注意:我的阿里提供的mmdetection镜像内的根目录下已经含有mmdetection的代码,我这里的mmdetection_ahf是从它这个镜像里复制到本地,然后修改的。关于怎么将镜像中的文件复制到本地的方法,我将放到 "3. 其他" 这块进行讲解)。
run.sh如果不明白,看下我下面的例子就意会了,就是你要线上怎么去运行代码得到最终的结果:
# UTF-8, 确保python的默认编码为utf-8 export PYTHONIOENCODING=utf-8 # 回到根目录 cd / # 1. 为目标检测模型准备测试数据 python mmdetection_ahf/data/test_data_prepare.py # 2. 目标检测模型模型预测结果 cd mmdetection_ahf ./tools/dist_test.sh configs/baseline_config.py mmdet/models/pretrained_models/epoch_100.pth 1 --json_out ./work_dirs/result.json
最后正式开始构建此镜像:
# 一个仓库包含一个工程不同版本的镜像,例如ubuntu:16.04和ubuntu:18.04 sudo nvidia-docker build -t registry.cn-shenzhen.aliyuncs.com/[命名空间]/[仓库名称]:[版本号]
3. 检查镜像(可选)
检查镜像该环节是可选的,它主要目的是在线下确认你的之前构建的镜像是否成功?是否能正常走完整个代码项目的流程?
首先,先查看镜像:
sudo docker images # 查看CPU镜像 sudo nvidia-docker images # 查看GPU镜像
如果镜像存在,则说明镜像构建成功了一半,那么接下来看看镜像能不能成功运行run.sh的所有命令:
# 构建完成后可先验证是否正常运行,正常运行后再进行推送。 # 需要注意的事,如果你本地工程代码里需要用到云服务器的数据例如tcdata/时,你直接验证该代码可能会报错, # 因为你没有云服务器上的数据,所以你可以先在本地搞一个类似目录结构的demo_data数据文件,使用``-v /demo_data:/tcdata``去将本地目录挂载到容器上, # 简单来说就是将本地数据demo_data文件,改成别名tcdata,挂载你之前构建的镜像上的容器上面。 # 这样就能实现线下测试,如果测试没有报错,就能放心推送镜像到云上。 # --shm-size 50G 是为了防止运行过程中尝试的中间文件过大,导致现有容器装不下 sudo nvidia-docker run --shm-size 50G -v /demo_data:/tcdata registry.cn-shenzhen.aliyuncs.com/[命名空间]/[仓库名称]:[版本号] sh run.sh
(注意:docker和nvidia-docker区别在于后者可以使用GPU。它们的安装请参考:docker和nvidia-docker安装教程。)
4. 推送镜像
如果上面线下的镜像检查环节没问题,就可以把镜像推送到我们之前建的阿里云私有仓库里去了。
sudo nvidia-docker push registry.cn-shenzhen.aliyuncs.com/[命名空间]/[仓库名称]:[版本号] .
完成!可以到比赛页面提交结果了!
三、其他
我们前两个部分只是讲了一些docker大概是怎么使用的,那么在这部分里,我整理了我在docker使用过程中,对我有帮助的一些操作命令。
1. 怎么创建空白的Dockerfile?
touch Dockerfile # 建立一个Dockerfile空配置文件
2. 怎么查看镜像和容器?
sudo docker images # 查看镜像 sudo docker ps -a # 查看容器
3. 怎么删除指定/所有的镜像和容器?
sudo docker rmi [image id] # 删除指定镜像 sudo docker rmi $(sudo docker images -q -f "dangling=true") # 删除无用镜像,即image id为<none>的镜像 sudo docker rmi $(sudo docker images) # 删除所有镜像 sudo docker stop [container id] # 暂停指定容器才能实现删除操作 sudo docker rm [container id] # 删除指定容器 sudo docker rm $(sudo docker ps -a -q) # 删除所有容器
4. 怎么创建,进入和退出容器?
# 在指定镜像上创建容器,并进入容器交互界面,--shm-size指定容器大小 sudo nvidia-docker run --shm-size 80G -t -i [image id] /bin/bash # 在容器交互界面内退出容器 exit # 进入指定容器(在sudo docker ps -a下确保该指定容器已开启) sudo nvidia-docker exec -it [container id] /bin/bash # 开启容器 sudo docker start [container id] # 退出容器 sudo docker stop [container id] # 注意如果要删除某个挂有容器的镜像,一定要先确认该容器已退出并被删除才能删除镜像。
5. 怎么在镜像和本地之间进行上传或复制操作?
# 本地上传文件到容器上 sudo docker cp [本地路径] [container id]:[容器中路径] # 从容器复制文件到本地 sudo docker cp [container id]:[容器中路径] [本地路径]