Dokerfile编写
ARG指令与CMD
FROM alpine
LABEL maintainer="test dockerfile"
# 定义以后剩下环节能生效
# 可以在构建时进行编号
ARG param=12345
# 构建时期我们会运行的指令
RUN echo 11113;echo $param
# 运行时期我们会运行的指令(根据之前创建的镜像启动的一个容器,容器启动默认运行的命令)
# docker run/docker start
# CMD 和 ENTRYPOINT 都是指定运行时命令
CMD echo 111
ARG
ARG是Dockerfile中唯一可以在FROM之前的指令。
ARG 不像ENV不能并排写。
FROM
- FROM指令初始化新的构建阶段,并为后续指令设置Base Image。因此,有效的Dockerfile必须以FROM指令开头
- 可选地,可以通过向FROM指令添加AS名称来为新的构建阶段指定名称。该名称可以在随后的FROM和COPY-FROM=指令中使用,以引用在此阶段中构建的映像。
- FROM可以在一个Dockerfile中多次出现,以创建多个映像或将一个构建阶段用作另一个构建的依赖项。只需在每个新的FROM指令之前记录提交输出的最后一个图像ID。每个FROM指令都会清除先前指令创建的任何状态。
RUN
RUN有两种形式:
-
RUN(shell形式,命令在shell中运行,在Linux上默认为/bin/sh-c,在Windows上默认为cmd/S/c)
-
RUN[“executable”,“param1”,“param2”](exec形式)
RUN /bin/bash -c 'source $HOME/.bashrc && \
echo $HOME'
要使用除“/bin/sh”之外的其他shell,请使用传入所需shell的exec表单。例如:
# -c 指后面的命令为完全命令
# run 指令没有上下文关系
RUN ["/bin/bash", "-c", "echo hello"]
ENV
定义环境变量
ENV <key>=<value> ...
ENV指令将环境变量设置为值。该值将在构建阶段的所有后续指令的环境中,也可以在许多指令中内联替换。该值将被解释为其他环境变量,因此引号字符如果不转义,将被删除。与命令行解析一样,引号和反斜杠可以用于在值中包含空格。
Example:
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
ENV指令允许多个=。。。变量,下面的示例将在最终图像中产生相同的净结果:
Example:
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy
当从结果图像运行容器时,使用ENV设置的环境变量将保持不变。您可以使用docker inspect查看值,并使用docker run --env =更改它们。
docker run --env <key>=<value>
ADD 和 COPY
ADD
ADD有两种形式:
ADD [--chown=<user>:<group>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]
-
ADD指令从复制新文件、目录或远程文件URL,并将它们添加到路径处的映像文件系统中。
-
可以指定多个<src>资源,但如果它们是文件或目录,则它们的路径将被解释为相对于构建上下文的源。 每个<src>都可能包含通配符,匹配将使用Go的文件路径.Match规则完成。例如: 要添加以“hom”开头的所有文件:
ADD hom* /mydir/
ADD遵守以下规则:
- 路径必须位于构建的上下文中;无法添加/something/something,因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护进程。
- 如果<src>是一个URL,而<dest>不以斜杠结尾,则会从该URL下载一个文件并将其复制到<dest>。
- 如果<src>是一个URL,而<dest>以斜杠结尾,则从URL推断出文件名,并将文件下载到<dest>/<filename>。例如,ADDhttp://example.com/foobar/将创建文件/foobar。URL必须具有非平凡路径,以便在这种情况下可以找到适当的文件名(http://example.com不会工作)。
- 如果<src>是一个目录,则复制目录的全部内容,包括文件系统元数据。
- 添加压缩包会自动解压。
COPY
注意复制的文件有没有执行权限。把复制过来的文件给所有权
COPY --chown=<user>:<group>
用法与ADD基本一致,COPY 不会自动解压和下载。
USER
以后的所有命令会用这个用户执行,有可能没有执行权限
USER指令设置用户名(或UID)和可选的用户组(或GID),以用作当前阶段剩余时间的默认用户和组。指定的用户用于RUN指令,并在运行时运行相关的ENTRYPOINT和CMD命令。
USER redis:redis
WORKDIR
为以下所有的命令运行指定了基础目录。
多个workdir 可以嵌套
WORKDIR /ABC
WORKDIR ACD
目录为 /ABC/ACD
VOLUME
VOLUME指令使用指定的名称创建装载点,并将其标记为保存来自本机主机或其他容器的外部装载卷。该值可以是JSON数组 VOLUME [“/var/log/”],也可以是具有多个参数的纯字符串,例如VOLUME /var/log或 VOLUME /var/log/var/db。有关通过Docker客户端的更多信息/示例和安装说明,请参阅通过卷共享目录文档。 docker run命令使用存在于基本映像中指定位置的任何数据初始化新创建的卷。例如,考虑以下Dockerfile片段:
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
## 挂载容器指定文件夹,如果不存在则创建
## 指定了VOLUME,即使启动容器没有指定-v 参数,我们也会自动进行卷挂载。
EXPOSE
EXPOSE指令通知Docker容器在运行时侦听指定的网络端口。您可以指定端口侦听TCP还是UDP,如果未指定协议,则默认为TCP。 EXPOSE指令实际上并不发布端口。它作为构建映像的人和运行容器的人之间的一种文档,用于发布哪些端口。要在运行容器时实际发布端口,请在docker run上使用-p标志发布并映射一个或多个端口,或使用-p标志发布所有公开的端口并将它们映射到高阶端口。 默认情况下,EXPOSE采用TCP。也可以指定UDP:
EXPOSE 80/tcp
无论EXPOSE设置如何,您都可以在运行时使用-p标志覆盖它们。例如
docker run -p 80:80/tcp -p 80:80/udp ...
CMD 和ENTRYPOINT
ENTRYPOINT
ENTRYPOINT是真正的入口,CMD为ENTRYPOINT提供参数。
ENTRYPOINT "ping"
# 指令有多种方式,官方建议用exec方式[]方式
CMD www.baidu.com
CMD ["bin/sh""-c""ping""www.baidu.com"]
ENTRYPOINT "ping"
CMD ["bin/sh","-c","www.baidu.com"]
下表显示了对不同ENTRYPOINT/CMD组合执行的命令:
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
多阶段构建
Docker 17.05版本以后,官方就提供了一个新的特性:Multi-stage builds
(多阶段构建)。
使用多阶段构建,你可以在一个 Dockerfile
中使用多个 FROM 语句。每个 FROM 指令都可以使用不同的基础镜像,并表示开始一个新的构建阶段。你可以很方便的将一个阶段的文件复制到另外一个阶段,在最终的镜像中保留下你需要的内容即可。
多阶段构建之前
构建映像最具挑战性的事情之一是保留映像 缩小尺寸。Dockerfile 中的每个、 和 指令都会向映像添加一个图层,然后您 在继续之前,需要记住清理任何不需要的工件 下一层。要编写一个真正高效的 Dockerfile,您传统上必须 需要使用 shell 技巧和其他逻辑来保持层尽可能小 可能并确保每一层都有它需要的工件 前一层,仅此而已。RUN``COPY``ADD
实际上,有一个 Dockerfile 用于开发是很常见的(这 包含构建应用程序所需的一切),并且精简了一个 用于生产,其中仅包含您的应用程序以及确切的内容 需要运行它。这被称为“建设者 模式”。维护两个 Dockerfile 并不理想。
下面是一个示例,它遵循 上面的生成器模式:build.Dockerfile``Dockerfile
build.Dockerfile
:
# syntax=docker/dockerfile:1
FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go ./
RUN go get -d -v golang.org/x/net/html \
&& CGO_ENABLED=0 go build -a -installsuffix cgo -o app .
请注意,此示例还人为地将两个命令压缩在一起 使用 Bash 运算符,以避免在图像中创建额外的图层。 这容易出现故障且难以维护。插入另一个命令很容易 例如,忘记使用字符继续行。RUN``&&``\
Dockerfile
:
# syntax=docker/dockerfile:1
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app ./
CMD ["./app"]
build.sh
:
#!/bin/sh
echo Building alexellis2/href-counter:build
docker build -t alexellis2/href-counter:build . -f build.Dockerfile
docker container create --name extract alexellis2/href-counter:build
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker container rm -f extract
echo Building alexellis2/href-counter:latest
docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app
运行脚本时,它需要构建第一个映像,创建 一个容器,用于从中复制工件,然后构建第二个 图像。这两个映像都会占用系统上的空间,并且本地磁盘上仍有工件。build.sh``app
多阶段构建大大简化了这种情况!
使用多阶段构建
对于多阶段构建,您可以在 Dockerfile 中使用多个语句。 每条指令都可以使用不同的基数,并且每条指令都开始一个新的 构建阶段。您可以有选择地将项目从一个阶段复制到 另一个,在最终图像中留下你不想要的一切。要显示 这是如何工作的,让我们调整上一节的使用 多阶段构建。FROM``FROM``Dockerfile
# syntax=docker/dockerfile:1
FROM golang:1.16
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go ./
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]
您只需要单个 Dockerfile。您不需要单独的构建脚本, 也。只需运行.docker build
$ docker build -t alexellis2/href-counter:latest .
最终结果是与以前相同的微小生产映像,具有 显著降低复杂性。您无需创建任何中间体 图像,并且您根本不需要将任何工件提取到本地系统。
它是如何工作的?第二条指令开始一个新的构建阶段 图像作为其基础。该行仅复制 将神器从前一阶段构建到这个新阶段。Go SDK 和任何 中间伪影会留下,不会保存在最终图像中。FROM``alpine:latest``COPY --from=0
命名构建阶段
默认情况下,阶段不命名,您可以通过其整数引用它们 数字,以 0 开头表示第一条指令。但是,您可以 通过在指令中添加 A 来命名阶段。这 示例通过命名阶段并使用 指令。这意味着即使您的说明 Dockerfile 稍后重新排序,不会中断。FROM``AS <NAME>``FROM``COPY``COPY
# syntax=docker/dockerfile:1
FROM golang:1.16 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go ./
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app ./
CMD ["./app"]