Dockerfile概述与用法

本文最后更新于:1 年前

前言

学习本文需要一些了解Docker的概念以及一些名词。

一、Docker概述

1、Docker简介

Docker 镜像是通过读取Dockerfile来构建镜像文件的。Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令来组装镜像,每条指令都是独立运行的,并会创建一个新的镜像层 。使用docker build 命令用户用户可以创建一个自动构建,该构建可以连续执行几个命令行指令。

2、为什么要用Dockerfile?

为什么要用Dockerfile?这个问题的本身其实是说为什么我们要自定义镜像,明明Docker Hub上有这么多镜像可以用,我们还要自己费心思做镜像。

主要原因:Docker Hub上许多官方镜像只是基础包,很多功能都没有,需要我们自己对官方镜像做扩展,以打包成我们生产应用的镜像。
参考我的第一篇文章Docker入门篇:《Docker的入门与安装

二、docker build工作原理

1、镜像构建过程

docker build命令从 Dockerfile 和 context 构建一个镜像。构建的 context 是位于指定位置PATH或URL的一组文件。PATH是本地文件系统上的一个目录。URL是Git存储库位置。

1
docker build [OPTIONS] PATH | URL | -

完整的镜像架构图:

构建context是递归处理的。每条指令都是独立运行的,并会创建一个新的镜像层。

例子中docker build后面使用(.)表示当前目录,作为指定context的路径:

1
2
3
4
$ 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
2
3
4
5
6
7
8
9
10
11
12
13
14
格式:
(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

  • 作用:构建镜像时需要执行的命令
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
格式:
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
2
3
4
5
6
7
8
9
10
11
12
13
14
格式:
(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
2
3
4
5
6
7
8
9
10
11
格式:
(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指令向镜像添加元数据。
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
格式:
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指令设置生成的镜像的作者信息。
  • 格式:
1
2
3
4
5
6
7
格式:
MAINTAINER <name>

示例:
MAINTAINER linzy
MAINTAINER xxx@qq.com
MAINTAINER linzy <xxx@qq.com>

9、EXPOSE

  • 作用:EXPOSE指令告诉Docker容器在运行时监听指定的网口
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
格式:
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指令将设置环境变量。
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
12
格式:
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
2
3
4
5
6
7
8
9
10
11
12
格式:
(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
2
3
4
5
6
7
8
9
10
11
12
格式:
(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

  • 作用:用于指定持久化目录(指定此目录可以被挂载出去)
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
格式:
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
2
3
4
5
6
7
8
9
10
格式:
(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 的一部分调用的命令来完成。

1
2
3
4
5
FROM microsoft/windowsservercore
# Create Windows user in the container
RUN net user /add linzy
# Set it for subsequent commands
USER linzy

15、WORKDIR

  • 作用:设置工作目录。
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
12
格式:
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,例如:

1
2
3
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

最终pwd命令的输出Dockerfile将是 /path/$DIRNAME
如果未指定,默认工作目录为/。在实践中,如果你不是从头开始构建Dockerfile, WORKDIR可能由你正在使用的基础镜像设置。
因此,为了避免在未知目录中进行意外操作,最好是显式设置WORKDIR

16、ARG

  • 作用:用于指定传递给构建运行时的变量(给Dockerfile传参),相当于构建镜像时可以在外部为里面传参
  • 格式:
1
2
3
4
5
6
7
8
9
10
11
格式:
ARG <name>[=<default value>]

注:
--build-arg <varname>=<value> 将变量传递给构建器。如果用户指定了未在Dockerfile中定义的构建参数,那么构建将输出一个警告。

示例:
FROM busybox
ARG user1
ARG buildno
# ...
  • 说明:

ARG指令定义了一个变量,用户可以在构建时通过使用 标志的docker build命令将其传递给构建器。

注意:
不建议使用构建时变量来传递 github 密钥、用户凭据等机密信息。使用该docker history命令的映像的任何用户都可以看到构建时变量值。

1)默认值

ARG指令可以设置默认值:

1
2
3
4
FROM busybox
ARG user1=someuser
ARG buildno=1
# ...

如果ARG指令具有默认值并且在构建时没有传递任何值,则构建器将使用默认值。

2)使用 ARG 变量

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

带有ENVARG指令的 Dockerfile 例子:

1
2
3
4
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=v1.0.0
RUN echo $CONT_IMG_VER

镜像是用这个命令构建的:

1
docker build --build-arg CONT_IMG_VER=v2.0.1 .

RUN指令使用v1.0.0而不是ARG用户传递的设置。

17、ONBUILD

  • 作用:向镜像添加了一条触发指令。
  • 格式:
1
2
3
4
5
6
7
8
9
格式:
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文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 基础镜像
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文件。

1
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镜像,并进入交互界面
1
docker run -it -P centos

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

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



出现Complete的就是成功了

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

  1. 修改一下我们的Dockerfile里的FROM基础镜像这块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 基础镜像 修改为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命令
1
docker build -t mycentos:1.0 .



构建centos镜像成功!

4、测试运行

1)使用 docker history 镜像id 查看镜像构建过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

Dockerfile概述与用法
https://gopherlinzy.github.io/2022/09/06/docker-Dockerfile/
作者
孙禄毅
发布于
2022年9月6日
许可协议