https://juejin.cn/post/6844903880526921741
 

精简Docker镜像的好处很多,不仅可以节省存储空间和带宽,还能减少安全隐患。优化镜像大小的手段多种多样,因服务所使用的基础开发语言不同而有差异。本文将介绍精简Docker镜像的几种通用方法。 精简Docker镜像大小的必要性 Docker镜像由很多镜像层(Layers)组成(最多127层),镜像层依赖于一系列的底层技术,比如文件系统(filesystems)、写时复制(copy-on-write)、联合挂载(union mounts)等技术,你可以查看Docker社区文档以了解更多有关Docker存储驱动的内容,这里就不再赘述技术细节。总的来说,Dockerfile中的每条指令都会创建一个镜像层,继而会增加整体镜像的尺寸。

下面是精简Docker镜像尺寸的好处:

1、减少构建时间

2、减少磁盘使用量

3、减少下载时间

4、因为包含文件少,攻击面减小,提高了安全性

5、提高部署速度

五点建议减小Docker镜像尺寸 0 1 优化基础镜像 优化基础镜像的方法就是选用合适的更小的基础镜像,常用的 Linux 系统镜像一般有 Ubuntu、CentOs、Alpine,其中Alpine更推荐使用。大小对比如下: lynzabo@ubuntu ~/s> docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest 74f8760a2a8b 8 days ago 82.4MB alpine latest 11cd0b38bc3c 2 weeks ago 4.41MB centos 7 49f7960eb7e4 7 weeks ago 200MB debian latest 3bbb526d2608 8 days ago 101MB lynzabo@ubuntu ~/s>

Alpine是一个高度精简又包含了基本工具的轻量级Linux发行版,基础镜像只有4.41M,各开发语言和框架都有基于Alpine制作的基础镜像,强烈推荐使用它。

查看上面的镜像尺寸对比结果,你会发现最小的镜像也有4.41M,那么有办法构建更小的镜像吗?答案是肯定的,例如 gcr.io/google_containers/pause-amd64:3.1 镜像仅有742KB。为什么这个镜像能这么小?在为大家解密之前,再推荐两个基础镜像:

scratch镜像

scratch是一个空镜像,只能用于构建其他镜像,比如你要运行一个包含所有依赖的二进制文件,如Golang程序,可以直接使用scratch作为基础镜像。现在给大家展示一下上文提到的Google pause镜像Dockerfile:

FROM scratch ARG ARCH ADD bin/pause-${ARCH} /pause ENTRYPOINT ["/pause"]

Google pause镜像使用了scratch作为基础镜像,这个镜像本身是不占空间的,使用它构建的镜像大小几乎和二进制文件本身一样大,所以镜像非常小。当然在我们的Golang程序中也会使用。对于一些Golang/C程序,可能会依赖一些动态库,你可以使用自动提取动态库工具,比如ldd、linuxdeployqt等提取所有动态库,然后将二进制文件和依赖动态库一起打包到镜像中。

busybox镜像

scratch是个空镜像,如果希望镜像里可以包含一些常用的Linux工具,busybox镜像是个不错选择,镜像本身只有1.16M,非常便于构建小镜像。

0 2 串联 Dockerfile 指令 大家在定义Dockerfile时,如果太多的使用RUN指令,经常会导致镜像有特别多的层,镜像很臃肿,而且甚至会碰到超出最大层数(127层)限制的问题,遵循 Dockerfile 最佳实践,我们应该把多个命令串联合并为一个 RUN(通过运算符&&和/ 来实现),每一个 RUN 要精心设计,确保安装构建最后进行清理,这样才可以降低镜像体积,以及最大化的利用构建缓存。 下面是一个优化前Dockerfile:

FROM ubuntu

ENV VER 3.0.0
ENV TARBALL download.redis.io/releases/re…

==> Install curl and helper tools...

RUN apt-get update
RUN apt-get install -y curl make gcc

==> Download, compile, and install...

RUN curl -L VER
RUN make
RUN make install
#...

==> Clean up...

WORKDIR /
RUN apt-get remove -y --auto-remove curl make gcc
RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/* /redis-$VER
#... CMD ["redis-server"]

构建镜像,名称叫 test/test:0.1。

我们对Dockerfile做优化,优化后Dockerfile:

FROM ubuntu

ENV VER 3.0.0
ENV TARBALL download.redis.io/releases/re…

RUN echo "==> Install curl and helper tools..." && \
apt-get update &&
apt-get install -y curl make gcc &&
echo "==> Download, compile, and install..." &&
curl -L VER &&
make &&
make install &&
echo "==> Clean up..." &&
apt-get remove -y --auto-remove curl make gcc &&
apt-get clean &&
rm -rf /var/lib/apt/lists/* /redis-$VER #... CMD ["redis-server"]

构建镜像,名称叫 test/test:0.2。

对比两个镜像大小:

root@k8s-master:/tmp/iops# docker images REPOSITORY TAG IMAGE ID CREATED SIZE test/test 0.2 58468c0222ed 2 minutes ago 98.1MB test/test 0.1 e496cf7243f2 6 minutes ago 307MB root@k8s-master:/tmp/iops#

可以看到,将多条RUN命令串联起来构建的镜像大小是每条命令分别RUN的三分之一。

提示:为了应对镜像中存在太多镜像层,Docker 1.13版本以后,提供了一个压扁镜像功能,即将 Dockerfile 中所有的操作压缩为一层。这个特性还处于实验阶段,Docker默认没有开启,如果要开启,需要在启动Docker时添加-experimental 选项,并在Docker build 构建镜像时候添加 --squash 。我们不推荐使用这个办法,请在撰写 Dockerfile 时遵循最佳实践编写,不要试图用这种办法去压缩镜像。

0 3 使用多阶段构建 Dockerfile中每条指令都会为镜像增加一个镜像层,并且你需要在移动到下一个镜像层之前清理不需要的组件。实际上,有一个Dockerfile用于开发(其中包含构建应用程序所需的所有内容)以及一个用于生产的瘦客户端,它只包含你的应用程序以及运行它所需的内容。这被称为“建造者模式”。Docker 17.05.0-ce版本以后支持多阶段构建。使用多阶段构建,你可以在Dockerfile中使用多个FROM语句,每条FROM指令可以使用不同的基础镜像,这样您可以选择性地将服务组件从一个阶段COPY到另一个阶段,在最终镜像中只保留需要的内容。 下面是一个使用COPY --from 和 FROM ... AS ... 的Dockerfile:

Compile

FROM golang:1.9.0 AS builder WORKDIR /go/src/v9.git...com/.../k8s-monitor COPY . . WORKDIR /go/src/v9.git...com/.../k8s-monitor RUN make build RUN mv k8s-monitor /root

Package

Use scratch image

FROM scratch WORKDIR /root/ COPY --from=builder /root . EXPOSE 8080 CMD ["/root/k8s-monitor"]

构建镜像,你会发现生成的镜像只有上面COPY 指令指定的内容,镜像大小只有2M。这样在以前使用两个Dockerfile(一个Dockerfile用于开发和一个用于生产的瘦客户端),现在使用多阶段构建就可以搞定。

0 4 构建业务服务镜像技巧 Docker在build镜像的时候,如果某个命令相关的内容没有变化,会使用上一次缓存(cache)的文件层,在构建业务镜像的时候可以注意下面两点:

1、不变或者变化很少的体积较大的依赖库和经常修改的自有代码分开;

2、因为cache缓存在运行Docker build命令的本地机器上,建议固定使用某台机器来进行Docker build,以便利用cache。

下面是构建Spring Boot应用镜像的例子,用来说明如何分层。其他类型的应用,比如Java WAR包,Nodejs的npm 模块等,可以采取类似的方式。

1、在Dockerfile所在目录,解压缩maven生成的jar包

$ unzip .jar -d app

2、Dockerfile 我们把应用的内容分成4个部分COPY到镜像里面:其中前面3个基本不变,第4个是经常变化的自有代码。最后一行是解压缩后,启动spring boot应用的方式。

FROM openjdk:8-jre-alpine

LABEL maintainer "opl-xws@xiaomi.com" COPY app/BOOT-INF/lib/ /app/BOOT-INF/lib/ COPY app/org /app/org COPY app/META-INF /app/META-INF COPY app/BOOT-INF/classes /app/BOOT-INF/classes EXPOSE 8080 CMD ["/usr/bin/java", "-cp", "/app", "org.springframework.boot.loader.JarLauncher"]

这样在构建镜像时候可大大提高构建速度。

0 5 其他优化办法 当然,除了以上4类办法外,还有其他优化办法可以精简镜像;

  1. RUN命令中执行apt、apk或者yum类工具技巧 如果在RUN命令中执行apt、apk或者yum类工具,可以借助这些工具提供的一些小技巧来减少镜像层数量及镜像大小。举几个例子:

(1)在执行apt-get install -y 时增加选项— no-install-recommends ,可以不用安装建议性(非必须)的依赖,也可以在执行apk add 时添加选项--no-cache 达到同样效果;

(2)执行yum install -y 时候, 可以同时安装多个工具,比如yum install -y gcc gcc-c++ make ...。将所有yum install 任务放在一条RUN命令上执行,从而减少镜像层的数量;

(3)组件的安装和清理要串联在一条指令里面,如 apk --update add php7 && rm -rf /var/cache/apk/* ,因为Dockerfile的每条指令都会产生一个文件层,如果将apk add ...和 rm -rf ... 命令分开,清理无法减小apk命令产生的文件层的大小。Ubuntu或Debian可以使用 rm -rf /var/lib/apt/lists/* 清理镜像中缓存文件;CentOS等系统使用yum clean all 命令清理。 2. 压缩镜像 Docker 自带的一些命令还能协助压缩镜像,比如 export 和 import

 docker export 747dc0e72d13 | docker import - test/test:0.3

使用这种方式需要先将容器运行起来,而且这个过程中会丢失镜像原有的一些信息,比如:导出端口,环境变量,默认指令。

查看这两个镜像history信息,如下,可以看到test/test:0.3 丢失了所有的镜像层信息:

root@k8s-master:/tmp/iops# docker history test/test:0.3 IMAGE CREATED CREATED BY SIZE COMMENT 6fb3f00b7a72 15 seconds ago 84.7MB Imported from - root@k8s-master:/tmp/iops# docker history test/test:0.2 IMAGE CREATED CREATED BY SIZE COMMENT 58468c0222ed 2 hours ago /bin/sh -c #(nop) CMD ["redis-server"] 0B
1af7ffe3d163 2 hours ago /bin/sh -c echo "==> Install curl and helper… 15.7MB
8bac6e733d54 2 hours ago /bin/sh -c #(nop) ENV TARBALL=http://downlo… 0B
793282f3ef7a 2 hours ago /bin/sh -c #(nop) ENV VER=3.0.0 0B
74f8760a2a8b 8 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
8 days ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B 8 days ago /bin/sh -c sed -i 's/^#\s*(deb.universe)$… 2.76kB 8 days ago /bin/sh -c rm -rf /var/lib/apt/lists/ 0B 8 days ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 745B
8 days ago /bin/sh -c #(nop) ADD file:5fabb77ea8d61e02d… 82.4MB
root@k8s-master:/tmp/iops#

社区里还有很多压缩工具,比如Docker-squash ,用起来更简单方便,并且不会丢失原有镜像的自带信息,大家有兴趣可以试试。

总结

Docker镜像的精简手段和精简效果值得深入探讨和实践,希望本文能为大家带来帮助。如果你有更好的方法和经验,欢迎交流~

[转帖]Docker最佳实践:5个方法精简镜像的更多相关文章

  1. docker最佳实践-----美团点评的分享

    美团点评容器平台简介 本文介绍美团点评的Docker容器集群管理平台(以下简称“容器平台”).该平台始于2015年,是基于美团云的基础架构和组件而开发的Docker容器集群管理平台.目前该平台为美团点 ...

  2. Docker最佳实践-部署LNMP环境

    标签(linux): docker 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 环境准备 [root@docker ~]# cat /etc/redhat-r ...

  3. Docker笔记(十一):Dockerfile详解与最佳实践

    Dockerfile是一个文本文件,包含了一条条指令,每条指令对应构建一层镜像,Docker基于它来构建一个完整镜像.本文介绍Dockerfile的常用指令及相应的最佳实践建议. 1. 理解构建上下文 ...

  4. 8、Dockerfile介绍和最佳实践

    一.Dockerfile 概念 1.Dockerfile是什么 Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序.库.资源.配置等文件外,还包含了一些为运行时准备的一些配置参数(如 ...

  5. 镜像命名的最佳实践 - 每天5分钟玩转 Docker 容器技术(18)

    我们已经学会构建自己的镜像了.接下来的问题是如何在多个 Docker Host 上使用镜像. 这里有几种可用的方法: 用相同的 Dockerfile 在其他 host 构建镜像. 将镜像上传到公共 R ...

  6. Docker监控:最佳实践以及cAdvisor和Prometheus监控工具的对比

    在DockerCon EU 2015上,Brian Christner阐述了“Docker监控”的概况,分享了这方面的最佳实践和Docker stats API的指南,并对比了三个流行的监控方案:cA ...

  7. Docker 使用杂记 - 最佳实践尝试 - 实战

    目录 Docker 使用杂记 - 最佳实践尝试 - 实战 Docker简介 项目背景 内在原因 外在原因 基础镜像 需求 镜像维护者 工作文件夹 文件 ADD COPY 宗卷 命令 入口点 Docke ...

  8. 可能是Asp.net Core On host、 docker、kubernetes(K8s) 配置读取的最佳实践

    写在前面 为了不违反广告法,我竭尽全力,不过"最佳实践"确是标题党无疑,如果硬要说的话 只能是个人最佳实践. 问题引出 ​ 可能很多新手都会遇到同样的问题:我要我的Asp.net ...

  9. 8.云原生之Docker容器镜像构建最佳实践浅析

    转载自:https://www.bilibili.com/read/cv15220861/?from=readlist 本章目录 0x02 Docker 镜像构建最佳实践浅析 1.Dockerfile ...

  10. Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全

    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全 1. #====提升抽象层次1 2. #----使用通用单词1 3. #===使用术语..1 4.  ...

随机推荐

  1. 从零玩转Docker之docker-compose-azdocker-compose

    title: 从零玩转Docker之docker-compose date: 2023-04-04 17:39:40.699 updated: 2023-04-04 17:52:15.329 url: ...

  2. 5 款开源热搜项目「GitHub 热点速览」

    随着 2023 年的结束,我们也迎来了 2024 年的第一个工作日,新的一年就让「GitHub 热点速递」陪你一起进入工作状态吧! 说到上周的 GitHub 热搜项目就不得不提一下,一周飙升了 8 千 ...

  3. 良心国产工具,比Xshell好用还免费!

    使用或维护Linux系统的都知道,我们日常对服务器的操作,一般都会借助SSH工具远程登录到服务器之后进行操作.常用的SSH工具有不少,比如:Xshell.Putty.SSH Secure Shell ...

  4. [集训队作业2013] 城市规划(NTT)

    一周一博客二专题计划 题面 n 个点的简单 (无重边无自环) 有标号无向连通图数目. 看着就很典 思路 设\(f(n)\)为n点连通图数目.设\(g(n)\)为n点不一定联通图数目,显然直接枚举每条边 ...

  5. GitHub星标1k+的C#/.NET/.NET Core学习、工作、面试指南(让现在的自己不再迷茫✨)

    缘起 概述:发现现如今网上关于Java.前端.Android.Golang...等相关技术的学习资料,面试指南一搜都是一大把,但是我们大.NET/C#的相关学习资料,面试指南和一些常见的面试题都是寥寥 ...

  6. Draco使用笔记(1)——图形解压缩

    目录 1. 概述 2. 详论 2.1. 工具 2.2. 代码 1. 概述 Draco是Google开发的图形压缩库,用于压缩和解压缩3D几何网格(geometric mesh)和点云(point cl ...

  7. Eclipse部署虚拟项目目录

    目录 1. 问题 2. 方案 3. 参考 1. 问题 对于一些附带了大量本地资源的项目(例如,用户上传的文件,地图切片或者三维模型等),在Eclipse中部署调试是我一直头痛的问题.因为Eclipse ...

  8. Java 展开或折叠PDF中的书签

    PDF中的书签功能可快速定位到指定阅读位置.对多层书签可根据阅读喜好设置层级展开或折叠.本文将通过Java程序代码介绍如何来实现PDF书签展开或折叠. 程序环境 Spire.Pdf.jar( 免费版3 ...

  9. GaussDB技术解读系列之应用无损透明(ALT)

    本文作者 :华为云GaussDB研发高级工程师 藏琦 1.背景 GaussDB作为一款企业级分布式数据库,提供了"同城跨AZ双活.两地三中心.双集群强一致"等极致的高可用容灾能力. ...

  10. 从缓存的本质说起,说服技术大佬用Redis

    摘要:在技术领域中,没有银弹.我们需要不断探索和研究新的技术,结合具体问题和需求,选择最适合的解决方案. 本文分享自华为云社区<知乎问题:如何说服技术老大用 Redis ?>,作者:勇哥j ...