什么是docker?
通过本节您可以了解Docker镜像的使用以及在本平台内关于镜像的基本操作。
Docker的起源
在曾经没有docker的日子里,假设我们正在开发一个“如何把人逗笑”的人工智能项目,这个项目需要的依赖比较多,需要很多的编译库,不同的语言包,以及很多的软件和服务。从头到尾搭建一套环境可能需要一天时间,然后开始写代码,写完代码后,我们要把代码交给另一个伙伴进行测试,这时测试伙伴如果不能使用你的开发环境(你只能给他代码,例如github),他就需要开始从头到尾搭建这套环境,这时由于操作系统差异,系统版本问题,依赖包的版本问题,各种各样的原因,最终导致测试花费大量时间最终没有运行起来代码,或者运行过程中出现大量奇葩问题。这是作为开发者的我们只能弱弱的回一句“明明在人家的环境上可以运行的”。这还仅仅是测试,到了上线,发布产品的时候或者分享给其他人使用时,他们又需要重新再搭建一遍,可想而知,变数还是挺大的。
从整个过程可以看到,不但我们重复搭建了三套环境,非常的浪费时间和效率,聪明的程序员是永远不会满足现状的,因此又到了程序员改变世界的时候了,容器技术应运而生。
Docker是一个用Go语言实现的开源项目,它是一个功能强大的开放软件平台。通过封装软件到Docker镜像(image)中,可以使软件在不同平台与环境下运行。同时人们可以将Docker镜像放在公共(Docker Hub)或私有的Docker仓库(repository)中,互相分享。这些都极大地方便了软件的安装、分享以及运行。
Docker的安装
用户可以通过下面的链接了解Docker的安装,Docker为用户提供了linux / windows / macOS 各个平台的下载安装教程,您可以选择具体的平台查看安装详情。 https://docs.docker.com/engine/install/
Linux ubuntu系统安装Docker示例
以Ubuntu系统安装为例,安装教程可以访问https://docs.docker.com/engine/install/ubuntu/
操作系统最低要求
为了保证docker能够正常运行,您的系统版本需要需要满足一下任意要求
>= Ubuntu Groovy 20.10
>= Ubuntu Focal 20.04 (LTS)
>= Ubuntu Bionic 18.04 (LTS)
>= Ubuntu Xenial 16.04 (LTS)
删除旧版本
较旧的Docker版本称为docker,docker.io或docker-engine。 如果安装了这些,请卸载它们:
sudo apt-get remove docker docker-engine docker.io containerd runc
docker依赖安装
首次安装Docker之前,需要安装Docker仓库。 之后,您可以从Docker仓库中安装和更新Docker。
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
添加Docker的官方GPG密钥,如果失效,请访问 https://docs.docker.com/engine/install/ubuntu/
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
设置稳定的镜像源,如果您的系统是x86_64 / amd64,您可以使用下方命令,其他系统请参考上方教程
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
Docker安装
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
要安装特定版本的Docker Engine,请在存储库中列出可用版本,然后选择并安装:
# 列出可用版本
apt-cache madison docker-ce
# 返回结果
# docker-ce | 5:18.09.1~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
# docker-ce | 5:18.09.0~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
# docker-ce | 18.06.1~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
# docker-ce | 18.06.0~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
使用第二列中的版本字符串安装特定版本,例如 5:18.09.1~3-0~ubuntu-xenial
sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io
安装完成后,可以测试是否安装成功:
sudo docker run hello-world
Docker工具的使用
详细的Docker工具参考教程您可以访问https://docs.docker.com/get-started/,我们在此将展示简单的命令演示。
从远程仓库拉基础镜像
# dockr pull 镜像名:版本
# 从远程仓库拉取基础镜像ubuntu:16.04
docker pull ubuntu:16.04
# 从远程仓库拉取安装有R语言的基础镜像r-base:3.6.3
docker pull r-base:3.6.3
# 从远程仓库拉取生物信息分析工具fastaqc,可访问https://biocontainers.pro/registry获取更多生物信息工具镜像
docker pull biocontainers/fastqc:v0.11.9_cv7
查看本地镜像仓库
# 查看本地镜像
docker images
# 返回结果为镜像列表,第一行为列名,包括了仓库名 / 标签 / 镜像ID / 创建时间 / 镜像大小
# REPOSITORY TAG IMAGE ID CREATED SIZE
# biocontainers/fastqc v0.11.9_cv7 e5e3008d2bd1 5 months ago 834MB
# r-base 3.6.3 cec2502269fb 11 months ago 682MB
# ubuntu 16.04 dec3202189t1 1 months ago 1.5GB
使用镜像开启一个容器并运行
# 启动r-base:3.6.3镜像,并将服务器下的/data目录与镜像容器内的/data目录相映射,并设置容器名称为DEMO
docker run -it --name DEMO -v /data:/data r-base:3.6.3 /bin/bash
# 如果要退出
exit
查看所有容器列表
docker ps -a
# 返回结果为容器列表,第一行为容器ID / 依赖的镜像名 / 命令 / 创建时间 / 状态 / 端口 / 容器名称
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 8759b9205aba r-base:3.6.3 "/bin/bash" 9 seconds ago Exited (0) 6 seconds ago DEMO
# 如果要重启退出的容器 docker start 容器ID
docker start 8759b9205aba
docker ps -a
# 返回结果为容器列表,第一行为容器ID / 依赖的镜像名 / 命令 / 创建时间 / 状态 / 端口 / 容器名称
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 8759b9205aba r-base:3.6.3 "/bin/bash" 9 seconds ago Up 4 seconds DEMO
# 如果要以命令行模式进入已经启动的容器中 docker exec -it 容器ID /bin/bash
docker exec -it 8759b9205aba /bin/bash
将容器成为新的镜像
# docker commit 容器ID 新的镜像名:设置版本
docker commit 8759b9205aba new_r:1
docker images
# 新增了一个新的工具镜像
# REPOSITORY TAG IMAGE ID CREATED SIZE
# new_r 1 1877eedea49d 1 second ago 682MB
# biocontainers/fastqc v0.11.9_cv7 e5e3008d2bd1 5 months ago 834MB
# r-base 3.6.3 cec2502269fb 11 months ago 682MB
# ubuntu 16.04 dec3202189t1 1 months ago 1.5GB
删除容器
# 如果容器正在运行,则先停止运行 docker stop 容器ID
docker stop 8759b9205aba
# 删除容器 docker rm 容器ID
docker rm 8759b9205aba
删除镜像
# 如果有容器依赖要删除的镜像,则需要先将这些容器删除,参考删除容器
# 删除镜像 docker rmi 镜像名:版本
docker rmi new_r:1
基于DockerFile安装镜像
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。由于工具的制作可能不仅依赖一些基础的镜像,我们还需要在这些基础镜像上进一步修改,添加一些程序,以实现工具的正常运行。如果通过在基础镜像中开启一个容器,然后进入容器修改后保存成一个新的镜像,这样的镜像大小会相对较大,不利于传输,我们更建议使用DockerFile制作工具镜像。具体介绍可以访问https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
什么是dockerfile?
Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。docker build命令用于从Dockerfile构建映像。
用户可以在docker build命令中使用-f标志指向文件系统中任何位置的Dockerfile,-t用来指定docker名字及版本号。例:
# docker build -f /path/to/a/Dockerfile -t fastp:latest
Dockerfile的基本结构
Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,#为 Dockerfile 中的注释。
Dockerfile文件说明
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。在这里列出了一些常用的指令。
FROM说明
FROM:指定基础镜像,必须为第一个命令格式:
例如:FROM <image>
例如:FROM <image>:<tag>
例如:FROM <image>@<digest>
示例:
FROM gatk:4.0
注: tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
MAINTAINER说明
MAINTAINER: 维护者信息格式:
MAINTAINER <name>
示例:
MAINTAINER flowhub MAINTAINER flowhub_team@flowhub.com.cn
MAINTAINER flowhub <flowhub_team@flowhub.com.cn>
ADD说明
ADD:将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget格式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
示例:
ADD hom* /mydir/
# 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/
# ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/
# 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/
# 添加 "test" 到 /absoluteDir/
COPY说明
COPY:功能类似ADD,但是是不会自动解压文件,也不能访问网络资源
RUN说明
RUN:构建镜像时执行的命令RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行格式:
RUN <command>
exec执行格式:
RUN executable param1 param2
示例:
RUN executable param1 param2
RUN apk update
RUN /etc/execfile arg1 arg1
注:RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
ENV说明
ENV:设置环境变量格式:
ENV <key> <value>
#<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ...
#可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
示例:
ENV myName John Doe
ENV myCat=fluffy
以下是一个基于dockerfile制作pytorch工具镜像的小例子:
# 设置变量后续通过${xxx}可以获取到这个变量
ARG BASE_IMAGE=ubuntu:18.04
ARG PYTHON_VERSION=3.8
# 该镜像以ubuntu:18.04构建
FROM ${BASE_IMAGE} as dev-base
# RUN命令可以理解为直接在终端中执行命令
RUN --mount=type=cache,id=apt-dev,target=/var/cache/apt \
apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
ccache \
cmake \
curl \
git \
libjpeg-dev \
libpng-dev && \
rm -rf /var/lib/apt/lists/*
RUN /usr/sbin/update-ccache-symlinks
RUN mkdir /opt/ccache && ccache --set-config=cache_dir=/opt/ccache
# 设置conda环境变量
ENV PATH /opt/conda/bin:$PATH
# 多个 FROM 指令并不是为了生成多根的层关系,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃,那么之前的FROM 又有什么意义呢?
# 每一条 FROM 指令都是一个构建阶段,多条 FROM 就是多阶段构建,虽然最后生成的镜像只能是最后一个阶段的结果,
# 但是,能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。
# 最大的使用场景是将编译环境和运行环境分离
FROM dev-base as conda
ARG PYTHON_VERSION=3.8
RUN curl -fsSL -v -o ~/miniconda.sh -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \
chmod +x ~/miniconda.sh && \
~/miniconda.sh -b -p /opt/conda && \
rm ~/miniconda.sh && \
/opt/conda/bin/conda install -y python=${PYTHON_VERSION} conda-build pyyaml numpy ipython&& \
/opt/conda/bin/conda clean -ya
FROM dev-base as submodule-update
# 设置当前的工作目录,可以理解为cd /opt/pytorch
WORKDIR /opt/pytorch
# 将当前执行命令的文件复制进docker的build目录
COPY . .
RUN git submodule update --init --recursive
FROM conda as build
WORKDIR /opt/pytorch
COPY --from=conda /opt/conda /opt/conda
COPY --from=submodule-update /opt/pytorch /opt/pytorch
RUN --mount=type=cache,target=/opt/ccache \
TORCH_CUDA_ARCH_LIST="3.5 5.2 6.0 6.1 7.0+PTX 8.0" TORCH_NVCC_FLAGS="-Xfatbin -compress-all" \
CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" \
python setup.py install
FROM conda as conda-installs
ARG PYTHON_VERSION=3.8
ARG CUDA_VERSION=11.1
ARG CUDA_CHANNEL=nvidia
ARG INSTALL_CHANNEL=pytorch-nightly
ENV CONDA_OVERRIDE_CUDA=${CUDA_VERSION}
RUN /opt/conda/bin/conda install -c "${INSTALL_CHANNEL}" -c "${CUDA_CHANNEL}" -y python=${PYTHON_VERSION} pytorch torchvision torchtext "cudatoolkit=${CUDA_VERSION}" && \
/opt/conda/bin/conda clean -ya
RUN /opt/conda/bin/pip install torchelastic
FROM ${BASE_IMAGE} as official
ARG PYTORCH_VERSION
LABEL com.nvidia.volumes.needed="nvidia_driver"
RUN --mount=type=cache,id=apt-final,target=/var/cache/apt \
apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
libjpeg-dev \
libpng-dev && \
rm -rf /var/lib/apt/lists/*
COPY --from=conda-installs /opt/conda /opt/conda
ENV PATH /opt/conda/bin:$PATH
ENV NVIDIA_VISIBLE_DEVICES all
ENV NVIDIA_DRIVER_CAPABILITIES compute,utility
ENV LD_LIBRARY_PATH /usr/local/nvidia/lib:/usr/local/nvidia/lib64
ENV PYTORCH_VERSION ${PYTORCH_VERSION}
WORKDIR /workspace
FROM official as dev
# Should override the already installed version from the official-image stage
COPY --from=build /opt/conda /opt/conda