news 2026/9/11 11:56:17

gRPC 定制 rake-compiler-dock Docker 镜像构建全流程指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
gRPC 定制 rake-compiler-dock Docker 镜像构建全流程指南

gRPC 定制 rake-compiler-dock Docker 镜像构建全流程指南

【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc

导读

本文基于 gRPC 仓库的 third_party/rake-compiler-dock/README.md,完整还原 gRPC 团队构建内部专用rake-compiler-dockDocker 镜像的方法:从克隆上游仓库、打定制补丁、本地构建、重新打标、推送到 Google Artifact Registry,到最终把新镜像 digest 写回 CI Dockerfile 并发布。读完本文,你将掌握 gRPC Ruby 原生扩展(C 扩展)跨平台交叉编译镜像的整套维护流程,并理解仓库中update_cross_compilers.patch、各平台Dockerfile.current_version文件与push_testing_images.sh脚本之间的协作关系。

⚠️INTERNAL ONLY:该文档明确标注为 gRPC 内部流程,目标是 gRPC 测试基础设施镜像,不面向最终用户。普通用户构建/安装 grpc Ruby gem 时不需要执行本文任何步骤。


一、背景:gRPC 为何需要定制的 rake-compiler-dock

gRPC 的 Ruby 实现包含用 C 写的原生扩展(grpc_native扩展),需要针对 Windows、macOS、多种 Linux 发行版与 CPU 架构分别编译并发布预编译 gem。rake-compiler-dock是 Ruby 社区用于交叉编译 C 扩展的标准工具链容器,它把交叉编译器、Ruby 头文件与构建脚本打包进 Docker 镜像,使开发者可以在单台 x86_64 Linux 主机上为x86_64-linux-gnuaarch64-linux-gnuarm64-darwinx64-mingw-ucrt等十余种平台产出原生扩展。

gRPC 仓库对上游镜像做了三处关键定制(详见下文补丁分析):升级到 clang 14 / GCC 10 以获得更好的 C++17 支持、固定 musl 工具链版本、升级 osxcross 的 LLVM 与 macOS SDK——这是因为 gRPC 核心本身是大型 C++ 代码库,对编译器的语言标准支持要求远高于普通 Ruby gem。

从源码侧也能看到镜像与构建流程的衔接:在 src/ruby/ext/grpc/extconf.rb 中,gRPC 通过读取环境变量RCD_HOST_RUBY_VERSION(该变量由 rake-compiler-dock 在构建容器内设置)来判定是否处于交叉编译模式:

cross_compiling = ENV['RCD_HOST_RUBY_VERSION'] # set by rake-compiler-dock in build containers

同文件 src/ruby/ext/grpc/extconf.rb 还处理了 rake-compiler-dock 交叉编译时在 LDFLAG 中注入-s(链接时 strip)的问题:当需要保留调试符号时,会先从标志列表中剔除-s,保证"先链接共享库、再保存调试符号、最后才 strip"的顺序。


二、前置条件与仓库布局

1. 环境变量

执行构建前需要设置两个环境变量:

export GEM_ROOT=<path_to_clone_rake_compiler_dock_repo> export GIT_ROOT=<path_to_grpc_git_repo>
变量含义
GEM_ROOT本地克隆的上游rake-compiler-dock仓库路径(用于打补丁与构建)
GIT_ROOT当前 gRPC 仓库根目录路径(用于引用补丁、Dockerfile 与发布脚本)

2. gRPC 仓库内的镜像定义目录

third_party/rake-compiler-dock/目录下按平台存放 Dockerfile 与版本信息,共 10 个平台:

平台目录目标平台
rake_aarch64-linux-gnuARM64 Linux(glibc)
rake_aarch64-linux-muslARM64 Linux(musl)
rake_arm64-darwinApple Silicon macOS
rake_x64-mingw-ucrt64 位 Windows(UCRT 运行时)
rake_x86-linux-gnu32 位 Linux(glibc)
rake_x86-linux-musl32 位 Linux(musl)
rake_x86-mingw3232 位 Windows(MSVCRT)
rake_x86_64-darwinIntel macOS
rake_x86_64-linux-gnu64 位 Linux(glibc)
rake_x86_64-linux-musl64 位 Linux(musl)

每个平台目录包含Dockerfile,并配有一个同名.current_version文件记录当前镜像的 tag 与 sha256 digest(机制见第六节)。另有根级文件update_cross_compilers.patch(定制补丁)与README.md(本文档)。


三、逐步构建流程(上游五步法)

步骤 1:克隆上游仓库

(umask 0022; git clone https://github.com/rake-compiler/rake-compiler-dock -b v1.12.0 "$GEM_ROOT")

为什么用umask 0022:构建过程中,build/目录下的脚本会在 Docker 内部以另一个 Linux 用户执行。默认umask生成的文件权限可能只对属主可写,导致容器内用户无法读取/写入克隆出来的文件。使用umask 0022可以保证其他用户获得必要的读/写权限(文件 644、目录 755)。注意umask只在子 shell 内生效,不影响当前 shell 环境。

版本固定为上游v1.12.0,与镜像 tag 前缀1.12.0-mri-*一一对应。

步骤 2:打补丁并本地构建镜像

cd "$GEM_ROOT" && git apply "${GIT_ROOT}/third_party/rake-compiler-dock/update_cross_compilers.patch" bundle config set --local path '.bundle/gems' bundle install bundle exec rake build:images

执行逻辑:

  1. git apply把 gRPC 的定制补丁 update_cross_compilers.patch 应用到上游仓库(补丁详细内容见第四节);
  2. bundle config set --local path '.bundle/gems'把 gem 安装到仓库内局部路径,避免污染系统环境;
  3. bundle install安装 rake-compiler-dock 的构建依赖;
  4. bundle exec rake build:images使用该 gem 的 Rake 任务构建全部平台镜像。

步骤 3:重新打标(re-tag)到 gRPC 测试仓库

docker image ls --filter "reference=ghcr.io/rake-compiler/rake-compiler-dock-image" --format "{{.Repository}}:{{.Tag}}" | grep '1\.12\.0' | sed -E 's@^[^:]+:@@' | xargs -r -n1 -I{} docker tag ghcr.io/rake-compiler/rake-compiler-dock-image:{} us-docker.pkg.dev/grpc-testing/testing-images-public/rake-compiler-dock-image:{}

该命令做四件事:

  1. 列出本地镜像中引用前缀为ghcr.io/rake-compiler/rake-compiler-dock-image的所有镜像;
  2. grep '1\.12\.0'筛选出 1.12.0 系列;
  3. sed剥掉仓库前缀,只保留 tag(如mri-x86_64-linux-gnu);
  4. xargs逐一对每个 tag 执行docker tag,把镜像从上游参考前缀(ghcr.io/rake-compiler/rake-compiler-dock-image)打标为 gRPC 公共测试镜像前缀(us-docker.pkg.dev/grpc-testing/testing-images-public/rake-compiler-dock-image)。

步骤 4:上传到 Google Artifact Registry

docker image ls --filter "reference=ghcr.io/rake-compiler/rake-compiler-dock-image" --format "{{.Repository}}:{{.Tag}}" | grep '1\.12\.0' | sed -E 's@^[^:]+:@@' | xargs -r -n1 -I{} docker push us-docker.pkg.dev/grpc-testing/testing-images-public/rake-compiler-dock-image:{}

与步骤 3 相同的筛选逻辑,把重新打标后的镜像逐张docker push到远程 Artifact Registry。执行前需要先完成 GCP 认证:

gcloud auth configure-docker us-docker.pkg.dev gcloud auth login

(这两条认证命令来自发布脚本 tools/dockerfile/push_testing_images.sh 的注释。)

步骤 5:重建 CI Dockerfile 并发布

镜像推送到远端后,Docker 会为每张镜像生成 sha256 digest。下一步是把 digest 写回 gRPC 仓库内各平台的Dockerfile,使这些测试镜像固定引用新构建的基础镜像:

docker image ls --format "{{.Tag}} {{.Repository}}:{{.Tag}}@{{.Digest}}" \ us-docker.pkg.dev/grpc-testing/testing-images-public/rake-compiler-dock-image \ | rg '^1.12.0-mri' \ | while read -r tag image; do dockerfile="${GIT_ROOT}/third_party/rake-compiler-dock/rake_${tag#1.12.0-mri-}/Dockerfile" if [[ -f "$dockerfile" ]]; then sed -E -i "s|^FROM [^ ]+\$|FROM ${image}|" "$dockerfile" fi done

脚本逻辑拆解:

  • docker image ls --format "{{.Tag}} {{.Repository}}:{{.Tag}}@{{.Digest}}"输出每张镜像的 tag、完整引用(含 digest)两列;
  • rg '^1.12.0-mri'只保留1.12.0-mri-*系列;
  • ${tag#1.12.0-mri-}是 bash 参数展开,剥离1.12.0-mri-前缀得到平台名(如x86_64-linux-gnu),拼出对应 Dockerfile 路径third_party/rake-compiler-dock/rake_<platform>/Dockerfile
  • 若文件存在,sed -E -i "s|^FROM [^ ]+\$|FROM ${image}|"把 Dockerfile 中的FROM行替换为仓库:tag@sha256:digest的完整引用,保证后续构建可复现。

最后运行发布脚本推送最终测试镜像:

tools/dockerfile/push_testing_images.sh

该脚本会遍历third_party/rake-compiler-dock/*等目录(见第六节),把更新后的 Dockerfile 构建成镜像并上传,同时同步.current_version文件。


四、定制补丁详解:update_cross_compilers.patch

补丁 update_cross_compilers.patch 改编自上游 rake-compiler-dock 的 PR #201,核心动机只有一句:安装 clang 14 与 GCC 10,以获得更好的 C++17 支持(gRPC 核心的 C++ 代码依赖较新的语言特性)。补丁共修改上游仓库三个文件:

1.Dockerfile.mri.erb(镜像模板,改动最大)

针对不同平台条件替换编译器:

平台变更
darwin(macOS)新增llvm-toolchain-focal-14软件源,安装clang-14 llvm-14-dev libc++-14-dev libc++abi-14-dev python3 lzma-dev libxml2-dev libssl-dev,并通过update-alternatives/usr/bin/clang/usr/bin/clang++指向 14 版
arm-linux-gnu(32 位 ARM)交叉编译包升级为gcc-10-arm-linux-gnueabihf g++-10-arm-linux-gnueabihf,再用update-alternatives接管arm-linux-gnueabihf-gcc/g++
x86-linux-gnu(32 位)升级为gcc-10-i686-linux-gnu g++-10-i686-linux-gnu,接管i686-linux-gnu-gcc/g++
aarch64-linux-gnu(ARM64)TARGETPLATFORM分支:在linux/arm64上直接装gcc-10 g++-10并以update-alternatives注册为aarch64-linux-gnu-gcc/g++;否则装交叉编译包gcc-10-aarch64-linux-gnu g++-10-aarch64-linux-gnu
x86_64-linux-gnu(x86_64)TARGETPLATFORM分支:在linux/amd64上装gcc-10 g++-10并同时接管gcc/g++x86_64-linux-gnu-gcc/g++;否则装gcc-x86-64-linux-gnu g++-x86-64-linux-gnu

关键设计:TARGETPLATFORM是 Docker BuildKit 提供的构建参数,这里用它区分"本机原生编译"与"真正的交叉编译"两种情形——原生平台直接复用宿主架构的 GCC 10,其他平台才安装对应的交叉编译器,从而同时覆盖同架构与跨架构两种构建路径。

2.build/mk_musl_cross.sh(musl 工具链)

新增固定版本参数:

# Use GCC 10.3.0. Binutils 2.33.1 is the newest version that still has a # verified hash in musl-cross-make and works with GCC 10 - newer versions # (like 2.44, the current default) break the GCC 10 build. GCC_VER = 10.3.0 BINUTILS_VER = 2.33.1

即把 musl 交叉工具链固定为GCC 10.3.0 + Binutils 2.33.1。注释说明了原因:musl-cross-make 中带校验哈希、且与 GCC 10 兼容的最新 Binutils 是 2.33.1,而更新的版本(如当时的默认 2.44)会破坏 GCC 10 的构建。

3.build/mk_osxcross.sh(macOS 交叉编译)

  • 下载的 SDK 固定为MacOSX11.1,并设置OSX_VERSION_MIN=10.13
  • 将 SDK 内嵌入的 C++ 头文件来源从llvm-10升级为llvm-14/usr/lib/llvm-14/include/c++),bits 头文件匹配也相应放宽为*通配版本目录;
  • llvm-config软链接从llvm-config-10改为llvm-config-14
  • x86_64/aarch64-apple-darwin-objdump软链接从llvm-10/bin/llvm-objdump改为llvm-14/bin/llvm-objdump(osxcross 没有自带 objdump,借用 LLVM 的);
  • 同时签入 sigtool 以解决链接行库顺序问题。

补丁改动与第一节的源码衔接一致:容器内RCD_HOST_RUBY_VERSION环境变量即由这套定制的构建环境注入。


五、各平台 Dockerfile 剖析

以 rake_x86_64-linux-gnu/Dockerfile 为例,镜像分两层:

FROM us-docker.pkg.dev/grpc-testing/testing-images-public/rake-compiler-dock-image:1.12.0-mri-x86_64-linux-gnu@sha256:2ce8d74b6072e567a5592b330535d473ffc515857d914158fc66ebea7d12f55b #================= # Install ccache # Install ccache from source since ccache 3.x packaged with most linux distributions # does not support Redis backend for caching. RUN curl -sSL -o ccache.tar.gz https://github.com/ccache/ccache/releases/download/v4.5.1/ccache-4.5.1.tar.gz \ && tar -zxf ccache.tar.gz \ && cd ccache-4.5.1 \ && mkdir build && cd build \ && cmake -DCMAKE_BUILD_TYPE=Release -DZSTD_FROM_INTERNET=ON -DHIREDIS_FROM_INTERNET=ON .. \ && make -j4 && make install \ && cd ../.. \ && rm -rf ccache-4.5.1 ccache.tar.gz
  • 基础镜像:直接引用第三节步骤 3/4 上传的定制镜像,1.12.0-mri-x86_64-linux-gnutag 后跟@sha256:...digest,实现内容寻址、不可变引用;
  • 额外层:从源码编译安装ccache 4.5.1。Dockerfile 注释给出原因——大多数 Linux 发行版自带的 ccache 3.x 不支持 Redis 后端缓存(gRPC 的构建缓存使用 Redis 后端),因此必须从源码安装新版本。编译时通过-DZSTD_FROM_INTERNET=ON -DHIREDIS_FROM_INTERNET=ON从网络拉取 zstd 与 hiredis 依赖。

其他平台结构相同,仅基础镜像的 tag/digest 不同。其中 rake_arm64-darwin/Dockerfile 与 rake_x86_64-darwin/Dockerfile 是纯基础镜像(只含FROM一行,无 ccache 层),而 Linux 平台镜像(glibc/musl 各架构)普遍带有 ccache 层。


六、发布脚本 push_testing_images.sh 与 .current_version 机制

第三节步骤 5 提到的 tools/dockerfile/push_testing_images.sh 是整个 CI 镜像基础设施的总入口,它把third_party/rake-compiler-dock/*tools/dockerfile/test/*grpc_artifact_*interoptest/*distribtest/*一起纳入统一管理(脚本第 65-71 行)。

1. 支持的运行模式(环境变量)

环境变量作用
LOCAL_ONLY_MODE仅本地操作,不查询 Artifact Registry、不执行上传
CHECK_MODE仅校验所有.current_version文件是否最新(供 CI sanity 测试使用)
SKIP_UPLOAD构建后不推送镜像到 Artifact Registry
HOST_ARCH_ONLY只构建与运行主机同架构的镜像
ALWAYS_BUILD无论 Dockerfile 是否变化都强制构建(构建时追加--no-cache --pull
KEEP_GOING单个镜像构建失败不中止,继续其余构建
MAX_CONCURRENCY并发构建数上限(默认 8)

2. 环境自检

非 CHECK_MODE 下,脚本会先做两项环境检查:

  • 验证免 sudo 的 Docker 可用:docker run --rm debian:11 ...
  • 验证 x64 主机能运行 arm64 镜像(依赖qemu-user-static的 binfmt-misc 钩子):docker run --rm --platform=linux/arm64 arm64v8/debian:11 ...,失败时会提示先安装qemu-user-static——这是 ARM_DOCKERFILE_DIRS 列表 中众多 arm64 镜像能在 x64 CI 上构建的前提。

3. 镜像 tag 与 .current_version 的对应关系

脚本的版本管理核心是"Dockerfile 内容哈希即镜像 tag":

local DOCKER_IMAGE_TAG=$(sha1sum $DOCKERFILE_DIR/Dockerfile | cut -f1 -d\ )

Dockerfile 的任何改动都会产生新 tag,从而触发重建。镜像构建/发布状态记录在同目录的<平台>.current_version文件中,其内容为:

<镜像仓库>/<镜像名>:<sha1-tag>@sha256:<repo-digest>

例如 rake_x86_64-linux-gnu.current_version:

us-docker.pkg.dev/grpc-testing/testing-images-public/rake_x86_64-linux-gnu:caf2c2ae2cabdd2a2f9cd793bea744170d1fb281@sha256:80f1b44eaa660b93f696fc338f6c4c9bb05931d36a55d906d45fe1ea9f6bf921

关键语义(对应 脚本 143-258 行的状态机):

  • 只有tag@sha256:...完整形式才表示"镜像已推送、可被测试使用";
  • 仅含tag(无 digest)表示"本地构建过但尚未推送",docker push成功后才把 RepoDigest 补写回.current_version
  • 若远端已存在相同 digest 且.current_version已同步,则跳过构建;
  • CHECK_MODE下任何失同步(stale 版本文件、已变更未推送、Dockerfile 已变但版本文件未更新)都会被判定为 CHECK FAILED。

脚本末尾还会调用tools/bazelify_tests/generate_dockerimage_current_versions_bzl.sh重新生成 bazel 侧的镜像版本映射(CHECK_MODE 下则校验其是否为最新),保证 Bazel 测试与 Docker 测试引用同一套镜像。


七、维护工作流小结

一次完整的"升级交叉编译器并发布"维护周期可归纳为:

  1. 准备:设置GEM_ROOTGIT_ROOTumask 0022克隆上游v1.12.0
  2. 定制:应用 update_cross_compilers.patch(clang 14 / GCC 10 / musl 固定版本 / osxcross 升级);
  3. 构建bundle exec rake build:images本地产出全部平台镜像;
  4. 重打标并推送:从ghcr.io/rake-compiler/rake-compiler-dock-image改挂到us-docker.pkg.dev/grpc-testing/testing-images-public/rake-compiler-dock-imagedocker push
  5. 固化引用:用 digest 回写各平台DockerfileFROM行;
  6. 发布:运行 tools/dockerfile/push_testing_images.sh,构建rake_*系列测试镜像、推送并同步.current_version文件与 Bazel 版本映射。

整个体系通过"内容哈希 tag + sha256 digest +.current_version状态文件"三重机制,保证了 gRPC Ruby 原生扩展跨平台构建环境(Windows UCRT/MSVCRT、macOS Intel/Apple Silicon、Linux glibc/musl 的 x86/x86_64/aarch64)的可复现性与可审计性,也让 CI 能够自动判断"何时需要重建、何时可以直接复用远端镜像"。

【免费下载链接】grpcC++ based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)项目地址: https://gitcode.com/GitHub_Trending/gr/grpc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 11:55:08

混合架构实战:行为树负责高层战略,GOAP 负责底层战术规划

混合架构实战&#xff1a;行为树负责高层战略&#xff0c;GOAP 负责底层战术规划在复杂 3A 射击与潜行游戏中&#xff0c;单靠行为树&#xff08;Behavior Tree, BT&#xff09;或单靠 GOAP&#xff08;Goal-Oriented Action Planning&#xff09;都会遭遇架构层面的维护瓶颈。…

作者头像 李华
网站建设 2026/9/11 11:52:52

AI短剧自动生成全流程:剧本、分镜、画面、配音、剪辑一机搞定

AI短剧自动生成这个方向&#xff0c;我实打实折腾了小半年&#xff0c;从一个人对着满屏报错发呆&#xff0c;到现在能在半小时左右产出一集完整短片&#xff0c;中间踩过的坑比拍出来的片子还多。这篇文章不卖课、不扯概念&#xff0c;就讲我自己跑通的这套全流程&#xff1a;…

作者头像 李华
网站建设 2026/9/11 11:52:25

让Claude评价Gemini:双模型互评实战工作流与提示词设计

最近技术圈里冒出一句挺魔性的提问&#xff1a;“元芳&#xff08;Gemini&#xff09;你怎么看&#xff1f;”我第一次看到时还以为是什么新梗&#xff0c;点进去才发现&#xff0c;原来是有人直接在Claude对话框里输入这句话&#xff0c;前面挂个“元芳”&#xff0c;括号里注…

作者头像 李华
网站建设 2026/9/11 11:50:21

Java+Vue构建高并发手机商城系统实战

1. 项目概述&#xff1a;欢迪迈手机商城系统技术全景 这个基于Java技术栈的手机商城系统&#xff0c;是我去年带队为某3C零售品牌交付的线上销售平台。系统采用前后端分离架构&#xff0c;后端基于SpringBootMyBatis实现业务逻辑&#xff0c;前端使用VueElementUI构建管理后台&…

作者头像 李华