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. linq 集合按照多列进行distinct

    List<TaskBatch> sourceList = (from c in BatchCollecion                                         ...

  2. 解决git bash闪退问题 报openssl错误

    问题描述:今天安装git之后发现Git Bash工具闪退. 于是试了各种办法之后,最后终于解决. 背景描述:git 下载地址:https://git-scm.com/download/win 下载成功 ...

  3. 【转载】flag标志什么?哦,它标志代码馊了

    几乎每次在代码中发现flag变量,我总是能嗅到一股馊味.不管你闻没闻到,反正我闻到了. 在代码中,flag通常作为标志变量的名字.但问题在于,不是所有的问题或代码都需要使用这种标志变量,更不是使用标志 ...

  4. java版gRPC实战之七:基于eureka的注册发现

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. JAVA语言程序设计课程评价

    紧张的又短暂的一个学期结束了,这个学期也许将成为我人生中一个重要的转折点,作为一名半路出家的选手,在初次了解Java语言时我感到非常的迷茫与不知所措.因为之前很多同学都是通过假期时间在家自学,刚转入新 ...

  6. 重磅来袭!!!Elasticsearch7.14.1(ES 7.14.1)与Springboot2.5.4的整合

    1. 概述 前面我们聊了 Elasticsearch(ES)集群的搭建,今天我们来聊一下,Elasticsearch(ES)集群如何与 Springboot 进行整合. Elasticsearch(E ...

  7. 『Python』优雅的记录日志——loguru

    1. 安装 pip install loguru 2. 初识 from loguru import logger logger.debug("This is a debug..." ...

  8. 小米路由器4a千兆版刷openwrt

    现在网上搜小米路由器4a千兆版刷机的都是刷的padavan的,很少能找到openwrt的刷机教程. 首先刷openwrt系统的时候要先刷入引导程序breed,网上有一篇帖子写的很详细(https:// ...

  9. GoLang设计模式08 - 命令模式

    命令模式是一种行为型模式.它建议将请求封装为一个独立的对象.在这个对象里包含请求相关的全部信息,因此可以将其独立执行. 在命令模式中有如下基础组件: Receiver:唯一包含业务逻辑的类,命令对象会 ...

  10. NOIP模拟69

    T1 石子游戏 大坑未补 T2 大鱼吃小鱼 解题思路 set+桶可以得到 60pts (code) 线段树上二分每一次优先递归右区间从右区间贪心选择,并且记录下更改过的值,在处理完答案之后再复原回去. ...