Dockerfile是什么

一个包含用于组合 image 的命令的文本文件,docker 通过 dockerfile 和构建环境的上下文来构建 image 。

编写Dockerfile

FROM

首先,我们必须用 FROM 指定一个基础image,然后后续的指令会运行在该image上

FROM [--platform=<platform>] <image>[:<tag>] [AS <名称>]
FROM [--platform=<platform>] <image>[:@<digest>] [AS <名称>]

示例:

FROM redis:5.0.12

LABEL

然后让我们添加维护者的基本信息

MAINTAINER <name>

不过要注意的是该命令已经被标记为deprecated,所以我们最好用 LABEl 代替它

LABEL maintainer="lihua@163.com"

同时,LABEL 指令还可以设置任何的元数据,就像这样

LABEL version="1.0"

当我们对 build 后的 image 使用 docker inspect 命令时会就能看见该 LABEl

WORKDIR

好的,现在我们已经编写完了基本的指令,接下来来编写执行指令。

在我们正式工作之前,先来设置工作目录。

设置一个工作目录只需要

WORKDIR /mydata

然后我们下面的大多数命令都会在这个目录下运行。

当然,如果它不存在则会自动创建的。

RUN

RUN 指令应该是最重要的指令之一,它可以在容器内执行指定的指令,并把结果保存下来,一条 RUN 指令应该长这样:

RUN <command>			# 这是shell格式
RUN ["exec", "arg1", "arg2"] # 这是exec格式
Exec 和 Shell 格式

shell 格式需要一个字符串,其会传给 "/bin/sh -c" 执行

exec 格式需要接收一个JSON数组,其中第一个元素是可执行文件,其他元素为执行时用到的参数

和shell对比,exec格式适用于需要规避shell对字符串作出错误解析的情况,或当基础镜像里没有 "/bin/sh" 时

你可以这样使用它,以下两条命令是等价的:

RUN bash -c 'touch /hello.txt'
RUN ["bash", "-c", "touch", "/hello.txt"]
ENV、ARG

如果你想设置环境变量,那么你将用到 ENV 指令,就像这样:

ENV <key>=<value> ...
ENV name=lihua local=cn

当你想使用它时只需要 ${<key>}

ENV my_version=1.0
RUN apt-get install -y mypackage=${my_version}

不过,exec格式下不会调用命令shell,所以变量替换不会生效。

RUN ["echo", "$name"]

要使其生效可以使用

RUN ["sh", "echo $name"]

你还可以使用标准的bash修饰符

${name:-default} - name未设置则为"default"

${name:+lihua} - name设置了则为"lihua",否则为空字符串

需要注意的是,该环境变量会保留到容器中,如果只是想在构建中使用变量,可以使用 ARG 指令,ARG 的用法和 ENV 十分接近

ADD、COPY

然后我们还需要一个能够将本地文件添加到容器中的指令,ADD 指令,像这样

ADD <src> ... <dest>
ADD ["<src>", ..., "<dest>"]

该指令将当前上下文中的 <src> 添加 image 里的 <dest> 文件或目录中,<src> 可以是文件系统,也可以网络,同时如果是压缩文件会自动解压。并且,你还可以用 ?* 匹配一个或多个字符

ADD ./system.jar /app.jar

特别要注意的是,我们不能指定上下文以外的 src 路径,例如 ../book/b.txt ,这是不被允许的

COPY 指令是功能与 ADD 相近的指令,区别在于,COPY 不会访问网络资源且不会解压文件

ENTRYPOINT、CMD

通常我们可以用 ENTRYPOINT 来设置一个可执行文件在容器启动后运行,同样的,你可以使用 shell 格式或 exec 格式。不过如果定义了多个 ENTRYPOINT ,那只有最后一个会生效。

ENTRYPOINT ["java", "-jar", "/service.jar"]

CMD 指令和 ENTRYPOINT 相似,也是在容器启动时执行指定的指令,不过如果还同时出现了 ENTRYPOINT 指令,那 CMD 将作为 ENTRYPOINT 默认参数的形式在容器中执行。

同时如果 CMD 只是被用来为 ENTRYPOINT 提供参数,则可以使用 ["param1","param2"] 的格式。

并且,CMD 的参数会被 docker run 的参数覆盖,而 ENTRYPOINT 不会。

关于 CMDENTRYPOINT 的组合示例可以看看这张图。

从图中我们可以看出,ENTRYPOINT 如果以 shell 的形式,则它会忽略所有的 CMD 参数和 docker run 的参数,而且会运行在 sh -c 内。

这就代表进程的 pid 不是 1 ,并且无法接收 UNIX 信号,也就是说接收不到 docker stopSIGTERM 信号,这样的话最后会被强制 kill 掉。

VOLUME

我们当然可以在 Dockerfile 中设置数据卷,可以使用以下两种方式

VOLUME ["/data"]
VOLUME /data

不过,主机的目录只能在使用 docker run 的时候申明,这是为了保持 image 的可移植性。

USER

这里有两种方法可以指定所使用的用户

USER <user>[:<group>]
USER <UID>[:<GID>]
EXPOSE

EXPOSE 能通知 Docker 在运行时监听指定的端口,但它不会实际的发布端口,只是一个文档信息,并且你可以在 docker run 时带上 -P 标签,这个标签能将 EXPOSE 通知的端口全部发布出去,不过会使用随机的端口。

EXPOSE 80/tcp

ONBUILD

这个命令可以让子镜像在构建时执行这个命令里的命令。

例如,我们在父镜像的 Dockerfile 的文件中编写

FROM grandfather
ONBUILD echo "hey! my child"

然后将该镜像构建好后,再构建子镜像

FROM father

构建子镜像的时候,就会打印出 hey! my child

build

在我们编写完 Dockerfile 文件后,就可以开始构建了。

我们使用 docker build 进行构建

docker build \
# -f 用来指定我们在本次build时需要用到的Dockerfile的位置,不指定则为当前目录
-f /path/to/a/Dockerfile \
# -t 用来指定build后的存储库的<名称>:<标记>
-t myApplication:latest \
# 此处的上下文为".",即当前目录,该目录下的所有文件会被放入一个tar文件(除非你编写了dockerignore文件)
.

当然也可以直接使用 docker build ,此时会在以当前目录为上下文,并在当前上下文中寻找 Dockerfile 文件。

Dockerfile 的 ADDCOPYRUN 指令会生成新的镜像层,下一个指令会用这个新的镜像层执行指令后,再保存为一个新的镜像层,提供给下一个指令使用。

这就代表即使用 RUN 指令运行了一些持久化的进程,在启动容器的时候也会消失。不过要想启动容器时同时运行一个进程,可以使用 ENTERPOINTCMD

我们甚至可以使用 docker history 来查看组成 image 的所有层,当构建失败的时候,我们可以将中间层启动起来,就如同下面这个例子一样。

我们先编写一个 Dockerfile

FROM ubuntu:20.04
RUN no_cmd
ENTRYPOINT ["echo","world"]

然后使用 docker build

[root@VM-0-3-centos docker]# docker build -t test:1 .
Sending build context to Docker daemon 4.608kB
Step 1/3 : FROM ubuntu:20.04
---> 26b77e58432b
Step 2/3 : RUN no_cmd
---> Running in 6e1c94e4ccf6
/bin/sh: 1: no_cmd: not found
The command '/bin/sh -c no_cmd' returned a non-zero code: 127

毫无疑问,它出错了。虽然这次演示的错误很简单,我们可以直接看出来。

然后我们使用使用 docker run 进入中间的层

[root@VM-0-3-centos docker]# docker run -it 26b77e58432b
root@59c3c3641111:/# no_cmd
bash: no_cmd: command not found

通过演示我们发现,原因当然就是这个不存在的命令。

最后,在你的 image 构建成功后,你就可以像一般的 image 来任意使用你构建的 image 了~

简明教程 | Docker篇 · 其二:Dockerfile的编写的更多相关文章

  1. 简明教程 | Docker篇 · 其一:基础入门

    了解Docker Docker是什么 Docker是指容器化技术,用于支持创建和使用 Linux 容器,同时Docker也是软件容器平台. 什么是容器(container) 容器是主机上与其他进程隔离 ...

  2. Docker简明教程

    Docker简明教程 [编者的话]使用Docker来写代码更高效并能有效提升自己的技能.Docker能打包你的开发环境,消除包的依赖冲突,并通过集装箱式的应用来减少开发时间和学习时间. Docker作 ...

  3. Docker入门教程(三)Dockerfile

    Docker入门教程(三)Dockerfile [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第三篇,介绍了Dockerfile的语法,DockerOn ...

  4. Docker的基本使用及DockerFile的编写

    前言: 最近在准备面试,在复习到Docker相关内容时,想写一些东西分享给大家然后加深一下自己的印象,有了这篇随笔. Docker的简介: docker从文件系统.网络互连到进程隔离等等,极大的简化了 ...

  5. Docker 与 K8S学习笔记(四)—— Dockerfile的编写

    在上一篇中我们简单介绍了Docker镜像的获取与使用,其中在镜像制作中提到在实际使用中一定要用Dockerfile方式去创建镜像而不要用docker commit方式,那么我们该如何编写Dockerf ...

  6. Docker 入门教程(3)——Dockerfile

    Dockerfile Dockerfile是一个文本文件,用来定制镜像. 镜像是分层存储的,前一层会是下一层的基础.而镜像的定制就是定制每一层镜像在上一层做了什么改变. Dockerfile其内包含一 ...

  7. Java8简明教程(转载)

    ImportNew注:有兴趣第一时间学习Java 8的Java开发者,欢迎围观<征集参与Java 8原创系列文章作者>. 以下是<Java 8简明教程>的正文. “Java并没 ...

  8. Lisp简明教程

    此教程是我花了一点时间和功夫整理出来的,希望能够帮到喜欢Lisp(Common Lisp)的朋友们.本人排版很烂还望多多海涵! <Lisp简明教程>PDF格式下载 <Lisp简明教程 ...

  9. NSIS安装制作基础教程[初级篇], 献给对NSIS有兴趣的初学者

    NSIS安装制作基础教程[初级篇], 献给对NSIS有兴趣的初学者 作者: raindy 来源:http://bbs.hanzify.org/index.php?showtopic=30029 时间: ...

随机推荐

  1. 第07课:GDB 常用命令详解(下)

    本课的核心内容: disassemble 命令 set args 和 show args 命令 tbreak 命令 watch 命令 display 命令 disassemble 命令 当进行一些高级 ...

  2. ubuntu 20.04 发邮件配置

    安装sendmail后,发邮件一直没有成功,因此卸载sendmail后,安装heirloom-mailx. # unbuntu 18.04和20.04移除了heirloom-mailx,需要另外配置软 ...

  3. C# List集合类常用操作:四、删除

    Clear() 从 List<T> 中移除所有元素. List<Employees> employees = new List<Employees>(); empl ...

  4. JS001. antd vue遍历setFieldsValue表单键值对无效 ( {} -> new Object() )

    问题代码: const tempFieldsValue = this.form.getFieldsValue() Object.keys(tempFieldsValue).map((k) => ...

  5. IDEA SpotBugs代码安全审计插件

    IDEA SpotBugs代码安全审计插件 在寻找idea代码审计插件的时候,发现Findbugs已经停止更新,无法在idea2020.01版本运行,由此找到SpotBugs SpotBugs介绍 S ...

  6. systemctl添加自定义系统服务

    [Service] Type=forking ExecStart=绝对路径 ExecStop=绝对路径 ExecReload=绝对路径 以上最精简版,文件/usr/lib/systemd/system ...

  7. CodeForce-807C Success Rate(二分数学)

    Success Rate CodeForces - 807C 给你4个数字 x y p q ,要求让你求最小的非负整数b,使得 (x+a)/(y+b)==p/q,同时a为一个整数且0<=a< ...

  8. 一起学习PHP中断言函数的使用

    原来一直以为断言相关的函数是 PHPUnit 这些单元测试组件提供的,在阅读手册后才发现,这个 assert() 断言函数是 PHP 本身就自带的一个函数.也就是说,我们在代码中进行简单的测试的时候是 ...

  9. php去除html标签

    function cutstr_html($string){ $string = strip_tags($string); $string = preg_replace(["\t" ...

  10. Java基础系列(7)- 标识符和关键字

    关键字 标识符 Java所有的组成部分都需要名字.类名.变量名.方法名都称为标识符 首字母以字母(A-Z或者a-z),美元符号($),或者下划线(_)开头 首字母之后可以用字母.美元符号.下划线.数字 ...