Docker 使用Dockerfile文件构建镜像

docker通过读取Dockerfile中的指令来自动构建镜像。一个Dockerfile是一个文本文档,包含着用户会调用的所有的命令来用于集成这个镜像。使用 docker build 来自动构建命令,并在执行成功时执行一些命令。

用法:

docker build 命令使用dockerfile和一个构建环境来构建镜像。构建环境是一个指定的位置或URL并且该位置有一些文件。PATH是本地文件系统的路径。(默认会使用 PATH/Dockerfile 文件来进行构建)URL是一个GIT仓库位置。

一个环境可以被递归地处理。所以,PATH包含很多的子目录,URL包含着仓库和子模块。

如使用当前环境作为构建目录(.来指定当前构建环境)

docker build .

构建是被docker daemon来运行的,而不是这个CLI。构建处理要做的第一件事就是递归地来发送这整个环境给守护线程。在大多数情况下,最好是一个空目录来作为上下文环,并把Dockerfile保存至那个目录当中。仅仅添加dockerfile需要的文件

不要使用 / 目录作为环境 !

为了在构建环境当中使用一个文件,Dockerfile需要通过一个指令(eg: COPY)关联一个文件.为了提高构建性能,可以通过 .dockerignore 排除一些文件和目录.

一般来说,Dockerfile文件命名为Dockerfile并位于上下文环境当中。可以-f标志来指定文件系统任何的Dockerfile文件来构建。如:(注意:最后的点是用来指定构建环境)

docker build -f /path/to/a/Dockerfile .

为镜像定义仓库和标签(如果构建成功)

docker build -t myapp:latest

Docker一条一条地执行Dockerfile文件中的指令,并将每条指令的结果提交给新的镜像(需要的话),最后输出新镜像的id。docker守护线程会自动清理你发送的上下文环境。

每一条指令都会独立地运行并且会导致一个新的镜像产生,所以 RUN cd  /tmp 不会对下一条指令有任何的影响。

如果可能,docker会重新使用缓存的镜像去加速docker构建。会通过 Using cache 将信息输出到控制台。

关于使用构建缓存的进一步说明就不翻译了:

Build cache is only used from images that have a local parent chain. This means that these images were created by previous builds or the whole chain of images was loaded with docker load. If you wish to use build cache of a specific image you can specify it with --cache-from option. Images specified with --cache-from do not need to have a parent chain and may be pulled from other registries.

When you’re done with your build, you’re ready to look into Pushing a repository to its registry.

BuildKit(构建工具)

从18.09版本开始,Docker支持一种后端程序用于运行你的构建,它可以被moby/buildkit project提供。这个 BuildKit backend 提供很多与旧版本的好处。如:

    1. 捕获并跳过执行未使用的构建阶段

    2. 并行构建独立地阶段

    3. 加强传输在构建阶段更改的文件

    4. 捕获并跳过传输没有用的文件

    5. 使用外部Dockerfile实现很多新的特性

    6. 避免REST API的副用作影响

    7. 通过自动优化来提升构建缓存

    使用BuildKit, 设置环境变量 DOCKER_BUILDKIT=1

格式

格式如下:

# comment 

INSTRUCTION arguments 

指令非大小写敏感,但约定是:指令大写,参数小写

Dockerfile必须以 FROM 指令开始,FROM定义根镜像,只能在ARG指令之后,ARG定义声明参数。

Docker会将 # 开头的行作为注释,除非这一行有有效的解析指令。其它地方出现#标记,都是作为参数处理。比如
# Comment

RUN echo 'we are running some #.....'

注释不支持换行符号

解析指令(Parser directive)

解析指令是可选的,并且会影响Dockerfile中后续行处理的方式。指令不会增加构建层,也不会再构建步骤当中显示。解析指令会以一种注释的特殊类型 如: 

# directive=value

注释开始后,空行或者构建指令处理后,Docker将不会再去查找解析指令。代替的是将任何格式的指令均作为注释,也不会去验证它是否是一个指令。因此,所有指令必须在Dockerfile的顶端

指令是非大小写敏感。约定是

    1. 字母小写

    2. 所有指令后有一个空行

指令不支持行连续字符

无效:

# direc \

tive=value

无效:重复出现

# directive=value1

# directive=value2

FROM ImageName

指令在构建指令后,作为注释:

FROM ImageName

# directive=value

视为注释(在一个非指令之后):

# About my dockerfile

# directive=value

FROM ImageName


常用指令

syntax escape 

1.  syntax=[ remote image reference ]

eg:

# syntax=docker/dockerfile

# syntax=docker/dockerfile:1.0

# syntax=docker.io/docker/dockerfile:1

# syntax=docker/dockerfile:1.0.0-experimental

# syntax=example.com/user/repo:tag@sha256:abcdef

BuildKit 使用的时候,这个功能才会起作用

syntax指令定义Dockerfile builder的位置,builder用于构建当前的Dockerfile。这个Buildkit后端程序可以无缝地使用外部的builder实现,builder由镜像发布并在一个容器沙箱环境当中执行。

自定义的Dockerfile实现:

    1. 在没有更新守护线程时,自动获取bug修复

    2. 确保所有的用户都在使用相同的实现来构造你的Dockerfile

    3. 在没有更新守护线程时,使用最新的功能

    4. 可以尝试新的实验性特征或第三方特征

官方版本:

    Docker发布的用于构建Dockerfile镜像的镜像的官方版本在Docker Hub的docker/dockerfile 仓库中。这里有两个通道:稳定版本实验版本

两个版本的具体详情信息:

Stable channel follows semantic versioning. For example:

docker/dockerfile:1.0.0 - only allow immutable version 1.0.0

docker/dockerfile:1.0 - allow versions 1.0.*

docker/dockerfile:1 - allow versions 1..

docker/dockerfile:latest - latest release on stable channel

The experimental channel uses incremental versioning with the major and minor component from the stable channel on the time of the release. For example:

docker/dockerfile:1.0.1-experimental - only allow immutable version 1.0.1-experimental

docker/dockerfile:1.0-experimental - latest experimental releases after 1.0

docker/dockerfile:experimental - latest release on experimental channel

You should choose a channel that best fits your needs. If you only want bugfixes, you should use docker/dockerfile:1.0. If you want to benefit from experimental features, you should use the experimental channel. If you are using the experimental channel, newer releases may not be backwards compatible, so it is recommended to use an immutable full version variant.

For master builds and nightly feature releases refer to the description in the source repository.

2. escape 指令

# escape=\ (backslash)

# escape=` (backtick)

用来定义转义字符, 默认是反斜线(\

不管是否在Docker声明转义字符,转义字符都不会再RUN命令当中执行,除了每一行的末尾。这会导致在使用路径的时候,导致一些错误。如:

FROM microsoft/nanoserver

COPY testfile.txt c:\\

RUN dir c:\

构建结果:

PS C:\John> docker build -t cmd .

Sending build context to Docker daemon 3.072 kB

Step 1/2 : FROM microsoft/nanoserver

---> 22738ff49c6d

Step 2/2 : COPY testfile.txt c:\RUN dir c:

GetFileAttributesEx c:RUN: The system cannot find the file specified.

PS C:\John>

解决方式:定义反斜线(`)作为转义字符

# escape=`

环境变量替代(Environment replacement)

环境变量(通过ENV语句进行声明)可以被用于适当的指令当中被插值到Dockerfile当中。转义也可以被处理用于包含像变量的语言到语句当中。

环境变量通过符号显示,要么是 $variable_name 或者 ${variable_name }.他们都会被同样地被处理。

同时支持一些bash标示符:

    ${ variable: -word} word是默认值

    ${ variable: +word} 变量为word时有效,否则返回的是空字符串

支持环境变量的指令:

ADD COPY ENV EXPOSE FROM LABEL STOPSIGNAL USER VOLUME WORKDIR

ONBUILD当和以上结合时,也会支持

环境变量替换将在整个指令中对每个变量使用相同的值

ENV abc=hello

ENV abc=bye def=$abc

ENV ghi=$abc

def的值是hello  ghi的值是 bye

.dockerignore file

避免发送大型和敏感文件或路径到守护线程当中,比如node里面的node_module路径,这个路径是很庞大且文件数量很多,如果用于构建镜像,这会耗费很多的时间。

很多管理工具都会有这种类似的文件,具体见以下链接:http://home.hyywk.top:4000/engine/reference/builder/#dockerignore-file

FROM 控制命令

FROM <image> [ AS <name>]

FROM <image>[:<tag>] [AS <name>]

FROM <image>[@<digest>] [AS <name>]

FROM会初始化一个新构建的阶段并设置基本镜像用于子序列指令。比如,一个有效的Dockerfile必须以一个FROM命令开始。image可以是任何有效的镜像,并容易从镜像仓库中拉取。

    1. ARG 是唯一一个指令可以先于FROM指令,http://home.hyywk.top:4000/engine/reference/builder/#understand-how-arg-and-from-interact

    2. FROM 可以在一个Dockerfile文件中创建多个镜像文件,每一个创建后会输出一个镜像id

    3. 可以为某个构建起别名,用于后续的FROM命令和COPY --from=<name|index>命令相关的镜像来基于这个阶段

    4. tag或digest是可选的,默认是latest。如果找不到tag值,则会报错。

理解 ARG 和 FROM的交互

FROM 命令支持通过任何ARG命令声明的变量

如:

ARG CODE_VERSION=latest

FROM base:${CODE_VERSION}

CMD  /code/run-app

FROM extras:${CODE_VERSION}

CMD  /code/run-extras

一个ARG在FROM之前声明的变量是在构建作用域之外,所以不能在FROM之后再次使用,如果要使用之前声明的默认值,需要在FROM再次无参数声明:

ARG VERSION=latest

FROM busybox:$VERSION

ARG VERSION

RUN echo $VERSION > image_version

RUN命令(构建时运行)

格式:

RUN <command> (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows)

RUN ["executable", "param1", "param2"] (exec form)

RUN指令会在当前镜像的顶层执行一些指令,并提交这个结果用于Dockerfile的下一步

shell from可以更改默认shell,使用 \ 继续写入第二行的执行命令,如下:

RUN /bin/bash -c 'source $HOME/.bashrc; \

echo $HOME'

exec from 形式更改默认shelll,且exec from格式必须使用双括号格式

RUN ["/bin/bash", "-c", "echo hello"]

exec form进一步说明,exec form形式不会调用一个命令行外壳。不会有shell处理,RUN [ "echo", "$home" ],将不会被变量替代。如果希望shell处理,要么使用shell form形式,要么直接执行一个shell:RUN [ "sh", "-c", "echo $HOME" ]

exec form必须使用双引号,并且必要时,必须使用转义,如windows下的路径

RUN命令在下一次构建执行时,会默认使用缓存。不是用缓存

docker build --no-cache


Known issues(RUN)

http://home.hyywk.top:4000/engine/reference/builder/#known-issues-run

CMD(容器运行时执行)

CMD命令有三种格式

CMD ["executable","param1","param2"] (exec form, this is the preferred form)

CMD ["param1","param2"] (as default parameters to ENTRYPOINT)

CMD command param1 param2 (shell form)

CMD命令只能在Dockerfile当中有一个(the latest有效)。

CMD的主要目的是为执行容器提供默认值。这些默认包括可执行程序或者可忽略的执行程序,在那种情况下你必须定义一个ENTRYPOINT命令

docker容器的主线程(dockfile中CMD执行的命令)结束,容器会退出

1. 如果CMD为ENTRYPOINT提供默认参数,则必须都要是JSON array format

2. exec form使用双引号

LABEL(添加meta信息)

格式:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

示例:

LABEL "com.example.vendor"="ACME Incorporated"

LABEL com.example.label-with-value="foo"

LABEL version="1.0"

LABEL description="This text illustrates \

that label-values can span multiple lines."

EXPOSE

EXPOSE <port> [<port>/<protocol>...]

定义容器在运行时监听的网络端口(tcp或udp类型,默认是tcp),该命令仅用于文档声明,不会确切地坚定,应通过docker run -p 选项来执行运行时监听的端口。

示例:

expose 80/udp

expose 80/tcp


ENV 为声明环境变量(可在容器中或构建阶段直接使用)

格式

ENV <key> <value>

ENV <key>=<value> ...

示例:

ENV myName="John Doe" myDog=Rex\ The\ Dog \

    myCat=fluffy

ENV myName John Doe

ENV myDog Rex The Dog

ENV myCat fluffy

ADD

该命令用从src中的 文件、目录和远程url复制,添加到 dest当中

格式:

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

ADD [--chown=<user>:<group>] ["<src>",... "<dest>"] (this form is required for paths containing whitespace)

<src>可以通过通配符号来匹配,如 * ? 

具体匹配规则:http://golang.org/pkg/path/filepath#Match

<dest> 是绝对路径或者相对WORKDIR的相对路径

ADD test relativeDir/ # adds "test" to `WORKDIR`/relativeDir/

ADD test /absoluteDir/        # adds "test" to /absoluteDir/

新的文件被复制之后,默认UID和GID(权限控制)均为0,使用--chown来定义给定的用户名、组名或者UID和GID的结合,可以提供一个用户名没有组名、一个没有gid的uid会使用相同的uid作为gid、如果提供了用户名或组名,容器的根文件系统/etc/passwd和/etc/group文件将分别用于将名称转换为整数UID或GI,eg:

ADD --chown=55:mygroup files* /somedir/

ADD --chown=bin files* /somedir/

ADD --chown=1 files* /somedir/

ADD --chown=10:11 files* /somedir/

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

注意:目录本身不会被复制,只是它的内容。

如果<src>是以可识别压缩格式(identity、gzip、bzip2或xz)的本地tar归档文件,则将其解压为目录。来自远程url的资源不会被解压。复制或解压目录时,其行为与tar -x相同,结果为:

在目标路径和

源树的内容,其中冲突的解决有利于“2”。“在一个文件一个文件的基础上。

注意:文件是否被识别为可识别的压缩格式,完全取决于文件的内容,而不是文件的名称。例如,如果一个空文件恰好以.tar结尾。这将不会被识别为压缩文件,也不会生成任何类型的解压缩错误消息,而是将文件简单地复制到目的地。

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

如果指定了多个<src>资源,要么直接指定,要么使用通配符指定,那么<dest>必须是一个目录,并且必须以斜杠/结束。

如果<dest>没有结尾斜杠,它将被认为是一个常规文件,<src>的内容将写在<dest>。

如果<dest>不存在,它将与其路径中所有丢失的目录一起创建。

COPY

几乎和ADD一样,不同之处就是,只能使用本地文件

ENTRYPOINT

配置容器可作为一个可执行文件运行

格式:

ENTRYPOINT ["executable", "param1", "param2"] (exec form, preferred)

ENTRYPOINT command param1 param2 (shell form)

功能是启动时的默认命令


语法如下:

1. ENTRYPOINT ["executable", "param1", "param2"]

2. ENTRYPOINT command param1 param2

ENTRYPOINT的命令作为 /bin/sh -c的子命令,意味着不会接受Unix信号,所以执行的程序的pid不会是1,也不会接受docker stop <container>.发送的信号

如果从上到下看到这里的话,那么你应该对这两种语法很熟悉啦。

第二种就是写shell

第一种就是可执行文件加参数


与CMD比较说明(这俩命令太像了,而且还可以配合使用):

1. 相同点:

只能写一条,如果写了多条,那么只有最后一条生效

容器启动时才运行,运行时机相同


2. 不同点:

 ENTRYPOINT不会被运行的command覆盖,而CMD则会被覆盖

 如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数

如下:

FROM ubuntu

ENTRYPOINT ["top", "-b"]

CMD ["-c"]

如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效

如下:

FROM ubuntu

ENTRYPOINT ["top", "-b"]

CMD ls -al

那么将执行ls -al ,top -b不会执行。

官方比较图:



CMD 和 ENTRYPOINT 的比较


VOLUME 

可实现挂载功能,可以将内地文件夹或者其他容器种得文件夹挂在到这个容器种

语法为:

VOLUME ["/data"]

说明:

   ["/data"]可以是一个JsonArray ,也可以是多个值。所以如下几种写法都是正确的

VOLUME ["/var/log/"]

VOLUME /var/log

VOLUME /var/log /var/db

一般的使用场景为需要持久化存储数据时

容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。

所以当数据需要持久化时用这个命令。\

User 

设置启动容器的用户,可以是用户名或UID,所以,只有下面的两种写法是正确的

USER daemo

USER UID

注意:如果设置了容器以daemon用户去运行,那么RUN, CMD 和 ENTRYPOINT 都会以这个用户去运行

WORKDIR


语法:

WORKDIR /path/to/workdir


设置工作目录,对RUN,CMD,ENTRYPOINT,COPY,ADD生效。如果不存在则会创建,也可以设置多次。


如:

WORKDIR /a

WORKDIR b

WORKDIR c

RUN pwd

pwd执行的结果是/a/b/c


WORKDIR也可以解析环境变量

如:

ENV DIRPATH /path

WORKDIR $DIRPATH/$DIRNAME

RUN pwd

pwd的执行结果是/path/$DIRNAME


ARG

语法:

ARG <name>[=<default value>]

设置变量命令,ARG命令定义了一个变量,在docker build创建镜像的时候,使用 --build-arg <varname>=<value>来指定参数

如果用户在build镜像时指定了一个参数没有定义在Dockerfile种,那么将有一个Warning

提示如下:

[Warning] One or more build-args [foo] were not consumed.


我们可以定义一个或多个参数,如下:

FROM busybox

ARG user1

ARG buildno

...

也可以给参数一个默认值:

FROM busybox

ARG user1=someuser

ARG buildno=1

...

如果我们给了ARG定义的参数默认值,那么当build镜像时没有指定参数值,将会使用这个默认值



ONBUILD

语法:

ONBUILD [INSTRUCTION]

这个命令只对当前镜像的子镜像生效。

比如当前镜像为A,在Dockerfile种添加:

ONBUILD RUN ls -al

这个 ls -al 命令不会在A镜像构建或启动的时候执行


此时有一个镜像B是基于A镜像构建的,那么这个ls -al 命令会在B镜像构建的时候被执行。



STOPSIGNAL

语法:

STOPSIGNAL signal

STOPSIGNAL命令是的作用是当容器推出时给系统发送什么样的指令



HEALTHCHECK

 容器健康状况检查命令

语法有两种:

1. HEALTHCHECK [OPTIONS] CMD command

2. HEALTHCHECK NONE

第一个的功能是在容器内部运行一个命令来检查容器的健康状况

第二个的功能是在基础镜像中取消健康检查命令


[OPTIONS]的选项支持以下三中选项:

    --interval=DURATION 两次检查默认的时间间隔为30秒

    --timeout=DURATION 健康检查命令运行超时时长,默认30秒

    --retries=N 当连续失败指定次数后,则容器被认为是不健康的,状态为unhealthy,默认次数是3


注意:

HEALTHCHECK命令只能出现一次,如果出现了多次,只有最后一个生效。


CMD后边的命令的返回值决定了本次健康检查是否成功,具体的返回值如下:

0: success - 表示容器是健康的

1: unhealthy - 表示容器已经不能工作了

2: reserved - 保留值


例子:

HEALTHCHECK --interval=5m --timeout=3s \

CMD curl -f http://localhost/ || exit 1


健康检查命令是:curl -f http://localhost/ || exit 1

两次检查的间隔时间是5秒

命令超时时间为3秒

ONBUILD 

 ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN , COPY 等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。

 Dockerfile 中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD 是为了帮助别人定制自己而准备的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容