在 Docker 中,可以通过 docker build
命令和 Dockerfile 文件来构造自己的 Docker 镜像。
值得注意的是,docker build
命令会递归地将构建整个上下文(如,通常是某个本地目录)发送给 docker daemon,所以不要将根目录 /
作为构建上下文。
1. 常用指令
需要注意的是,RUN, COPY, ADD
指令会创建新的层。
FROM
FROM
指令用于初始化一个新的构建阶段,并为后续指令指定一个基础镜像。
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
如果基础镜像有多个平台版本,可以通过 --platform
来指定要使用哪个版本,如,linux/amd64
。
可以通过 AS <name>
为当前构建阶段指定一个名字,后续可通过 COPY --from=<name>
来引用此阶段所构建的镜像。
如,
FROM busybox
LABEL
LABEL
指令用于为镜像添加元数据,可以通过 docker image inspect
命令来查看。
LABEL <key>=<value> <key>=<value> <key>=<value> ...
如,
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL description="This text illustrates \
that label-values can span multiple lines."
ENV
ENV
指令用于设置环境变量,其设置的环境变量可以被后续指令,且会持久地存储,容器运行时依旧存在;可以被 docker run
命令覆盖。
ENV <key>=<value> ...
如,
ENV DIRPATH=/path
WORKDIR $DIRPATH/a
ARG
ARG
指令用于定义变量,用户可以在构造镜像是传递相应的变量值。不同于 ENV
,ARG
不会将变量持久化至容器运行时。
ARG <name>[=<default value>]
如,
FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
$ sudo docker build --build-arg CONT_IMG_VER=2.3 .
WORKDIR
WORKDIR
指令用于为后续的 RUN, CMD, ENTRYPOINT, ADD, COPY
指令指定工作路径。如果后面的 WORKDIR
指令的参数是一个相对路径,则它是相对于上一个 WORKDIR
指令指定的工作路径的。
WORKDIR /path/to/workdir
如,
WORKDIR /a
WORKDIR b
WORKDIR c
RUN
RUN
指令会在当前镜像之上执行给定的命令,并将执行结果作为一个新的层。
RUN <command>
RUN ["executable", "param1", "param2", ...]
- 第一种形式是在 shell 中执行命令,也就是说基础镜像需要包含指定的 shell 才行;
- 第二种形式是 exec 形式,不会调用 shell(也就不会执行环境变量替换),可以执行任意可执行文件,其参数会被解析成 JSON 数组,所以需要使用双引号!
如,
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]
ADD
ADD
指令用于拷贝本地文件、目录、远程文件至镜像中。
ADD [--chown=<user>[:<group>]] <src>... <dest>
ADD [--chown=<user>[:<group>]] ["<src>",... "<dest>"]
<src>
中可以包含通配符;如果 <dest>
不存在,则它会被自动创建(包括缺失的父目录)。
文件的默认属主/属组为 root,可以通过 --chown
来修改。
一些注意事项:
<src>
必须在构建上下文中;- 如果
<src>
是一个本地 tar 文件(被压缩为 gzip、bzip2、xz),则它会被解包为一个目录; - 如果
<dest>
表示一个目录,则它需要以/
开始,以/
结束。
如,
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
COPY
和 ADD
指令类似,但不支持从远程拷贝文件,此外它也不会自动解包 tar 文件。
COPY [--chown=<user>[:<group>]] <src>... <dest>
COPY [--chown=<user>[:<group>]] ["<src>",... "<dest>"]
如,
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
VOLUME
VOLUME
指令用于将容器中指定的目录挂载为匿名卷,可以通过 docker inspect
命令来查看其挂载到了主机哪个位置。
VOLUME path
VOLUME ["path1", "path2", ...]
如,
VOLUME ["/data1","/data2"]
EXPOSE
EXPOSE
指令用于指明容器应当在运行时监听指定的端口,而实际上,EXPOSE
指令并没有开放端口的功能,这需要通过 docker run
命令的 -p
选项来实现。
EXPOSE <port>[/protocol] [<port>[/<protocol>]...]
如,
EXPOSE 80/tcp
EXPOSE 80/udp
$ sudo docker run -p 80:80/tcp -p 80:80/udp ...
CMD
CMD
指令有3种形式,前两种和 RUN
指令类似,但不同于 RUN
,CMD
指令指定的命令是在容器运行时执行的(和 ENTRYPOINT
指令功能相同),而不是构建容器时;第三种形式的作用是给 ENTRYPOINT
指令提供默认参数(可以被 docker run
命令指定的参数给覆盖掉)。
CMD command param1 param2 ...
CMD ["executable", "param1", "param2", ...]
CMD ["param1","param2", ...]
如,
FROM ubuntu
CMD echo "This is a test." | wc -
FROM ubuntu
CMD ["/usr/bin/wc", "--help"]
ENTRYPOINT
ENTRYPOINT
指令用于指定容器启动(执行 docker run <image>
)时的默认动作。 类似于 RUN
指令,ENTRYPOINT
指令也有 shell 和 exec 两种形式。
ENTRYPOINT command param1 param2 ...
ENTRYPOINT ["executable", "param1", "param2", ...]
当使用 shell 形式时,需要以 exec
开始,以保证 docker stop
命令能够正确地终止容器。
如,
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
FROM ubuntu
ENTRYPOINT exec top -b
2. 多阶段构建
多阶段构建可以有效地减小镜像的大小。它是通过为每个构建阶段提供一个名字,并结合 COPY --from=<build-stage-name>
来实现的,即,从其它构建阶段构建的镜像中拷贝需要的内容至当前镜像,如此可以大大减小最终镜像的体积。
如,
FROM golang:1.16-alpine AS build
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
RUN dep ensure -vendor-only
COPY . /go/src/project/
RUN go build -o /bin/project
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]
3. 构建镜像
默认情况下,docker build
使用构建上下文中的 Dockerfile
文件来构建镜像:
$ sudo docker build .
当然也可以通过 -f
选项来自定义 Dockerfile 文件:
$ sudo build -f /path/to/a/Dockerfile .
此外,也可以通过 -t
选项为生成的镜像打上(一个或多个)标签:
$ sudo docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
最后,可以在构建上下文中添加 .dockerignore
文件来排除掉与构建无关的文件,格式类似于 .gitignore
文件。