写点什么

微型容器挑战:构建一个 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:572673
用户头像

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

关注

评论

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

获5项大奖,发布《云计算开放应用架构标准》,阿里云持续领航云原生

阿里巴巴中间件

云计算 最佳实践 云原生 案例 白皮书

编曲新手可以用什么编曲软件?

奈奈的杂社

编曲 编曲宿主 编曲软件

云原生加速落地,金融行业应用上云来打样儿

BoCloud博云

云原生

1小时内被全网疯转 29.8w 次,最终被所有大V协力封杀!

Java架构师迁哥

公安局重点人员研判分析系统解决方案

Vue-1-初识

Python研究所

签约计划

牛x运维常用的工具系列-1

运维研习社

运维 工具分享 5月日更

服务可达,达者为先,产品发布会嘉宾精彩观点分享!

博睿数据

博睿数据 数据链DNA 服务可达

论证:iOS安全性,为什么需要审核?

37手游iOS技术运营团队

ios SIP Sandbox iOS Developer ios安全

🔎【Java 源码探索】深入浅出的分析ThreadLocal

码界西柚

Java 多线程 ThreadLocal 5月日更 ThreadLocalMap

python脚本编写——自动剪切移动文件夹

YUKI0506

OKR 八问 —— 关于 OKR 的常见问题与思考

CODING DevOps

团队管理 DevOps OKR

从零开始学习ThingJS之创建App对象

ThingJS数字孪生引擎

可视化 3D可视化 数字孪生

40K成功入职:六年开发终获小米Offer(附面经+面试题+答案详解)

Java架构师迁哥

🔎【Java源码探索】深入浅出的分析HashMap(JDK8)

码界西柚

Java 源码 源码分析 hashmap 5月日更

百余大企业共赴新文明之约:2021 DEMO WORLD 世界创新峰会拉开帷幕

创业邦

创新

使用Docker运行DataX定时全量备份关键数据表

白粥

DataX 数据表备份

走向机器智能时代:移动机器人的困局与创新

晨山资本

机器人 移动机器人 AMR

如何评估 Serverless 服务能力?这份报告给出了 40 条标准

Serverless Devs

云计算 云原生 Forrester Wave #Serverless

Fabric | 自动化神器

Python研究所

签约计划

100W点击 10w人获取,阿里Java高级面试题及答案 到底有多强

???

面试 java真题分享

面阿里P7,竟问这么简单的题目?

Java架构师迁哥

MPP大规模并行处理架构详解

五分钟学大数据

大数据 MPP 5月日更

AI年中钜惠来袭—全场低至6折 企业新客1元优享福利翻倍

百度大脑

福利 Iphone12

列举出常见的Java面试题,我靠这个在春招拿到了阿里的offer

???

面试 Java面经 java真题分享

我厂与张家港市达成全面战略合作,共推数据中心和城市智能化转型

百度大脑

数据中心 城市智能化

中国呼叫中心与卓越客服产业峰会,百度智能客服再提行业创新

百度大脑

解决方案 行业创新

Bugless 异常监控系统 (iOS端)

37手游iOS技术运营团队

ios iOS Developer 崩溃分析 bugless

ARM和X86云服务器的算力对比

Python研究所

签约计划

MeterSphere | 超好用的开源测试平台

Python研究所

签约计划

脉脉3小时转发65w次!这份Java面试宝典发生了什么?

Java架构师迁哥

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