• 我为什么决定放弃Alpine Linux
  • 发布于 2个月前
  • 218 热度
    0 评论
  • 冰魄
  • 0 粉丝 39 篇博客
  •   
现在,Alpine Linux 已经成为最受欢迎的容器基础镜像之一。许多人(你可能也是其中之一)用它干很多事情。有些人选择 Alpine 是因为它体积小,有些人是因为习惯,还有些人可能只是从某个教程中复制粘贴了 Dockerfile。但是,也有很多人告诉你,在某些情况下不应该使用 Alpine 作为容器镜像,因为这可能会给你带来麻烦。

01 麻烦的根源
要了解 Alpine 在某些情况下为什么不是一个好选择,我们首先需要谈论一下 musl。musl 是 C 标准库的一种实现。相对于其他 Linux 发行版(如 Ubuntu)所使用的 glibc,musl 更轻量、更快、更简单。大部分情况下,这两种实现可以互换,这就是为什么你可以从 Ubuntu 切换到 Alpine 而几乎察觉不到任何差异。

然而,正是这些微小的差异可能带来一系列麻烦。其中一些问题源于 musl(因此也包括 Alpine)如何处理 DNS(怎么总是 DNS?)。更具体地说,musl(出于设计考虑)不支持 DNS-over-TCP。

通常情况下,你可能不会注意到这种差异,因为大多数情况下,一个单独的 UDP 数据包(512 字节)足以解析主机名…… 直到有一天,当某些外部网络变化导致解析某个特定域名需要超过单个 UDP 数据包可容纳的 512 字节时,你的应用程序(在 Kubernetes 上运行)突然对一个非常关键的主机名抛出 “Unknown Host” 异常。最糟糕的是,这个问题可能随机出现,只要某些外部网络变化导致解析某个特定域名需要更多字节,就可能会发生这种情况。

通过使用 Alpine,你实际上为你的集群获得了一种 “免费” 的混沌工程。

如果你运行几十甚至数百个基于 Alpine 的微服务/应用程序,它们突然停止工作,唯一的解决方法是切换到另一个 Linux 发行版,这将需要重新构建所有应用程序并重新部署,那么你可能面临着一个极具破坏性的、持续好几天的停机时间。

这个 DNS 问题不会在 Docker 容器中暴露出来。它只会在 Kubernetes 中发生,所以如果你在本地进行测试,一切工作都正常,只有在将应用程序部署到集群中时才会发现无法解决的问题。此外,Kubernetes 文档声称 DNS 问题只与 “Alpine 3.3 或更早版本”,但我在 Alpine 3.16 上也遇到了上述问题,所以你可以自行判断。

作为额外福利,许多流行的工具也使用 Alpine 作为基础镜像,例如 nicolaka/netshoot 或 giantswarm/tiny-tools,前者专门用于网络故障排除。当你的网络故障排除工具本身也出现问题时,那只能祝你好运了。

02 其他问题
虽然 DNS 问题是 musl 的最常见问题,但还有更多原因要重新考虑使用它。任何依赖于 C 标准库的编程语言或其库都会受到 musl 和 glibc 之间差异的影响。例如,对于 Python 来说,许多流行的库如 NumPy 或 Cryptography 依赖于 C 代码进行优化。幸运的是,至少对于像 NumPy 这样的一些库,你很有可能找到基于 Alpine 编译的软件包和相关依赖项。

然而,对于不太流行的库,你可能需要自己进行编译,但是这样做值得吗?在我看来…… 不值得。此外,即使你成功构建了一个包含 NumPy 等库的镜像,其大小也将达到约 400MB,这时候使用 Alpine 来节省空间并没有太多帮助。

此外,构建这样一个镜像的时间将会非常长。你可以自己尝试一下;下面的 Dockerfile 构建过程几乎需要十分钟:
# 堆代码 duidaima.com
FROM python:3.11-alpine
RUN apk --update add gcc build-base
RUN pip install --no-cache-dir numpy

当然,其他编程语言也可能遇到类似问题。例如,Node.js 使用插件(add-ons),这些插件是用 C++ 编写并使用 node-gyp 进行编译的,它们依赖于 C 库,因此也依赖于 glibc。


另一个例子是 Golang,其标准库(特别是 net/http 或 os/user 模块)依赖于 C 库,因此也依赖于 glibc。即使你不使用这些特定模块,如果你的应用程序需要设置 CGO_ENABLED=1,那么在 Alpine 上也可能会遇到问题。

03 代替方案
如果上述问题促使你重新考虑使用 Alpine,你可能会想知道有什么可以使用的代替方案。其实选择有很多,而且它们各有优缺点,使用的时候需要你来权衡。Alpine 最大的吸引力在于其小巧的体积,所以如果你非常关注这一点,那么 Wolfi(例如,cgr.dev/chainguard/wolfi-base 只有 12MB)或 Distroless 是不错的选择。

如果你需要一个大小合适且不基于 musl 的通用基础镜像,那么可以考虑使用 Red Hat 提供的 UBI(Universal Base Image),其 “micro” 版本只有 26.7MB(registry.access.redhat.com/ubi8-micro),这与 Alpine 相差无几。

选择 Alpine 的另一个原因是其安全性。这也与其小巧的体积有关,因为小巧的体积通常意味着较少的软件包,从而减少了潜在的漏洞。前面提到的 Wolfi 镜像在这方面特别出色。

但是实事求是地说,除非你需要频繁拉取镜像(你可能本应该避免这样做),否则通过 Alpine 的小巧体积节省几兆字节的空间可能并不重要,因此基于 Ubuntu 或 Debian 的基础镜像也是不错的选择。

04 结论
总之,虽然使用 Alpine 没有问题,并且作为基础容器镜像操作系统是很好的选择,但就我个人而言,因为前面描述的 DNS 问题,我可能再也不会完全信任它,也不会信任使用 musl 的其他系统。本文的目的不是诋毁 Alpine,而是提醒人们。尽管 Alpine 可能看起来是一个不错的选择(考虑到上述问题),但至少是有一定风险的,在某些特定情况下决定使用 Alpine 可能是很鲁莽的。当然,Alpine 也有许多优点,所以如果你不担心或不受本文描述的问题影响,那么继续使用它也是可以的。

因此,我们应该得出结论:在决定使用任何东西(无论是容器操作系统、框架还是库)之前,要进行合理的研究。一个系统受欢迎并且声誉良好并不意味着它会自动成为一个好的选择。
用户评论