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. PHP没有定时器?

    确实,PHP没有类似于JS中的setInterval或者setTimeout这样的原生定时器相关的函数.但是我们可以通过其他方式来实现,比如使用declare. 先来看看是如何实现的,然后我们再好好学 ...

  2. Docker系列(17)- MySQL同步数据

    #获取镜像 [root@localhost ~]# docker pull mysql:5.7 #启动容器,需要做数据挂载!安装启动mysql,需要配置密码的,这是注意点! #官方安装文档:docke ...

  3. Park-2018论文

    太阳射电图:Solar magnetograms, EUV images, GOES X-ray flux data. Magnetograms:    http://solar-center.sta ...

  4. 封装excel导出方法

    封装读取excel内容方法 /** * 获取Excel内容 * @param type $filename * @return type */ public function getExcelCont ...

  5. Linux下实现高可用软件-Keepalived基础知识梳理

    Keepalived介绍 Keepalived软件起初是专门为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能.因此,Keepali ...

  6. mac使用brew update无反应,更新慢解决办法

    一.原因 主要是资源访问太慢的原因造成的,替换一下镜像就可以了 有点耐心,大概5分钟就可以了,刚开始的时候terminal 只有顶部的title栏会变化,最后才会出现更新结果 使用中科大的镜像 替换默 ...

  7. P7737-[NOI2021]庆典【tarjan,虚树】

    正题 题目链接:https://www.luogu.com.cn/problem/P7737 题目大意 给出一张无向图满足若\(x\Rightarrow z,y\Rightarrow z\)那么有\( ...

  8. spring 创建Bean最全实现方法

    创建bean方式,spring创建bean的方式包含:自动注入方式和人工注入方式.分别为:1)xml 配置化方式  2)@bean注解注入方式3)@Component方式 4)接口注入方式 5)imp ...

  9. 每日总结:String类(2021.10.6)

    String创建的字符串存储在公共池中 如: String s1="Runoob": new创建的字符串对象在堆上 如: String s2=new String("Ru ...

  10. C#开发BIMFACE系列47 IIS部署并加载离线数据包

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在前两篇博客<C#开发BIMFACE系列45 服务端API之创建离线数据包>与<C#开发BIMFACE系 ...