使用Dockerfile创建镜像
文本格式的配置文件
基本结构
由一行行命令语句组成,支持 # 注释
一般,主体分为4部分:基础镜像信息,维护者信息,镜像操作指令,容器启动时执行指令
# 镜像名称
FROM ubuntu:xeniel
# 维护者信息
LABEL maintainer docker_user<docker_user@email.com>
# 镜像操作指令 每运行一条RUN指令,镜像添加新的一层,并提交
RUN echo "deb http://archive.ubuntu.com/ubuntu/ xeniel main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# CMD指令 运行容器时的操作指令
CMD /usr/sbin/nginx
指令说明
1.ARG
定义创建创建镜像过程中使用的变量
格式:
- ARG <name> [=<default value>]
在执行docker build时,可以通过 -build-arg [=] 来为变量赋值,当镜像编译成功后,ARG指定的变量将不再存在(ENV指定的变量将在镜像中保留)
内置变量(不区分大小写):HTTP_PROXY, HTTPS_PROXY, FTP_PROXY, NO_PROXY
2.FROM
指定所创建镜像的基础镜像
格式:
- FROM <image> [AS <name>]
- FROM <image>:<tag> [AS <name>]
- FROM <image>@<digest> [AS <name>]
任何Dockerfile第一条指定必须是 FROM 指令。如果在统一个Dockerfile中创建了多个镜像时,可以使用多个FROM指令(每个镜像一次)
ARG VERSION=9.3
FROM debian:${VERSION}
3.LABEL
添加元数据标签信息,辅助过滤镜像
格式:
- LABEL <key>=<value> <key>=<value> <key>=<value> ...
4.EXPOSE
声明镜像内服务监听的端口
格式:
- EXPOSE <port> [<port>/<protocol> ...]
该指令只起到声明作用,并不会自动完成端口映射
5.ENV
指定环境变量,在镜像生成过程中,会被后续RUN指令使用,在镜像启动的容器中也会存在。
格式:
- ENV <key> <value> ...
- ENV <key>=<value> ...
指令指定的环境变量在运行时可以被覆盖掉,如docker run --env <key>=<value> built_image
当一条ENV指令中同时为多个环境变量赋值并且值也是从环境变量读取时,会为变量都赋值后再更新。如下面的指令,最终结果为key1=value1 key2=value2:
ENV key1=value2
ENV key1=value1 key2=${ key1 }
6.ENTRYPOINT
指定镜像默认入口命令,该命令会在启动容器时作为根命令执行,所有传入值作为根命令的参数。
格式:
- ENTRYPOINT ["executable", "param1", "param2"] exec调用执行
- ENTRYPOINT command param1 param2 shell中执行
此时,CMD指令指定值将作为根命令的参数
每个Dockerfile只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效
在运行时,可以被 --entrypoint 参数覆盖掉
7.VOLUME
创建一个数据卷挂载点
格式:
- VOLUME ["/data"]
运行时可以从本地主机或其他容器挂载数据卷,一般用来存放数据库和需要保持的数据等
8.USER
指定运行容器时的用户名或UID,后续的RUN等指令也会使用指定的用户身份
格式:
- USER daemon
当服务不需要管理员权限时,可以通过该命令指定运行用户,并且可以在Dockerfile中创建所需要的用户。例如:
RUN groundadd -r postgres && useradd --no-log-init -r -g postgres postgres
要临时获取管理员权限可以使用 gosu 命令
9.WORKDIR
为后续的 RUN,CMD,ENTRYPOINT指定配置工作目录
格式:
- WORKDIR /path/to/workdir
可以使用多个WORKDIR指令,后续命令如果参数时相对路径,则会基于之前命令指定的路径。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c
推荐只使用绝对路径
10.ONBUILD
指定当基于所生成镜像创建子镜像时,自动执行的操作指令
格式:
- ONBUILD [INSTRUCTION]
隐式执行,推荐在使用它的镜像标签中进行标注,例如 ruby:2.1-onbuild
ONBUILD指令在创建专门用于自动翻译、检查等操作的基础镜像时,十分有用。
11.STOPSIGNAL
指定所创建镜像启动的容器接收退出的信号值:
- STOPSIGNAL signal
12.HEALTHCHECK
配置所启动容器如何进行健康检查(如何判断健康与否),1.12之后支持
格式:
- HEALTHCHECK [OPTIONS] CMD command 根据所执行指令是否为0来判断
- HEALTHCHECK NONE 禁止基础镜像中的健康检查
OPTION支持参数如下:
- -interval=DURATION (default 30s) 过多久检查一次
- -timeout=DURATION (default 30s) 每次检查等待结果的超时
- -retries=N (default 3) 如果失败了,重试几次才最终确认失败
13.SHELL
指定其他命令使用shell时的默认shell类型:
- SHELL ["executable", "parameters"]
默认值为 ["/bin/sh", "-c"]
对于windows系统,shell路径中使用了“\”作为分隔符,建议在Dockerfile开头添加 # escape=`来指定转义符
1.RUN
运行指令命令
格式:
- RUN <command>
- RUN ["executable", "param1", "param2"]
后者指定会被解析成json数组,因此必须用双引号。
前者默认在shell终端中运行命令,即 /bin/sh -c,后者则使用 exec 执行,不启动shell 环境
指定使用其他终端类型,可以用后者实现,例如:
RUN ["/bin/bash", "-c", "echo hello"]
每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像层,当命令较长时可以使用 \ 来换行。
2.CMD
指定启动容器时默认执行的命令
格式:
- CMD ["executable", ”param1“, "param2"] 相当于执行 executable param1 param2, 推荐方式
- CMD command param1 param2 默认在shell中执行,提供给需要交互的应用
- CMD ["param1", "param2"] 提供给ENTRYPOINT的默认参数
每个Dockerfile只能有一条CMD命令,如果指定了多条命令,只有最后一条被执行
如果启动容器时手动指定了运行的命令(作为run命令的参数),则会覆盖掉CMD指定的命令
3.ADD
添加内容到镜像
格式:
- ADD <src> <dest>
该命令将复制指定的<src>路径下内容到容器中的<dest>路径下
其中<src>可以是Dockerfile所在目录的一个相对路径(文件或目录),也可以是一个URL,还可以是一个tar文件(自动解压为目录)
<dest>可以是镜像内绝对路径,或者相对于工作目录(WORKDIR)的相对路径
路径支持正则格式,例如:
ADD *.c /code/
4.COPY
复制内容到镜像
格式为:
- COPY <src> <dest>
复制本地主机的<src>(为Dockerfile所在目录的相对路径,文件或目录)下内容到镜像中的<dest>。目标路径不存在时,会自动创建。
路径同样支持正则格式
COPY和ADD指令功能类似,当使用本地目录为源目录时,推荐使用COPY
创建镜像
- docker [image] build [OPTIONS] PATH | URL | -
该命令将读取指定路径(包括子目录)下的Dockerfile,并将路径下的所有数据作为上下文发送给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/
命令选项 ...
选择父镜像
普通镜像:往往不存在FROM指令,或基于scratch镜像(FROM scratch)
基础镜像(第三方创建,基于基础镜像)
使用.dockerignore文件
忽略匹配路径或文件,创建竟像是不将无关文件发送到服务端
支持golang风格的正则表达式:
- * 任意多个字符
- ? 单个字符
- ! 不匹配(即不忽略指定的路径或文件)
多步骤创建
17.05版本开始支持 Multi-stage build。
对于需要编译的应用,如c, go, java等来说,通常来说需要准备两个环境的docker镜像:
- 编译环境镜像:包括完整的编译引擎,依赖库等,往往比较庞大。目的是编译应用为二进制文件
- 运行环境镜像:体积较小
以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完整内容为:
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 --from=builder /go/src/test/app .
CMD ["./app"]
执行如下命令创建镜像,并运行应用:
docker build -t yeasy/test-multistage:latest .
docker run --rm yeasy/test-multistage:latest
查看生成的最终镜像:
docker image|grep test-multistage
最佳实践
尽量吃透每个指令的含义和执行结果
阅读学习docker hub的镜像及对应的dockerfile
- 精简镜像用途
- 选择合适的基础镜像 大系统镜像ubuntu 小系统镜像alpine、busybox、debian 应用镜像 node:slim
- 提供注释和维护者信息
- 正确使用版本号 避免环境不一致导致的问题 latest
- 减少镜像层数 尽量合并RUN、ADD、COPY指令
- 恰当使用多步骤创建
- 使用 .dockerignore文件
- 及时删除临时文件和缓存文件 使用apt-get后,/var/cache/apt 会缓存一些安装包
- 提高生成速度 合理使用cache,减少目录下的文件,使用 .dockerignore文件指定等
- 调整合理的指令顺序 在开启cache的情况下,内容不变的指令尽量放在前面,这样可以尽复用
- 减少外部源的干扰 如果确实要从外部引入数据,需要指定持久的地址,并带版本信息等,让他人可以复用而不出错