写点什么

微型容器挑战:构建一个 6kB 的容器化 HTTP 服务器

devopsdirective.com

  • 2021-06-15
  • 本文字数:2112 字

    阅读完需:约 7 分钟

微型容器挑战:构建一个6kB的容器化HTTP服务器

我着手构建一个我所能构建的最小且仍然有一些用途的容器镜像。通过利用多阶段构建、一个 scratch 基础镜像以及一个微型的基于汇编语言的 http 服务器,我将这个镜像减小到 6.32kB!


膨胀的容器


容器通常被吹捧为一颗银弹,能够解决与操作软件相关的每一个挑战。虽然我喜欢容器,但我经常遇到有各种各样问题的容器镜像。一个常见的问题是容器大小,容器镜像有时候会达到几个 GB!

正因为如此,我决定进行挑战,来构建尽可能最小的镜像。


挑战


规则很简单:

  • 这个容器应该在你指定的端口上通过 http 提供一个文件的内容

  • 不允许挂载卷(也即“Marek 规则”)


初始方案


为了获得符合基准的镜像大小,我们可以使用 node.js 创建一个简单的服务器index.js:


const fs = require("fs");const http = require('http');const server = http.createServer((req, res) => {  res.writeHead(200, { 'content-type': 'text/html' })  fs.createReadStream('index.html').pipe(res)})server.listen(port, hostname, () => {  console.log(`Server: http://0.0.0.0:8080/`);});
复制代码


然后将它构建到一个启用官方的 node 基础镜像的镜像中:


FROM node:14COPY . .CMD ["node", "index.js"]
复制代码


这就有943MB


更小的基础镜像


减小镜像大小的最简单最明显的策略之一就是使用一个更小的基础镜像。官方的 node 镜像有一个slim变体(仍然基于 debian,但是预装的依赖更少),以及一个基于 Alpine Linux 的alpine变体。


使用node:14-slimnode:14-alpine作为基础镜像可以分别将镜像大小降低到167MB116MB

由于 docker 镜像是添加型的,每一层都是构建在另一层的基础上,因此我们不能做太多别的事情来进一步减小 node.js 方案。


编译型语言


为了更进一步,我们可以转换到一个具有更少运行时依赖的编译型语言。有很多选项,但对于构建 Web 服务,golang 是一个比较流行的选择。


我创建了一个基础的文件服务器server.go:


package mainimport (    "fmt"    "log"    "net/http")func main() {    fileServer := http.FileServer(http.Dir("./"))    http.Handle("/", fileServer)    fmt.Printf("Starting server at port 8080\n")    if err := http.ListenAndServe(":8080", nil); err != nil {            log.Fatal(err)    }}
复制代码


然后将它构建到一个使用官方的 golang 基础镜像的容器镜像:


FROM golang:1.14COPY . .RUN go build -o server .CMD ["./server"]
复制代码


这有818MB. 这里的问题是 golang 基础镜像有很多预安装的依赖,这些依赖在构建 go 软件时有用,但是在运行软件时并不是必需的。


多阶段构建


Docker 有一个叫做“多阶段构建(multi-stage builds)”的特性,它可以轻易在一个具有所有必需依赖的环境中构建代码,然后将可执行结果拷贝到另一个不同的镜像中。


这样做有很多好处,但最明显的是镜像大小!通过如下重构 dockerfile:


### build stage ###FROM golang:1.14-alpine AS builderCOPY . .RUN go build -o server .### run stage ###FROM alpine:3.12COPY --from=builder /go/server ./serverCOPY index.html index.htmlCMD ["./server"]
复制代码


结果镜像只有13.2MB! 🙂


静态编译 +scratch 镜像


13MB 还不错,但我们还可以利用一些手段将镜像变得更小。


有一个名为 scratch 的基础镜像,它是空的且大小为零。由于scratch内部没有任何内容,因此任何基于它构建的镜像必须包含所有必需的依赖。


为了使我们的 go 基础服务器能够运行,我们需要在编译步骤中添加几个标志,从而确保必要的库静态链接到可执行程序中:


### build stage ###FROM golang:1.14 as builderCOPY . .RUN go build \  -ldflags "-linkmode external -extldflags -static" \  -a server.go### run stage ###FROM scratchCOPY --from=builder /go/server ./serverCOPY index.html index.htmlCMD ["./server"]
复制代码


具体来说,我们将链接模式设置为external,并将-static标志传给外部链接器。这两个更改使得镜像大小减小到8.65MB😀


ASM 决定胜局!


一个小于 10MB、用 Go 这样的语言编写的镜像,对于任何情况来说都已经是很小了... 但是我们可以让它变得更小!Github 用户 nemasu 在名为 assmttpd 的 github 上用汇编语言编写了一个功能齐全的 http 服务器。


在运行提供的make release脚本之前,需要将一些构建依赖安装到 ubuntu 基础镜像中,从而进行容器化:


### build stage ###FROM ubuntu:18.04 as builderRUN apt updateRUN apt install -y make yasm as31 nasm binutils COPY . .RUN make release### run stage ###FROM scratchCOPY --from=builder /asmttpd /asmttpdCOPY /web_root/index.html /web_root/index.htmlCMD ["/asmttpd", "/web_root", "8080"]
复制代码

然后将生成的asmttpd可执行文件复制到 scratch 镜像中,并使用CMD调用。这样下来,镜像大小只有 6.34kB! 🥳


容器镜像大小的进展!


希望你能从我们这段从最初的 943MB 的 Node.js 镜像一直到微型的 6.34kB 的汇编镜像的过程中,学到一些技术,将来用于减小你的容器镜像大小。


作者介绍:


devopsdirective.com


原文链接:


https://devopsdirective.com/posts/2021/04/tiny-container-image/index.html?fileGuid=KxkC6jGXydCvRRrg


2021-06-15 15:572526
用户头像

发布了 74 篇内容, 共 28.6 次阅读, 收获喜欢 83 次。

关注

评论

发布
暂无评论
发现更多内容

借助AI模型目标检测打标签工具 :Makesense.ai , 解放双手 ! ! !

码农的后花园

人工智能 深度学习 目标检测 yolo YOLOv5

Git的实战教学 | 从0到1

卢卡多多

git 8月日更

Druid 从控制台(Druid console)中删除过滤器和运行查询

HoneyMoose

2020-2021阿里巴巴Java面试真题解析,详细的Java学习指南

Geek_f90455

Java 程序员 后端

网络攻防学习笔记 Day93

穿过生命散发芬芳

网络攻防 8月日更

3分钟搭建一个网站?腾讯云Serverless开发体验

Zhendong

腾讯云 Serverless 云原生 云开发

10万字Mysql实战文档,Java面试题汇总

Geek_f90455

Java 程序员 后端

Linux之iostat命令

入门小站

Linux

15分钟的字节跳动视频面试,工作感悟

Geek_f90455

Java 程序员 后端

2020-2021华为Java面试真题,统统都会!

Geek_f90455

Java 程序员 后端

在线分数加减乘除计算器

入门小站

工具

设计电商秒杀系统

好吃不贵

即战力:职场上如鱼得水的一种能力

非著名程序员

个人成长 提升认知 职场成长 8月日更

iOS 开发没市场了吗?移动开发真的凉了吗? | 【话题讨论】

HelloWorld杰少

话题讨论 iOS Developer

2021Java开发现状分析,给大家安排上!

Geek_f90455

Java 程序员 后端

架构师实战营 模块十总结

代廉洁

架构实战营

社会心理学-自尊

箭上有毒

读书笔记 8月日更

2020-2021华为Java面试真题,内含福利

Geek_f90455

Java 程序员 后端

怎么对数据指标管理

水滴

指标体系 数据指标 8月日更 指标管理

配置手机测试环境

IT蜗壳-Tango

8月日更

webstorm之开发工具Vim(一)

Augus

8月日更

2021Java大厂高频面试题,原理+实战+视频+源码

Geek_f90455

Java 程序员 后端

【设计模式】建造者

Andy阿辉

C# 编程 后端 设计模式 8月日更

写作业写作业写作业

Nydia

10天拿到字节跳动Java岗位offer,互联网行业“中年”危机

Geek_f90455

Java 程序员 后端

12年高级工程师的“飞升之路”,面试题+笔记+项目实战

Geek_f90455

Java 程序员 后端

架构实战营 - 模块四作业

思梦乐

Druid 从控制台(Druid console)中查询字段

HoneyMoose

醍醐灌顶学习RTMP,从总体介绍到各个细节

hanaper

音视频

SpringBoot 中使用Redis缓存

xcbeyond

redis 8月日更

02 设计模式之策略模式

陈皮的JavaLib

Java 设计模式 策略模式 8月日更

微型容器挑战:构建一个6kB的容器化HTTP服务器_语言 & 开发_InfoQ精选文章