Creazione deterministica di calcolo federato sul dispositivo personalizzazione sul dispositivo

Sono necessarie build deterministiche per l'attestazione del carico di lavoro nella funzionalità sul dispositivo Trusted Execution Environment (TEE) per la personalizzazione (ODP), disponibile pubblicamente su Google Cloud come Confidential Space (CS).

Le immagini del carico di lavoro devono generare un hash delle immagini deterministico che possa essere utilizzato CS per l'attestazione del carico di lavoro (che utilizza l'attestazione remota RFC 9334) del NIST architettura procedureS (RATS)).

Questo documento illustrerà l'implementazione e il supporto per modelli deterministici nel odp-federatedcompute repository Git. I servizi ODP Aggregator e Model Updater verranno eseguiti Spazio riservato. Il repository supporta build deterministiche per tutti i nostri necessari per i casi d'uso di produzione.

Build deterministiche

Le build deterministiche sono costituite da due componenti principali:

  1. La compilazione di file binari richiesti. Sono inclusi jar, librerie condivise e metadati.
  2. Le dipendenze dell'immagine di base e del runtime. La base dell'ambiente di runtime immagine utilizzata per eseguire i file binari compilati.

Al momento, il repository Federated Compute di ODP supporta i seguenti tipi di carichi di lavoro standard:

  • Carichi di lavoro Java + Spring
    • TaskAssignment, TaskManagement, Raccoglitore
  • Java + Spring con carichi di lavoro TensorFlow JNI
    • ModelUpdater, aggregatore
  • Carichi di lavoro Python
    • TaskBuilder

Dipendenze

Di seguito sono elencate le dipendenze su cui si basa ODP per mantenere il determinismo e disponibilità:

  • Bazel
  • GitHub
  • Maven
  • PyPi
  • Snapshot Debian
  • Registro DockerHub
  • Google Container Registry (GCR)

Carichi di lavoro deterministici

Tutti i carichi di lavoro vengono compilati utilizzando Bazel con e immagini container specifiche per i vari linguaggi rules_oci. L'AREA DI LAVORO file definisce tutte le dipendenze con le versioni e gli hash corrispondenti.

Snapshot Debian

Tutte le immagini dei carichi di lavoro devono essere create all'interno dockerfile basato su uno snapshot Debian. Debian forniscono uno snapshot del repository stabile con dati deterministici:

Carichi di lavoro Java Spring

Bazel's remotejdk_17 è utilizzato per fornire un Java ermetico per la compilazione. Altre dipendenze Java gestiti e definiti in WORKSPACE .

I carichi di lavoro Java Spring vengono compilati in un file jar denominato <service>_application.jar. Il barattolo contiene:

  • File di classe Java
  • META-INF/
    • Dati manifest Bazel
  • build-data.properties
    • Dati di build Bazel
  • BOOT-INF/

Livelli immagine

L'immagine del carico di lavoro Java Spring è costituita da due livelli:

Configurazione immagine

  • Punto di ingresso
    • java -jar <service>_application.jar

Carichi di lavoro TensorFlow JNI

I carichi di lavoro TensorFlow TensorFlow si basano sui carichi di lavoro Java Spring. R la toolchain ermetica Clang+LLVM Bazel viene fornita utilizzando Clang+LLVM predefinito 16 con un sysroot fornito dall'immagine snapshot Debian per compilare il codice della macchina.

I carichi di lavoro JNI vengono compilati in una libreria condivisa denominata libtensorflow.so con <service>_application.jar.

Livelli immagine

L'immagine del carico di lavoro JNI TensorFlow è composta da diversi livelli:

  • Livello immagine di base
  • Livelli di dipendenza dei pacchetti Debian. Gli strati vengono generati utilizzando deb archivi scaricati da debian-snapshot e riproposti come livelli immagine
    • libc++1-16_amd64.tar
    • libc++abi1-16_amd64.tar
    • libc6_amd64.tar
    • libunwind-16_amd64.tar
    • libgcc-s1_amd64.tar
    • gcc-13-base_amd64.tar
  • Livello carico di lavoro
    • binary_tar.tar
      • <service>_application.jar
      • libtensorflow-jni.so
      • libaggregation-jni.so

Configurazione immagine

  • Etichette (solo per immagini create per essere eseguite all'interno di TEE)
    • "tee.launch_policy.allow_env_override": "FCP_OPTS"
    • "tee.launch_policy.log_redirect": "always"
    • "tee.launch_policy.monitoring_memory_allow": "always"
  • Punto di ingresso
    • java -Djava.library.path=. -jar <service>_application.jar

Carichi di lavoro Python

rules_python di Bazel è utilizzato per una toolchain ermetica per Python 3.10. Requisiti di un pip bloccato file viene utilizzato per il recupero deterministico delle dipendenze pip. Lo snapshot Debian garantisce che le distribuzioni deterministiche vengano recuperate in base alla piattaforma compatibilità e fornisce una toolchain C++ per compilare le distribuzioni di origine.

I carichi di lavoro Python verranno pacchettizzati in un insieme di pacchetti pip scaricati, la distribuzione in Python 3.10, il codice sorgente Python ODP e un avvio Python lo script.

  • <service>.runfiles/
    • La distribuzione Python è archiviata in python_x86_64-unknown-linux-gnu/
    • Il codice sorgente si trova in com_google_ondevicepersonalization_federatedcompute/
    • I pacchetti PIP sono memorizzati in pypi_<dependency_name>/
  • <service>.runfiles_manifest
    • File manifest per la directory <service>.runfiles/
  • <service>
    • Script Python per eseguire il carico di lavoro Python utilizzando i runfile

Livelli immagine

L'immagine del carico di lavoro Python è composta da quattro livelli:

  • Livello immagine di base
  • Livello Interprete
    • interpreter_layer.jar
      • <service>/<service>.runfiles/python_x86_64-unknown-linux-gnu/**
  • Livello pacchetti
    • packages_layer.jar
      • <service>/<service>.runfiles/**/site-packages/**
  • Livello carico di lavoro
    • app_tar_manifest.tar
      • Contiene codice sorgente, script di avvio e manifest.
        • <service>/<service>.runfiles_manifest
        • <service>/<service>
        • <service>/<service>.runfiles/com_google_ondevicepersonalization_federatedcompute/**

Configurazione immagine

  • Punto di ingresso
    • /<service>/<service>

Crea immagini

Una volta scelti i carichi di lavoro, puoi creare e pubblicare in formato Docker.

Prerequisiti

Procedura

Le immagini devono essere create all'interno del container Docker creato dockerfile. Vengono forniti due script per aiutare a creare le immagini deterministiche finali.

  • docker_run.sh
    • docker_run.sh creerà l'immagine Docker dal file Docker, monta la directory di lavoro, monta il daemon docker host ed esegui docker con il comando bash fornito. Qualsiasi variabile passata prima del comando bash come flag dell'esecuzione di Docker.
  • build_images.sh
    • build_images.sh eseguirà bazel build per tutte le immagini e restituirà il codice per ogni immagine creata.

Crea tutte le immagini

./scripts/docker/docker_run.sh "./scripts/build_images.sh"

Gli hash delle immagini previsti per ogni release sono disponibili in GitHub odp-federatedcompute di machine learning.

Pubblica immagini

La pubblicazione viene configurata utilizzando oci_push Regole bazel. Per ogni servizio, il repository di destinazione deve essere configurato tutti:

  • aggregatore
  • raccoglitore
  • model_updater
  • task_assignment
  • task_management
  • task_scheduler
  • task_builder

Pubblica una singola immagine

Per pubblicare una singola immagine:

./scripts/docker/docker_run.sh "bazel run //shuffler/services/<servicename_no_underscore>:<servicename_with_underscore>_image_publish"

Immagini create

Tutte le immagini create dovranno essere archiviate e ospitate dal creator, ad esempio in un Artifact Registry di Google Cloud.