- 云原生
【免费下载链接】distroless
🥑 Language focused docker images, minus the operating system.
导读
gcr.io/distroless/cc是 distroless 系列中专门为“mostly-statically compiled”(大部分静态编译)语言——如 Rust、D、C/C++——准备的精简运行镜像。它只包含一个最小的 Linux + glibc 运行环境,不包含 shell、包管理器或任何与运行无关的工具,用户需要自行将编译好的应用放进去并设置 CMD。本文将以 cc/README.md 为核心,结合 cc/cc.bzl、cc/config.bzl 与 examples/cc 中的示例,完整讲解该镜像的内容构成、Bazel 构建原理、多架构发布方式与实战用法。
一、镜像定位:为“大部分静态编译”语言准备的 glibc 运行时
按照官方文档的描述,gcr.io/distroless/cc镜像包含一个最小的 Linux、glibc 运行环境,面向的是一类特殊语言:它们在编译时把依赖大多静态链接进二进制,但运行时仍需要 glibc(标准 C 库)的支持——典型代表就是Rust 和 D,C/C++ 应用同样适用。
这与 distroless 系列其他镜像的分工有关。在 base/README.md 中可以看到完整的分层关系:
| 镜像 | 内容定位 |
|---|---|
gcr.io/distroless/static | ca-certificates、root 用户的 /etc/passwd 条目、/tmp 目录、tzdata。适合纯静态编译、不依赖 libc 的 Go 程序 |
gcr.io/distroless/base-nossl | 上述全部内容 + glibc。适合需要 libc 但不需要 libssl 的应用 |
gcr.io/distroless/base | 上述全部内容 + glibc + libssl(Debian 13 起还附带 zlib、libzstd 等 libssl 依赖) |
gcr.io/distroless/cc | base 镜像的全部内容 + libgcc1(含其依赖),即本文主角 |
用官方文档的原话:cc镜像包含 base 镜像中的一切,再加上 libgcc1 及其依赖。libgcc1(在 Debian 13 中对应包名为libgcc-s1)是 GCC 的底层运行时库,Rust/D 这类“mostly-static”二进制在运行期调用编译器内建函数(如 64 位整数除法、软浮点辅助函数等)时依赖它,因此它是这类应用运行不可或缺的组成部分。
注意:
cc镜像不包含 OpenSSL legacy 算法。Debian 13 base 镜像本身就不包含这些算法,如确需使用,需自行添加openssl-legacy-provider(详见 base/README.md)。
二、镜像内容的源码级证据:实际打包了哪些包
虽然 README 概括为“base + libgcc1”,但从构建配置 cc/config.bzl 可以看到 Debian 13 上cc镜像实际安装的软件包清单:
CC_DISTROS = ["debian13"] CC_ARCHITECTURES = { "debian13": ["amd64", "arm64", "arm", "s390x", "ppc64le", "riscv64"], } CC_PACKAGES = { "debian13": [ "libgomp1", # GCC OpenMP 并行运行时库 "libstdc++6", # GNU 标准 C++ 库(libstdc++) "libgcc-s1", # GCC 底层支持库(即 README 中的 libgcc1,Debian 13 的新包名) "gcc-14-base", # GCC 14 基础包,提供 GCC 版本相关的基础文件 ], }对照 base 镜像的配置(base/config.bzl),base 打包的是libc6、libssl3t64、libzstd1、zlib1g,而cc在此之上叠加了上述 GCC 相关库。几个值得注意的细节:
libgcc-s1与libgcc1的关系:README 沿用了历史上libgcc1的包名;在 Debian 13(trixie)中,该运行时库以libgcc-s1形式发布,配置文件里也如实使用了新包名。libstdc++6的存在:说明该镜像不仅能跑纯 C/Rust 程序,也直接支持 C++ 运行时——这是 README 一行“libgcc1 及其依赖”背后被展开的实际含义之一。libgomp1:GCC 的 OpenMP 并行运行时,供使用并行计算能力的程序按需加载。
这些包会通过 cc/cc.bzl 中的deb.package(arch, distro, pkg)从 Debian 仓库解析、解包并打进镜像层,最终产物是一个不含 shell、不含包管理器的纯运行镜像。
三、镜像如何构建:Bazel 规则与多架构索引
3.1cc_image:单架构镜像的组装
cc/cc.bzl 中的cc_image(distro, arch, packages)负责为每个 distro/arch 组合生成镜像。核心逻辑清晰直白:
oci_image( name = "cc" + mode + "_" + user + "_" + arch + "_" + distro, base = "//base:base" + mode + "_" + user + "_" + arch + "_" + distro, tars = [ deb.package(arch, distro, pkg) for pkg in packages ], annotations = {"org.opencontainers.image.source": OS_RELEASE["HOME_URL"]}, )关键点:
- 以 base 镜像为基底:
base = "//base:base...",即直接叠加在 base 镜像之上,继承其 ca-certificates、tzdata、glibc 等全部内容。 - 包以 tar 层注入:每个 Debian 包经
deb.package()解析成 tar 层,合并进镜像。 - 命名矩阵:镜像名由
mode × user × arch × distro四维展开(见 3.3)。 - OCI 注解:写入
org.opencontainers.image.source等元数据。
3.2cc_image_index:多架构镜像索引
cc/cc.bzl 中的cc_image_index(distro, architectures)使用oci_image_index把同一 distro 下各架构镜像聚合成一个 multi-arch 索引,拉取时 Docker/OCI 客户端会按平台自动选择对应架构的镜像。
3.3 命名矩阵:mode × user × arch × distro
从 cc/cc.bzl 和 common/variables.bzl 可以还原出完整的镜像命名体系:
DEBUG_MODE = ["", "_debug"] # 普通版与 debug 版(含 busybox 调试工具) USERS = ["root", "nonroot"] # root 与 nonroot(UID 65532)两种运行用户于是每个 distro/arch 会产出 4 个镜像,例如 Debian 13 amd64 下有:
cc_root_amd64_debian13(普通版 root)cc_nonroot_amd64_debian13(普通版 nonroot,UID 65532)cc_root_debug_amd64_debian13(debug 版 root)cc_nonroot_debug_amd64_debian13(debug 版 nonroot)
cc/BUILD 中通过两层列表推导式,把CC_DISTROS与CC_ARCHITECTURES组合出全矩阵的构建与索引目标:
[ cc_image( arch = arch, distro = distro, packages = CC_PACKAGES[distro], ) for distro in CC_DISTROS for arch in CC_ARCHITECTURES[distro] ]当前cc镜像支持的 distro 为debian13,支持架构覆盖amd64、arm64、arm、s390x、ppc64le、riscv64六种。uid/gid 约定来自 common/variables.bzl:ROOT = 0、NONROOT = 65532、NOBODY = 65534,nonroot 镜像即以 65532 用户运行,这是 distroless 安全设计(最小权限)的核心体现。
四、实战用法一:Dockerfile 多阶段构建
官方文档明确给出使用方式:“用户应把自己的编译产物放进镜像,并设置正确的 CMD”。cc镜像没有包管理器也没有编译器,它只负责“运行”。最典型的做法是多阶段构建——在带编译器的镜像里编译,在cc镜像里运行。
仓库自带的示例 examples/cc/Dockerfile 展示了完整流程:
FROM gcc:6 AS build-env COPY . /app WORKDIR /app RUN cc hello.c -o hello FROM gcr.io/distroless/cc COPY --from=build-env /app /app WORKDIR /app CMD ["./hello"]要点拆解:
- 编译阶段:使用
gcc官方镜像作为构建环境,cc hello.c -o hello编译出静态程度较高的二进制。 - 运行阶段:切换到
gcr.io/distroless/cc,只把编译产物COPY进去。 - 设置 CMD:
CMD ["./hello"]使用exec 形式(JSON 数组)。在 distroless 镜像中没有 shell,因此 CMD 必须以 exec 形式给出——CMD ./hello这种 shell 形式会因为找不到/bin/sh而失败。 - ENTRYPOINT 可选:如果你希望镜像固定入口(例如作为服务镜像),可以用
ENTRYPOINT声明可执行文件,CMD 仅传参数。仓库的 Bazel 封装 private/oci/cc_image.bzl 正是这样做的:
oci_image( name = name, base = base, entrypoint = [ "/%s_binary" % name, ], tars = [":%s_layer" % name], )它把cc_binary编译出的二进制打包成 tar 层并设置为entrypoint,无需外部再写 CMD。
安全提示
cc镜像没有 shell、没有包管理器,攻击面极小;生产环境建议优先选择nonroot变体(gcr.io/distroless/cc:nonroot)并以 UID 65532 运行,进一步降低提权风险。
五、实战用法二:Bazel 原生构建cc_image
对于 Bazel 项目,仓库在 private/oci/cc_image.bzl 提供了高层次的cc_image(name, srcs, base)规则,自动完成“编译 → 打包 → 组装镜像”全流程:
def cc_image(name, srcs, base): cc_binary( name = "%s_binary" % name, srcs = srcs, ) tar( name = "%s_layer" % name, extension = "tar.gz", srcs = [":%s_binary" % name], ) oci_image( name = name, base = base, entrypoint = ["/%s_binary" % name], tars = [":%s_layer" % name], )examples/cc/BUILD 中即用此规则为 C 与 C++ 两种语言各生成镜像,并以//cc:cc_root_amd64_debian13作为基底:
[cc_image( name = "hello_" + distro, srcs = ["hello.c"], base = "//cc:cc_root_amd64_" + distro, ) for distro in DISTROS] [cc_image( name = "hello_cc_" + distro, srcs = ["hello_cc.cc"], base = "//cc:cc_root_amd64_" + distro, ) for distro in DISTROS]对应的 C 与 C++ 示例源码分别为 examples/cc/hello.c 和 examples/cc/hello_cc.cc,编译产物分别以/hello_debian13_binary、/hello_cc_debian13_binary为入口运行。
六、如何验证镜像:container_structure_test
仓库用container_structure_test对cc镜像做结构测试,这既是 CI 的质量保障,也可以作为读者验证自己镜像是否可用的参考模板。测试声明见 examples/cc/BUILD:
container_structure_test( name = "hello_cc_" + distro + "_test", size = "small", configs = ["testdata/hello_cc_" + distro + ".yaml"], image = ":hello_cc_" + distro, tags = ["amd64", "manual"], )测试配置 examples/cc/testdata/hello_cc_debian13.yaml 验证镜像内的二进制能真实运行并输出预期内容:
schemaVersion: "1.0.0" commandTests: - name: hello_cc command: ['/hello_cc_debian13_binary'] expectedOutput: ['Hello from distroless C\+\+!']对应的 C 版测试 examples/cc/testdata/hello_debian13.yaml 验证printf("Hello from distroless C!\n")的输出。这套测试从侧面印证了cc镜像的运行能力:只放二进制 + 设置入口,即可在无 shell 环境下正常启动执行。
七、使用限制与注意事项
- 必须自带编译产物:镜像内没有编译器、链接器,也没有包管理器,无法在容器内编译或安装依赖;一切编译必须在构建阶段完成。
- CMD/ENTRYPOINT 必须用 exec 形式:无 shell 环境意味着字符串形式命令无法执行。
- 运行用户:默认镜像以 root 运行;如需最小权限,使用
nonroot变体(UID 65532)。 - 依赖完整性由你负责:若二进制动态链接了其他库(例如自定义的
.so),需要自行在 Dockerfile 中一并复制进镜像,cc镜像只预装 README 与 cc/config.bzl 列出的 GCC/glibc 运行时库。 - OpenSSL legacy 算法缺失:如应用依赖传统加密算法,需自行补充
openssl-legacy-provider。 - 架构匹配:拉取或构建时确保平台架构与 cc/config.bzl 中列出的六种架构之一对应;多架构环境下应使用
cc_image_index生成的索引镜像,由运行时自动选择。
总结
gcr.io/distroless/cc是 distroless 家族中面向 Rust、D、C/C++ 等“mostly-static”语言的最小 glibc 运行镜像:它在 base 镜像(ca-certificates、tzdata、glibc、libssl)之上叠加了libgcc-s1、libstdc++6、libgomp1、gcc-14-base等 GCC 运行时库,支持 root/nonroot、debug 变体与六大架构。使用上遵循“编译在外、运行在内”的多阶段构建模式,配以 exec 形式的 CMD,即可获得一个无 shell、攻击面极小、可安全运行 C/C++/Rust/D 应用的极简容器。深入阅读 cc/README.md、cc/cc.bzl、cc/config.bzl 与 examples/cc 目录,可以掌握镜像的全部构建细节与可复制的示例。
- 云原生
【免费下载链接】distroless
🥑 Language focused docker images, minus the operating system.
相关推荐
还在为"另存为"抓狂?5分钟上手这款免费开源完整网站下载工具
还在为"另存为"抓狂?5分钟上手这款免费开源完整网站下载工具 核心关键词:完整网站下载工具 长尾关键词:网站源码一键下载、整站资源离线保存、Node.js 网站
后端网页爬虫自定义代币桥实战:如何通过Custom Gateway将ERC20桥接到Scroll
自定义代币桥实战:如何通过Custom Gateway将ERC20桥接到Scroll 想要把自己的 ERC20 代币从以太坊主网(或 Sepolia 测试网)安
探索Distroless:轻量级、安全的容器运行环境
探索Distroless:轻量级、安全的容器运行环境 是由 Google Container Tools 开发的一个开源项目,旨在提供一个最小化的、无操作系统的
云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考