docker公司在容器技术发展中提出了镜像分层的理念,可以说也是这个革命性的理念让原本只不过是整合linux内核特性的容器,开始野蛮生长。

docker通过UnionFS联合文件系统将镜像的分层实现合并,关于镜像相关知识有兴趣的同学可参考我们之前文章docker容器技术基础之联合文件系统OverlayFS

本文是对docker官方文档Dockerfile reference学习与实践,在学习docker容器相关技术的同学别光收藏,你要动起来!实践起来!

提示:没有人比docker公司更懂docker,本小作文含部分自己的理解,有英文阅读习惯的同学,建议直接阅读官方文档哈。


docker build

Dockerfile是一个镜像构建命令集合的文本文件,下面是我们最常见的Dockerfile构建,假如我们目录下有一个文件Dockerfile

[root@localhost nginx_project]# ls
Dockerfile
[root@localhost nginx_project]# docker build -t nginx:v1 .

通过build指定了目标镜像的标签为nginx:v1,以及Dockerfile的上下文context .

什么是docker上下文?

一个面向服务端的目录夹结构,除了Dockerfile,你的一切构建资源都应该在这个目录(指定的上下文)中。

上下文是递归处理的。因此, 如果是PATH则包含任何子目录,如果是一个URL则包含存储库及其子模块。

关键点,构建是由 Docker 守护程序运行,而不是由 CLI 运行,所以docker会把上下文资源打包传输给守护进程进行构建,为了减少不必要的臃肿,最好从一个空目录作为上下文开始,并将 Dockerfile 保存在该目录中。仅添加构建 Dockerfile 所需的文件。

我们可以使用-f选项指定dockerfile

[root@localhost folder]# docker build -f ../Dockerfile -t nginx:v1 .

使用多个-t选项保持多个tag

[root@localhost folder]# docker build  -t nginx:v1 -t dockerhub.com/nginx:v2 .
Sending build context to Docker daemon 1.583kB
Step 1/2 : FROM nginx
---> 08b152afcfae
Step 2/2 : run echo 123
---> Using cache
---> 3b636c79fbfa
Successfully built 3b636c79fbfa
Successfully tagged nginx:v1
Successfully tagged dockerhub.com/nginx:v2

这样就构建两个不同tag的同一ID镜像

[root@localhost folder]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dockerhub.com/nginx v2 3b636c79fbfa 23 minutes ago 133MB
nginx v1 3b636c79fbfa 23 minutes ago 133MB

BuildKit

buildkit将 Dockerfile 变成了 Docker 镜像。它不只是构建 Docker 镜像;它可以构建 OCI 图像和其他几种输出格式。

从版本18.09开始,Docker支持由moby / buildkit项目提供的用于执行构建的新后端。与旧的实现相比,BuildKit后端提供了许多好处。例如,BuildKit可以:

  • 检测并跳过执行未使用的构建阶段。
  • 平行构建独立的构建阶段。
  • 在不同的构建过程中,只增加传输构建上下文中的更改文件。
  • 在构建上下文中检测并跳过传输未使用的文件。
  • 使用外部Dockerfile实现许多新功能。
  • 避免与API的其他部分(中间镜像和容器)产生副作用。
  • 优先处理您的构建缓存,以便自动修剪。

要使用BuildKit后端,只需要在调用 DOCKER_BUILDKIT=1 docker build 之前在CLI上设置环境变量DOCKER_BUILDKIT = 1。或者配置/etc/docker/daemon.json启用。

[root@localhost folder]#  DOCKER_BUILDKIT=1 docker build -f ../Dockerfile -t nginx:v1 -t dockerhub.com/nginx:v2 .
[+] Building 5.2s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.7s
=> => transferring dockerfile: 118B 0.0s
=> [internal] load .dockerignore 0.6s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/nginx:latest 0.0s
=> [1/2] FROM docker.io/library/nginx 2.2s
=> [2/2] RUN echo 123 1.3s
=> exporting to image 0.5s
=> => exporting layers 0.2s
=> => writing image sha256:813b09c58322dce98ee28e717baeb9f3593ce3e46a032488949250f761004495 0.0s
=> => naming to docker.io/library/nginx:v1 0.0s
=> => naming to dockerhub.com/nginx:v2

dockerfile格式

1、注释

一个标准的dockerfile,注释是必须的。

#这是dockerfile注释,dockerfile中指令以"CMD args"格式出现
CMD args
CMD args
...

一个Dockerfile 第一个指令必须是FROM指令,用于指定基础镜像,那么基础镜像的父镜像从哪里来?答案是scratch带有该FROM scratch指令的 Dockerfile会创建一个基本映像

2.解析器指令

解析器指令是可选的,会影响 aDockerfile中后续行的处理方式。解析器指令不会向构建添加层,也不会显示为构建步骤,单个指令只能使用一次。

dockerfile目前支持以下两个解析器指令:

  • syntax
  • escape
2.1syntax

此功能仅在使用BuildKit后端时可用,在使用经典构建器后端时会被忽略。

我们可以在dockerfile文件开头指定此dockerfile语法解析器,如下:

# syntax=docker/dockerfile:1
# syntax=docker.io/docker/dockerfile:1
# syntax=example.com/user/repo:tag@sha256:abcdef...

通过syntax自定义 Dockerfile 语法解析器可以实现如下:

  • 在不更新 Docker 守护进程的情况下自动修复错误
  • 确保所有用户都使用相同的解析器来构建您的 Dockerfile
  • 无需更新 Docker 守护程序即可使用最新功能
  • 在将新功能或第三方功能集成到 Docker 守护进程之前试用它们
  • 使用替代的构建定义,或创建自己的定义

官方dockerfile解析器:

  • docker/dockerfile:1 不断更新最新的1.x.x次要补丁版本
  • docker/dockerfile:1.2 保持更新最新的1.2.x补丁版本,一旦版本1.3.0发布就停止接收更新。
  • docker/dockerfile:1.2.1 不可变:从不更新1.2版本

比如我们使用1.2最新补丁版本,我们的Dockerfile如下:

#syntax=docker/dockerfile:1.2
FROM busybox
run echo 123

我们启用buildkit构建

# DOCKER_BUILDKIT=1 docker build -t busybox:v1 .
[+] Building 5.8s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.3s
=> => transferring dockerfile: 150B 0.0s
=> [internal] load .dockerignore 0.4s
=> => transferring context: 2B 0.0s
=> resolve image config for docker.io/docker/dockerfile:1.2 2.6s
=> CACHED docker-image://docker.io/docker/dockerfile:1.2@sha256:e2a8561e419ab1ba6b2fe6cbdf49fd92b95 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [1/2] FROM docker.io/library/busybox 0.3s
=> [2/2] RUN echo 123 1.1s
=> exporting to image 0.3s
=> => exporting layers 0.3s
=> => writing image sha256:bd66a3db9598d942b68450a7ac08117830b4d66b68180b6e9d63599d01bc8a04 0.0s
=> => naming to docker.io/library/busybox:v1
2.2 escape

通过escape定义dockerfile的换行拼接转义符

# escape=\

如果要构建一个window镜像就有大用处了,我们看下面dockerfile

FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\

由于默认转义符为\,则在构建的第二步step2会是这样COPY testfile.txt c:\RUN dir c:显然与我们的预期不符。

我们把转义符换成`号即可

# escape=`

FROM microsoft/nanoserver
COPY testfile.txt c:\ `
RUN dir c:\
3.类bash的环境变量
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO} # WORKDIR /bar
ADD . $FOO # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux

${variable_name}语法还支持bash 指定的一些标准修饰符:

  • ${variable:-word}表示如果variable变量被设置(存在),则结果将是该值。如果variable未设置,word则将是结果。
  • ${variable:+word}表示如果variable被设置则为word结果,否则为空字符串。
4. .dockerignore

.dockerignore用于忽略CLI发送到docker守护进程的文件或目录。以下是一个.dockerignore文件

#.dockeringre可以有注释
*.md
!README.md
temp?
*/temp*
*/*/temp*
规则 行为
*/temp* 排除名称以temp根目录的任何直接子目录开头的文件和目录。例如,纯文件/somedir/temporary.txt被排除在外,目录/somedir/temp.
*/*/temp* 排除temp从根目录下两级的任何子目录开始的文件和目录。例如,/somedir/subdir/temporary.txt被排除在外。
temp? 排除根目录中名称为一个字符扩展名的文件和目录temp。例如,/tempa/tempb被排除在外。
不排除到文件

dockerfile命令

1.FROM

指定基础镜像。一般格式如下,[]括号内容可省略:

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

特别需要注意的是FROM在一个dockerfile中可以多次出现,以实现多阶段构建。并且可以和ARG 参数交互。如下:

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app FROM extras:${CODE_VERSION}
CMD /code/run-extras

我们加载了两个通过arg参数指定的不同版本基础镜像。

2.RUN

RUN的两种形式

  • RUN 首选, (命令在shell中运行,即默认为/bin/sh -c )
  • RUN ["exec",param1,param2]

RUN命令主要是在镜像构建时执行,形成新层。比如我们经常会看到在构建镜像时安装相关软件。

RUN yum install -y gcc

当我们不想使用默认shell是可以采用exec形式实现

RUN ["/bin/bash","-c","yum install -y gcc"]

当然,exec形式可以不使用shell

RUN ["yum","install","-y","gcc"]

EXEC形式被解析为一个JSON阵列,所以必须使用双引号

3.CMD

CMD指令有三种形式:

  • CMD ["executable","param1","param2"]exec形式,这是首选形式)
  • CMD ["param1","param2"](作为ENTRYPOINT 的默认参数
  • CMD command param1 param2(shell形式)

一个dockerfile中,应该只写一个CMD,如果有多个只有最后一个生效。在实际编写dockerfie时CMD命令常常用于为ENTRYPOINT提供默认值,后面我们会讲到。

与RUN相比,CMD在构建时不会执行任何操作,主要用于指定镜像的启动命令。CMD的启动命令可以被docker run 参数代替。

我们在dockerfile中添加如下CMD命令

CMD echo hello

构建镜像后,docker run 不添加参数,启动容器

[root@localhost dockerfiles]# docker run centos:v1
hello

当我们在docker run 添加参数后

[root@localhost dockerfiles]# docker run centos_env:v1 echo container
container

显然我们CMD命令echo hello已被docker run中的参数echo container取代。

4. LABEL

label用于添加镜像的元数据,采用key-value的形式。

LABEL <key>=<value>

比如我们添加如下LABEL

LABEL "miantainer"="iqsing.github.io"
LABEL "version"="v1.2"
LABEL "author"="waterman&&iqsing"

为了防止创建三层,我们最好通过一个标签来写。

LABEL "miantainer"="iqsing.github.io" \
"version"="v1.2" \
"author"="waterman&&iqsing"

我们通过docker inspect 来查看镜像label信息

#docker inspect centos_labels:v1

"Labels": {
"author": "waterman&&iqsing",
"miantainer": "iqsing.github.io",
"org.label-schema.build-date": "20201204",
"org.label-schema.license": "GPLv2",
"org.label-schema.name": "CentOS Base Image",
"org.label-schema.schema-version": "1.0",
"org.label-schema.vendor": "CentOS",
"version": "v1.2"
}
5.EXPOSE
EXPOSE 80/tcp
EXPOSE 161/udp

注意,EXPOSE只是告诉dockerfile的阅读者,我们构建的镜像需要暴露哪些端口,只是一个信息。在容器中还是需要通过-p选项来暴露端口。

6.ENV
ENV <key>=<value> ... 首先方式

ENV <key> <value>

通过ENV指定环境变量,将作用于在构建阶段的所有后续指令的环境中。

ENV username="iqsing"

这样当我们启动这个容器后可以查看到容器信息已经附带了ENV环境变量

"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"username=iqsing"
],

当然我们也可以在启动容器时添加环境变量

docker run --env <key>=<value>

另外如果只需要在镜像构建期间使用环境变量,更好的选择是使用ARG参数来处理

7.ADD && COPY

ADD和COPY格式相似,有两种形式,包含空格的路径需要后一种形式:

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

在linux平台中可以对添加到远程目录或文件设置所属用户和组。

<SRC> 指复制新文件、目录或远程文件 URL,每<src>可以包含通配符,如下:

ADD hom* /mydir/
ADD hom?.txt /mydir/

一般使用中,ADD、COPY都遵守以下规则:

  • <src>路径必须是内部语境的构建; 你不能COPY ../something /something,因为 docker build是将上下文目录(和子目录)发送到 docker 守护进程。

  • 如果<src>是目录,则复制目录的全部内容,包括文件系统元数据。

  • 如果<src>是任何其他类型的文件,则将其与其元数据一起单独复制。在这种情况下,如果<dest>以斜杠结尾/,它将被视为一个目录,其内容<src>将被写入<dest>/base(<src>)

  • 如果<src>直接指定了多个资源,或者由于使用了通配符,则<dest>必须是目录,并且必须以斜杠结尾/

  • 如果<dest>不以斜杠结尾,则将其视为常规文件,并将其内容<src>写入<dest>.

  • 如果<dest>不存在,则在其路径中创建所有丢失的目录。

特别的,当是可识别的压缩包如gzip、bzip2等tar包时,首先会将包添加到镜像中,然后自动解压。这可以说是与COPY命令在使用中的最大的区别。

8.ENTRYPOINT

exec首选和shell形式:

ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2

ENTRYPOINT 和CMD很相似,都是指定启动命令,不同之处在于ENTRYPOINT 指定的命令无法被docker run 参数取代。

我们在dockerfile中添加ENTRYPOINT

ENTRYPOINT echo hello container

构建镜像并启动容器,可以看到docker run 中的参数并未取代ENTRYPOINT

[root@localhost dockerfiles]# docker run centos_entrtpoint:v1 echo hello docker
hello container

这指令优秀的另一个地方在于可以和CMD指令做交互。让容器以应用或者服务运行。

经典操作:ENTRYPOINT + CMD = 默认容器命令参数

ENTRYPOINT是dockerfile中非常重要的指令,有必要另写一篇小作文深入学习一下这东西。

9.VOLUME
VOLUME ["/data"]

volume指令可以用于创建存储卷,我来看一下实例:

FROM centos
RUN mkdir /volume
RUN echo "hello world" > /volume/greeting
VOLUME /volume

构建镜像后,创建一个容器

[root@localhost dockerfiles]# docker create   --name centos_volume  centos_volue:v1
[root@localhost dockerfiles]# docker inspect centos_volume "Mounts": [
{
"Type": "volume",
"Name": "494cdb193984680045c36a16bbc2b759cf568b55c7e9b0852ccf6dff8bf79c46",
"Source": "/var/lib/docker/volumes/494cdb193984680045c36a16bbc2b759cf568b55c7e9b0852ccf6dff8bf79c46/_data",
"Destination": "/volume",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],

这样我们就通过VOLUME指令创建一个存储卷,你可以通过--volumes-from共享这个容器,可参考我之前的小作文《docker容器存储》

10.USER

指定指令集所属用户和组。组默认为root。可以作用于RUNCMDENTRYPOINT它们后面的指令。

USER <user>[:<group>]

USER <UID>[:<GID>]
11.WORKDIR

指定指令集所在的工作目录,若目录不存在将会自动创建。可作用于RUNCMDENTRYPOINTCOPYADD

WORKDIR /path/to/workdir
12.ARG
ARG <name>[=<default value>]

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

  • 如果ARG指令具有默认值并且在构建时没有传递任何值,则构建器使用默认值。

  • 在多阶段构建应该添加多个ARG

  • ENV变量会覆盖ARG变量

  • 与ENV变量相比,ARG变量多用于构建,无法驻留在镜像中。

13.STOPSIGNAL

配置容器退出时的系统调用

STOPSIGNAL signal
14.HEALTHCHECK

HEALTHCHECK指令有两种形式:

  • HEALTHCHECK [OPTIONS] CMD command (通过在容器内运行命令来检查容器健康状况)
  • HEALTHCHECK NONE (禁用从基础镜像继承的任何健康检查)

OPTIONS支持如下参数:

  • --interval=DURATION(默认值:30s
  • --timeout=DURATION(默认值:30s
  • --start-period=DURATION(默认值:0s
  • --retries=N(默认值:3

比如我们可以添加如下参数用于检查web服务:

HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1

每五分钟左右检查一次web服务器能否在3s内响应。如果失败则返回状态码1

命令的退出状态指示容器的健康状态。可能的值为:

  • 0:成功 - 容器运行良好,可以使用
  • 1:不健康 - 容器无法正常工作
  • 2:reserved - 不要使用这个退出代码

编写一个优质的Dockerfile并不容易,你需要考虑所构建镜像的迭代、服务稳定运行、启动与停止、安全等等问题,希望这篇小作文可以帮助你对Dockerfile有多一点了解。

您可以随意转载、修改、发布本文章,无需经过本人同意。 个人blog:iqsing.github.io


NEXT

  • Dockerfile 理解ENTRYPOINT与CMD结合
  • Dockerfile 多阶段构建实践
  • Dockerfile 与docker容器安全实践

docker容器dockerfile详解的更多相关文章

  1. Docker:DockerFile详解与实例

    基本结构 Dockerfile 由一行行命令语句组成,并且支持已 # 开头的注释行. 一般而言,Dockerfile 的内容分为四个部分: 基础镜像信息. 维护者信息. 镜像操作指令. 容器启动时执行 ...

  2. (转)Docker入门——Dockerfile详解

    转:https://www.cnblogs.com/sorex/p/6481407.html 基本示例 FROM MAINTAINER LABEL RUN ADD COPY CMD ENTRYPOIN ...

  3. Dockerfile详解

    Dockerfile详解 利用Dockerfile文件,可以构建docker的image镜像 命令使用 通过-f参数指定Dockerfile路径,进行构建image docker build -f / ...

  4. 附005.Docker Compose文件详解

    一 Docker Compose文件简介 compose文件使用yml格式,主要分为了四个区域: version:用于指定当前docker-compose.yml语法遵循哪个版本 services:服 ...

  5. Dockerfile详解及优化

    Dockerfile详解 0. Dockerfile的作用 docker可以根据Dockerfile中的指令来构建docker镜像.Dockerfile是一个文本文件,其应当包含用户想要构建一个镜像的 ...

  6. 【转】Dockerfile详解

    Dockerfile详解 https://blog.csdn.net/wo18237095579/article/details/80540571 --------------------- 作者:大 ...

  7. 微信小程序flex容器属性详解

    flex容器属性详解 flex-direction决定元素的排列方向 flex-wrap决定元素如何换行 flex-flow 是 flex-direction 和flex-wrap的简写 justif ...

  8. Docker 使用指南 (五)—— Dockerfile 详解

    版权声明:本文由田飞雨原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/97 来源:腾云阁 https://www.qclou ...

  9. Docker 网络模式详解及容器间网络通信

    当项目大规模使用 Docker 时,容器通信的问题也就产生了.要解决容器通信问题,必须先了解很多关于网络的知识.Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜 ...

随机推荐

  1. 小程序开发 access_token 统一管理

    TOKEN 定时刷新器 一.背景 对于使用过公众平台的API功能的开发者来说,access_token绝对不会陌生,它就像一个打开家门的钥匙,只要拿着它,就能使用公众平台绝大部分的API功能.因此,对 ...

  2. 暑假自学java第二天

    今天学习了一些java规则 一个java源文件的公开类只能有一个,而且必学和源文件名相同. 了解到java的标识符规范,这对以后的团队协作有很大作用. 标识符规则和c++还是很相似的 java中的字面 ...

  3. webrtc之TURE、STUN、摄像头打开实战

    前言: 大家周末好,今天给 webrtc之TURE.STUN.摄像头打开实战 大家分享的是webrtc第一篇文章,在之前的音视频文章里面没有分享过关于webrtc的内容:在上个周末分享了一篇关于播放器 ...

  4. Linux:linux下解压*压缩tar.xz、tar、tar.gz、tar.bz2、tar.Z、rar、zip、war等文件方法

    tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 ------------------------------------------ 这 ...

  5. php 扩展 rabbitmq popt

    首先是rabbitmq-c-master.tar.gz包, 可以访问https://github.com/alanxz/rabbitmq-c去下载最新的 wget https://github.com ...

  6. 通过ajax方式在界面上加载loading状态(仅作记录)

    1 html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 2 pre, a, ab ...

  7. PHP Kafka 消息队列使用

      转载自:https://learnku.com/articles/44442 1. 安装 Kafka 服务# 直接到 kafka 官网 , 下载最新的 wget https://mirror.bi ...

  8. 简单学习java内存马

    看了雷石的内存马深入浅出,就心血来潮看了看,由于本人java贼菜就不介绍原理了,本文有关知识都贴链接吧 前置知识 本次主要看的是tomcat的内存马,所以前置知识有下列 1.tomcat结构,tomc ...

  9. 谷粒商城--分布式基础篇(P1~P27)

    谷粒商城--分布式基础篇P1~P27 去年3月份谷粒商城分布式基础.进阶.高级刚出的时候就开始学了,但是中途因为一些事就中断了,结果一直到现在才有时间重新开始学,看到现在网上这么多人都学完了,确实感觉 ...

  10. 团队开发day03

    完成安卓的登录和注册界面的设计,进行服务器端的开发,设计javabean实体 映射,零售商 ,商品,品牌商,订单类的构建 遇到问题:安卓发起网络请求,客户端回应请求,数据处理设置. 使用传统的方法 / ...