Alpine?Distroless?Busybox?到底谁才是容器镜像的瑞士军刀?


大多数情况下,我们构建容器镜像时选择的基础镜像无外乎是 busyboxalpinegoogle/distroless 这几种,这几个基础镜像在云原生的世界很吃香,被广泛应用于各个应用的容器化。

那么问题来了,为什么这几个基础镜像如此受欢迎呢?

我们先来看下这几个基础镜像的大小:

????  → podman image ls 
REPOSITORY                 TAG         IMAGE ID      CREATED       SIZE
docker.io/library/alpine   latest      14119a10abf4  6 days ago    5.87 MB
docker.io/library/busybox  latest      42b97d3c2ae9  13 days ago   1.46 MB
gcr.io/distroless/static   latest      e0851a4aa136  51 years ago  3.06 MB

可以看到这些镜像的体积都非常小,几乎可以忽略不计。

Busybox

先启动一个 Busybox 容器进去一探究竟:

这个镜像的大小只有 1.24MB,缺容纳了这么多 GNU 命令,麻雀虽小五脏俱全啊,这到底是怎么做到的?

事实上这一切都要归功于 Multi-Call binary。什么是 Multi-Call binary 呢?

顾名思义,Multi-Call binary 就是多重调用二进制文件,是一个用C语言编写的程序,它允许多次调用来执行二进制文件。它包含了很多函数,每个执行独特动作的函数都可以通过一个名字来调用,这个名字同时也是 Multi-Call binary 的一个符号链接。Multi-Call binary 最好的应用范例便是 Busybox。

Busybox 里面的函数可以通过两种方式来调用:

  • busybox ls

  • ls

例如:

Busybox 容器

很明显,这些不是我们所熟知的 GNU 二进制文件,因为所有的二进制文件都具有相同的属性,比如大小、日期等。这些都不是独立的二进制文件,而是 Multi-Call binary 每个调用函数的别名。这个 Multi-Call binary 就叫 Busybox

遗憾的是,这些 Busybox 命令并不完全等同于 GNU 命令,某些命令的某些参数是无法执行的,相当于阉割版。

Alpine

看完了 Busybox,我们再来看看 Alpine 是怎么做的。

巧了,Alpine 的二进制文件竟然是指向 busybox 二进制文件的,这就很明显了,Alpine 镜像的底层使用了 busybox 二进制文件。除此之外,Alpine 还包含了 apk 包管理器和一些额外的可执行文件,所以 Alpine 镜像的体积才会比 Busybox 大。

Distroless

Distroless 就不用说了,它来自 Google[1]。该镜像几乎就是空的,只包含应用程序及其运行时所需的依赖,不包含软件包管理器、shell 和其他 GNU 二进制文件,当然还包含一些时区配置和部分 ca-certificates。

可以看到这个镜像中既没有 shell 也没有 bash,为了一探究竟,可以先把镜像保存为 tar 包,然后把 rootfs 解压出来:

????  → mkdir image
????  → tar xvf distroless.tar.gz -C image/
16679402dc206c982b5552ab8de7d898547100e5468be29d4f67d393c0eadfdb.tar
e0851a4aa13657fc8dcd01e0e5e08cb817123ccb82e2c604b34f9ec9c1755e3f.json
2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c/layer.tar
2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c/VERSION
2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c/json
manifest.json
repositories

????  → cd image
????  → ls -lh
total 3.0M
-r--r--r--. 1 root root 3.0M Jan  1  1970 16679402dc206c982b5552ab8de7d898547100e5468be29d4f67d393c0eadfdb.tar
drwxr-xr-x. 2 root root   50 Sep  3 17:42 2e18de03719583329b7fa8374130e57cc7cddf2b5a487fe4a4988622ca60575c
-r--r--r--. 1 root root  462 Jan  1  1970 e0851a4aa13657fc8dcd01e0e5e08cb817123ccb82e2c604b34f9ec9c1755e3f.json
-r--r--r--. 1 root root  213 Jan  1  1970 manifest.json
-r--r--r--. 1 root root  106 Jan  1  1970 repositories

????  → mkdir rootfs
????  → tar xf 16679402dc206c982b5552ab8de7d898547100e5468be29d4f67d393c0eadfdb.tar -C rootfs

????  → tree rootfs
rootfs
├── bin
├── boot
├── dev
├── etc
│   ├── debian_version
│   ├── default
│   ├── dpkg
│   │   └── origins
│   │       └── debian
│   ├── group
│   ├── host.conf
│   ├── issue
│   ├── issue.net
│   ├── nsswitch.conf
│   ├── os-release
│   ├── passwd
│   ├── profile.d
│   ├── protocols
│   ├── rpc
│   ├── services
│   ├── skel
│   ├── ssl
│   │   └── certs
│   │       └── ca-certificates.crt
│   └── update-motd.d
│       └── 10-uname
├── home
│   └── nonroot
├── lib
├── proc
├── root
├── run
├── sbin
├── sys
├── tmp
├── usr
│   ├── bin
│   ├── games
│   ├── include
│   ├── lib
│   │   └── os-release
│   ├── sbin
│   │   └── tzconfig
│   ├── share
│   │   ├── base-files
│   │   │   ├── dot.bashrc
│   │   │   ├── dot.profile
│   │   │   ├── dot.profile.md5sums
│   │   │   ├── info.dir
│   │   │   ├── motd
│   │   │   ├── profile
│   │   │   ├── profile.md5sums
│   │   │   └── staff-group-for-usr-local
...
...

该镜像只有一层,大小为 3MB,也没有二进制文件,只有一些证书文件和目录。如果向下滚动,还能看到许可证和时区配置。看来 Distroless 采取的是非常极端的手段,直接把不需要的二进制文件全部抛弃了,只留下一个空镜像和部分必需品。

总结

由此看来,这几个基础镜像如此受欢迎的主要原因就是体积小。镜像越小,漏洞就越少,可攻击面也会大幅减少,而且很容易维护。所以大家构建镜像时尽量选择这些镜像作为基础镜像。

引用链接

[1]

Google: https://github.com/GoogleContainerTools/distroless


你可能还喜欢

点击下方图片即可阅读

继 K3s 之后,又来个 K0s...

云原生是一种信仰 ????

关注公众号

后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

点击 "阅读原文" 获取更好的阅读体验!

发现朋友圈变“安静”了吗?

Dockerfile是一个文本文件,它包含了用于自动化构建Docker镜像的指令集合。这些指令告诉Docker从基础镜像开始,如何安装软件、配置环境和复制文件。使用Dockerfile可以让开发者在不同环境中创建一致的软件部署,极大地提升了可移植性和复用性。 以下是使用Dockerfile的基本步骤: 1. **初始化Dockerfile**: 开始Dockerfile时,通常使用`FROM`指令指定基础镜像,如`FROM ubuntu:latest`或`FROM node:14-alpine`。 2. **运行命令(RUN)**: 在这个部分,你可以添加执行的命令,例如安装软件包、设置环境变量或下载文件。 ```bash RUN apt-get update && apt-get install -y nginx ``` 3. **复制文件(COPY)**: 将本地文件复制到镜像中。如果需要创建目录,可以先使用`mkdir`。 ```bash COPY . /app ``` 4. **暴露端口(EXPOSE)**: 如果应用有公开的网络端口,用`EXPOSE 80`声明。 5. **设置工作目录(WORKDIR)**: 指定容器内的默认工作目录。 6. **添加启动命令(CMD/ENTRYPOINT)**: 使用`CMD`设置默认命令,`ENTRYPOINT`更灵活,可以接受参数。 ```bash CMD ["nginx", "-g", "daemon off;"] ``` 7. **构建镜像docker build)**: 在主机上,使用`docker build -t myimage .`命令,其中`-t`指定标签,`.`表示当前目录作为Dockerfile的位置。 构建完成后,你可以使用`docker run`命令运行基于新镜像容器,或者使用`docker push`将镜像推送到Docker Hub或其他仓库,以便其他人也能使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值