Docker(六)进阶:Dockerfile概述与用法(两万字入门到精通)

前言

学习本文需要一些了解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不同于RUNCMD在构建时不执行任何操作,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作用一样,都是在容器运行时执行命令,两者都是重要的指令。

注意:
ENTRYPOINTCMD非常类似,不同的是通过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遵守以下规则

  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指令创建具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷。
参考《Docker卷(volumes)》

关于指定卷的注意事项:

  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中其后的命令RUNCMDENTRYPOINTADDCOPY等命令都会在该目录下执行。如果不存在,即使它没有在任何后续指令中使用,它也会被创建。

注意:
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 变量

可以使用ARGENV指令指定RUN指令可用的变量。使用ENV指令定义的环境变量总是覆盖同名的ARG指令。

带有ENVARG指令的 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指令可能不会触发FROMMAINTAINER指令。

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 ,只有最后一个运行
  • 如果CMDENTRYPOINT共存,只有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

在这里插入图片描述

  1. 进入到 yum 的 repos 目录
cd /etc/yum.repos.d/
  1. 修改 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-*
  1. 生成缓存更新(第一次更新,速度稍微有点慢,耐心等待两分钟左右)
yum makecache

在这里插入图片描述

  1. 运行 yum update (更新的东西很多,大约五分钟)
yum update -y

在这里插入图片描述
在这里插入图片描述
出现Complete的就是成功了

  1. 使用docker commit 将容器保存为新的镜像
docker commit c638ed426e64  mycentos:0.1

在这里插入图片描述

  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
  1. 重新执行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)运行容器,看看是否能够执行ifconfigvim命令

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
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lin钟一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值