44

编写 dockerfile 最佳实践

 5 years ago
source link: https://studygolang.com/articles/19723?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

docker 官方文档推荐使用 Dockerfile 构建镜像的最佳实践。

创建短生命周期容器

基于 Dockerfile 生成镜像,使用这个镜像生成的容器,我们要尽可能的缩短容器的生命周期。这里我的理解是,不要将容器当做vm 来使用, 这个容器可以被停止或者销毁, 然后可以根据设置和配置的变动重新生成新的容器。

理解构建上下文

当你触发 docker build 命令时,当前目录就被称为构建上下文( build context )。默认情况下 Dockerfile 文件就在这个目录下, 但是可以通过 -f 参数来指定 Dockerfile 的位置。不管 Dockerfile 在哪里,当前目录中的所有文件和目录都会作为构建上下文发送到 docker daemon 进程。

构建上下文示例

创建一个目录并且使用 cd 进入该目录。在 hello 文件中写"hello",同时创建 Dockerfile 文件并且 cat hello 文件。在当前上下文(.)中构建镜像:

mkdir myproject && cd myproject
echo "hello" > hello
echo -e "FROM busybox\nCOPY /hello /\nRUN cat /hello" > Dockerfile
docker build -t helloapp:v1 .

Dockerfilehello 文件移动到另一个目录中。并且再构建一个镜像(不使用上个镜像构建缓存)。使用 -f 来指定 Dockerfile 并且明确上下文目录:

mkdir -p dockerfiles context
mv Dockerfile dockerfiles && mv hello context
docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context

在构建过程中导入了不必要的文件将会导致更大的构建上下文,从而会构建出更大的镜像。这会增加构建镜像的时间,拉取和上传镜像的时间以及容器的大小。当你使用 Dockerfile 构建镜像时,可通过如下信息查看你的构建上下文的大小:

Sending build context to Docker daemon  187.8MB

使用.dockerignore 排除不需要加入镜像的文件

有的时候我们会需要排除一些与我们构建镜像不相关的文件,这时候我们可以通过编写 .dockerignore 在不改变代码结构的情况下达到这一目的。这个文件的实现方式与 .gitignore 很像,关于如何创建一个 .dockerignore ,可以参考 .dockerignore file

使用多阶段构建

multi-stage builds 技术可以大幅度减少最终镜像的大小,而不是想办法去减少构建过程中的层级数和文件。

因为镜像是在构建过程最后阶段生成的,因此我们可以通过 leveraging build cache 来最小化镜像层。

举个例子来说,如果构建一个镜像,这个镜像有很多层,可以按照镜像层的修改频率来排序(就是将不经常更新的层作为最底层,这样可以复用构建缓存):

  • 安装工具
  • 安装或者更新依赖
  • 生成你的应用

一个 Go 应用的 Dockerfile 示例:

FROM golang:1.11-alpine AS build

# Install tools required for project
# Run `docker build --no-cache .` to update dependencies
RUN apk add --no-cache git
RUN go get github.com/golang/dep/cmd/dep

# List project dependencies with Gopkg.toml and Gopkg.lock
# These layers are only re-built when Gopkg files are updated
COPY Gopkg.lock Gopkg.toml /go/src/project/
WORKDIR /go/src/project/
# Install library dependencies
RUN dep ensure -vendor-only

# Copy the entire project and build it
# This layer is rebuilt when a file changes in the project directory
COPY . /go/src/project/
RUN go build -o /bin/project

# This results in a single layer image
FROM scratch
COPY --from=build /bin/project /bin/project
ENTRYPOINT ["/bin/project"]
CMD ["--help"]

不安装不需要的包

为了减小镜像的复杂度和大小, 我们应当避免安装一些我们不需要的 packages。举个例子来说,你不需要在数据库镜像中安装文本编辑器

应用解耦

每个容器应当只含有一个应用实例, 将多个应用解耦至多个容器可以很方便的对应用进行水平扩展,并且可以复用容器。举个例子来说,一个 web 应用应当包含三个容器(web容器, 数据库容器, 缓存容器),每一个容器对应一个镜像。

每个容器中限制只能有一个进程是一个很好的经验法则, 但这也不是一个硬性的规定。容器中的进程不仅可以由 init 创建, 一些程序可能会额外的生成一些他们自己的进程。比如, Celery 会生成多个 worker 进程, Apache 对每一个请求创建一个进程。

每种场景不一样,规则也不一样。但是应该尽可能的保证我们的容器功能明确和模块化。如果容器之间相互依赖(容器之间可能需要通信), 你可以使用 Docker container networks 确保容器间通信

减小镜像层数

减少镜像层数对于镜像构建非常重要。在更老的版本的 docker 中需要特别注意,现在通过下面的这些特性我们可以方便的对镜像层数进行限制:

  • 只有 ONLY , COPY , ADD 这三个命令增加层数,其他的命令只会创建一些临时的镜像,并不会增加构建的镜像的层数
  • 使用 multi-stage builds 只拷贝真正需要的 artifaces (制品) 到最终的镜像。这可以使你在构建过程中使用工具和打印调试信息,但不会增加最终的镜像大小。

对多行参数排序

只要有可能, 将参数按照字母进行排序是一种非常好的实践,这种方式可以避免重复安装包(特指 apt-get 命令),也可以是开发人员更加容易的阅读和审查。

下面是 buildpack-deps 镜像的例子 images :

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion

借助构建缓存

在构建镜像的时候,docker 会按照 dockerfile 中的指令顺序来一次执行。每一个指令被执行的时候 docker 都会去缓存中检查是否有已经存在的镜像可以复用,而不是去创建一个新的镜像复制。

如果不想使用构建缓存,可以使用 docker build 参数选项 --no-cache=true 来禁用构建缓存。在使用镜像缓存时,要弄清楚缓存合适生效,何时失效。构建缓存最基本规则如下:

  • 如果引用的父镜像在构建缓存中,下一个命令将会和所有从该父进程派生的子镜像做比较,如果有子镜像使用相同的命令,那么缓存命中,否则缓存失效。
  • 在大部分情况下,通过比较 Dockerfile 中的指令和子镜像已经足够了。但是有些指令需要进一步的检查。
  • 对于 ADDCOPY 指令, 文件的内容会被检查,并且会计算每一个文件的校验码。但是文件最近一次的修改和访问时间不在校验码的考虑范围内。在构建过程中,docker 会比对已经存在的镜像,只要有文件内容和元数据发生变动,那么缓存就会失效。
  • 除了 ADDCOPY 指令,镜像缓存不会检查容器中文件来判断是否命中缓存。例如,在处理 RUN apt-get -y update 命令时,不会检查容器中的更新文件以确定是否命中缓存,这种情况下只会检查命令字符串是否相同。

下一篇将会记录下如何从标准输入流读取 Dockerfile ,以及如何使用 golang 构建 docker 镜像。

参考

Best practices for writing Dockerfiles


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK