Docker优秀实践:5个方法精简你的镜像

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

 [[323396]]

创建短生命周期容器

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

理解构建上下文

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

构建上下文示例

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

  1. mkdir myproject && cd myproject 
  2.  
  3. echo "hello" > hello 
  4.  
  5. echo -e "FROM busyboxnCOPY /hello /nRUN cat /hello" > Dockerfile 
  6.  
  7. docker build -t helloapp:v1 . 

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

  1. mkdir -p dockerfiles context 
  2.  
  3. mv Dockerfile dockerfiles && mv hello context 
  4.  
  5. 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示例:

  1. FROM golang:1.11-alpine AS build 
  2.  
  3. # Install tools required for project 
  4.  
  5. # Run `docker build --no-cache .` to update dependencies 
  6.  
  7. RUN apk add --no-cache git 
  8.  
  9. RUN go get github.com/golang/dep/cmd/dep 
  10.  
  11. # List project dependencies with Gopkg.toml and Gopkg.lock 
  12.  
  13. # These layers are only re-built when Gopkg files are updated 
  14.  
  15. COPY Gopkg.lock Gopkg.toml /go/src/project/ 
  16.  
  17. WORKDIR /go/src/project/ 
  18.  
  19. # Install library dependencies 
  20.  
  21. RUN dep ensure -vendor-only 
  22.  
  23. # Copy the entire project and build it 
  24.  
  25. # This layer is rebuilt when a file changes in the project directory 
  26.  
  27. COPY . /go/src/project/ 
  28.  
  29. RUN go build -o /bin/project 
  30.  
  31. # This results in a single layer image 
  32.  
  33. FROM scratch 
  34.  
  35. COPY --from=build /bin/project /bin/project 
  36.  
  37. ENTRYPOINT ["/bin/project"
  38.  
  39. 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:

  1. RUN apt-get update && apt-get install -y 
  2.  
  3. bzr 
  4.  
  5. cvs 
  6.  
  7. git 
  8.  
  9. mercurial 
  10.  
  11. subversion 

借助构建缓存

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

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

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

 

责任编辑:张燕妮 来源: 高效运维
相关推荐

2021-05-08 16:11:08

Java开发代码

2021-03-01 19:24:13

Kubernetes备份容器

2019-12-16 12:11:53

Docker容器Kubernetes

2023-01-13 16:34:08

2021-05-10 16:41:19

机器学习人工智能IT

2023-03-15 15:54:36

Java代码

2021-02-21 09:33:19

Docker容器安全 应用程序

2017-06-06 11:59:26

Docker工具容器

2023-09-20 09:00:00

2022-07-25 14:24:53

Docker容器安全

2022-10-10 14:53:00

云安全云计算云平台

2020-11-24 10:32:16

CIO首席信息官工具

2022-02-28 15:56:14

零信任企业

2023-04-10 11:25:29

工程交流DX

2020-09-29 07:19:54

InnoDBMySQL存储引擎

2022-04-19 10:17:20

云迁移云原生可观测

2022-05-13 08:17:05

HTTPRESTful架构

2020-04-06 12:20:51

Vim文本编辑器Linux

2020-02-03 12:25:35

Python工具服务器

2023-07-03 12:09:38

云日志云服务
点赞
收藏

51CTO技术栈公众号