Docker之十二:Dockerfile

  • Dockerfile 指令
  • Dockerfile 配置指令
  • ARG
  • FROM
  • LABEL
  • EXPOSE
  • ENV
  • ENTRYPOINT
  • VOLUM
  • USER
  • WORKDIR
  • ONBUILD
  • STOPSIGNAL
  • HEALTHCHECK
  • SHELL
  • Dockerfile 操作指令
  • RUN
  • CMD
  • ADD
  • COPY
  • 创建镜像
  • docker build 命令选项说明
  • 选择父镜像
  • 使用 .dockerignore 文件
  • 多步骤创建


Dockerfile 是一个用来构建镜像的文本文件。

Dockerfile 指令

Dockerfile 配置指令

指令

说明

ARG

定义创建镜像过程中使用的变量

FROM

指定所创建镜像的基础镜像

LABEL

为生成的镜像添加元数据标签信息

EXPOSE

声明镜像内服务监听的端口

ENV

指定环境变量

ENTRYPOINT

指定镜像的默认入口命令

VOLUME

创建一个数据卷挂载点

USER

指定运行容器时的用户名或 UID

WORKDIR

配置工作目录

ONBUILD

创建子镜像时指定自动执行的操作指令

STOPSIGNAL

指定退出的信号值

HEALTHCHECK

配置启动容器如何进行健康检查

SHELL

指定默认 shell 类型

ARG

定义创建镜像过程中使用的变量,但ARG定义的变量只在 Dockerfile 内有效,并在 docker build过程中发挥作用,但镜像编译成功后变量将不再存在(这点于 ENV 有区别,ENV指定的变量将在镜像中保留,也就是说 ENV 的作用域是全局的)。

# 格式
ARG <name>[=<default value>]
# name 表示变量名或参数名,default value 表示默认值
# docker build 时,可以通过 --build-arg<name>=<value> 来为变量重新赋值
# Docker 内置了一些镜像创建变量,用户可以直接使用而无需声明(不区分大小写):HTTP_PROXY、HTTPS_PROXY、FTP_PROXY、NO_PROXY

FROM

指定创建镜像使用的基础镜像。Dockerfile 中的第一条指令必须时 RROM 指令。同一个 Dockerfile 中创建多个镜像时,可以使用多个 FROM 指令。

# 格式
FROM <image>
# 或
FROM <image>:<tag>
# 例:
ARG VERSION=9.3
FROM debain:${VERSION}

LABEL

为生成的镜像添加元数据标签信息,这些数据可以用来辅助过滤出特定镜像。

# 格式
LABEL <key>=<value> <key>=<value> <key>=<value> ...
# 例:
LABEL version="1.0.0-rc3"
LABEL author="fgd" date="2020-12-10"
LABEL description="This text illustrates \
	that label-values can span multiple lines."

镜像创建完成后,生成容器,可以使用 docker inspect 查看镜像的 “Lables“ 信息。

EXPOSE

声明镜像内服务监听的端口,其作用也只是声明而已,可以帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口;或 -p HOST_PORT:CONTAINER_PORT 具体指定所映射的本地端口。

# 格式
EXPOSE <port1> [<port2>/<protocol>...]
# 如:EXPOSE 22 80 8843

ENV

指定环境变量,在进行生成过程中会被后续的 RUB 指令使用,在镜像启动的容器中也会存在。

# 格式
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
# 指令指定的环境变量在运行时可以被覆盖掉,如:docker run --env <key>=<value> image
# 例
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
# 当一条 ENV 指令中同时为多个环境变量赋值并且值也是从环境变量读取时,会为变量都赋值后再更新
ENV key1=value2
ENV key1=value1 key2=${key1}
# 最终的结果为 key1=value1 key2=value2

ENTRYPOINT

指定镜像的默认入口命令,该入口命令会再启动容器时作为根命令执行,所有传入值作为该命令的参数。Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

# 格式1:exec调用执行
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
# 格式2:shell中执行,CMD指令指定值作为根命令参数
ENTRYPOINT command param1 param2

运行时(docker run),可以被 --entrypoint 选项覆盖掉,如:docker run --entrypoint 。

VOLUM

创建一个数据挂在点。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

# 格式:
VOLUME ["<路径1>", "<路径2>"...]
# 或
VOLUME <路径>

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

USER

指定运行容器时的用户名或 UID(用户和用户组),后续的 RUN 等指令也会使用指定的用户身份。用户和用户组必须提前已经存在。

# 格式:
USER <用户名>[:<用户组>]

当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在Dockerfile中创建所需要的用户:

RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres
# 要临时获取管理员权限可以使用 gosu 命令

WORKDIR

为后续的 RUN、 CMD、ENTRYPOINT 指令指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在.。

# 格式:
WORKDIR <工作目录路径>

可以使用多个 WORKDIR 指令,后续的命令如果参数时相对路径,则会基于之前命令指定路径。例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

此时最终路径是:/a/b/c,因此为了避免出错,推荐 WORKDIR 指令中使用决定路径。

ONBUILD

指定当基于所生成镜像创建子镜像是,自动执行的操作指令。

# 格式:
ONBUILD [INSTRUCTION]

例如,使用如下的 Dockerfile 创建父镜像(ParentImage),指定 ONBULID指令:

# Dockerfile for ParentImage
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

使用 docker build 命令创建子镜像 ChildImage 时(FROM ParentImage),会首先执行 ParentImage 中配置 ONBUILD 指令:

# Dockerfile for ChildImage
FROM ParentImage

等价于在 ChildImage 的Dockerfile 中添加了如下指令:

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
...

由于 ONBUILD 指令时隐式执行的,推荐在使用它的镜像标签中进行标注,例如:ruby:2.1-onbuild 。
ONBUILD 指令在创建专门用于自动编译、检查等操作的基础镜像时,十分有用。

STOPSIGNAL

指定所创建镜像的穷奇接收退出信号的值。

# 格式
STOPSIGNAL signal
# signal 代表信号名或者信号所代表的值

HEALTHCHECK

配置所启动的容器如何进行健康检查,用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

# 格式1:设置检查容器健康状况的命令,根据所执行命令返回值是否为 0 来判断
HEALTHCHECK [OPTIONS] CMD <command>
# OPTIONS支持的参数:
#	-interval=DURATION(default:30s):过多久检查一次
#	-timeout=DURATION(default:30s):每次检查等待结果的超时
#	-retries=N(default:3):如果失败,重复几次才最终确定失败

# 格式2:进制基础镜像中的健康检查,如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK NONE

SHELL

指定其他命令使用 shell 时默认 shell 类型。

# 格式:
SHELL ["executable", "parameters"]
# 默认值为 ["/bin/sh", "-c"]

Dockerfile 操作指令

指令

说明

RUN

运行指定命令

CMD

启动容器时指定默认执行的命令

ADD

添加内容到镜像

COPY

复制内容到镜像

RUN

运行指令命令,用于执行其后跟的命令行命令。

# shell 格式 :
RUN <command>
# <command> 等同于在终端操作的 shell 命令,如:RUN apt-get update

# exec 格式:
RUN ["executable", "param1", "param2"]
# executable 为可执行文件,param 为参数,如:RUN ["/bin/bash", "-c", "echo hello"]

CMD

用于运行程序,指定启动容器(docker run)时默认执行的命令。为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。Dockerfile 中只能存在一个CMD命令,如果指定了多条命令,仅最后一条有效。

# 格式1:CMD <shell命令>
CMD command param1 param2

# 格式2:
CMD ["executable", "param1", "param2"]
# executable 为可执行文件或命令, param 为参数,相当于执行 executable param1 param2,Docker 推荐的使用方式

# 格式2:
CMD ["param1", "param2"]
# 为 ENTRYPOINT 指令指定的程序提供默认参数

ADD

添加内容到镜像。在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>;但在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

# 添加内容到
ADD <src> <dest>
# <src> 可以是Dockerfile 所在目录的一个相对路径(文件或目录);也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。
# <dest> 可以是镜像内绝对路径,或者相对于工作目录(WORKDIR)的相对路径
# 如:ADD *.c /code/

COPY

复制内容到镜像,可以从上下文目录中复制文件或者目录到容器里指定路径。

# 格式1:
COPY [--chown=<user>:<group>] <src1>...  <dest>
# 格式2:
COPY [--chown=<user>:<group>] ["<src1>",...  "<dest>"]

# [--chown=<user>:<group>] 是可选参数,可以改变复制到容器内文件的拥有者和属组
# <src1> 可以支持通配符,如:COPY myfile* /mydir/

创建镜像

编写完成 Dockerfile 后,需要通过 docker build 命令来创建进项。

# 格式
docker build [OPTIONS] PATH | URL | -
# 或
docker image build [OPTIONS] PATH | URL | -

该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下所有数据作为上下文(Context)发送给 Docker 服务端。Docker 服务端在校验 Dockerfile 格式通过后,逐条执行其中定义的指令,碰到 ADD、COPY 和 RUN 指令会生成一层新的镜像。最终如果创建镜像成功,会返回最终镜像的ID。
如果上下文过大,会导致发送大量数据给服务端,延缓创建过程。因此除非是生成镜像所必需的文件,不然不要放到上下文路径下。如果使用非上下文路径下的 Dockerfile,可以通过 -f 选项来指定其路径。
要指定生成镜像的标签信息,可以通过-t选项。该选项可以重复使用多次为镜像一次添加多个名称。

# 例如:
$ docker build -t builder/first_image:1.0.0 /tmp/docker_builder/
# builder/first_image:1.0.0 为指定的镜像标签
# /tmp/docker_builder/ 为指定的上下文路径

docker build 命令选项说明

arg参数 docker dockerfile arg env_arg参数 docker

选择父镜像

父镜像可以分为两类,一类是基础镜像(baseimage),另外一种是普通镜像(基于基础镜像)。
基础镜像的 Dockerfile 文件中往往不存在 FROM 指令,或者基于 seratch 镜像(FROM seratch),其在整个镜像树中处于根位置。
一个基础镜像的 Dockerfile 例子:

# 用户将提前编译好的二进制可执行文件 binary 复制到镜像中,运行容器时执行 binary 命令
FROM scratch
ADD binary
CMD ["/binary"]

普通镜像也可以作为父镜像来使用,包括常见的 busybox、debain、Ubuntu 等。

Docker不同类型镜像之间的继承关系:

arg参数 docker dockerfile arg env_arg参数 docker_02

使用 .dockerignore 文件

可以使用 .dockerignore 文件(每一行添加一条匹配模式)来让 Docker 忽略匹配路径或文件,在创建镜像的时候不将无关数据发送到服务端。

# .dockerignore 文件中可以定义忽略模式
*/temp*
*/*/tmmp*
tmp?
~*
Dockerfile
!README.md

.dockerignore 文件中模式语法支持 Golang 风格的路径正则格式。
”*“ 表示任意一个多字符。
”?” 表示单个字符。
“!“ 表示不匹配,即忽略指定的路径或文件

多步骤创建

Docker 支持多步骤创建(Multi-stage build)特性,可以精简最终生成的镜像大小。

对于需要编译的应用(如C、Java 或 Go 语言等)来说,通常情况下至少需要准备两个环境的 Docker 镜像:

  1. 编译环境镜像:包括完整的编译引擎、依赖库等,往往比较庞大。作用时编译应用为二进制文件。
  2. 运行环境镜像:利用编译好的二进制文件,运行应用,由于不需要编译环境,体积比较小。

使用多步骤创建,可以保证最终生成的运行环境保持精简的情况下,使用单一的 Dockerfile,降低维护复杂度。

以 Go 语言应用为例。创建干净目录,进入到目录中,创建 main.go 文件,内容是:

// main.go will output "Hello Docker"
package main

import {
	"fmt"
}

func main() {
	fmt.Println{"Hello Docker"}
}

创建 Dockerfile,使用 golang:1.9 镜像 编译应用二进制文件为 app, 使用精简镜像 alpine:latest 作为运行环境。Dockerfile 的完整内容为:

# define stage name as builder
FROM golang:1.9 as builder
RUN mkdir -p /go/src/test
WORKDIR /go/src/test
COPY main.go .
RUN CGO_ENABLED=0 GOOS=linux go build -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root
# copy file from the builder stage
COPY --from=builder /go/src/test/app . 
CMD ["./app"]

创建镜像:

fgd@ubuntu:/home/workspace/go$ docker build -t test-multistage:lastest .

arg参数 docker dockerfile arg env_Dockerfile_03


查看镜像:最终镜像只有7.94MB

arg参数 docker dockerfile arg env_docker_04


运行应用:

$ docker run --rm test-multistage:lastest

arg参数 docker dockerfile arg env_arg参数 docker_05