前言
学习本文需要一些了解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、约定
1.指令必须大写,以便更容易地将它们与参数区分开来。
2.Dockerfile必须以FROM指令开始 。
3.以# 开头视为注释。
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
《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遵守以下规则:
1.<src>路径必须在构建的上下文中;你不能ADD ../something /something,因为docker build的第一步是将上下文目录(和子目录)发送到 docker 守护进程。
2.如果<src>是一个URL,且<dest>不是以斜杠结尾,则从该URL下载文件并复制到<dest>。
3.如果<src>是一个URL,且<dest>是以斜杠结尾,则从 URL 推断文件名并将文件下载到 <dest>/<filename>.。例如:ADD http://example.com/foobar /将创建文件/foobar。 URL 必须有一个重要的路径,以便在这种情况下可以找到适当的文件名(http://example.com 将不起作用)。
4.如果<src>是目录,则复制目录的全部内容,包括文件系统元数据。
5.如果<src>是可识别压缩格式(identity、gzip、bzip2 或 xz)的本地tar 存档,则将其解压缩为目录。来自远程URL 的资源不会被解压缩。当一个目录被复制或解包时,它的行为与 相同tar -x。
。文件是否被识别为可识别的压缩格式完全取决于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以此结尾,.tar.gz则不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而是将文件简单地复制到目标位置。
6.如果<src>是任何其他类型的文件,它将与它的元数据一起被单独复制。在这种情况下,如果<dest>以斜杠结尾/,它将被视为一个目录,其内容<src>将写入<dest>/base(<src>)。
7.<src>直接指定了多个资源,或者使用了通配符,则<dest>必须是目录,并且必须以斜杠结尾/。
8.如果<dest>不以斜杠结尾,它将被认为是一个普通文件,其<src>的内容将写入<dest>。
9.如果<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遵守以下规则:
1.<src>路径必须在构建的上下文中;你不能ADD ../something /something,因为docker build的第一步是将上下文目录(和子目录)发送到 docker 守护进程。
2.如果<src>是目录,则复制目录的全部内容,包括文件系统元数据。
3.如果<src>是任何其他类型的文件,它将与它的元数据一起被单独复制。在这种情况下,如果<dest>以斜杠结尾/,它将被视为一个目录,其内容<src>将写入<dest>/base(<src>)。
4.<src>直接指定了多个资源,或者使用了通配符,则<dest>必须是目录,并且必须以斜杠结尾/。
5.如果<dest>不以斜杠结尾,它将被认为是一个普通文件,其<src>的内容将写入<dest>。
6.如果<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指令创建具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷。
关于指定卷的注意事项:
1.基于 Windows 的容器上的卷:使用基于 Windows 的容器时,容器内卷的目标必须是以下之一:
。不存在或为空的目录
。C盘以外的驱动器
2.从 Dockerfile 中更改卷:如果任何构建步骤在声明卷后更改了卷中的数据,则这些更改将被丢弃。
3.JSON 格式:列表被解析为 JSON 数组。必须用双引号 ( ") 而不是单引号 ( ') 将单词括起来。
4.主机目录在容器运行时声明:主机目录(挂载点)本质上是依赖于主机的。这是为了保持镜像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。由于这个原因,你不能从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、构建镜像步骤
构建镜像步骤:
1.编写一个Dockerfile 文件
2.通过 docker build 命令构建成一个镜像
3.docker run 命令运行镜像
4.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文件的指令:
1.FROM centos :该image文件继承官方的centos,他会先在你本地寻找centos镜像
2.ENV MYPATH /usr/local:设置环境变量MYPATH
3.WORKDIR $MYPATH:直接使用上面设置的环境变量,指定/usr/local为工作目录
4.RUN yum -y install vim && RUM yum -y install net-tools:在/usr/local目录下,运行yum -y install vim和yum -y install net-tools命令安装工具,注意安装后的所有依赖和工具都会打包到image文件中
5.EXPOSE 80·:将容器80端口暴露出来,允许外部连接这个端口
6.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,不能准备内部镜像列表。
错误原因:
1.可能的情况便是网络连接问题。检查是否可以连接外部网络,可以使用 ping baidu.com 查看是否有丢包情况。如果丢包,则进一步检查网络连接是否正常;如果没有丢包,继续阅读下文
2.第二种情况,便是 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镜像概述和分层原理》
1.运行centos镜像,并进入交互界面
docker run -it -P centos
2.进入到 yum 的 repos 目录
cd /etc/yum.repos.d/
3.修改 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-*
4.生成缓存更新(第一次更新,速度稍微有点慢,耐心等待两分钟左右)
yum makecache
5.运行 yum update (更新的东西很多,大约五分钟)
yum update -y
出现Complete的就是成功了
6.使用docker commit 将容器保存为新的镜像
docker commit c638ed426e64 mycentos:0.1
7.修改一下我们的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
8.重新执行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