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. Git:git commit时退出报错解决(Error45、Error325)

    Git 报错 在输入git commit编辑注释日志时强制退出git程式,文件会变成只读文件,于是出现下述报错: 解决方法(ERROR45) 我们提交代码的正常操作流程一般是: 输入git commi ...

  2. JDK7&JDK9处理异常新特性

    1.JDK7新特性是在 try (定义对象,作用域就是try方法体) 复制一个文件实例: 复制文件的原理: 先从硬盘写出到内存中,创建文件输入流对象 FileInputStream fis; 中间是在 ...

  3. k8s架构与组件详解

    没有那么多花里胡哨,直接进行一个K8s架构与组件的学习. 一.K8s架构 k8s系统在设计是遵循c-s架构的,也就是我们图中apiserver与其余组件的交互.在生产中通常会有多个Master以实现K ...

  4. [ Skill ] Cadence Skill 语言入门

    https://www.cnblogs.com/yeungchie/ 写个大笔记,低速更新中 ... Cadence Skill Cadence 提供二次开发的 SKILL 语言,它是一种基于通用人工 ...

  5. 大数据最后一公里——2021年五大开源数据可视化BI方案对比

    个人非常喜欢这种说法,最后一公里不是说目标全部达成,而是把整个路程从头到尾走了一遍. 大数据在经过前几年的野蛮生长以后,开始与数据中台的概念一同向着更实际的方向落地.有人问,数据可视化是不是等同于数据 ...

  6. apache php RabbitMQ配置方式

    确定自己的php版本号和位数,去pecl.php.net下载版本相应的rabbitmq扩展包, 以php5版本为例,在http://pecl.php.net/package/amqp里面选择php5对 ...

  7. linux centos系统 php安装GD库扩展

    yum --enablerepo=remi-php56 install php-gd php-mysql php-mbstring php-xml php-mcrypt //安装GD库扩展 servi ...

  8. Java面向对象系列(8)- Super详解

    场景一 场景二 场景三 场景四 注意:调用父类的构造器,super()必须在子类构造器的第一行 场景五 场景六 super注意点 super调用父类得构造方法(即构造器),必须在构造方法得第一个 su ...

  9. Android命令行启动模拟器

    我们在平时的开发中会经常需要使用模拟器进行调试,这个时候我们就要先打开Android Studio来启动模拟器,然后再运行App.这个流程中启动Android Studio需要花费一些时间,而模拟器的 ...

  10. 解决samba和SELINUX 冲突

    在使用Samba进行建立Window与Linux共享时,要是不能访问,出现"您可能没有权限使用网络资源", 那就是SELinux在作怪了 要是想让共享目录能访问,可以使用命令 #s ...