在设备端个性化 (ODP) 可信执行环境 (TEE) 中进行工作负载证明时,需要确定性 build,该环境在 Google Cloud 上以保密空间 (CS) 的形式公开提供。
工作负载映像必须生成确定性的映像哈希,供 CS 用于工作负载证明(使用 NIST 的 RFC 9334 远程证明程序 (RATS) 架构)。
本文档将介绍 odp-federatedcompute 代码库中确定性 build 的实现和支持。ODP 聚合器和模型更新器服务将在 Confidential Space 内运行。该代码库支持我们所有服务的确定性 build,而这对于生产用例是必需的。
确定性 build
确定性 build 包含两个主要组件:
- 必需二进制文件的编译。这包括 JAR、共享库和元数据。
- 基础映像和运行时依赖项。用于执行已编译二进制文件的运行时环境的基础映像。
目前,ODP Federated Compute 代码库支持以下类型的工作负载:
- Java + Spring 工作负载
- TaskAssignment、TaskManagement、Collector
- Java + Spring 与 JNI tensorflow 工作负载
- ModelUpdater、Aggregator
- Python 工作负载
- TaskBuilder
依赖项
以下是 ODP 为保持确定性和可用性而依赖的依赖项:
- Bazel
- GitHub
- Maven
- PyPi
- Debian 快照
- DockerHub 注册表
- Google Container Registry (GCR)
确定性工作负载
所有工作负载都使用 Bazel 进行编译,并使用 rules_oci 构建特定于语言的工具链和容器映像。WORKSPACE 文件定义了所有依赖项及其对应的版本和哈希值。
Debian 快照
所有工作负载映像都应在提供的 Dockerfile 中构建,该文件基于 Debian 快照构建。Debian 快照提供了一个稳定的代码库快照,具有确定性:
- 系统头文件和库
- 系统架构
- linux_x86_64
- Debian
- C++ 编译器
Java Spring 工作负载
Bazel 的 remotejdk_17 用于提供用于编译的封闭式 Java。其他 Java 依赖项在 WORKSPACE 文件中进行管理和定义。
Java Spring 工作负载会编译为名为 <service>_application.jar 的 jar 文件。该 JAR 包含:
- Java 类文件
META-INF/- Bazel 清单数据
build-data.properties- Bazel build-data
BOOT-INF/- 由 rules_spring 生成的打包 jar 依赖项。
图片层
Java Spring 工作负载映像包含两层:
- 基础映像层
- Java 基础映像:
gcr.io/distroless/java17-debian11
- Java 基础映像:
- 工作负载层
binary_tar.tar<service>_application.jar
映像配置
- 入口点
java -jar <service>_application.jar
JNI TensorFlow 工作负载
JNI Tensorflow 工作负载基于 Java Spring 工作负载构建。使用 预构建的 Clang+LLVM 16 和 Debian 快照映像提供的 sysroot 来编译机器代码,从而提供 hermetic Clang+LLVM Bazel 工具链。
JNI 工作负载会编译为名为 libtensorflow.so 的共享库以及 <service>_application.jar。
图片层
JNI TensorFlow 工作负载映像包含多个层:
- 基础映像层
- Java 基础映像:
gcr.io/distroless/java17-debian11
- Java 基础映像:
- Debian 软件包依赖项层。这些层是使用从 debian-snapshot 下载并重新打包为映像层的 deb 归档生成的
libc++1-16_amd64.tarlibc++abi1-16_amd64.tarlibc6_amd64.tarlibunwind-16_amd64.tarlibgcc-s1_amd64.targcc-13-base_amd64.tar
- 工作负载层
binary_tar.tar<service>_application.jarlibtensorflow-jni.solibaggregation-jni.so
映像配置
- 标签(仅适用于构建为在 TEE 中运行的映像)
- 入口点
java -Djava.library.path=. -jar <service>_application.jar
Python 工作负载
Bazel 的 rules_python 用于提供密封的 Python 3.10 工具链。锁定的 pip 需求文件用于确定性地提取 pip 依赖项。Debian 快照映像可确保根据平台兼容性提取确定性分发,并提供用于编译源分发的 C++ 工具链。
Python 工作负载将打包到一组下载的 pip 软件包、Python 3.10 分发版、ODP Python 源代码和一个 Python 启动脚本中。
<service>.runfiles/- Python 分发版存储在
python_x86_64-unknown-linux-gnu/下 - 源代码存储在
com_google_ondevicepersonalization_federatedcompute/下 - Pip 软件包存储在
pypi_<dependency_name>/下
- Python 分发版存储在
<service>.runfiles_manifest<service>.runfiles/目录的清单文件
<service>- 使用 runfiles 运行 Python 工作负载的 Python 脚本
图片层
Python 工作负载映像包含四个层:
- 基础映像层
- Python 基础映像 python:slim
- 解释器层
interpreter_layer.jar<service>/<service>.runfiles/python_x86_64-unknown-linux-gnu/**
- 软件包层
packages_layer.jar<service>/<service>.runfiles/**/site-packages/**
- 工作负载层
app_tar_manifest.tar- 包含源代码、启动脚本和清单。
<service>/<service>.runfiles_manifest<service>/<service><service>/<service>.runfiles/com_google_ondevicepersonalization_federatedcompute/**
- 包含源代码、启动脚本和清单。
映像配置
- 入口点
/<service>/<service>
构建映像
选择工作负载后,您就可以构建并发布映像了。
前提条件
过程
映像应在由提供的 Dockerfile 构建的 Docker 容器内构建。 我们提供了两个脚本,可帮助您构建最终的确定性映像。
- docker_run.sh
docker_run.sh将根据 Dockerfile 构建 Docker 映像,装载工作目录,装载宿主 Docker 守护进程,并使用提供的 Bash 命令运行 Docker。在 bash 命令之前传递的任何变量都将被视为 docker run 标志。
- build_images.sh
build_images.sh将针对所有映像运行bazel build,并输出每个已构建映像的生成映像哈希。
构建所有映像
./scripts/docker/docker_run.sh "./scripts/build_images.sh"
您可以在 odp-federatedcompute GitHub 版本下找到每个版本的预期映像哈希。
发布图片
发布是使用 oci_push Bazel 规则配置的。对于每项服务,目标代码库应配置为:
- 聚合信息网站
- 收集器
- model_updater
- task_assignment
- task_management
- task_scheduler
- task_builder
发布单张图片
如需发布单张图片,请执行以下操作:
./scripts/docker/docker_run.sh "bazel run //shuffler/services/<servicename_no_underscore>:<servicename_with_underscore>_image_publish"
已构建的映像
所有构建的映像都需要由创建者存储和托管,例如在 GCP Artifact Registry 中。