docker镜像瘦身

参考:https://mp.weixin.qq.com/s/hn8Env3e5PztLgOCAYZamA

在构建 docker 容器时,我们一般希望尽量减小镜像,以便加快镜像的分发;但是不恰当的镜像构建方式,很容易导致镜像过大,造成带宽和磁盘资源浪费,尤其是遇到 daemonset 这种需要在每台机器上拉取镜像的服务,会造成大量资源浪费;而且镜像过大还会影响服务的启动速度,尤其是处理紧急线上镜像变更时,直接影响变更的速度。如果不是刻意控制镜像大小、注意镜像瘦身,一般的业务系统中可能 90% 以上的大镜像都存在镜像空间浪费的现象(不信可以尝试检测看看)。因此我们非常有必要了解镜像瘦身方法,减小容器镜像。

 一、判断镜像是否需要瘦身

通常,我们可能都是在容器镜像过大,明显影响到镜像上传/拉取速度时,才会考虑到分析镜像,尝试镜像瘦身。此时采用的多是 docker image history 等 docker 自带的镜像分析命令,以查看镜像构建历史、镜像大小在各层的分布等。然后根据经验判断是否存在空间浪费,但是这种判断方式起点较高、没有量化,不方便自动化判断。当前,社区中也有很多镜像分析工具,其中比较流行的 dive 分析工具,就可以量化给出容器镜像有效率、镜像空间浪费率等指标。

# curl -OL https://github.com/wagoodman/dive/releases/download/v0.9.1/dive_0.9.1_linux_amd64.rpm 
# rpm -ivh dive_0.9.1_linux_amd64.rpm
# dive  mysql:lastest

采用 dive 对一个 mysql 镜像进行效率分析,发现镜像有效率只有 41%,镜像空间浪费率高达 59%,显然需要瘦身。

二、镜像瘦身方法

2.1 使用Alpine Linux

Alpine Linux是一个基于BusyBox和Musl Libc的Linux发行版,其最大的优势就是小。一个纯的基础Alpine Docker镜像在压缩后仅有2.67MB

 

2.2多阶段构建

所谓多阶段构建,实际上是允许在一个 Dockerfile 中出现多个 FROM 指令。最后生成的镜像,以最后一条 FROM 构建阶段为准,之前的 FROM 构建阶段会被抛弃。通过多阶段构建,后一个阶段的构建过程可以直接利用前一阶段的构建缓存,有效降低镜像大小。一个典型的场景是将编译环境和运行环境分离,以一个 go 项目镜像构建过程为例:

# Go语言编译环境基础镜像
FROM golang:1.16-alpine
# 拷贝源码到镜像
COPY server.go /build/
# 指定工作目录
WORKDIR /build
# 编译镜像时,运行 go build 编译生成 server 程序
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server
# 指定容器运行时入口程序
ENTRYPOINT ["/build/server"]

这种传统的构建方式有以下缺点:

  • 基础镜像为支持编译环境,包含大量go语言的工具/库,而运行时并不需要。
  • COPY 源码,增加了镜像分层,同时有源码泄漏风险。

采用多阶段构建方式,可以将上述传统的构建方式修改如下:

## 1 编译构建阶段
#  Go语言编译环境基础镜像
FROM golang:1.16-alpine AS build
# 拷贝源码到镜像
COPY server.go /build/
# 指定工作目录
WORKDIR /build
# 编译镜像时,运行 go build 编译生成 server 程序
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

## 2 运行构建阶段
#  采用更小的运行时基础镜像
FROM scratch
# 从编译阶段仅拷贝所需的编译结果到当前镜像中
COPY --from=build /build/server /build/server
# 指定容器运行时入口程序
ENTRYPOINT ["/build/server"]

可以看到,使用多阶段构建,可以获取如下好处:

  • 最终镜像只关心运行时,采用了更小的基础镜像。
  • 直接拷贝上一个编译阶段的编译结果,减少了镜像分层,还避免了源码泄漏。

 2.3减少镜像分层

镜像的层就像 Git 的提交(commit)一样,用于保存镜像的当前版本与上一版本之间的差异,但是镜像层会占用空间,拥有的层越多,最终的镜像就越大。在构建镜像时,RUN, ADD, COPY 指令对应的层会增加镜像大小,其他命令并不会增加最终的镜像大小。

可以尝试合并相关指令,以减小镜像分层;COPY 指令转换合并到 RUN 指令
posted @ 2022-06-20 17:55  灰蓝色的白云梦想  阅读(1215)  评论(0编辑  收藏  举报