前言
学习本文需要一些了解Docker的概念以及一些名词。
个人网站:https://linzyblog.netlify.app/
一、Docker概述
1、Docker简介
Docker 镜像是通过读取Dockerfile来构建镜像文件的
。Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令来组装镜像,每条指令都是独立运行的,并会创建一个新的镜像层
。使用docker build 命令用户用户可以创建一个自动构建,该构建可以连续执行几个命令行指令。
2、为什么要用Dockerfile?
为什么要用Dockerfile?这个问题的本身其实是说为什么我们要自定义镜像,明明Docker Hub上有这么多镜像可以用,我们还要自己费心思做镜像。
主要原因:Docker Hub上许多官方镜像只是基础包,很多功能都没有,需要我们自己对官方镜像做扩展,以打包成我们生产应用的镜像。
参考我的第一篇文章Docker入门篇:https://blog.csdn.net/weixin_46618592/article/details/126317527?spm=1001.2014.3001.5501
二、docker build工作原理
1、镜像构建过程
docker build命令从 Dockerfile 和 context 构建一个镜像。构建的 context 是位于指定位置PATH或URL的一组文件。PATH是本地文件系统上的一个目录。URL是Git存储库位置。
docker build [OPTIONS] PATH | URL | -
完整的镜像架构图:
构建context是递归处理的。每条指令都是独立运行的,并会创建一个新的镜像层。
例子中docker build后面使用(.)表示当前目录,作为指定context的路径:
$ docker build .
Send build context to Docker daemon 6.51MB
...
构建由 Docker 守护程序运行,而不是由 CLI 运行
。构建过程所做的第一件事是将整个 context(递归)发送到守护进程。
2、context(上下文)
Docker使用客户端-服务器架构
。Docker客户端 与 Docker守护进程 通信,守护进程 负责构建、运行和分发Docker容器。Docker客户端 和 守护进程 可以运行在同一个系统上,或者将 Docker客户端 连接到远程的Docker守护进程。Docker客户端 和 守护进程 通过UNIX套接字或网络接口使用REST API进行通信
。另一个 Docker客户端 是Docker Compose,它允许使用由一组容器组成的应用程序。
Docker客户端(Docker) 是许多Docker用户与Docker交互的主要方式。使用docker 命令时,则是通过docker API 与 Docker守护进程进行交互,从而完成各种功能,Docker客户端 可以与多个 守护进程 通信。因此,虽然表面上我们是在本机执行各种 docker 功能,但实际上,客户端会将这些命令发送到 Docker守护进程 执行它们
。也因为这种 C/S 设计,让我们操作远程服务器的 Docker守护进程 变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker守护进程构建的。
那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了
context(上下文)
的概念。当构建的时候,用户会指定构建镜像context(上下文)
的路径,docker build
命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker守护进程。这样Docker守护进程收到这个context(上下文)
包后,展开就会获得构建镜像所需的一切文件。
如果要在构建上下文中使用文件,Dockerfile引用指令中指定的文件,例如COPY指令。要提高构建的性能,可以通过在context目录中添加.dockerignore文件来排除文件和目录。
传统上,Dockerfile位于上下文的根目录中。实际上,Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,在docker build中使用-f标志来指向文件系统中的任何位置的Dockerfile。
一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。
注意:不要将根目录
/
用作PATH构建上下文,因为它会导致构建将硬盘驱动器的全部内容传输到 Docker 守护程序。
三、Dockerfile指令
1、约定
指令必须大写
,以便更容易地将它们与参数区分开来。Dockerfile必须以FROM指令开始
。- 以# 开头视为注释。
2、FROM
- 作用:指定基础镜像,Dockerfile必须以FROM指令开始。
- 格式:
格式:
(1) FROM [--platform=<platform>] <image> [AS <name>]
(2) FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
(3) FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
解释:
[--platform=< platform >]:如果引用多平台镜像,可选 --platform 标志可用于指定镜像的平台。
[AS <name>]:通过向FROM指令添加AS name,可以为新的构建阶段指定一个名称。
注:
tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像
示例:
FROM centos
- 说明:
Docker Hub中的大部分镜像都是从基础镜像 FROM scratch构建的,在基础镜像之上构建软件和配置
。FROM指令初始化一个新的构建阶段,并为后续指令设置基本镜像。因此,一个有效的Dockerfile必须以一个FROM指令开始。
镜像可以是任何有效的镜像—从Docker Hub提取镜像尤其容易。
3、RUN
- 作用:构建镜像时需要执行的命令
- 格式:
格式:
shell形式(命令运行在shell中,Linux默认为/bin/sh -c, Windows默认为cmd /S /C)
(1) RUN <command>
exec形式(exec形式可以避免shell字符串混杂,并使用不包含指定的shell可执行文件的基本镜像运行命令)
(2) RUN ["executable", "param1", "param2"]
注:
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。
示例:
RUN ["executable", "param1", "param2"]
RUN apk update
RUN ["/etc/execfile", "arg1", "arg1"]
- 说明:
镜像构建的时候需要运行的命令,有两种命令执行方式。RUN指令将在当前镜像之上的新层中执行任何命令并提交结果。生成的提交镜像将用于Dockerfile。
注意:在下一次构建期间,
RUN
指令的缓存不会自动失效。像RUN apt-get distupgrade -y
这样的指令的缓存将在下一次构建期间被重用。RUN指令的缓存可以通过使用--no-cache
标志来失效,例如docker build——no-cache
。
4、CMD
- 作用:在构建镜像之后调用,容器启动时调用命令
- 格式:
格式:
(1) CMD ["executable","param1","param2"] (exec形式,这是首选形式)
(2) CMD ["param1","param2"](作为ENTRYPOINT的默认参数)
(3) CMD command param1 param2(shell形式)
注:
Dockerfile中只能有一个CMD指令。如果列出多个CMD,则只有最后一个CMD生效。
示例:
FROM ubuntu
CMD echo "This is a test." | wc -
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
- 说明:
CMD的主要目的是为容器提供默认的执行命令。
包括可执行文件,也可以省略可执行文件,在这种情况下,您必须同时指定一个ENTRYPOINT指令。
注意:
CMD
不同于RUN
,CMD
在构建时不执行任何操作,CMD
是容器启动时执行的指令,RUN
是镜像构建时执行的指令。
5、ENTRYPOINT
- 作用:在构建镜像之后调用,容器启动时调用命令
- 格式:
格式:
(1) ENTRYPOINT ["executable", "param1", "param2"](exec形式,这是首选形式)
(2) ENTRYPOINT command param1 param2(shell形式)
注:
指定 ENTRYPOINT 指令为 exec 模式时,命令行上指定的参数会作为参数添加到 ENTRYPOINT 指定命令的参数列表中。
示例:
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
- 说明:
ENTRYOINT与CMD作用一样,都是在容器运行时执行命令,两者都是重要的指令。
注意:
ENTRYPOINT
与CMD
非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给CMD。
Dockerfile中只允许有一个ENTRYPOINT命令
,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令。
通常情况下, ENTRYPOINT 与CMD一起使用,ENTRYPOINT 写默认命令,当需要参数时候 使用CMD传参。
6、CMD vs ENTRYPOINT
《Docker官方文档》
《Dockerfile 的 CMD 与 ENTRYPOINT 傻傻分不清楚》
《ENTRYPOINT vs CMD: Back to Basics》
7、LABEL
- 作用:LABEL指令向镜像添加元数据。
- 格式:
格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
注:
一个镜像可以有多个标签,可以在一行中指定多个标签。
示例:
一条指令中指定多个标签:
(1) LABEL multi.label1="value1" multi.label2="value2" other="value3"
(2) LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
- 说明:
LABEL指令向镜像添加元数据。LABEL是一个键-值对
。要在LABEL值中包含空格,请在命令行解析中那样使用引号和反斜杠。
注意:
基础或父镜像(FROM行中的镜像)中包含的标签由你的镜像继承。如果标签已存在但具有不同的值,则最近应用的值将覆盖任何先前设置的值
。
8、MAINTAINER (已弃用)
- 作用:MAINTAINER指令设置生成的镜像的作者信息。
- 格式:
格式:
MAINTAINER <name>
示例:
MAINTAINER linzy
MAINTAINER xxx@qq.com
MAINTAINER linzy <xxx@qq.com>
9、EXPOSE
- 作用:EXPOSE指令告诉Docker容器在运行时
监听指定的网口
。 - 格式:
格式:
EXPOSE <port> [<port>/<protocol>...]
注:
EXPOSE指令实际上并不发布端口。它在构建镜像的人和运行容器的人之间充当一种文档,说明哪些端口将被发布。
要在运行容器时真正发布端口,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口。
示例:
EXPOSE 8080
同时在 TCP 和 UDP 上公开:
EXPOSE 80/tcp
EXPOSE 80/udp
- 说明:
EXPOSE指令告诉Docker容器在运行时监听指定的网口。可以指定端口侦听的协议类型是TCP还是UDP,如果不指定协议类型,默认为TCP
。
注意:
如果没有发布端口,后期也可以通过-p 8080:80方式映射端口,但是不能通过-P形式映射
10、ENV
- 作用:ENV指令将设置环境变量。
- 格式:
格式:
ENV <key> <value>
#<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量。
ENV <key>=<value> ...
#可以设置多个变量,每个变量为一个"<key>=<value>"的键值对。
注:
如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行。
示例:
ENV MY_NAME="linzy"
ENV MY_DOG=Wang\ Cai
ENV MY_CAT=Mei\ Lao\ Ban
- 说明:
ENV
当容器从生成的图像运行时,使用ENV设置的环境变量将一直存在。你可以使用 docker inspect
查看这些值,并使用 docker run --env < key >=< value >
更改它们。
注意:
ENV
指令还允许使用另一种语法ENV < key > < value >
,省略=
,例如:
ENV MY_VAR my-value
11、ADD
- 作用:将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget。
- 格式:
格式:
(1) ADD [--chown=<user>:<group>] <src>... <dest>
(2) ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
注:
该--chown功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器。由于用户和组所有权概念不能在 Linux 和 Windows 之间转换,因此使用/etc/passwd并/etc/group用于将用户和组名转换为 ID 会限制此功能仅适用于基于 Linux 操作系统的容器。
示例:
ADD hom* /mydir/ # 添加所有以“hom”开头的文件
ADD hom?.txt /mydir/ # ?替换为任何单个字符,例如“home.txt”。
ADD test.txt relativeDir/ # 使用相对路径,将“test.txt”添加到<WORKDIR>/relativeDir/
ADD test.txt /absoluteDir/ # 使用绝对路径,将“test.txt”添加到/absoluteDir/
- 说明:
该ADD
指令从<src>
路径复制新文件、目录或远程文件 URL,并将它们添加到镜像的文件系统中<dest>
。<src>
可以指定多个资源,但如果它们是文件或目录,则它们的路径被解释为相对于构建context(上下文)的源。
ADD遵守以下规则:
<src>
路径必须在构建的上下文中;你不能ADD ../something /something
,因为docker build
的第一步是将上下文目录(和子目录)发送到 docker 守护进程。- 如果
<src>
是一个URL,且<dest>
不是以斜杠结尾,则从该URL下载文件并复制到<dest>
。- 如果
<src>
是一个URL,且<dest>
是以斜杠结尾,则从 URL 推断文件名并将文件下载到<dest>/<filename>
.。例如:ADD http://example.com/foobar /
将创建文件/foobar
。 URL 必须有一个重要的路径,以便在这种情况下可以找到适当的文件名(http://example.com
将不起作用)。- 如果
<src>
是目录,则复制目录的全部内容,包括文件系统元数据。- 如果
<src>
是可识别压缩格式(identity、gzip、bzip2 或 xz)的本地tar 存档,则将其解压缩为目录。来自远程URL 的资源不会被解压缩
。当一个目录被复制或解包时,它的行为与 相同tar -x
。
- 文件是否被识别为可识别的压缩格式完全取决于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以此结尾,
.tar.gz
则不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而是将文件简单地复制到目标位置。- 如果
<src>
是任何其他类型的文件,它将与它的元数据一起被单独复制。在这种情况下,如果<dest>
以斜杠结尾/,它将被视为一个目录,其内容<src>
将写入<dest>/base(<src>)
。<src>
直接指定了多个资源,或者使用了通配符,则<dest>
必须是目录,并且必须以斜杠结尾/
。- 如果
<dest>
不以斜杠结尾,它将被认为是一个普通文件,其<src>
的内容将写入<dest>
。- 如果
<dest>
不存在,它会连同其路径中所有缺失的目录一起创建。
12、COPY
- 作用:将本地文件添加到容器中,但是是不会自动解压文件,也不能访问网络资源。
- 格式:
格式:
(1) COPY [--chown=<user>:<group>] <src>... <dest>
(2) COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
注:
该--chown功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,不适用于 Windows 容器。
示例:
COPY hom* /mydir/ # 添加所有以“hom”开头的文件
COPY hom?.txt /mydir/ # ?替换为任何单个字符,例如“home.txt”
COPY test.txt relativeDir/ # 使用相对路径,并将“test.txt”添加到<WORKDIR>/relativeDir/
COPY test.txt /absoluteDir/ # 使用绝对路径,并将“test.txt”添加到/absoluteDir/
- 说明:
COPY指令从路径复制新文件或目录<src>
并将它们添加到容器的文件系统中<dest>
。<src>
可以指定多个资源,但文件和目录的路径将被解释为相对于构建上下文的源。
COPY遵守以下规则:
<src>
路径必须在构建的上下文中;你不能ADD ../something /something
,因为docker build
的第一步是将上下文目录(和子目录)发送到 docker 守护进程。- 如果
<src>
是目录,则复制目录的全部内容,包括文件系统元数据。- 如果
<src>
是任何其他类型的文件,它将与它的元数据一起被单独复制。在这种情况下,如果<dest>
以斜杠结尾/,它将被视为一个目录,其内容<src>
将写入<dest>/base(<src>)
。<src>
直接指定了多个资源,或者使用了通配符,则<dest>
必须是目录,并且必须以斜杠结尾/
。- 如果
<dest>
不以斜杠结尾,它将被认为是一个普通文件,其<src>
的内容将写入<dest>
。- 如果
<dest>
不存在,它会连同其路径中所有缺失的目录一起创建。
13、VOLUME
- 作用:用于指定持久化目录(指定此目录可以被挂载出去)
- 格式:
格式:
VOLUME ["/data"]
注:
卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
1.卷可以容器间共享和重用
2.容器并不一定要和其它容器共享卷
3.修改卷后会立即生效
4.对卷的修改不会对镜像产生影响
5.卷会一直存在,直到没有容器在使用它
6.卷可以在 Linux 和 Windows 容器上运行。
示例:
VOLUME ["/data"]
VOLUME ["/var/mysql", "/var/log/mysql", "/etc/mysql"]
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
- 说明:
VOLUME指令创建具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷。
参考《Docker卷(volumes)》
关于指定卷的注意事项:
基于 Windows 的容器上的卷
:使用基于 Windows 的容器时,容器内卷的目标必须是以下之一:
- 不存在或为空的目录
- C盘以外的驱动器
从 Dockerfile 中更改卷
:如果任何构建步骤在声明卷后更改了卷中的数据,则这些更改将被丢弃。JSON 格式
:列表被解析为 JSON 数组。必须用双引号 ( “) 而不是单引号 ( ‘) 将单词括起来。主机目录在容器运行时声明
:主机目录(挂载点)本质上是依赖于主机的。这是为了保持镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。由于这个原因,你不能从Dockerfile内挂载主机目录。VOLUME
指令不支持指定host-dir
参数。在创建或运行容器时,必须指定挂载点
。
14、USER
- 作用:设置用户名(或 UID)和可选的用户组(或 GID)。
- 格式:
格式:
(1) USER <user>[:<group>]
(2) USER <UID>[:<GID>]
注:
使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。
镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。
示例:
USER linzy
- 说明:
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户。
注意:
在 Windows 上,如果用户不是内置帐户,则必须先创建用户。这可以通过net user作为 Dockerfile 的一部分调用的命令来完成。
FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add linzy
# Set it for subsequent commands
USER linzy
15、WORKDIR
- 作用:设置工作目录。
- 格式:
格式:
WORKDIR /path/to/workdir
注:
通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行。
在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。
示例:
WORKDIR /a (这时工作目录为/a)
WORKDIR b (这时工作目录为/a/b)
WORKDIR c (这时工作目录为/a/b/c)
RUN pwd # 最终pwd命令的输出Dockerfile将是/a/b/c.
- 说明:
WORKDIR
设置工作目录,类似于cd命令。设置工作目录后,Dockerfile中其后的命令RUN
、CMD
、ENTRYPOINT
、ADD
、COPY
等命令都会在该目录下执行。如果不存在,即使它没有在任何后续指令中使用,它也会被创建。
注意:
Dockerfile里WORKDIR
指令使用之前设置的环境变量ENV
,例如:
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
最终
pwd
命令的输出Dockerfile将是/path/$DIRNAME
。
如果未指定,默认工作目录为/
。在实践中,如果你不是从头开始构建Dockerfile,WORKDIR
可能由你正在使用的基础镜像设置。
因此,为了避免在未知目录中进行意外操作,最好是显式设置WORKDIR
。
16、ARG
- 作用:用于指定传递给构建运行时的变量(给Dockerfile传参),相当于构建镜像时可以在外部为里面传参
- 格式:
格式:
ARG <name>[=<default value>]
注:
--build-arg <varname>=<value> 将变量传递给构建器。如果用户指定了未在Dockerfile中定义的构建参数,那么构建将输出一个警告。
示例:
FROM busybox
ARG user1
ARG buildno
# ...
- 说明:
ARG
指令定义了一个变量,用户可以在构建时通过使用 标志的docker build
命令将其传递给构建器。
注意:
不建议使用构建时变量来传递 github 密钥、用户凭据等机密信息
。使用该docker history
命令的映像的任何用户都可以看到构建时变量值。
1)默认值
ARG
指令可以设置默认值:
FROM busybox
ARG user1=someuser
ARG buildno=1
# ...
如果ARG
指令具有默认值并且在构建时没有传递任何值,则构建器将使用默认值。
2)使用 ARG 变量
可以使用ARG
或ENV
指令指定RUN
指令可用的变量。使用ENV
指令定义的环境变量总是覆盖同名的ARG
指令。
带有ENV
和 ARG
指令的 Dockerfile
例子:
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER
镜像是用这个命令构建的:
docker build --build-arg CONT_IMG_VER=v2.0.1 .
RUN
指令使用v1.0.0而不是ARG
用户传递的设置。
17、ONBUILD
- 作用:向镜像添加了一条触发指令。
- 格式:
格式:
ONBUILD <INSTRUCTION>
注:
任何构建指令都可以注册为触发器。
示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
- 说明:
ONBUILD指令向镜像添加了一条触发指令。
NNBUID后面跟指令,当该镜像被用作另一个构建的基础镜像时,触发器将在其构建的上下文中执行。就好像它是FROM 在指令之后立即插入的一样Dockerfile。
如果你正在构建一个镜像,该镜像将用作构建其他镜像的基础,例如可以特定于用户的配置定制的应用程序构建环境或守护进程。
注意:
ONBUILD
指令可能不会触发FROM
或MAINTAINER
指令。
18、Dockerfile常用指令
指令 | 描述 |
---|---|
FROM | 构建新镜像使用的基础镜像 |
MAINTAINER(已弃用) | 构建镜像的作者或邮件地址 |
RUN | 构建镜像时执行命令 |
COPY | 拷贝文件或目录到镜像中 |
ENV | 设置环境变量 |
USER | 为RUN、CMD和ENTRYPOINT等执行命令指定运行用户 |
EXPOSE | 声明容器运行的服务端口 |
WORKDIR | 为RUN、CMD、ENTRYPOINT、COPY和ADD设置工作目录 |
ENTRYPOINT | 运行容器时执行,如果由多个ENTRYPOINT指令,最后一个生效,可以追加命令 |
CMD | 运行容器时执行,如果由多个CMD 指令,最后一个生效,可被替代 |
LABEL | 设置镜像的标签 |
VOLUME | 设置容器的挂载卷 |
ARG | 指令定义了一个变量 |
ONBUILD | 向镜像添加了一条触发指令 |
四、构建自己的镜像
1、构建镜像步骤
构建镜像步骤:
- 编写一个
Dockerfile
文件 - 通过
docker build
命令构建成一个镜像 docker run
命令运行镜像docker push
命令发布镜像到Docker Hub
注意:
- 如果有多个
RUN
,自上而下依次运行,每次运行都会形成新的层,建议&& 放入一行运行- 如果有多个
CMD
,只有最后一个运行- 如果有多个
ENTRYPOINT
,只有最后一个运行- 如果
CMD
和ENTRYPOINT
共存,只有ENTRYPOINT
运行,且最后的CMD
会当做ENTRYPOINT
的参数
2、编写Dockerfile文件
# 基础镜像
FROM centos
#MAINTAINER 维护者信息
MAINTAINER linzy<2350621012@qq.com>
#ENV 设置环境变量
ENV MYPATH /usr/local
#WORKDIR 相当于cd
WORKDIR $MYPATH
#RUN 执行命令
RUN yum -y install vim
RUM yum -y install net-tools
#EXPOSE 映射端口
EXPOSE 80
#CMD 运行命令
CMD echo $MYPATH
CMD echo "-----end----"
CMD /bin/bash
逐行解释该
Dockerfile
文件的指令:
FROM centos
:该image文件继承官方的centos,他会先在你本地寻找centos镜像ENV MYPATH /usr/local
:设置环境变量MYPATHWORKDIR $MYPATH
:直接使用上面设置的环境变量,指定/usr/local为工作目录RUN yum -y install vim && RUM yum -y install net-tools
:在/usr/local目录下,运行yum -y install vim和yum -y install net-tools命令安装工具,注意安装后的所有依赖和工具都会打包到image文件中EXPOSE 80
·:将容器80端口暴露出来,允许外部连接这个端口CMD
:指定容器启动的时候运行命令
- CMD echo $MYPATH:输出MYPATH环境变量
- CMD echo “—–end—-”:输出—–end—-
- CMD /bin/bash:进入/bin/bash命令行
3、执行build命令构建镜像
执行build命令生成image文件。
docker build -t mycentos:1.0 .
如果出现Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist
错误,不要惊慌!
报错信息的意思是:从仓库 ‘appstream’ 下载元数据失败:由于镜像列表中没有 URL,不能准备内部镜像列表。
错误原因:
- 可能的情况便是网络连接问题。检查是否可以连接外部网络,可以使用
ping baidu.com
查看是否有丢包情况。如果丢包,则进一步检查网络连接是否正常;如果没有丢包,继续阅读下文- 第二种情况,便是 CentOS 已经停止维护的问题。2020 年 12 月 8 号,CentOS 官方宣布了停止维护 CentOS Linux 的计划,并推出了 CentOS Stream 项目,CentOS Linux 8 作为 RHEL 8 的复刻版本,生命周期缩短,于 2021 年 12 月 31 日停止更新并停止维护(EOL)。
解决Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist
错误
因为我们的
Dockerfile
的基础镜像是可以基于本地的镜像,所以我们只需要修改本地的centos
镜像,再用我们的commit
指令生成新的镜像用来当我们Dockerfile
的基础镜像
commit指令
参考文章《Docker镜像概述和分层原理》
- 运行centos镜像,并进入交互界面
docker run -it -P centos
- 进入到 yum 的 repos 目录
cd /etc/yum.repos.d/
- 修改 centos 文件内容
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
- 生成缓存更新(第一次更新,速度稍微有点慢,耐心等待两分钟左右)
yum makecache
- 运行 yum update (更新的东西很多,大约五分钟)
yum update -y
出现Complete的就是成功了
- 使用
docker commit
将容器保存为新的镜像
docker commit c638ed426e64 mycentos:0.1
- 修改一下我们的Dockerfile里的FROM基础镜像这块
# 基础镜像 修改为mycentos:0.1
FROM mycentos:0.1
#MAINTAINER 维护者信息
MAINTAINER linzy<2350621012@qq.com>
#ENV 设置环境变量
ENV MYPATH /usr/local
#WORKDIR 相当于cd
WORKDIR $MYPATH
#RUN 执行命令
RUN yum -y install vim
RUN yum -y install net-tools
#EXPOSE 映射端口
EXPOSE 80
#CMD 运行命令
CMD echo $MYPATH
CMD echo "-----end----"
CMD /bin/bash
- 重新执行
build
命令
docker build -t mycentos:1.0 .
构建centos镜像成功!
4、测试运行
1)使用 docker history
镜像id 查看镜像构建过程
E:\dockerfile>docker history 2b92daa4f916
IMAGE CREATED CREATED BY SIZE COMMENT
2b92daa4f916 29 minutes ago CMD ["/bin/sh" "-c" "/bin/bash"] 0B buildkit.dockerfile.v0
<missing> 29 minutes ago CMD ["/bin/sh" "-c" "echo \"-----end----\""] 0B buildkit.dockerfile.v0
<missing> 29 minutes ago CMD ["/bin/sh" "-c" "echo $MYPATH"] 0B buildkit.dockerfile.v0
<missing> 29 minutes ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 29 minutes ago RUN /bin/sh -c yum -y install net-tools # bu… 28.7MB buildkit.dockerfile.v0
<missing> 29 minutes ago RUN /bin/sh -c yum -y install vim # buildkit 67.2MB buildkit.dockerfile.v0
<missing> 29 minutes ago WORKDIR /usr/local 0B buildkit.dockerfile.v0
<missing> 29 minutes ago ENV MYPATH=/usr/local 0B buildkit.dockerfile.v0
<missing> 29 minutes ago MAINTAINER linzy<2350621012@qq.com> 0B buildkit.dockerfile.v0
<missing> 32 minutes ago /bin/bash 302MB
<missing> 11 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 11 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 11 months ago /bin/sh -c #(nop) ADD file:805cb5e15fb6e0bb0… 231MB
2)运行容器,看看是否能够执行ifconfig
及vim
命令
E:\dockerfile>docker run -it mycentos:1.0
[root@392bed5a0bcd local]# pwd
/usr/local
[root@392bed5a0bcd local]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
RX packets 11 bytes 906 (906.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@392bed5a0bcd local]# vim