目录
一、镜像的创建
1、基于已有镜像创建
2、基于 Dockefile 创建镜像
2.1、镜像加载原理介绍
2.2、指令详解
2.2.1、FROM
2.2.2、RUN
2.2.3、CMD
2.2.4、ENTRYPOINT
2.2.5、ENV
2.2.6 、ARG
2.2.7、LABEL
2.2.8、VOLUME
2.2.9、EXPOSE
2.2.10、WORKDIR
2.2.11、USER
2.2.12、HEALTHCHECK
2.2.13、ONBUILD
2.2.14、ADD
2.2.15、COPY
2.2.17、ARG DEBIAN_FRONTEND=noninteractive作用说明
二、导入导出镜像、导入导出容器
1、导出镜像(docker save)与导入镜像(docker load)
适用场景
导出容器(docker export)与导入容器(docker import):
基于已有容器构建镜像主要是通过docker commit
命令来构建新的镜像。
dockercommit构建进行主要有三步:
比如在 ubuntu 镜像中安装vim编辑器并存为新的镜像
1、首先本地要有镜像或者需要从Docker Hub上下载所需的基础镜像:
docker pull ubuntu:18.04
2、创建并运行容器:
# 根据 unbuntu 镜像创建一个容器,容器名docker 默认给
docker run -it ubuntu
# 根据 unbuntu 镜像创建一个容器,容器名为 test
docker run -it --name test ubuntu
3、进入该容器,并且确认没有 vim 编辑器。
# 在 docker 容器中,有的需要先安装 pip 工具才能使用 pip 安装库
docker exec -it container_name /bin/bash # 进入docker
apt-get update && apt-get install -y python3-pip
# 安装 vim 工具
apt-get install vim
4、使用commit, 将修改后的容器存为新的镜像
docker commit 容器id/容器名 新镜像名
然后就可以使用新的镜像进行容器的创建
Pytorch Docker镜像构建教程(不同系统、CUDA、Python版本)
Pytorch Docker镜像构建教程(不同系统、CUDA、Python版本) - 知乎 (zhihu.com)
示例:
docker build -t nginx:test .
#基于dockerfile文件构建镜像命令
完整的写法: docker build -f dockerfile -t nginx:new .
docker build : 基于dockerfile 构建镜像
-f :指定dockerfile 文件(默认不写的话指的是当前目录)
-t :(tag) 打标签 ——》nginx:new
. :专业说法:指的是构建镜像时的上下文环境,简单理解:指的当前目录环境中的文件
联合文件系统:
- UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。AUFS、OverlayFS 及 Devicemapper 都是一种 UnionFS。
- Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
- 特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
- 我们下载的时候看到的一层层的就是联合文件系统。
镜像加载原理
- Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是UnionFS。
- bootfs主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统。
- 在Docker镜像的最底层是bootfs,这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
- rootfs,在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
我们可以理解成一开始内核里什么都没有,操作一个命令下载debian,
这时就会在内核上面加了一层基础镜像;再安装一个emacs,会在基础镜像上叠加一层image;
接着再安装一个apache,又会在images上面再叠加一层image。
最后它们看起来就像一个文件系统即容器的rootfs。在Docker的体系里把这些rootfs叫做Docker的镜像。
但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。
当我们创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。
Dockerfile 是什么
- Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
- 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
- Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了Dockerfile,当我们需要定制自己额外的需求时,只需在Dockerfile上添加或者修改指令,重新生成 image 即可, 省去了敲命令的麻烦。
- 除了手动生成Docker镜像之外,可以使用Dockerfile自动生成镜像。Dockerfile是由多条的指令组成的文件,其中每条指令对应 Linux 中的一条命令,Docker 程序将读取Dockerfile 中的指令生成指定镜像。
驱动程序overlay2
Dockerfile结构大致分为四个部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。
Dockerfile每行支持一条指令,每条指令可携带多个参数,支持使用以“#“号开头的注释。
Docker 镜像结构的分层
镜像不是一个单一的文件,而是有多层构成。
容器其实是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层。 如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。
Docker使用存储驱动管理镜像每层内容及可读写层的容器层。
- Dockerfile中的每一个指令都会创建一个新的镜像层(是一个临时的容器,执行完成后将不再存在,再往后进行重新的创建于操作)
- 镜像层将被缓存和复用(后续的镜像曾将基于前面的每一层,每一层都会由下几层的缓存)
- 当Dockerfile的指令被修改了,复制的文件变化了,或构建镜像时指定的变量不同了,那么对应的镜像层缓存就会失效(因为后续的操作必然更改前面的镜像层)
- 某一层的镜像缓存失效了之后,它之后的镜像层就会失效(第一层不成功,那么第二层也会失效)
- 容器的修改并不会影响镜像,如果在某一层中添加一个文件,在下一层中删除它,镜像中依然会包含该文件
Dockerfile 指令 | 说明 |
---|---|
FROM | 指定基础镜像,用于后续的指令构建。 |
MAINTAINER | 指定Dockerfile的作者/维护者。(已弃用,推荐使用LABEL指令) |
LABEL | 添加镜像的元数据,使用键值对的形式。 |
RUN | 在构建过程中在镜像中执行命令。在所基于的镜像上执行命令,并提交到新的镜像中 |
CMD | 指定容器创建时的默认命令。(可以被覆盖) |
ENTRYPOINT | 设置容器创建时的主要命令。(不可被覆盖) |
EXPOSE | 声明容器运行时监听的特定网络端口。 |
ENV | 在容器内部设置环境变量。 |
ADD | 将文件、目录或远程URL复制到镜像中。 |
COPY | 将文件或目录复制到镜像中。 |
VOLUME | 为容器创建挂载点或声明卷。 |
WORKDIR | 设置后续指令的工作目录。 |
USER | 指定后续指令的用户上下文。 |
ARG | 定义在构建过程中传递给构建器的变量,可使用 "docker build" 命令设置。 |
ONBUILD | 当该镜像被用作另一个构建过程的基础时,添加触发器。 |
STOPSIGNAL | 设置发送给容器以退出的系统调用信号。 |
HEALTHCHECK | 定义周期性检查容器健康状态的命令。 |
SHELL | 覆盖Docker中默认的shell,用于RUN、CMD和ENTRYPOINT指令。 |
指定新镜像所基于的镜像,第一条指令必须为FROM指令每创建一个镜像就需要一条FROM指令。且在同一个Dockerfile中创建多个镜像时,可以使用多个FROM指令。
语法格式如下:
FROM
FROM :
其中
是可选项,如果没有选择,那么默认值为latest
。
如果不以任何镜像为基础,那么写法为:FROM scratch
但是ARG可以放在FROM之前,例如
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
用于执行后面跟着的命令行命令。有以下俩种格式:
# shell格式:就像在命令行中输入的Shell脚本命令一样。
RUN
# exec格式:就像是函数调用的格式。
RUN ["executable", "param1", "param2"]
# 例如:
RUN ["./test.php", "dev", "offline"] # 等价于 RUN ./test.php dev offline
注意:Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。例如:
FROM centos
RUN yum -y install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
以上执行会创建 3 层镜像。可简化为以下格式:
FROM centos
RUN yum -y install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
如上,以 && 符号连接命令,这样执行后,只会创建 1 层镜像。
类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:
作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
注意:如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
格式:
CMD
CMD ["<可执行文件或命令>","","",...]
CMD ["","",...] # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
推荐使用第二种格式,执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种格式运行,并且默认可执行文件是 sh。
ENTRYPOINT 指令用于设置在启动容器时始终运行的可执行文件。类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。
但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。
优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。
注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。
格式:
ENTRYPOINT ["","","",...]
Docker ENTRYPOINT 指令支持 shell 和 exec 两种模式的写法:
ENTRYPOINT ["executable", "parameter1", "parameter2"]
ENTRYPOINT command parameter1 parameter2
参考: 使用exec模式与shell模式,执行ENTRYPOINT和CMD的区别 - 知乎 (zhihu.com)
设置环境变量,会被后面的RUN使用。定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
格式:
ENV
ENV = =...
以下示例设置 NODE_VERSION = 7.2.0 , 在后续的指令中可以通过 $NODE_VERSION 引用:
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
LABEL = = = ...
构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量。
构建命令 docker build 中可以用 --build-arg <参数名>=<值> 来覆盖。
格式:
ARG <参数名>[=<默认值>]
LABEL 指令用来给镜像添加一些元数据(metadata),以键值对的形式,语法格式如下:
LABEL = = = ...
比如我们可以添加镜像的作者:
LABEL org.opencontainers.image.authors="runoob"
定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。
作用:
格式:
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。
指定新镜像加载到Docker时要开启的端口,仅仅只是声明监听的端口,在监听是可以指定是TCP还是UDP协议,默认值为TCP
作用:
格式:
EXPOSE <端口号>
# 默认情况下一般如果我们未指定协议的话,那么都是使用TCP协议,
# 当然也可以指定具体的协议,也可以同时指定TCP和UDP协议。
EXPOSE 80/tcp
EXPOSE 80/udp
指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。
格式:
WORKDIR <工作目录路径>
用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。
格式:
USER <用户名>[:<用户组>]
用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
格式:
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。
用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。
格式:
ONBUILD <其它指令>
ADD指令是指将宿主机的文件或目录复制到镜像文件系统中指定的路径,源文件复制到镜像中,源文件要与 Dockerfile 位于相同目录中,或者是一个 URL 。
注意:如果源文件是个归档文件(压缩文件),则docker会自动帮解压。但是URL下载和解压特性不能一起使用。任何压缩文件通过URL拷贝,都不会自动解压。
其语法格式有两种:
ADD ...
复制文件或者目录到容器里指定路径。COPY 只是复制,不会进行解压。COPY指令只能从执行docker build所在的主机上读取资源并复制到镜像中。而ADD指令还支持通过URL从远程服务器读取资源并复制到镜像中
在Dockerfile中使用ARG指令定义变量并为其指定一个默认值。ARG指令用于在构建过程中传递变量的值。
对于DEBIAN_FRONTEND=noninteractive,它定义了一个名为DEBIAN_FRONTEND的变量,并将其默认值设置为noninteractive。在这个上下文中,noninteractive用于禁用任何交互式提示。
作用:
在这个Dockerfile中,设置DEBIAN_FRONTEND=noninteractive是为了避免在安装过程中出现交互式提示。
默认情况下会在终端上显示交互式提示,例如要求用户确认安装某个软件包、选择配置选项等。但在Docker容器中构建镜像时,没有终端可以显示这些提示,因此需要禁用交互提示。
通过将DEBIAN_FRONTEND设置为noninteractive,在安装过程中使用默认选项而不提示用户。
这样可以确保在构建Docker镜像时,整个过程是自动化的,不需要人工干预。
通过导入和导出镜像文件的方式可以将镜像从一个Docker宿主机迁移到另一个Docker宿主机。首先,在源Docker宿主机上使用以下命令导出镜像文件:
这是一对操作,用于处理 Docker 镜像。这个操作会将所有的镜像层以及元数据打包到一个 tar 文件中。然后,你可以使用 docker load 命令将这个 tar 文件导入到任何 Docker 环境中。例如:
导出:docker save -o <保存路径>/myimage.tar myimage:latest
导入:docker load -i <路径>/myimage.tar
这种方式主要用于分享或迁移整个镜像,包括所有版本、标签和历史。
-o 参数表示输出的文件路径和名称,后面紧跟着要保存的镜像名称。例如,docker save -o /pathtosave/myimage.tar myimage:tag
-i 参数表示输入的文件路径和名称。例如,docker load -i /pathtoload/myimage.tar
假设你在你的开发环境中创建了一个新的 Docker 镜像,这个镜像包含了你的应用和所有依赖项,你已经测试了这个镜像,并且打了一个标签,称其为 “myapp:1.0”。现在你想要将这个镜像移到生产环境。这个场景中,你应该使用 docker save 和 docker load 命令。具体操作如下:
在开发环境中,你运行 docker save -o myapp_1.0.tar myapp:1.0。这将创建一个名为 “myapp_1.0.tar” 的 tar 文件,其中包含了 “myapp:1.0” 镜像的所有层和元数据。
你可以将这个 tar 文件复制到你的生产环境,然后在那里运行 docker load -i myapp_1.0.tar。这将导入 “myapp:1.0” 镜像,你可以立即在生产环境中使用它。
这也是一对操作,用于处理 Docker 容器。docker export 命令可以将运行中的容器的文件系统导出为一个 tar 文件。然后,你可以使用 docker import 命令将这个 tar 文件作为一个新的镜像导入。例如:
导出:docker export <容器ID> > mycontainer.tar
导入:docker import mycontainer.tar
这种方式主要用于分享或迁移容器的当前状态。这不包括容器的历史或元数据,如环境变量,所以它常常用于对容器进行快照。
docker export 命令后直接跟容器的 ID 或名称
docker import 的参数包括输入的文件路径和名称,以及新镜像的名称和标签。例如docker import /path/to/import/mycontainer.tar newimage:tag
总的来说,如果你想要保存整个镜像,包括它的所有历史和标签,那么你应该使用 docker save 和 docker load命令。而如果你只是想要保存一个容器的当前状态,那么你应该使用 docker export 和 docker import 命令。