Docker Dockerfile创建镜像说明(十三)

Docker Dockerfile创建镜像说明(十三)

Scroll Down

本篇上接文章《Docker Dockerfile指令说明》

讲述了dockerfile文件的制作,然后来制作镜像文件就需要用到docker build命令创建。

1. docker build

docker build [OPTIONS] PATH | URL | -

       --add-host list           # 添加自定义的主机名到IP地址的映射
       --build-arg list          # 设置镜像创建时的变量
       --cache-from strings      # 使用指定镜像作为缓存源
       --cgroup-parent string    # 继承的上层cgroup
       --compress                # 使用gzip来压缩创建上下文数据
       
       --cpu-period int          # 限制 CPU CFS 周期
    -c,--cpu-quota  int          # 限制 CPU CFS 配额
       --cpu-shares int          # 设置 CPU 使用权重
       --cpuset-cpus string      # 多个CPU指定使用的 CPU
       --cpuset-mems string      # 多个CPU指定使用的内存 id

       --disable-content-trust   # 忽略镜像检验,默认开启

   -f,-file string              # 指定要使用的 Dockerfile 路径 (默认是 'PATH/Dockerfile')
       --force-rm                # 设置镜像过程中删除中间容器
       --iidfile string          # 将镜像ID写入到文件
       --isolation string        # 使用容器隔离技术
       --label list              # 设置镜像使用的元数据
   -m, --memory bytes            # 限制使用内存量
       --memory-swap             # 设置 Swap 的最大值为内存和缓存的总量,'-1' 表示不限 swap
       --network string          # 指定run命令的网络模式
       --no-cache                # 创建镜像的过程中不适用缓存

       --pull                    # 尝试更新镜像的最新版本
    -q,--quiet                   # 安静模式,不打印创建过程的日志信息
       --rm                      # 设置镜像成功后删除中间容器
       --shm-size                # 设置 /dev/shm 的大小,默认值是 64M
       --security-opt strings    # 安全选项
       --ulimit Ulimit           # Ulimit 配置
      
   -t, --tag list                # 镜像的名字及标签,通常 name:tag 或者 name 格式,可以在一次构建中为一个镜像设置多个标签
       --target string           # 指定创建的目标阶段

该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下所有数据作为上下文(Context)发送给Docker服务端。Docker服务端在校验Dockerfile格式通过后,逐条执行其中定义的指令,碰到ADD、COPY和RUN指令会生成一层新的镜像。最终如果创建镜像成功,会返回最终镜像的ID。

如果上下文过大,会导致发送大量数据给服务端,延缓创建过程。因此除非是生成镜像所必需的文件,不然不要放到上下文路径下。如果使用非上下文路径下的Dockerfile,可以通过-f选项来指定其路径。

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

例如,上下文路径为/tmp/docker_builder/,并且希望生成镜像标签为builder/first image:1.0.0,

可以使用下面的命令:

docker build -t builder/first_image:1.0.0 /tmp/docker_builder/

使用当前目录的 Dockerfile 创建镜像,标签为 dongzao/example:v1

docker build -t dongzao/example:v1 

使用 URL http://zabbx.cn/www/file 的 Dockerfile 创建镜像

docker build http://zabbx.cn/www/file

通过 -f Dockerfile 文件的位置

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

例如:

docker build -f /var/nginx_build/Dockerfile

2. 选择父镜像

大部分情况下,生成新的镜像都需要通过FROM指令来指定父镜像。父镜像是生成镜像的基础,会直接影响到所生成镜像的大小和功能。

1_okuuRBIPznLeqACWKVRSg.png

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

  • 一种是所谓的基础镜像(baseimage)
  • 一种是普通的镜像(往往由第三方创建,基于基础镜像)

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

下面的Dockerfile定义了一个简单的基础镜像,将用户提前编译好的二进制可执行文件binary复制到镜像中,运行容器时执行binary命令:

FROM scratch
ADD binary/
CMD["/binary"]

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

3. .dockerignore文件

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

当我们在 docker build 的过程中,首先会将指定的上下文目录打包传递给 docker引擎,而这个上下文目录中可能并不是所有的文件我们都会在 Dockerfile 中使用到,那么这个时候就可以在 .dockerignore 文件中指定在传递给 docker引擎 时需要忽略掉的文件或文件夹。

例如下面的例子中包括了6行忽略的模式(第一行为注释):

#.dockerignore文件中可以定义忽略模式

#排除 test 目录下的所有文件
test/*
#排除 md 目录下的 xttblog.md 文件
md/xttblog.md
#排除 xttblog 目录下的所有 .md 的文件
xttblog/*.md
#排除以 xttblog 为前缀的文件和文件夹
xttblog?
#排除所有目录下的 .sql 文件夹
**/*.sql 
  • dockerignore文件中模式语法支持Golang风格的路径正则格式:
  • “*”表示任意多个字符;
  • “?”代表单个字符;
  • “!”表示不匹配(即不忽略指定的路径或文件)

4. 多步骤创建

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

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

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

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

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

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

package main

import(

“fmt"

func main(){

fmt.Print1n("Hello,Docker")

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

FROM golang:1.9 as builder #define stage name 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--fromabuilder/go/src/test/app.# copy file from the builder stage

CMD["./app"]

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

docker build-t yeasy/test-multistage:latest.

Sending build context to Docker daemon 3.072kB

step 1/10:FROM golang:1.9

Successfully built 5fdocb93dda0

Successfully tagged yeasy/test-multistage:latest

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

Hel1o,Docker

查看生成的最终镜像,大小只有6.55MB:

docker imageslgrep test-multistage

yeasy/test-multistage latest 5fdocb93dda0 1 minutes ago 6.55MB

5. 实践经验

所谓最佳实践,就是从需求出发,来定制适合自己、高效方便的镜像。

首先,要尽量吃透每个指令的含义和执行效果,多编写一些简单的例子进行测试,弄清楚了再撰写正式的Dockerfile。此外,Docker Hub官方仓库中提供了大量的优秀镜像和对应的Dockefile,可以通过阅读它们来学习如何撰写高效的Dockerfile。

  • 精简镜像用途:尽量让每个镜像的用途都比较集中单一,避免构造大而复杂、多功能的镜像;
  • 选用合适的基础镜像:容器的核心是应用。选择过大的父镜像(如Ubuntu系统镜像)会造成最终生成应用镜像的臃肿,推荐选用瘦身过的应用镜像(如node:slim),或者较为小巧的系统镜像(如alpine、busybox或debian);
  • 提供注释和维护者信息:Dockerfile也是一种代码,需要考虑方便后续的扩展和他人的使用;
  • 正确使用版本号:使用明确的版本号信息,如1.0,2.0,而非依赖于默认的latest。通过版本号可以避免环境不一致导致的问题;
  • 减少镜像层数:如果希望所生成镜像的层数尽量少,则要尽量合并RUN、ADD和COPY指令。通常情况下,多个RUN指令可以合并为一条RUN指令;
  • 恰当使用多步骤创建:通过多步骤创建,可以将编译和运行等过程分开,保证最终生成的镜像只包括运行应用所需要的最小化环境。当然,用户也可以通过分别构造编译镜像和运行镜像来达到类似的结果,但这种方式需要维护多个Dockerfile。
  • 使用.dockerignore文件:使用它可以标记在执行docker build时忽略的路径和文件,避免发送不必要的数据内容,从而加快整个镜像创建过程。
  • 及时删除临时文件和缓存文件:特别是在执行apt-get指令后,/var/cache/apt下面会缓存了一些安装包;
  • 提高生成速度:如合理使用cache,减少内容目录下的文件,或使用.dockerignore文件指定等;
  • 调整合理的指令顺序:在开启cache的情况下,内容不变的指令尽量放在前面,这样可以尽量复用;
  • 减少外部源的干扰:如果确实要从外部引入数据,需要指定持久的地址,并带版本信息等,让他人可以复用而不出错。
  • 不要相信任意基础图像:始终使用漏洞扫描程序,一些开发人员将从别人创建的DockerHub中获取基本图像-因为乍一看它具有他们需要的软件包-然后将任意选择的容器推入生产环境。这样做有很多错误:您可能使用的代码版本错误,带有漏洞,其中有错误,或者更糟的是,它可能是故意捆绑了恶意软件-您只是不知道。
  • 每个容器仅使用一个进程:与保持基本映像较小有关的是,每个容器只能进行一个处理。容器与其托管的应用程序具有相同的生命周期,这意味着每个容器应只包含一个父进程。通常,容器和应用程序应同时启动。同样,当应用停止时,容器也应停止。如果一个容器中有多个进程,则可能会出现应用程序状态混合的情况,这可能导致Kubernetes无法确定容器是否正常。

76430292_p0_master1200.jpg