Alpine 作为基础镜像利弊

Alpine

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。它不同于通常 Linux 发行版,Alpine 采用了 musl libcbusybox 以减小系统的体积和运行时资源消耗,但功能上比 busybox 又完善的多,因此得到开源社区越来越多的青睐。在保持瘦身的同时,Alpine 还提供了自己的包管理工具 apk,可以通过 https://pkgs.alpinelinux.org/packages 网站上查询包信息,也可以直接通过 apk 命令直接查询和安装各种软件。

spaces_-M5xTVjmK7ax94c8ZQcm_uploads_git-blob-acfe2b19a46d65232e6180e44e2ee7b64e26b52a_alpinelinux-logo.png

1、优劣势

 1.1、优势

  • 镜像体积小

    Alpine 的目标就是减少基础镜像的构建成本,让构建出来的镜像占据更少的空间,因此底层必定是一个动态链接的关系。但是它比较特殊,没有使用 GLIBC 这样比较重的动态库,而是使用 busybox + musl libc 的这样方式,musl libc 这个库相比于 glibc 更小、更简单、更安全,大大降低了空间并保持相对应的功能。

  • 包管理工具速度快

    容器在周期中是经常变化的,可能会定期构建新镜像,也可能会在运行的容器中临时安装某些调试工具,如果软件包的安装速度很慢,会很快消磨掉我们的耐心,而 Alpine 镜像的包管理工具的执行速度非常快,安装软件体验非常顺滑。

 1.2、劣势

  • 小众劣势

    CentOSUbuntu 这些常见的操作系统不同,Alpine 并没有像 Red Hat 或 Canonical 之类的大公司为其提供维护支持,而是由非商业组织进行维护,因此软件包的数量也就比这些发行版少很多(如果只看开箱即用的默认软件仓库,Alpine 只有 10000 个软件包,而 Ubuntu、Debian 和 Fedora 的软件包数量均大于 50000)。

    如果在项目中需要使用一个 musl libc 链接的二进制文件,但是这个工具 Alpine 本身并没有进行一个提供的话,就需要自己去进行编译打包,这个过程本身就是一个很复杂的事情,必须了解 make 编译的几大阶段状态,还得在发生错误时会进行调试,很浪费时间。

  • 底层依赖缺陷

    底层使用 musl libc 作为依赖库,他的体积小是一个优势,但是也是他的一个劣势,不能像 glibc 拥有完整的底层动态库,在多层构建过程中就可能会出现找不到相关的依赖问题,这个问题也是造成 Alpine 作为底层镜像产生大量坑的一个埋点。

    musl libc 常用嵌入式系统当中,因为这类系统本身的资源就很稀缺,不适合使用很大的一个底层库,所以必须用这类小型的库依赖。

2、常见的问题

  1. 使用 Alpine 构建 Python docker 容器速度慢了50倍

    pythonspeed.com/articles/al…

    这是一篇博文中给出的数据验证,上面地址是他的具体情况链接, 这种情况是很好理解的,因为 Python 是一种解释型的语言,它内部的很多依赖都已经打包好了,用 PIP 可以直接进行一个引入安装,但这些都绑定了特定的 C 库,这就意味着在大多数使用 glibc 的镜像中都可以正常安装,但 Alpine 镜像就不行。如果非要在 Alpine 中安装,你需要安装很多依赖,重头构建,耗时又费力。

  2. 构建多层容器镜像时发生错误

    github.com/elastic/ela…

    Docker 的容器概念本来就是一个分层的多层文件系统,从底层开始一层一层的添加所属层来增加对于业务层的支撑,但是在这个问题中在超过 4 层就发生了构建错误......

  3. DNS 转发失败

    musl libc does not use domain or search directives in the /etc/resolv.conf file. For example, if you started your Docker daemon with --dns-search=service.consul, and then tried to resolve consul from within an Alpine Linux container, it would fail as the name consul.service.consul would not be tried. You will need to work around this by using fully qualified names.

    Another difference is parallel querying of name servers. This can be problematic if your first name server has a different DNS view (such as service discovery through DNS). For example, if you started your Docker daemon with --dns=172.17.42.1 --dns=10.0.2.15 where 172.17.42.1 is a local DNS server to resolve name for service discovery and 10.0.2.15 is for external DNS resolving, you wouldn't be able to guarantee that 172.17.42.1 will always be queried first. There will be sporadic failures.

    In both of these cases, it can help to run a local caching DNS server such as dnsmasq, that can be used for both caching and search path routing. Running dnsmasq with --server /consul/10.0.0.1 would forward queries for the .consul to 10.0.0.1.

  4. 二进制编译不兼容

    While there are binaries that will run on musl libc without needing to be recompiled, you will likely encounter binaries and applications that rely on specific glibc functionality that will fail to start up. An example of this would be Oracle Java which relies on specific symbols only found in glibc. You can often use ldd to determine the exact symbol:

    # ldd bin/java
    /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000)
    libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000)
    libjli.so => bin/../lib/amd64/jli/libjli.so (0x7f542e9a0000)
    libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000)
    libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000)
    Error relocating bin/../lib/amd64/jli/libjli.so: __rawmemchr: symbol not found
    

    In this case, the upstream would need to remove the support for this offending symbol or have the ability to compile the software natively on musl libc. Be sure to check the Alpine Linux package index to see if a suitable replacement package already exists.

3、方案替换

 如果程序仅用到了标准库或者依赖项和程序本身使用的是同一种语言,且无需调用 C 库和外部依赖,那么使用 Alpine 作为基础镜像一般是没有啥问题的。

 一旦程序需要调用外部依赖,情况就复杂了,想继续使用 Alpine 镜像,就得安装这些依赖。

  1. 依赖库有针对 Alpine 的安装说明,一般会说明需要安装哪些软件包以及如何建立依赖关系。
  2. 依赖库没有针对 Alpine 的安装说明,我们可以通过对比找到与别的发行版的软件包相匹配的 Alpine 软件包。
  3. 依赖库没有针对 Alpine 的安装说明,也没有与之对应的软件,这种情况就必须从源码开始构建。

 我们大概可以得出一个结论,Alpine 通过降低底层依赖来减少自身的一个体积成为了后续最大的问题隐患,在场景比较复杂的情况下我们就需要切换基础镜像,使用功能更加全面、体积也相对较为轻便的镜像。

 通过 Docker hub 中的镜像比对,可以选择几款替代品,虽然在相对体积上还是比不上 Alpine ,但是容器上面体积小并不是最重要的一个指标,在一定条件下一个持续稳健的容器比一个精细的容器要更加的好。

下表是仓库中最新的 Alpine 和其他基础镜像的镜像大小比对(只比较了一个操作系统下的情况)

IMAGE DIGEST OS/ARCH COMPRESSED SIZE
alpine 4ff3ca912757 linux/amd64 2.67 MB
debian 3345381beaed linux/amd64 28.3 MB
ubuntu 386bace9fb0d592 linux/amd64 29.01 MB
fedora 486fd5578f93 linux/amd64 56.2 MB

 虽然 docker 极力想要把基础镜像从 ubuntu 迁移到 alpine ,但是考虑到用户的选择问题,还是做出了妥协,从docker 官方发布的镜像的发行版中可以看出一些的端倪,可以使用 debian 作为基础镜像的替换,彼此之间的体积差异也不是很大。

猜你喜欢

转载自juejin.im/post/7120557446682116132
今日推荐