如何将我首个发布的Docker镜像体积缩减40%——关于将Shell脚本Docker化的经验分享
工作中我每天都要接触Dockerfile,自己也写过几个,搭建过容器等等。但从未在Docker Hub注册表上发布过镜像。我想让ugit – 撤销Git命令的工具(以Shell脚本形式编写)能供那些不愿从网上安装随机Shell脚本的人使用。
我知道,我知道。用Go/Rust/魔法语言重写吧。但这段脚本已有500多行Bash代码,除非有人拿枪指着我(或者最终赞助我??),否则我绝不会改写。况且ugit功能已基本完善(只剩几个不常用的撤销命令未实现)。
总之,本文后续将详细说明我如何为ugit(一个shell脚本)编写官方Dockerfile,并通过逐步引导式优化尝试将镜像大小缩减近40% (从31.4 MB降至17.6 MB)的官方Dockerfile。希望这能激励其他Shell爱好者也将其脚本发布为Docker镜像!
PS:我并非DevOps或Docker专家,若您是相关领域的行家,发现任何错误或改进空间,请在评论区告知或通过此处联系我。最终Docker镜像已发布于Docker Hub
- 首个
Dockerfile尝试 - 优化之路——缩减镜像体积40%
- 第二次尝试 –
alpine容器运行alpine镜像 - 第三次尝试——在第二阶段使用
scratch - 运行
ugit的全部需求 - 能否进一步缩减体积?
- 你没试过
docker-slim吗? - 你还没试过
docker-squash吗? - 经验总结
- 鸣谢
- 资源
- 致谢
首个 Dockerfile 尝试
# Use an official Alpine runtime as a parent image
FROM alpine:3.18
# Set the working directory in the container to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install dependencies
RUN apk add --no-cache
bash
gawk
findutils
coreutils
git
ncurses
fzf
# Set permissions and move the script to path
RUN chmod +x ugit && mv ugit /usr/local/bin/
# Run ugit when the container launches
CMD ["ugit"]
看起来很简单吧?确实如此。只要当前目录已克隆 ugit,你完全可以直接复制粘贴这个 Dockerfile 自行构建镜像。
docker build -t ugit .
docker run --rm -it -v $(pwd):/app ugit
这应该能在容器内成功运行 ugit。
ugit 需要以下二进制文件:bash (>=4.0)、awk、xargs、git、fzf、tput、cut、tr、nl。
以上就是UNIX系统或容器中运行ugit所需的全部组件。当前镜像大小为31.4 MB,初次尝试已相当不错。接下来我们将探索进一步压缩的方法。
优化路径——缩减镜像大小40%
在后续章节的(极致)微优化尝试中,我们将围绕以下核心目标展开:
- 采用多阶段构建缩减镜像体积
- 移除
sleep、watch、du等非ugit运行必需的二进制文件 - 清除这些二进制工具引入的冗余依赖项
- 将
ugit运行所需的所有依赖项降至最低版本 - 最终镜像仅加载必需的二进制工具及其依赖项
第二轮尝试 – alpine容器运行alpine镜像
我现选择创建多阶段构建。第二阶段将仅用于复制所需二进制文件及其依赖项。我再次选择使用alpine作为该阶段的基础镜像。
# First stage: Install packages
FROM alpine:3.18 as builder
RUN apk add --no-cache
bash
gawk
findutils
coreutils
git
ncurses
fzf
# Copy only the ugit script into the container at /app
WORKDIR /app
COPY ugit .
# Set permissions and move the script to path
RUN chmod +x ugit && mv ugit /usr/local/bin/
# Second stage: Copy only necessary binaries and their dependencies
FROM alpine
COPY --from=builder /usr/local/bin/ugit /usr/bin/
COPY --from=builder /usr/bin/git /usr/bin/
COPY --from=builder /usr/bin/fzf /usr/bin/
COPY --from=builder /usr/bin/tput /usr/bin/
COPY --from=builder /usr/bin/cut /usr/bin/
COPY --from=builder /usr/bin/tr /usr/bin/
COPY --from=builder /usr/bin/nl /usr/bin/
COPY --from=builder /usr/bin/gawk /usr/bin/
COPY --from=builder /usr/bin/xargs /usr/bin
COPY --from=builder /usr/bin/env /bin/
COPY --from=builder /bin/bash /bin/
WORKDIR /app
# Run ugit when the container launches
CMD ["ugit"]
通过简单的多阶段构建,我们成功将镜像大小压缩至惊人的20.6 MB。当前镜像虽能成功构建,但仍无法运行ugit。
Error loading shared library libreadline.so.8: No such file or directory (needed by /bin/bash)
问题出在缺失传递性依赖项。更多细节详见我们的第三次尝试。
xargs和awk竟是免费附赠的?
事实证明,Alpine镜像默认已包含xargs和awk。可通过以下命令验证:
docker run -it alpine /bin/sh -c "awk --help"
docker run -it alpine /bin/sh -c "xargs --help"
新手失误。让我们从Dockerfile中移除gawk和findutils。
# First stage: Install packages
FROM alpine:3.18 as builder
RUN apk add --no-cache
bash
coreutils
git
ncurses
fzf
# Copy only the ugit script into the container at /app
WORKDIR /app
COPY ugit .
# Set permissions and move the script to path
RUN chmod +x ugit && mv ugit /usr/local/bin/
# Second stage: Copy only necessary binaries and their dependencies
FROM alpine:3.18
COPY --from=builder /usr/local/bin/ugit /usr/bin/
COPY --from=builder /usr/bin/git /usr/bin/
COPY --from=builder /usr/bin/fzf /usr/bin/
COPY --from=builder /usr/bin/tput /usr/bin/
COPY --from=builder /usr/bin/cut /usr/bin/
COPY --from=builder /usr/bin/tr /usr/bin/
COPY --from=builder /usr/bin/nl /usr/bin/
COPY --from=builder /usr/bin/env /bin/
COPY --from=builder /bin/bash /bin/
WORKDIR /app
# Run ugit when the container launches
CMD ["ugit"]
镜像大小现已缩减至20 MB。我们正逐步接近目标。不过 ugit 仍无法运行。
第三次尝试 – 在第二阶段使用scratch
这是多数人不敢尝试的部分。它令人胆怯且需要极大决心。我起初也心存顾虑,因为深知所有内容都可能被清空 🙃。
SCRATCH Docker镜像本质上是空文件系统,完全没有任何内容。若需尝试SCRATCH镜像,可参考Docker Hub的官方说明。
你只需要知道,所有内容都必须由我们自行整合。让我们保持专注,将alpine替换为scratch。
# First stage: Install packages
FROM alpine:3.18 as builder
RUN apk add --no-cache
bash
coreutils
git
ncurses
fzf
# Copy only the ugit script into the container at /app
COPY ugit .
# Set permissions and move the script to path
RUN chmod +x ugit && mv ugit /usr/local/bin/
# Second stage: Copy only necessary binaries and their dependencies
FROM scratch
COPY --from=builder /usr/local/bin/ugit /usr/bin/
COPY --from=builder /usr/bin/git /usr/bin/
COPY --from=builder /usr/bin/fzf /usr/bin/
COPY --from=builder /usr/bin/tput /usr/bin/
COPY --from=builder /usr/bin/cut /usr/bin/
COPY --from=builder /usr/bin/tr /usr/bin/
COPY --from=builder /usr/bin/nl /usr/bin/
COPY --from=builder /usr/bin/env /bin/
COPY --from=builder /bin/bash /bin/
WORKDIR /app
# Run ugit when the container launches
CMD ["ugit"]
此举将镜像大小缩减至12.4MB,足足减少了60%??难道我们自食其果了吗?现在尝试运行ugit。
$ docker run --rm -it -v $(pwd):/app ugit-a3
exec /usr/bin/ugit: no such file or directory
$ docker run --rm -it --entrypoint /bin/bash ugit-a4
exec /bin/bash: no such file or directory
结果发现,由于未打包依赖项,导致bash二进制文件无法运行。让我们看看如何解决。
识别传递性依赖
现在该谈谈传递性依赖了。我们的脚本依赖于git、tput、bash等二进制文件;而这些工具本身可能还有依赖项。
这些依赖在技术上称为共享库。共享库是 .so 文件(Windows 中为 .dll,OS X 中为 .dylib)。ldd 是识别这些依赖的绝佳工具,它能列出二进制文件执行所需的所有库。例如在全新Alpine容器中运行ldd /bin/bash,将得到如下输出:
/ # ldd /bin/ls
/lib/ld-musl-aarch64.so.1 (0xffff8c9c0000)
libc.musl-aarch64.so.1 => /lib/ld-musl-aarch64.so.1 (0xffff8c9c0000)
/ # ldd /bin/bash
/lib/ld-musl-aarch64.so.1 (0xffffb905a000)
libreadline.so.8 => /usr/lib/libreadline.so.8 (0xffffb8f06000)
libc.musl-aarch64.so.1 => /lib/ld-musl-aarch64.so.1 (0xffffb905a000)
libncursesw.so.6 => /usr/lib/libncursesw.so.6 (0xffffb8e95000)
共享库基础知识
- 每个库都有一个_soname_,即库文件名去除版本号的部分。其前缀为
lib,后缀为.so。例如,libpcre2-8.so.0的 soname 为libpcre2-8.so。 - 完整路径的库文件名包含其所在目录,例如
/usr/lib/libpcre2-8.so.0。 - 每个库都有一个真实名称,即包含版本号的库文件名。例如,
libpcre2-8.so.0.3.6可能就是一个真实名称。 - 十六进制数表示该库在内存中加载的基础地址。这对我们没有用处,我们只关注库名称。现在为所有需要运行 ugit 的二进制文件完成此操作。
=>符号右侧的路径指向该共享库的实际路径。- 以
libc开头的库名代表该架构的C库。还记得我们遇到的“exec /bin/bash: no such file or directory”错误吗?原因就在于此——我们未随系统分发该架构的C库。
以下摘自道格拉斯·克雷格关于共享库版本的文章,其中概述了共享库的原理。若需深入了解共享库,请阅读全文。
共享库的特性在于:只需编译一次,将其安装到文件系统中的共享位置(Linux系统通常为
/usr/lib),任何依赖该共享库的项目即可直接使用这个已编译好的共享库。
大多数Linux发行版通过分发流行库的二进制包进一步缩短编译时间——这些包由发行版的打包系统预先编译完成。安装包时,您只需下载(理想情况下已签名的)编译库副本并将其放置到共享位置,全程无需调用编译器(或生成该库的任何构建链组件)。
我们采用类似方法识别运行 ugit 所需所有二进制文件的独特依赖项。
ℹ️ 具体实现方式是执行类似命令:
for cmd in /bin/*; do echo $cmd; ldd $cmd; done
# copy lib files
COPY --from=ugit-ops /usr/lib/libncursesw.so.6 /usr/lib/
COPY --from=ugit-ops /usr/lib/libncursesw.so.6.4 /usr/lib/
COPY --from=ugit-ops /usr/lib/libpcre* /usr/lib/
COPY --from=ugit-ops /usr/lib/libreadline* /usr/lib/
COPY --from=ugit-ops /lib/libacl.so.1 /lib/
COPY --from=ugit-ops /lib/libattr.so.1 /lib/
COPY --from=ugit-ops /lib/libc.musl-* /lib/
COPY --from=ugit-ops /lib/ld-musl-* /lib/
COPY --from=ugit-ops /lib/libutmps.so.0.1 /lib/
COPY --from=ugit-ops /lib/libskarnet.so.2.13 /lib/
COPY --from=ugit-ops /lib/libz.so.1 /lib/
请注意,我们从构建器镜像中复制了 libc.musl-* 和 ld-musl-*。这是因为这些库的构建依赖于主机机器的架构。
shebang #! 注释毫无意义
查看 ugit 的第一行代码,可见 shebang 注释 #!/usr/bin/env bash。在编写shell脚本时,使用env被视为良好实践,用于告知操作系统应使用哪个shell解释器运行脚本,这在日常开发机上非常理想。Linux(及旧版macOS)默认搭载sh和bash,用户还常额外安装zsh等。
但由于shebang是可选的,且我们已复制了bash二进制文件,只需通过它调用脚本即可。这还能节省镜像大小的几个字节——精确来说接近1.9 MB。
# Run ugit when the container launches
CMD ["/bin/bash", "/bin/ugit"]
# or
# ENTRYPOINT ["/bin/bash", "/bin/ugit"]
解析 terminfo 数据库
ugit 的彩色输出得益于 tput 工具。我们加载一个仅含 bash 的精简 Alpine 镜像,进入 /etc/terminfo 目录。该目录存储着终端信息数据库。
37a1a77f70ed:/app# cd /etc/terminfo/
37a1a77f70ed:/etc/terminfo# ls
a d g k l p r s t v x
这些字母命名的“目录”分别代表不同的$TERM类型。例如xterm即为终端类型。若在本地执行tput -T xterm colors,即可获取终端支持的颜色数——xterm应为8,而xterm-256color则为256。
现在正是机会——在terminfo数据库收录的40余种终端类型中,我们只需支持其中一种。其余终端类型均可移除,此举虽仅节省97KB空间,却是清理冗余数据的必要之举。
# copy terminfo database for only xterm-256color
COPY --from=ugit-ops /etc/terminfo/x/xterm-256color /usr/share/terminfo/x/
# Gib me all the colors
ENV TERM=xterm-256color
运行ugit所需全部组件
最终生成的Docker镜像大小为17.6MB,且不存在安全漏洞(根据docker scout在本文撰写时的检测结果)。相较于首次尝试,我们成功将镜像体积缩减了40%。
最终Dockerfile如下:
FROM alpine:3.18 as ugit-ops
RUN apk add --no-cache
bash
coreutils
git
ncurses
curl
# Download fzf binary from GitHub, pin to 0.46.0, ugit requires minimum 0.21.0
RUN curl -L -o fzf.tar.gz https://github.com/junegunn/fzf/releases/download/0.46.0/fzf-0.46.0-linux_amd64.tar.gz &&
tar -xzf fzf.tar.gz &&
mv fzf /usr/bin/
# Copy only the ugit script into the container at /app
COPY ugit .
# Set permissions and move the script to path
RUN chmod +x ugit && mv ugit /usr/bin/
# Second stage: Copy only necessary binaries and their dependencies
FROM scratch
COPY --from=ugit-ops /usr/bin/ugit /bin/
COPY --from=ugit-ops /usr/bin/git /usr/bin/
COPY --from=ugit-ops /usr/bin/fzf /usr/bin/
COPY --from=ugit-ops /usr/bin/tput /usr/bin/
COPY --from=ugit-ops /usr/bin/nl /usr/bin/
COPY --from=ugit-ops /usr/bin/awk /usr/bin/
COPY --from=ugit-ops /usr/bin/xargs /usr/bin/
COPY --from=ugit-ops /usr/bin/cut /usr/bin/cut
COPY --from=ugit-ops /usr/bin/tr /usr/bin/tr
COPY --from=ugit-ops /bin/bash /bin/
COPY --from=ugit-ops /bin/sh /bin/
# copy lib files
COPY --from=ugit-ops /usr/lib/libncursesw.so.6 /usr/lib/
COPY --from=ugit-ops /usr/lib/libncursesw.so.6.4 /usr/lib/
COPY --from=ugit-ops /usr/lib/libpcre* /usr/lib/
COPY --from=ugit-ops /usr/lib/libreadline* /usr/lib/
COPY --from=ugit-ops /lib/libacl.so.1 /lib/
COPY --from=ugit-ops /lib/libattr.so.1 /lib/
COPY --from=ugit-ops /lib/libc.musl-* /lib/
COPY --from=ugit-ops /lib/ld-musl-* /lib/
COPY --from=ugit-ops /lib/libutmps.so.0.1 /lib/
COPY --from=ugit-ops /lib/libskarnet.so.2.13 /lib/
COPY --from=ugit-ops /lib/libz.so.1 /lib/
# copy terminfo database
COPY --from=ugit-ops /etc/terminfo/x/xterm-256color /usr/share/terminfo/x/
# Gib me all the colors
ENV TERM=xterm-256color
WORKDIR /app
# Run ugit when the container launches
CMD ["/bin/bash", "/bin/ugit"]
我决定将fzf版本固定为0.46.0(本文撰写时的最新版本),因为ugit运行需要至少0.21.0版本,而且我心想反正就固定到最新版本吧。
ℹ️ 执行
docker run --rm -it -v $(pwd):/app ugit可启动 ugit 容器。请确保当前目录为 git 仓库。
最终文件系统结构如下:
Permission UID:GID Size Filetree drwxr-xr-x 0:0 0 B ├── app drwxr-xr-x 0:0 886 kB ├── bin -rwxr-xr-x 0:0 866 kB │ ├── bash -rwxr-xr-x 0:0 21 kB │ └── ugit drwxr-xr-x 0:0 1.9 MB ├── lib -rwxr-xr-x 0:0 658 kB │ ├── ld-musl-aarch64.so.1 -rwxr-xr-x 0:0 67 kB │ ├── libacl.so.1 -rwxr-xr-x 0:0 67 kB │ ├── libattr.so.1 -rwxr-xr-x 0:0 658 kB │ ├── libc.musl-aarch64.so.1 -rwxr-xr-x 0:0 265 kB │ ├── libskarnet.so.2.13 -rwxr-xr-x 0:0 67 kB │ ├── libutmps.so.0.1 -rwxr-xr-x 0:0 133 kB │ └── libz.so.1 drwxr-xr-x 0:0 15 MB └── usr drwxr-xr-x 0:0 12 MB ├── bin -rwxr-xr-x 0:0 919 kB │ ├── awk -rwxr-xr-x 0:0 1.2 MB │ ├── cut -rwxr-xr-x 502:20 3.6 MB │ ├── fzf -rwxr-xr-x 0:0 3.0 MB │ ├── git -rwxr-xr-x 0:0 1.2 MB │ ├── nl -rwxr-xr-x 0:0 67 kB │ ├── tput -rwxr-xr-x 0:0 1.2 MB │ ├── tr -rwxr-xr-x 0:0 919 kB │ └── xargs drwxr-xr-x 0:0 2.8 MB ├── lib -rwxr-xr-x 0:0 396 kB │ ├── libncursesw.so.6 -rwxr-xr-x 0:0 396 kB │ ├── libncursesw.so.6.4 -rwxr-xr-x 0:0 592 kB │ ├── libpcre2-8.so.0 -rwxr-xr-x 0:0 592 kB │ ├── libpcre2-8.so.0.11.2 -rwxr-xr-x 0:0 67 kB │ ├── libpcre2-posix.so.3 -rwxr-xr-x 0:0 67 kB │ ├── libpcre2-posix.so.3.0.4 -rwxr-xr-x 0:0 351 kB │ ├── libreadline.so.8 -rwxr-xr-x 0:0 351 kB │ └── libreadline.so.8.2 drwxr-xr-x 0:0 4.0 kB └── share drwxr-xr-x 0:0 4.0 kB └── terminfo drwxr-xr-x 0:0 4.0 kB └── x -rw-r--r-- 0:0 4.0 kB └── xterm-256color
这便是让我们的 shell 应用运行的全部内容。不多不少。该喝杯啤酒了?🍺
PS:最终Docker镜像压缩至16.2MB。更新版Dockerfile详见此处。为配合本文说明,我将镜像大小保留在17.6MB。
能否进一步压缩体积?
可以,但我未继续压缩有两个原因:
原因一:固定fzf的最低版本?
在编写 ugit 脚本时,其依赖的 fzf 最低版本为 0.20.0 详见。显然最新版本会比最低要求版本更大。那么我们应该固定旧版本吗?不。因为这会引入 fzf 依赖项(即 Golang)的安全漏洞。根据 docker scout quickview 报告,旧版 Golang 存在总计 66 个安全问题。这些漏洞可能影响镜像,也可能不会。但我不会冒这个风险,希望保持镜像尽可能干净。
ℹ️ 在Alpine生态系统中,通常不建议为软件包设置最低版本限制。
原因二:使用最新bash特性?
编写ugit时,我依赖的是bash版本4.0。若升级至新版bash(如_5.0_),tr和cut命令均可被替代。但这将引发重大变更——移除这些命令虽能节省几字节空间,却可能破坏仍在使用bash 4.0的用户脚本。即便仅我一人仍在使用旧版,我的机器仍需兼容bash。
你没试过docker-slim?
- 试过,它确实缩减了镜像体积,但导致脚本因依赖缺失而失效。Slim很棒,读者不妨试试。可惜在有限时间内,我没能让它在ugit上正常工作。
- 况且我更想亲手掌握优化技巧,而非依赖工具代劳。
你没试过docker-squash?
试过了,但体积优化效果微乎其微。以下是我在Linux(AMD架构)机器上运行的squash日志(忽略镜像大小差异,因架构不同导致体积不同):
2024-01-26 19:53:30,292 root INFO docker-squash version 1.1.0, Docker 20.10.22, API 1.41... 2024-01-26 19:53:30,293 root INFO Using v2 image format 2024-01-26 19:53:30,310 root INFO Old image has 30 layers 2024-01-26 19:53:30,310 root INFO Checking if squashing is necessary... 2024-01-26 19:53:30,310 root INFO Attempting to squash last 30 layers... 2024-01-26 19:53:30,310 root INFO Saving image sha256:1b9107b09dec50bd8134a935ad58ef07deb7dc2a639804abc2cb3d47c1e74206 to /tmp/docker-squash-20kl1kbx/old directory... 2024-01-26 19:53:30,536 root INFO Image saved! 2024-01-26 19:53:30,537 root INFO Squashing image 'ugit:latest'... 2024-01-26 19:53:30,538 root INFO Starting squashing... 2024-01-26 19:53:30,538 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/3b03ba6aad8ab3b95e5ae7c173b5fe8980a4bb2b57e6e3c78d4c851e62befacd/layer.tar'... 2024-01-26 19:53:30,539 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/af8614dc670d37bc2b4ee235fba1fba32f79cc665e1685d17bc09103206add88/layer.tar'... 2024-01-26 19:53:30,541 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/fdfcf6e294b868ac5c9bb79f479da7365f074a24932d35cf88e6ee87313be39f/layer.tar'... 2024-01-26 19:53:30,542 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/fea7bea22fc0e61f8b3bb33795946844efca8e6e2ece2d4cfb5d9d5dc50fa29c/layer.tar'... 2024-01-26 19:53:30,543 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/c6b22f335e663d959cf9ecb676d297de3b818c608c10811f857fbd0950d39126/layer.tar'... 2024-01-26 19:53:30,543 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/482d23995758cbffbedc338a3be0325106fc6172a7c54d23a22e8a1d585d2b9e/layer.tar'... 2024-01-26 19:53:30,545 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/3b9a86d49326da225c5b1c85e01764d210c15bec579c5af34f0fc171127f5b5f/layer.tar'... 2024-01-26 19:53:30,546 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/0ca82f27b278a4c9bb0bfe1ff3f38716051fb4849374eec8925f48efe436009b/layer.tar'... 2024-01-26 19:53:30,546 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/3cca8b35bff33c3f212c956b4c71dd419bd86c146ec38afdcdd5a0288d1659b6/layer.tar'... 2024-01-26 19:53:30,547 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/81dfb23402548ba22dff2162d2c4291d16e3553a3fc47b761d21013316571a82/layer.tar'... 2024-01-26 19:53:30,548 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/273d7b9c894ca049315a77ce845423f819ee684544f171eaf6b8689c77f6ad05/layer.tar'... 2024-01-26 19:53:30,550 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/27be6a1ed5a8507dc43bd30cb56ddddc9c0c9d52b6806eec8037f1fa09395552/layer.tar'... 2024-01-26 19:53:30,551 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/d5f577b8ef6ec1875b1d986ca79da0339c1d228cc31bce4d42988556135c5909/layer.tar'... 2024-01-26 19:53:30,551 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/f292c627dcee37256b008f33c3bc91c65463e7b8a6758306c130f53d80b045e5/layer.tar'... 2024-01-26 19:53:30,553 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/41a4f2afaf2abd9098d62d9b4d5c6ee88c2d9478f48be9c8f407a81c7b207d6e/layer.tar'... 2024-01-26 19:53:30,555 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/5dc1b8c25faa63c58a7905850043c14c59dced4746d7d34453edad7b86956849/layer.tar'... 2024-01-26 19:53:30,557 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/f91a2a9df95e299850a3c512d292167e12f7df86dfc6a598279d35db4d804b7d/layer.tar'... 2024-01-26 19:53:30,560 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/72447920860e9e88f3680623d2ac1d10f78111f16c3563b43009852d704d7515/layer.tar'... 2024-01-26 19:53:30,563 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/cc9b36b470edfc560a35e67fa7d3adbcf3fe91acfaf82cd484080418a1168c7f/layer.tar'... 2024-01-26 19:53:30,566 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/05a8e77bfb6dd759bacb2ba9c1505bc66979885591d623d83ce58cbe0047a539/layer.tar'... 2024-01-26 19:53:30,568 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/a3452a5b414ce5be1c02a8d88374419f8de637552486ca47d6d810adc351386e/layer.tar'... 2024-01-26 19:53:30,570 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/38af27767b34ce2676958ed305d78508fe745597b548d65b4a5532481ffd3296/layer.tar'... 2024-01-26 19:53:30,570 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/c2e188390cb6b51470e879b62e2686add024114bcb76eb163053382cb424928a/layer.tar'... 2024-01-26 19:53:30,574 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/d03cc62007f0fa8d1c09d0f34f5e74e0e6f65f48c9a53177b189c57f47be8074/layer.tar'... 2024-01-26 19:53:30,578 root INFO Squashing file '/tmp/docker-squash-20kl1kbx/old/fa90033ac9d3c9085ba57fc406aed549a0dc14823c8f63f6358c9557b8f0a9ea/layer.tar'... 2024-01-26 19:53:30,578 root INFO Squashing finished! 2024-01-26 19:53:30,608 root INFO Original image size: 17.34 MB 2024-01-26 19:53:30,608 root INFO Squashed image size: 17.29 MB 2024-01-26 19:53:30,608 root INFO Image size decreased by 0.32 % 2024-01-26 19:53:30,608 root INFO New squashed image ID is 2c697df9cb2d9fd5f4534e6358d68f1e1a04bb5b6d27087f894dbf492964b791 2024-01-26 19:53:30,741 root INFO Image registered in Docker daemon as ugit-squashed:squashed 2024-01-26 19:53:30,746 root INFO Done
心得体会
- Linux 令人惊叹。源自它的每个设计决策、每款工具的灵感都继承了这份卓越。
- 切勿回避深入探索微优化这个🐇兔子洞。你将收获良多,总有新知待发现。
鸣谢
- 衷心感谢
ldd的作者,以及docker与alpineLinux 社区的每一位成员。 - 感谢 developersIndia Discord 频道的同仁们提供的建议与帮助。
- dive 帮助我可视化镜像层级。
参考资源
希望大家今天读到有趣的内容。下次再见,祝编程愉快!离开前请记得给ugit点个星标,并执行docker pull?🧡


