2022 最新版本 Docker 学习_13.Dockerfile 简介 & 常用指令

一、什么是 dockerfile

首先来看一下官方介绍

画红框中文字的意思是:我们可以按照需要通过 dockerfile 来构建运行时候所需要的环境。

通过上文了解之后,我们可以知道,通过 dockerfile 可以构建属于我们自己的镜像,这样一来就省去了以前那种方式了。好比我们现在想要构建一个我们自己的 tomcat 服务器,并且让里面跑我们所需要的程序,按照以前的方式,我们需要拉取 tomcat 镜像,然后启动镜像后,将 war 上传至容器,然后再重启容器,这样一来是很麻烦的。而现在有了 dockerfile 之后,我们可以直接自己编写镜像,而且这样一来可复用性也提高很多。

二、Dockerfile 常用指令

官方文档:Dockerfile reference | Docker Documentation

1. FROM

FROM 指令初始化一个新的构建阶段,并为后续指令设置基本镜像。一般用于构建 image 时指定基础镜像。

格式:
  FROM xxximage
  FROM image:版本
举例:  
	FROM centos

这里有一点注意,很多博客都说 FROM 必须是 dockerfile 第一个指令,其实这个是错误的,来看官网原文档的叙述:

ARG 指令可以写在 FROM 前面,作为第一个指令。

2. RUN

RUN 指令可以在构建 dockerfile 时运行一些命令,可以写多条。

格式:
    RUN shell脚本
    RUN ["可执行文件", "参数1", "参数2", ...]
举例:
    RUN yum -y install net-tools
    RUN yum -y install vim
    RUN ["./test.php", "dev", "offline"] 该命令等价于 RUN ./test.php dev offline

注意点:

  • 在下一次构造 dockerfile 时,RUN 指令的缓存不会自动失效。像 RUN apt-get dist-upgrade-y 这样的指令的缓存将在下一次构建期间重用。可以使用 -- no-cache 标志使 RUN 指令的缓存失效,例如 docker build -- no-cache。
  • Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层,会造成镜像膨胀过大。
举例:
FROM xxx
RUN xxx1
RUN xxx2
RUN xxx3
以上 dockerfile 如果在被构建时会创建 3 层镜像。

改进:
FROM xxx
RUN xxx1 \
    && xxx2 \
    && xxx3
我们将多个 RUN 命令使用 && 进行连接,这样再进行构建 dockerfile 时,就只会创建 1 层。\ 为换行符

3. CMD

CMD 的主要目的是为正在执行的容器提供缺省值。这些缺省值可以包括一个可执行文件,也可以省略可执行文件,在这种情况下,还必须指定 ENTRYPOINT 指令。

一个 Dockerfile 中只能有一条 CMD 指令。如果在一个 dockerfile 中出现了多个 CMD,那么只有最后一个 CMD 才会生效。

格式:
    CMD ["可执行文件", "参数1", "参数2", ...] (执行可执行文件,优先)
    CMD ["参数1", "参数2", ...] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
    CMD shell脚本 (执行shell内部命令)
举例:
    CMD /bin/bash
    CMD ["/home/apache-tomcat-8.5.37/bin/catalina.sh","run"]

注意点:

  • CMD 不同于 RUN,CMD 用于指定在容器启动时所要执行的命令,而 RUN 用于指定镜像构建时所要执行的命令。
  • 如果用户在 docker run 中指定了参数,那么它们将覆盖 CMD 中指定的缺省值。

4. LABEL

LABEL 指令向映像添加元数据。LABEL 添加的数据为 k-v 类型的数据,同一个 dockerfile 可以添加多个 LABEL。

格式:
    LABEL <key>=<value> <key>=<value> <key>=<value> ...
举例:
    LABEL "com.example.vendor"="ACME Incorporated"
    LABEL com.example.label-with-value="foo"
    LABEL description="This text illustrates \
          that label-values can span multiple lines."
    LABEL multi.label1="value1" multi.label2="value2" other="value3"

(上面例子中有一个用到了 \ ,作用和上面 RUN 的一样,换行)

上面的这些元数据可以通过 docker image inspect 命令进行查看,或者可以增加 --format 进行具体的查看

docker image inspect --format='' 镜像id 或 镜像:版本

5. MAINTAINER(已废弃)

MAINTAINER 指令可以设置生成镜像的作者字段。

格式:
    MAINTAINER 作者名称
举例:
    MAINTAINER lemon1234
    MAINTAINER apache

但是这个命令已经被废弃掉了,大家如果想增加作者信息啥的,可以使用 LABEL。

6. EXPOSE

EXPOSE 指令通知 Docker 容器在运行时监听指定的网络端口。我们可以指定端口是否侦听 TCP 或 UDP,如果没有指定协议,则默认为 TCP。

格式:
    EXPOSE <port> [<port>/<protocol>...]
举例:
    EXPOSE 80
    EXPOSE 80/udp

EXPOSE 指令实际上并不发布端口。它作为一种文档,给构建 dockerfile 的人和使用镜像的人去看的。所以不管 EXPOSE 设置如何,我们都可以在运行时使用 -p 标志覆盖它们。

7. ENV

ENV 指令用于设置环境变量的值,存储方式为 k-v 形式。

格式:
    ENV key1=value1 key2=value2 ...
    ENV key1 value1
举例:
    ENV name=lemon123
    ENV age=15 phone=18512341234
    ENV company xxxx有限责任公司

通过 ENV 设置的环境变量,我们也可以通过 docker inspect 查看这些内容,而且启动的时候还可以通过 docker run --env key=value 改变他们。

注意点:

  • 当镜像被运行生成容器后,这些被 ENV 定义的环境变量是保持不变的。

8. ADD

ADD 指令用来复制文件、目录、远程文件 URL 到镜像的文件系统。

格式:
    ADD [--chown=<user>:<group>] 要复制的东西.... 镜像中的地址
    ADD [--chown=<user>:<group>] ["要复制的东西",... "镜像中的地址"]
举例:
    ADD apache-tomcat-8.5.37.tar.gz /home/
    ADD http://example.com/foobar /

注意点:

  • --chown=<user>:<group> 这玩意只支持 linux,windows 是不支持的。
  • 要复制的东西的路径必须位于构建的上下文中,不能 ADD ../xxx.xx /home 这样的出现。所以一般我都是将 dockerfile 和要 ADD 的东西扔在一个地方。
  • ADD 还支持通配符,类似 ADD hom* /mydir/,可以将宿主机当前名字开头是 hom 的文件都复制到 /mydir 下面。详细通配符的介绍可以去看 golang 的 filepath.Match 方法。
  • 镜像中的地址是绝对路径,或者相对于 WORKDIR 的路径,源文件将被复制到目标容器中。
  • 如果要复制的东西是目录,ADD 指令不会复制目录本身,只复制其内容。
  • 如果 ADD 是一个压缩文件,在复制到镜像中,会自动解压;但如果要复制的东西是一个压缩包 URL,下载之后是不会自动解压的。

更详细的 ADD 建议去看 dockerfile 的官方文档:Dockerfile reference | Docker Documentation​​​​​​​​​​​

9. COPY

COPY 指令其实和 ADD 类似,但是 COPY 仅仅之后拷贝。

格式:
    COPY [--chown=<user>:<group>] <src>... <dest>
    COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
举例:
    COPY apache-jmeter-5.4.1.tgz /jmeterdocker
    COPY config.py /

10. ENTRYPOINT

ENTRYPOINT 指令与 CMD 相似,但是却又有这一定的区别。

格式:
    ENTRYPOINT ["可执行文件", "参数1", "参数2", ...]
    ENTRYPOINT shell脚本
举例:
    ENTRYPOINT ["/docker-entrypoint.sh"]
    ENTRYPOINT docker-entrypoint.sh

见过格式之后,我们再来说区别,这里通过一个例子来说明:

首先来看 CMD 指令,这个指令在一个 dockerfile 中可以写多个,但是执行的只有最后一个,好比 tomcat 镜像最后一个 CMD

CMD ["catalina.sh" "run"]

如果我们在启动镜像的时候,在命令最后面加上其他的参数(这样做可以覆盖 CMD 要执行的命令)

这里我在启动的时候增加了一个参数:ls -h 的 shell 命令,然后我们可以看到,下面我们将 tomcat 的目录查了出来。而这样的 tomcat 其实是没有启动成功的。

接着我们再来看一下 nginx 的 dockerfile

可以看到,nginx 的 dockerfile 是先有一个 ENTRYPOINT,接着下面跟着一个 CMD。如果我们去启动 nginx,那么 nginx 镜像中会执行 docker-entrypoint.sh nginx -g daemon off;

如果我们将 nginx 的启动扔在 ENTRYPOINT 中执行,这样就不怕用户在最后修改 CMD 的命令了(虽然 ENTRYPOINT 也可以修改~~~)。

11. VOLUME

VOLUME 指令创建一个具有指定名称的挂载点,并将其标记为保存来自本机主机或其他容器的外部挂载卷。

格式:
    VOLUME ["/data", "/xxx", ...]
    VOLUME "/var/www", "/var/log/apache2", ...
举例:
    VOLUME ["/var", "/home"]
    VOLUME "/var", "/home"

这里我们使用 VOLUME 是为了定义一个容器卷。这里可能会有人问了,之前学习的 docker run -v 是什么,和这里的 VOLUME 有什么联系?

首先来说 docker run -v 这个命令,它是用来挂载宿主机目录和容器目录的,挂载之后的文件,可以在宿主机看到,而且还会实时同步。

接着再来说 VOLUME 指令,这个指令其实就是指定一个匿名卷,指定好这个卷后,docker 会将这个卷自动绑定到宿主机的一个地方(不同版本的 docker 可能不一样),我现在绑定的地方在:/var/lib/docker/volumes/{容器ID},小伙伴们可以自己去查看一下。

那为啥我们要弄 VOLUME,这里再来举个例子。好比 MySQL 这个镜像,我们通过镜像生成了容器,现在我们如果将这个 MySQL 的容器删除,要是没有 VOLUME,那么我们删除容器之后,会连带 MySQL 的数据一起删除,而且还是永久性删除。但是如果有了 VOLUEM,及时删除了容器,我们还可以通过之前的容器 ID 找到默认绑定到宿主机的位置进行恢复数据。

VOLUME 相当于是指定了一个默认的卷,而 docker run -v 是用户自定义指定一个卷。

那为啥不在 dockerfile 中提供一个可以直接映射宿主机目录和容器目录的指令呢?很简单,如果真的有这个指令,那么 dockerfile 的可移植性就变差了,毕竟谁能保证我们每个人所需要映射的目录是一样的?所以最好的办法是将这个映射的决定扔给运行这个镜像的人。

12. USER

USER 指令设置用户名(或 UID)和可选的用户组(或 GID),以在运行映像时以及 Dockerfile 中跟随它的任何 RUN、CMD 和 ENTRYPOINT 指令使用。

格式:
    USER <user>[:<group>]
    USER <UID>[:<GID>]
举例:
    USER lemon1234
    USER patrick

注意点:

  • 其中用户名或 ID 是指可以在容器基础镜像中找到的用户。如果在容器基础镜像中没有创建特定用户,则在 USER 指令之前添加 useradd 命令以添加特定用户。
  • 使用 USER 指定用户后,Dockerfile 中后续的命令 RUN、CMD、ENTRYPOINT 都将使用该用户。
Linux 添加用户:RUN useradd -d /home/username -m -s /bin/bash username USER username
Windows 添加用户:RUN net user /add patrick

13. WORKDIR

WORKDIR 指令为 Dockerfile 中任何 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令设置工作目录。

格式:
    WORKDIR /path/to/workdir
举例:
    WORKDIR /home
    WORKDIR /var/local

这个 WORKDIR 类似于 cd 命令。

注意点:

  • 如果 dockerfile 中增加了一个 WORKDIR /a,那么此时工作目录为 /a,然后再增加一个 WORKDIR b,那么此时工作目录为 /a/b。所以这里建议最好使用绝对路径。
  • 如果工作目录不存在,则Docker Daemon会自动创建

WORKDIR 还可以解析环境变量,例如:

ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

这时输出的是 /path/$DIRNAME

14. ARG

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

格式:
    ARG <name>[=<default value>]
举例:
    ARG site
    ARG build_user=www

注意点:

  • 不建议使用构建时变量来传递 github 密钥、用户凭据等机密信息。使用 docker history 命令的图像的任何用户都可以看到构建时变量值。
  • 如果 ARG 指令具有默认值,并且在构建时没有传递任何值,则构建器将使用默认值。
  • ARG 变量定义从 Dockerfile 中定义它的行开始生效,而不是从命令行或其他地方的参数使用开始。

这里主要说一下 ARG 的作用域。

如果在第一个 FROM 之前定义的 ARG,那么可以在下面任何 FROM 去使用;如果是在 FROM 里面定义的,就不行了,如果需要使用,需要在每一个 FROM 中进行定义。

FROM centos
RUN echo $user 
ARG user
RUN echo $user

来看这个 dockerfile,第二句 RUN echo $user 一定是啥也输出不出来的。

15. ONBUILD

ONBUILD 指令向镜像添加了一条触发指令,该指令将在稍后的时间执行,此时镜像用作另一个构建的基础。

格式:
    ONBUILD <INSTRUCTION>
举例:
    ONBUILD ADD . /app/src
    ONBUILD RUN yum -y install vim

这个 ONBUILD 一般是用在父镜像中,子镜像继承父镜像后,当我们构建子镜像时,会触发父镜像的 ONBUILD,这样就省的子镜像再去写了。

16. SHELL

格式:
    SHELL ["executable", "parameters"]
举例:
    SHELL ["/bin/sh", "-c"]
    SHELL ["cmd", "/S", "/C"]

这个比较简单,SHELL 就是用来去执行一些 SHELL 脚本,自己可以去学学 shell 脚本,然后自己去用用这个命令。

三、构建 dockerfile

上面我们讲了很多 dockerfile 的指令,接下来看一下,有了 dockerfile,如何通过 dockerfile 生成镜像。

docker build 参数... PATH | URL | -

这里参数有点多,我们主要说几个常用的参数。

-f:指定要使用的 dockerfile 路径;

--build-arg=[] :设置镜像创建时的变量;

--tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。

下面来一个简单的实例~

自己在 /home 下面创建一个 dockerfile 的文件夹,然后在文件夹里面创建一个 dockerfile 的文件。

touch dockerfile;

接着编辑一下 dockerfile 文件。

vi dockerfile;

FROM centos
RUN echo $user 
ARG user
RUN echo $user

保存退出编辑模式,因为这个 dockerfile 需要我们传递一个 user 的参数,所以这里我们就用到了 --build-arg 这个参数,详细来看一下具体命令。

docker build --build-arg user=lemon1234 -f dockerfile -t dockerfile/test:1.0 .

这里有两个地方需要注意一下,一个是在这个命令最后面是有一个 . 的,千万别漏了;还有一个是我执行这个命令的时候,目录的位置是在 /home/dockerfile 下,所以 -f 后面直接写 dockerfile 即可。

可以看到,是没有问题,接着我们来看一下我们刚刚生成的镜像~

 


这一讲就讲到这里,有问题可以联系我:QQ 2100363119,欢迎大家访问我的个人网站:https://www.lemon1234.com

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

他 他 = new 他()

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

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

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

打赏作者

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

抵扣说明:

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

余额充值