Alpine Docker image和Busybox Docker image有什么区别?

27

alpine docker镜像和busybox docker镜像之间有什么区别?

当我查看它们的dockerfile时,alpine的dockerfile如下(适用于Alpine v3.12 - 3.12.7)

FROM scratch
ADD alpine-minirootfs-3.12.7-x86_64.tar.gz /
CMD ["/bin/sh"]

busybox就是这样

FROM scratch
ADD busybox.tar.xz /
CMD ["sh"]

但正如https://alpinelinux.org/about/所述:

Alpine Linux是围绕musl libcbusybox构建的。

那么这两者有什么区别呢?

我也很好奇为什么许多docker镜像(例如nodejs/nginx/php)提供基于alpine而不是busybox的镜像。为什么会这样?那么busybox镜像的用例是什么呢?请注意,我不是在寻求关于为什么A比B更好或反之的答案,也不需要软件推荐。

正如musl-libc - Alpine's Greatest WeaknessDoes Alpine have known DNS issue within Kubernetes?所述,我一直在经历alpine docker的间歇性DNS查找失败。这就是我提出这个问题的原因之一。

PS,https://musl.libc.org/说"musl是在Linux系统调用API之上构建的C标准库的实现",而https://en.wikipedia.org/wiki/Alpine_Linux则提到:

以前它使用uClibc作为其C标准库,而不是通常使用的传统GNU C库(glibc)。尽管它更轻量级,但它确实有一个显著的缺点,即与glibc不兼容。因此,所有软件必须编译为使用uClibc才能正常工作。截至2014年4月9日,Alpine Linux切换到了musl,它与glibc部分二进制兼容。


busybox 镜像使用的是哪个 libc 库?我们需要分析 busybox.tar.xz 文件才能知道。 - Charles Duffy
我不会感到惊讶如果它是静态链接的,但即使如此,在静态链接到glibc和静态链接到musl之间存在巨大的大小差异。基本上,需要考虑busybox.tar.xz是如何构建的才能回答这个问题。 - Charles Duffy
顺便说一句,就我个人而言,我认为这两种方法都有点糟糕(但是,我认为整个Docker生态系统都有点糟糕)。Nixpkgs构建容器镜像(或系统镜像,或其他所有东西)的方法允许您精确地指定您想要的内容;告诉Nix你想要pkgsMusl.busybox,你就会得到一个动态编译的针对musl libc的busybox;告诉它你想要pkgsStatic.busybox,你就会得到一个静态编译的针对musl libc的busybox;而只有pkgs.busybox是针对glibc编译的busybox。而且,通过使用dockerTools,您可以告诉Nix将其中任何一个制作成Docker镜像。 - Charles Duffy
pkgsStaticpkgsMusl 只是代码 -- 它们并没有做任何你不能在几行代码中自己编写的事情,所以如果你想要 glibc-and-static,那就相对容易(“相对”是因为 glibc 通过 dlopen() 加载存根解析器来执行 DNS,这也是忙碌的盒子镜像不再是 glibc-and-static 的原因)。 - Charles Duffy
这是我第一次听到有人说Docker生态系统很糟糕,哈哈。 - Qiulang
显示剩余10条评论
2个回答

25
这两者的主要区别在于旧版本的 busybox 镜像是使用 glibc 进行静态链接的(由于即使在静态配置中也使用 libnss,当前版本使用动态链接),而 alpine 镜像则使用 musl libc 进行动态链接。
详细介绍选择这些镜像的权重因素在这里是不合适的(软件推荐请求),但是有一些关键点:
将 glibc 与 musl libc 进行比较,有一些显著的点(虽然还有许多其他因素):
- glibc 是为性能和可移植性而构建的,而不是大小(通常添加特殊情况下的性能优化,需要大量的代码)。 - musl libc 是为正确性和大小而构建的,而不是为了性能(它愿意稍微慢一些,以便具有更小的代码大小,并且可以在更少的 RAM 中运行);而且在资源耗尽时,它更积极地进行正确的错误报告(而不仅仅是立即退出)。 - glibc 被广泛使用,因此针对其实现表现出来的错误往往会更快地被发现。通常,当一个人第一次使用 musl 构建给定的软件时,他们会遇到错误(通常是在该软件中而不是在 musl 中),或者会发现维护人员明确选择使用 GNU 扩展而不是坚持 libc 标准的地方。
  • glibc 采用 LGPL 许可证,只有符合 GPL 兼容许可证条款的软件可以静态链接它;而 musl 采用 MIT 许可证,使用时更少限制。
  • 比较静态构建和动态构建的优势:

    • 如果您的系统镜像仅包含一个二进制可执行文件(使用 C 或其他 libc 编写),则静态构建始终更好,因为它会丢弃库中那些实际上没有被该可执行文件使用的部分。
    • 如果您的系统镜像将添加更多以 C 编写的二进制文件,则使用动态链接将使整体大小保持不变,因为它允许这些二进制文件使用已经存在的 libc。
    • 如果您的系统镜像将添加更多使用不依赖 libc 的语言编写的二进制文件(例如 Go 和 Rust),那么您就不会从动态链接中获益,因为您不需要那里的未使用的 libc 部分,因为您将不再使用它们。

    老实说,这两个图像并不能覆盖所有可能性的矩阵空间;有些情况下,它们都不是最优的选择。如果您要添加的内容都是非 C 语言,并且只需使用 musl libc,那么只包含 busybox 的图像具有静态链接 musl libc 的价值;或者,如果您要添加更多需要 libc 但与 musl 不兼容的二进制文件,则包含 busybox 并动态链接 glibc 的图像则更好。

    感谢您提供详细的答案。我猜想MIT许可证可能是一个关键因素。 - Qiulang
    耸肩。LGPL(与常规GPL不同)支持动态链接,这就是为什么glibc即使用于商业软件也很常见的原因;大多数人不需要静态链接其商业工具。 - Charles Duffy
    https://wiki.musl-libc.org/ 上说:“musl相对于glibc和uClibc/uClibc-ng的主要优势在于其体积小、正确性高、静态链接支持好、代码整洁”。 - Qiulang
    1
    是的,所有这些都是真的。话虽如此,glibc作为“标准”意味着它具有一些相当强大的网络效应;需要将软件移植到musl上进行构建是一项工作,而基本上所有东西都可以与glibc开箱即用。 - Charles Duffy
    嗨,我又遇到了这个问题并提供了一个答案,但仍然有很多疑问。你能看一下吗? - Qiulang

    3
    当我第一次提出这个问题时,我对busybox docker镜像的用例并不确定,而且我关于busybox dockerfile的链接也不完全正确。 这是正确的dockerfile链接,并且它解释了很多事情。所以busybox提供了3个不同版本,构建在glibc、musl和uclibc上。

    busybox dockerfile

    一个更恰当的问题是,基于musl构建的alpine镜像和busybox镜像有什么区别?除了alpine镜像更活跃地维护外,我还不知道答案。

    使用BusyBox Docker官方镜像的用例和提示》发布于2022年7月14日(所以相对较新),其中提到:“在Docker中,维护BusyBox镜像也一直是一个持续的重点。”

    我仍然希望看到有人能够提供关于在glibc或uclibc上构建BusyBox镜像的用例。

    --- 更新 ---

    在这里讨论用于运行Busybox的Docker容器的软件包管理器:uclibc“基于Busybox的任何东西都没有软件包管理器。它是一个单一的二进制文件,其中包含一堆符号链接,添加软件的方法是编写C代码并重新编译。”而在这里Busybox的软件包管理器也解释了,busybox没有软件包管理器,这可能是大多数人使用alpine的原因。
    至于DNS故障,我经常遇到,我发现为什么我再也不会使用Alpine Linux很好地解释了这个问题。
    musl(按设计)不支持DNS-over-TCP...最糟糕的是,这可能会随机出现,任何时候当某个外部网络变化导致解析某个特定域名需要超过单个UDP数据包中可用的512字节时。
    最后,这个DNS问题在Docker容器中不会出现。它只会发生在Kubernetes中...Kubernetes文档声称DNS问题仅适用于“Alpine版本3.3或更早版本”,但我在Alpine 3.16上遇到了上述问题,所以你懂的。 Alpine是否正确解析DNS? 对问题给出了确切的描述:
    TC位用于当DNS服务器要发送给客户端的DNS响应长度超过UDP数据包的512字节时...这是向DNS解析器客户端发出的信号,表明它需要从标准的UDP DNS查询切换到新的TCP DNS查询。
    幸运的是,alpine 3.18它表示现在修复了这个问题:“musl libc 1.2.4 - 现在DNS解析器中带有TCP回退功能”。
    但是我使用的Docker容器不支持alpine 3.18(截至2023年7月),所以我会等待看它是否能正常工作。

    顺便说一下,就个人而言,我并不认为DNS的论点有说服力。我做过很多创业项目,在担任运维角色时,我从未遇到过没有自己运行本地缓存DNS服务器的网站,并且通常会同时提供TCP和UDP实现。如今,你甚至可以使用诸如systemd-resolved这样的工具,在每台计算机上运行一个本地DNS服务器,将请求转发到外部基础设施。如果你不运行自己的DNS服务器,就无法进行分割视图,最终会将内部主机名暴露给外界,所以我不明白为什么会有人处于这种情况。 - Charles Duffy
    还有一些安全性的论点:如果您想加密上行DNS流量,可以依赖可能存在或不存在的每个应用程序的支持,或者您可以只使用一个本地代理来负责处理所有这些;DNS被恶意软件用于数据泄露或命令与控制也并非罕见,因此出于安全敏感的背景考虑,除了您正式批准的企业IT DNS服务之外,阻止其他任何DNS服务都是可辩护的。 - Charles Duffy
    你是在暗示“运行自己的本地缓存DNS服务器”是非常必要的吗?因为我对Kubernetes的知识有限,我真的希望它可以在没有额外设置的情况下正常工作。此外,我确实使用了dnsmasq,但遇到了这个问题https://dev59.com/qG0NtIcB2Jgan1znPdoP。再次强调,对我来说,正确设置Kubernetes是一项艰巨的任务。我真的希望它可以顺利运行。 - Qiulang
    嗯。当人们做错事情时,对某种东西的普遍需求只意味着很少有人做对了。 :) -- 不过,请将其视为来自一个老式的UNIX胡子程序员,因为确实如此。对于Kubernetes作为整体,我也非常不信任,同样地,对于任何其他允许太多在视野之外发生的编排层;当一切可供操作员诊断、理解、推理和修复时,最容易做好工作。自己运行DNS与此相符;有助于更轻松地进行故障排除。 - Charles Duffy
    我同意这些话:“做得对的人很少”。但问题是,很多人不具备知识,也不愿意去学习。以我为例,我可能是公司里最了解Docker和Kubernetes的人,但我仍然在努力让它们正常运行。而我公司的其他人甚至没有学习的意愿。他们总是说Docker/Kubernetes的东西“吓到”他们。 - Qiulang
    显示剩余2条评论

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接