两个奇技淫巧,将 Docker 镜像体积减小 99%
对于刚接触容器的人来说,他们很容易被自己构建的 Docker 镜像体积吓到,我只需要一个几 MB 的可执行文件而已,为何镜像的体积会达到 1 GB
以上?本文将会介绍几个奇技淫巧来帮助你精简镜像,同时又不牺牲开发人员和运维人员的操作便利性。本系列文章将分为三个部分:
第一部分着重介绍多阶段构建(multi-stage builds),因为这是镜像精简之路至关重要的一环。在这部分内容中,我会解释静态链接和动态链接的区别,它们对镜像带来的影响,以及如何避免那些不好的影响。中间会穿插一部分对 Alpine
镜像的介绍。
第二部分将会针对不同的语言来选择适当的精简策略,其中主要讨论 Go
,同时也涉及到了 Java
,Node
,Python
,Ruby
和 Rust
。这一部分也会详细介绍 Alpine 镜像的避坑指南。什么?你不知道 Alpine
镜像有哪些坑?我来告诉你。
第三部分将会探讨适用于大多数语言和框架的通用精简策略,例如使用常见的基础镜像、提取可执行文件和减小每一层的体积。同时还会介绍一些更加奇特或激进的工具,例如 Bazel
,Distroless
,DockerSlim
和 UPX
,虽然这些工具在某些特定场景下能带来奇效,但大多情况下会起到反作用。
本文介绍第一部分。
1. 万恶之源
我敢打赌,每一个初次使用自己写好的代码构建 Docker 镜像的人都会被镜像的体积吓到,来看一个例子。
让我们搬出那个屡试不爽的 hello world
C 程序:
/* hello.c */
int main () {
puts("Hello, world!");
return 0;
}
并通过下面的 Dockerfile 构建镜像:
FROM gcc
COPY hello.c .
RUN gcc -o hello hello.c
CMD ["./hello"]
然后你会发现构建成功的镜像体积远远超过了 1 GB
。。。因为该镜像包含了整个 gcc
镜像的内容。
如果使用 Ubuntu
镜像,安装 C 编译器,最后编译程序,你会得到一个大概 300 MB
大小的镜像,比上面的镜像小多了。但还是不够小,因为编译好的可执行文件还不到 20 KB
:
$ ls -l hello
-rwxr-xr-x 1 root root 16384 Nov 18 14:36 hello
类似地,Go 语言版本的 hello world
会得到相同的结果:
package main
import "fmt"
func main () {
fmt.Println("Hello, world!")
}
使用基础镜像 golang
构建的镜像大小是 800 MB
,而编译后的可执行文件只有 2 MB
大小:
$ ls -l hello
-rwxr-xr-x 1 root root 2008801 Jan 15 16:41 hello
还是不太理想,有没有办法大幅度减少镜像的体积呢?往下看。
为了更直观地对比不同镜像的大小,所有镜像都使用相同的镜像名,不同的标签。例如:hello:gcc
,hello:ubuntu
,hello:thisweirdtrick
等等,这样就可以直接使用命令 docker images hello
列出所有镜像名为 hello 的镜像,不会被其他镜像所干扰。
2. 多阶段构建
要想大幅度减少镜像的体积,多阶段构建是必不可少的。多阶段构建的想法很简单:“我不想在最终的镜像中包含一堆 C 或 Go 编译器和整个编译工具链,我只要一个编译好的可执行文件!”
多阶段构建可以由多个 FROM
指令识别,每一个 FROM
语句表示一个新的构建阶段,阶段名称可以用 AS
参数指定,例如:
FROM gcc AS mybuildstage
COPY hello.c .
RUN gcc -o hello hello.c
FROM ubuntu
COPY --from=mybuildstage hello .
CMD ["./hello"]
本例使用基础镜像 gcc
来编译程序 hello.c
,然后启动一个新的构建阶段,它以 ubuntu
作为基础镜像,将可执行文件 hello
从上一阶段拷贝到最终的镜像中。最终的镜像大小是 64 MB
,比之前的 1.1 GB
减少了 95%
:
两个奇技淫巧,将 Docker 镜像体积减小 99%的更多相关文章
- 如何合并两个Docker 镜像
http://www.open-open.com/lib/view/open1437746544709.html 在你的机器上使用docker pull来从Docker Hub下载镜像. docker ...
- Docker镜像构建的两种方式(六)--技术流ken
镜像构建介绍 在什么情况下我们需要自己构建镜像那? (1)当我们找不到现有的镜像,比如自己开发的应用程序 (2)需要在镜像中加入特定的功能 docker构建镜像有两种方式:docker commit命 ...
- 构建Docker镜像两种方式的比较-Dockerfile方式和S2I方式
前言 写Dockerfile是构建Docker镜像最通常的方式,接触过Docker的童鞋多少了解一些.前段时间研究OpenShift(paas的一种),发现了另外一种构建Docker镜像的方式:S2I ...
- 制作Docker镜像的两种方式
此文已由作者朱笑天授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.使用docker commit命令制作docker镜像 1. pull一个centos6.6的基础镜像, ...
- [转] 构建Docker镜像两种方式的比较-Dockerfile方式和S2I方式
原文地址:https://www.cnblogs.com/tianshifu/p/8127837.html 前言 写Dockerfile是构建Docker镜像最通常的方式,接触过Docker的童鞋多少 ...
- Docker镜像构建的两种方式(六)
镜像构建介绍 在什么情况下我们需要自己构建镜像那? (1)当我们找不到现有的镜像,比如自己开发的应用程序 (2)需要在镜像中加入特定的功能 docker构建镜像有两种方式:docker commit命 ...
- 两种github action 打包.Net Core 项目docker镜像推送到阿里云镜像仓库
两种github action 打包.Net Core 项目docker镜像推送到阿里云镜像仓库 1.GitHub Actions 是什么? 大家知道,持续集成由很多操作组成,比如抓取代码.运行测试. ...
- springboot整合docker部署(两种构建Docker镜像方式)--2019-3-5转
原文:https://www.cnblogs.com/shamo89/p/9201513.html 项目结构 package hello; import org.springframework.boo ...
- springboot整合docker部署(两种构建Docker镜像方式)
项目结构 package hello; import org.springframework.boot.SpringApplication; import org.springframework.bo ...
随机推荐
- Ionic3学习笔记(十五)自定义 tab icon
本文为原创文章,转载请标明出处 美工做了一套 icon,自然是要用的.将 icon copy 到 assets 文件夹下. 例如 .icon-ios-home-custom 为 iOS icon 选中 ...
- 吴裕雄--天生自然 人工智能机器学习实战代码:LASSO回归
import numpy as np import matplotlib.pyplot as plt from sklearn import datasets, linear_model from s ...
- hexo NexT主题首页title链接的优化
在默认设置下,文章链接都会改变,不利于搜索引擎收录,也不利于分享 更改index.swig文件 文件路径是your-hexo-sitethemesnextlayout,将下面代码 1 {% block ...
- echarts饼图字体大小修改
const option = { tooltip: { trigger: 'item', formatter: "{a} {b}: {c} ({d}%)" }, series: [ ...
- Binder机制简析(三)
注册Service Service组件运行在Server进程中,首先要将Service注册到Service Manager中,再启动一个Binder线程池来等待和处理Client的通信请求. 注册过程 ...
- drbd配置
DRBD就是网络RAID1,可以通过网络同步两个主机上的硬盘,drbd每次只允许对一个节点进行读写访问. 一.安装DRBD CentOS 6.x rpm -ivh http://www.elrepo. ...
- 某某项目SDV软件测试报告范例
说明:本范例为符合CMMI 5级要求的范例 Prepared by 拟制 小张 Date 日期 2008-04-09 Reviewed by 评审人 小丽.小王.小李.小莉.小三.小四.小猪.小猫.小 ...
- Swift 进阶 第 4 课 集合类型协议
• Read 序列 一个序列 (sequence) 代表的是一系列具有相同类型 的值,你可以对这些值进行迭代.遍历一个序列最简单的方式是使用 for 循环: 123 for element in so ...
- resourcequota分析(一)-evaluator-v1.5.2
什么是evaluator 大家都知道,Kubernetes中使用resourcequota对配额进行管理.配额的管理涉及两个步骤:1.计算请求所需要的资源:2.比较并更新配额.所以解读resource ...
- 《前端之路》--- 重温 Egg.js
目录 <前端之路>--- 重温 Egg.js 一.基础功能 > 日志系统包含了 四大层面的 日志对象, 分别是 App Logger.App CoreLogger.Context L ...