Docker: 使用Dockerfile构建镜像

Dockerfile文件构建镜像:

  • Dockerfile的基本结构
  • 所支持的内部指令
  • 使用它创建镜像的基本过程
  • 合理的构建镜像

Dockerfile基本结构:

Dockerfile是一个文本格式的配置文件,可以使用Dockerfile来快速创建自定义的镜像。

Dockerfile:一行行命令语句+以#开头的注释行。

Dockefile主体内容:

  1. 基础镜像信息
  2. 维护者信息
  3. 镜像操作命令
  4. 容器启动时执行指令

简单示例:

首行通过注释来指定解析器指令,后续通过注释说明镜像的相关信息。

主体部分首先用FROM指令来指明所基于的镜像名称。接下来是使用LABEL指令说明维护者信息。接下来是镜像的操作指令。没运行一条RUN指令,镜像添加新的一层,最后是CMD指令,指定运行容器时的操作命令。

可查看更多官方镜像的Dockerfile写法。


指令说明:

Dockerfile中的指令一般格式:INSTRUCTION arguments.包括”配置指令(配置镜像信息)“、”操作指令(具体执行操作)“

Dockerfile中的指令及说明
分类 指令 说明
配置指令

ARG

FROM

LABEL

EXPOSE

ENV

ENTRYPOINT

VOLUME

USER

WORKDIR

ONBUILD

STOPSIGNAL

HEALTHCHECK

SHELL

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

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

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

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

指定环境变量

指定镜像的默认入口命令

创建一个数据卷挂载点

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

配置工作目录

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

指定退出的信号值

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

指定默认shell类型

操作指令

RUN 

CMD

ADD

COPY 

运行指定命令。

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

添加镜像

复制内容到镜像

上述指令的具体说明:

1.配置镜像:

1.1 ARG:定义创建镜像过程中使用的变量。

格式:ARG <name>[=<default value>]

执行docker build时,通过-build--arg[=]来为变量复制。当镜像编译成功后,ARG指定的变量将不再存在。(ENV指定的变量将在镜像中保留)。

docker内置了一些镜像创建变量(直接使用不需要声明):HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY

1.2 FROM:指定所创建镜像的基础镜像。

格式:FROM <image> [AS <name> ] 

     或 FROM <imgae>:<tag> [AS <name>]

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

任何Dockerfile中第一条指令必须是FROM指令。如果在同一个Dockerfile创建多个镜像时,可以使用多个FROM指令(每个镜像一次)。

示例:

ARG VERSION = 3

FROM nginx:${VERSION}

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

格式:LABEL <key>=<value> .....

示例:

LABEL version = "1.0.0"

LABEL author = "yinlei" data = "2020-01-16"

LABEL description = "This text is the test"

1.4 EXPOSE:声明镜像内服务监听的端口。

格式:EXPOSE <port> [<port>/<protocol>...]

EXPOSE指令只是声明作用,并不会自动完成端口映射。

如果要映射端口出来,在启动容器的时候应该使用-p参数。

示例:‘

EXPOSE 22 80 443

1.5 ENV: 指定环境变量,在镜像生成过程中hi被后续的RUN指令使用,在镜像启动的容器中也会存在。

格式: ENV <key> <value.

       或 ENV <key>=<value> ...

指定的环境变量在运行时候可以被覆盖掉。如docker run --env=xxxx built_image

示例:

ENV APP_VERSION = 1.0.0

ENV APP_HOME=/usr/local/app

ENV PATH $PATH:/usr/local/bin

当一条ENV指令中同时为多i个环境变量复制并且值也是从环境变量读取时,会为变量都赋值后再更新:

ENV key1 = value2

ENV key1 = value1 key2 = ${key1}

最终结果是key1=value1 key2 =value2

1.6 ENTRYPOINT:指定镜像的默认入口命令,该入口命令会再在启动容器时作为根命令执行,所i有传入值作为该命令的参数。

2种格式:

  • ENTRYPOINT ["executable","param1","param2"]: exec调用执行
  • ENTRYPOINT command param1 param2: shell中执行

此时,CMD指令指定值将作为根命令的参数

每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效。

在运行时,可以被--entrypoint参数覆盖掉。

1.7 VOLUME:创建一个数据卷挂载点。

格式: VOLUME ["/data"]

运行容器时候可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保持的数据等。

1.8 USER:指定运行容器时的用户名或UID。后续的RUN等指令也会使用指定的用户身份。

格式: USER daemon

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

RUYN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

要临时获取管理员权限可以使用gosu命令。

1.9 WORKDIR:为后续的RUN CMD ENTRYPOINT指定配置工作目录。

格式:WORKDIR /path/to/workdir

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

例如:

WORKDIR /a

WORKDIR /b

WORKDIR /c

最终路径:/a/b/c

推荐只使用绝对路径

1.10 ONBUILD:指定基于所生成镜像创建子镜像时自动执行的操作指令。

格式:ONBUILD [INSTRUCTION]

示例:使用dockerfile构建父镜像ParentImage,指定ONBUILD指令

# Docker for parentImage

ONBUILD ADD . /app/src

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

使用docker build 创建子镜像childImage时,会首先执行ParentImage中配置的ONBUILD指令:

FROM parentImage

等价于在ChildImage的dockerfile中添加了:

ADD . /app/src

RUN /usr/local/bin/python-build --dir /app/src

由于

ONBUILD是隐式执行的,推荐在使用它的镜像标签中进行标注:如ruby:2.1-onbuild

ONBUILD在创建专门用于自动编译、检查等操作的基础镜像的时候非常有用。

1.11 STOPSIGNAL:指定所创建镜像启动的容器接收退出的信号值。

STOPSIGNAL signal 

1.12 HEALTHCHECK:配置所启动容器如何进行健康检查。(dockjer 1.12+支持)

2种格式:

  • HEALTHCHECK [OPTIONS] CMD command:根据所执行命令返回值是否为0来判断
  • HEALTHCHECK NONE: 禁止基础镜像中的健康检查

OPTIONS支持的参数:

  • -interval = DURATION (default: 30s) 过多久检查一次
  • -timeout = DURATION(default: 30s)每次检查等待结果的超时时间
  • -retries = N (default: 3) 如果失败了,重试几次才最终确定失败

1.13 SHELL: 指定其他命令使用shell时的默认shell类型:

SHELL ["executable","parameters"]

默认值:["/bin/sh","-c"]

windows: shell路径使用了 \ 做为分隔符,一般在dockerfile开头加#eccape=' 来指定转义符。

2.操作指令:

2.1 RUN : 运行指定命令。

格式: RUN <command> 

       或 RUN  ["executable","parameter1",""parameter2"]

RUN  ["executable","parameter1",""parameter2"]这种方式的指令会被解析为json数组,所以需用双引号。且使用exec执行,不会启动shell环境。

RUN <command> 这种方式默认将在shell终端中运行命令。即/bin/sh -c;

指定使用其他终端类型可以使用第2个方式:

例如 RUN["/bin/bash","-c","echo yinlei"]

每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像层。

命令较长使用 \ 来换行。

2.2 CMD: 用来指定启动容器时默认执行的命令。

支持3种格式:

  • CMD ["executable","parameter1",""parameter2"]:相当于executable param1 param2
  • CMD cmmand param1 param2 :在默认的shell中执行,提供给需要交互的应用。
  • CMD ["param1","param2"]: 提供给ENTRYPOINT的默认参数

每个dockerfile只能有一条CMD命令。

指定多条命令,只有最后一条生效。

用户启动容器时候手动指定了运行的命令参数,则会覆盖掉dockerfile中指定的命令。

2.3 ADD:添加内容到镜像。

格式:ADD <src> <dest>

复制指定的<src>路径下的内容到容器中的<dest>路径下。

<src>可以是dockefile所在目录下的一个相对路径,可以是一个URL,可以是一个tar文件(自动解压为目录).

<dest>可以是镜像内绝对路径,或相对于工作目录WORKDIR的相对路径

路径支持正则。

例如:ADD *.c / code/

2.4 COPY: 复制内容到镜像。

格式: COPY <src> <dest>

复制本地主机的<src>(dockerfile所在目录的相对路径,文件或目录)下内容到镜像中的<dest>。目标路径不存在时会自动创建。

路径支持正则。

COPY与ADD指令功能类似,当使用本地目录为源目录时,推荐COPY


创建镜像:

编写完dockefile后,通过docker [image] build命令来创建镜像。

格式:docker build [OPTIONS] PATH | URL | - 

读取指定路径下(包括子目录)的dockefile.并将该路径下所有数据作为上下文context发送给docker服务端。

dockerf服务端在校验dockefile格式通过后,逐条执行其中定义的命令,碰到ADD COPY RUN指令会生成一层新的镜像。最终如果创建镜像成功会返回镜像的ID。

如果上下文过大,会导致发送大量数据给服务端,延缓创建过程。

除非是生成镜像所必须的文件,不然不要放回到上下文路径下。如果使用非上下文路径下的dockerfile,可以通过-f选项来指定其路径。

要指定生成镜像的标签信息,可通-t选项。可以重复使用-t为镜像一次添加多个名称。

OPTIONS支持的选项:

创建镜像的命令选项 及其说明
选项 说明
-add-host list 添加自定义的主机名到IP的映射
-build-arg list

添加创建时的变量

-cache-from strings 使用指定镜像作为缓存源
-cgroup-parent string

继承的上层cgroup

-compress 使用gzip来压缩创建上下文数据
-cpu-period int  分配的CFS调度器时长
-cpu-quota int CFS调度器总份额
-c, -cpu-shares int  CPU权重
-cpuset-cpus string 多CPU允许使用的CPU
-cpuset-mems string 多CPU允许使用的内存
-disable-content-trust 不进行镜像校验,默认为真
-f, -file string dockerfile名称
-force-rm  总是删除中间过程的容器
-iidfile string  将镜像id写入到文件
-isolation string 容器的隔离机制
-label list  配置镜像的元数据
-m,-memory bytes 限制内存和缓存的总量
-memory-swap bytes k限制内存和缓存的总量
-network string 指定RUN命令时候的网络模式
-no-cache

创建镜像时不使用缓存

-platform string 指定平台类型
-pull  总是尝试获取镜像的最新版本
-q,-quiet 不打印创建过程中的日志信息
-rm

创建成功后自动删除中间过程容器,默认为真

-security-opt strings 指定安全相关的选项
-shm-size bytest /dev/shm的大小
-squash 将新创建的多层挤压放入到一层中
-stream

持续获取创建的上下文

-t,-tag list 指定镜像的标签列表
-target string 指定创建的目标阶段
-ulimit ulimit 指定ulimit的配置

选择父镜像:

大部分情况下,生成的新镜像都需要通过FROM指令来指定父镜像。

父镜像是生成镜像的基础,会直接影响到所生成镜像的带下和功能。

用户可以选择2种镜像作为父镜像:

  1. 基础镜像
  2. 普通镜像(往往由第三方创建,基于基础镜像)

基础镜像比较特殊,其dockfile中往往不存在FROM指令,或者基于scratch镜像(FROM scratch),意味着其在整个镜像树中处于根的位置。

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

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


使用.dockerignore文件:

每一行添加一条匹配模式让Docker忽略匹配路径或文件,在创建镜像时不将无关的数据发送到服务器。使用方法和git的.gitignore类似。


多步骤创建:docker17.05+

Docker支持多步骤镜像创建特性,精简最终生成的镜像大小i。

对于需要编译的应用,通常至少需要准备2个环境的docker镜像:

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

优点:使用单一的dockefile,降低维护复杂度。

示例:

以go为例:

创建干净目录,并创建main,go

package main
import("fmt")

func main(){
    fmt.Println("hello world");
}

创建dockerfile,使用golang:1.9镜像编译应用2进制文件为app,使用精简的镜像alpine:latest作为运行环境。

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 -cacertificates
WORDIR /root/
COPY --from=builder /go/src/test/app . 
CMD ["./app"]

执行如下命令创建镜像并运行应用:

docker build -t yeasy/test-multistage:latest


docker run --rm yeasy/test-multistage:latest

查看成功的镜像大小:
docker images|grep test-multistage

合理的构建镜像:

尝试从下面几个角度进行思考完善镜像:

  • 精简镜像用途:尽量让每个镜像的用途都比较单一集中,避免构造大而复杂、多功能的镜像
  • 选用合适的基础镜像:容器的核心是应用。选择过大的父镜像会造成最终生成应用镜像的臃肿,推荐选用瘦身过的应用镜像或选择较为小巧的系统镜像
  • 提供注释和维护者信息
  • 正确使用版本号:通过版本号可以避免环境不一致导致的问题
  • 减少镜像层数:若希望所生成的镜像的层数尽量少,则要尽量合并RUN、ADD、COPY指令。多个RUN指令可以合并为1条RUN指令
  • 恰当使用多步骤创建:通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应用所需要的最小化环境。也可以通过分别构造编译镜像和运行镜像来达到类似的结果,但需要维护多个Dockefile.
  • 使用.dockerignore文件:标记在执行docker build时忽略的路径和文件,避免发送不必要的数据内容。
  • 及时删除临时文件和缓存文件:特别是在执行apt-get指令后,/var/cache/apt下会缓存一些安装包
  • 提升生成速度:合理的使用cache或.dockerignore
  • 调整合理的指令顺序:开启cache的情况下,内容不变的指令尽量放在前面,这样可以尽量复用
  • 减少外部源的告饶:需要从外部引入数据时,需要指定持久的地址,并带版本信息等。
发布了268 篇原创文章 · 获赞 36 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/qq_39969226/article/details/103999788