Les compilations déterministes sont requises pour l'attestation de charge de travail dans l'environnement d'exécution sécurisé (TEE, Trusted Execution Environment) de la personnalisation sur l'appareil (ODP, On Device Personalization), disponible publiquement sur Google Cloud sous le nom d'espace confidentiel (CS, Confidential Space).
Les images de charge de travail doivent générer un hachage d'image déterministe qui peut être utilisé par CS pour l'attestation de charge de travail (qui utilise l'architecture RATS (Remote ATtestation procedureS) de la RFC 9334 du NIST).
Ce document décrit l'implémentation et la compatibilité des builds déterministes dans le dépôt odp-federatedcompute. Les services ODP Aggregator et Model Updater s'exécuteront dans Confidential Space. Le dépôt est compatible avec les builds déterministes pour tous nos services, qui sont nécessaires pour les cas d'utilisation en production.
Compilations déterministes
Les builds déterministes se composent de deux éléments principaux :
- Compilation des binaires requis. Cela inclut les fichiers JAR, les bibliothèques partagées et les métadonnées.
- Image de base et dépendances de l'environnement d'exécution. Image de base de l'environnement d'exécution utilisée pour exécuter les binaires compilés.
À l'heure actuelle, le dépôt ODP Federated Compute est compatible avec les types de charges de travail suivants :
- Charges de travail Java + Spring
- TaskAssignment, TaskManagement, Collector
- Java + Spring avec des charges de travail TensorFlow JNI
- ModelUpdater, Aggregator
- Charges de travail Python
- TaskBuilder
Dépendances
La liste suivante présente les dépendances sur lesquelles s'appuie ODP pour maintenir le déterminisme et la disponibilité :
- Bazel
- GitHub
- Maven
- PyPi
- Instantanés Debian
- Registre DockerHub
- Google Container Registry (GCR)
Charges de travail déterministes
Toutes les charges de travail sont compilées à l'aide de Bazel avec des chaînes d'outils spécifiques à la langue et des images de conteneur créées à l'aide de rules_oci. Le fichier WORKSPACE définit toutes les dépendances avec les versions et les hachages correspondants.
Instantanés Debian
Toutes les images de charge de travail doivent être créées dans le dockerfile fourni, qui est basé sur un instantané Debian. Les instantanés Debian fournissent un instantané de dépôt stable avec les éléments déterministes suivants :
- Bibliothèques et en-têtes système
- Architecture du système
- linux_x86_64
- Debian
- Compilateur C++
Charges de travail Java Spring
remotejdk_17 de Bazel est utilisé pour fournir un Java hermétique pour la compilation. Les autres dépendances Java sont gérées et définies dans le fichier WORKSPACE.
Les charges de travail Java Spring sont compilées dans un fichier JAR nommé <service>_application.jar. Le fichier JAR contient :
- Fichiers de classe Java
META-INF/- Données du fichier manifeste Bazel
build-data.properties- Données de compilation Bazel
BOOT-INF/- Dépendances jar empaquetées, générées par rules_spring.
Calques d'images
L'image de charge de travail Java Spring se compose de deux couches :
- Calque d'image de base
- Image de base Java :
gcr.io/distroless/java17-debian11
- Image de base Java :
- Couche de charge de travail
binary_tar.tar<service>_application.jar
Configuration des images
- Point d'entrée
java -jar <service>_application.jar
Charges de travail JNI Tensorflow
Les charges de travail TensorFlow JNI sont basées sur les charges de travail Java Spring. Une chaîne d'outils Bazel Clang+LLVM hermétique est fournie à l'aide de Clang+LLVM 16 prédéfini avec un sysroot fourni par l'image instantanée Debian pour compiler le code machine.
Les charges de travail JNI sont compilées dans une bibliothèque partagée nommée libtensorflow.so, ainsi que dans <service>_application.jar.
Calques d'images
L'image de charge de travail TensorFlow JNI se compose de plusieurs couches :
- Calque d'image de base
- Image de base Java :
gcr.io/distroless/java17-debian11
- Image de base Java :
- Couches de dépendances des packages Debian. Les calques sont générés à l'aide d'archives deb téléchargées à partir de debian-snapshot et reconditionnées en tant que calques d'image.
libc++1-16_amd64.tarlibc++abi1-16_amd64.tarlibc6_amd64.tarlibunwind-16_amd64.tarlibgcc-s1_amd64.targcc-13-base_amd64.tar
- Couche de charge de travail
binary_tar.tar<service>_application.jarlibtensorflow-jni.solibaggregation-jni.so
Configuration des images
- Libellés (uniquement pour les images conçues pour s'exécuter dans un environnement d'exécution sécurisé)
"tee.launch_policy.allow_env_override": "FCP_OPTS"- Permet de définir la variable d'environnement
FCP_OPTSdans un espace confidentiel. La charge de travail consommeraFCP_OPTSau démarrage pour configurer les paramètres requis. - La variable d'environnement
FCP_OPTSest définie lorsque l'image est exécutée (au lieu d'être compilée) pour maintenir le déterminisme de la compilation.
- Permet de définir la variable d'environnement
"tee.launch_policy.log_redirect": "always""tee.launch_policy.monitoring_memory_allow": "always"
- Point d'entrée
java -Djava.library.path=. -jar <service>_application.jar
Charges de travail Python
rules_python de Bazel est utilisé pour fournir une chaîne d'outils Python 3.10 hermétique. Un fichier d'exigences pip verrouillé est utilisé pour récupérer les dépendances pip de manière déterministe. L'image instantanée Debian garantit que des distributions déterministes sont récupérées en fonction de la compatibilité de la plate-forme et fournit une chaîne d'outils C++ pour compiler les distributions sources.
Les charges de travail Python seront empaquetées dans un ensemble de packages pip téléchargés, une distribution Python 3.10, le code source Python ODP et un script de démarrage Python.
<service>.runfiles/- La distribution Python est stockée sous
python_x86_64-unknown-linux-gnu/. - Le code source est stocké sous
com_google_ondevicepersonalization_federatedcompute/. - Les packages Pip sont stockés sous
pypi_<dependency_name>/.
- La distribution Python est stockée sous
<service>.runfiles_manifest- Fichier manifeste du répertoire
<service>.runfiles/
- Fichier manifeste du répertoire
<service>- Script Python permettant d'exécuter la charge de travail Python à l'aide des runfiles
Calques d'images
L'image de charge de travail Python se compose de quatre couches :
- Calque d'image de base
- Image de base Python python:slim
- Couche de l'interprète
interpreter_layer.jar<service>/<service>.runfiles/python_x86_64-unknown-linux-gnu/**
- Couche "Packages"
packages_layer.jar<service>/<service>.runfiles/**/site-packages/**
- Couche de charge de travail
app_tar_manifest.tar- Contient le code source, le script de démarrage et le fichier manifeste.
<service>/<service>.runfiles_manifest<service>/<service><service>/<service>.runfiles/com_google_ondevicepersonalization_federatedcompute/**
- Contient le code source, le script de démarrage et le fichier manifeste.
Configuration des images
- Point d'entrée
/<service>/<service>
Compiler des images
Une fois vos charges de travail choisies, vous êtes prêt à créer et à publier vos images.
Prérequis
Procédure
Les images doivent être créées dans le conteneur Docker créé par le dockerfile fourni. Deux scripts sont fournis pour vous aider à créer les images déterministes finales.
- docker_run.sh
docker_run.shcrée l'image Docker à partir du fichier Dockerfile, monte le répertoire de travail et le daemon Docker de l'hôte, puis exécute Docker avec la commande Bash fournie. Toutes les variables transmises avant la commande Bash seront traitées comme des indicateurs de l'exécution Docker.
- build_images.sh
build_images.shexécuterabazel buildpour toutes les images et générera les hachages d'image pour chaque image créée.
Compiler toutes les images
./scripts/docker/docker_run.sh "./scripts/build_images.sh"
Les hachages d'image attendus pour chaque version sont disponibles dans les versions GitHub d'odp-federatedcompute.
Publier des images
La publication est configurée à l'aide des règles Bazel oci_push. Pour chaque service, le dépôt cible doit être configuré pour tous les éléments suivants :
- agrégateur
- collecteur
- model_updater
- task_assignment
- task_management
- task_scheduler
- task_builder
Publier une seule image
Pour publier une seule image :
./scripts/docker/docker_run.sh "bazel run //shuffler/services/<servicename_no_underscore>:<servicename_with_underscore>_image_publish"
Images compilées
Toutes les images compilées devront être stockées et hébergées par le créateur, par exemple dans un registre d'artefacts GCP.