As builds deterministas são necessárias para o atestado de carga de trabalho no ambiente de execução confiável (TEE) da personalização no dispositivo (ODP), disponível publicamente no Google Cloud como Confidential Space (CS).
As imagens de carga de trabalho precisam gerar um hash de imagem determinista que possa ser usado pelo CS para atestado de carga de trabalho, que usa a arquitetura de procedimentos de atestado remoto (RATS) RFC 9334 do NIST.
Este documento aborda a implementação e o suporte para builds determinísticos no repositório odp-federatedcompute. Os serviços ODP Aggregator e Model Updater serão executados no Confidential Space. O repositório oferece suporte a builds deterministas para todos os nossos serviços, que são necessários para casos de uso de produção.
Builds deterministas
As builds deterministas consistem em dois componentes principais:
- A compilação dos binários necessários. Isso inclui jars, bibliotecas compartilhadas e metadados.
- A imagem de base e as dependências de ambiente de execução. A imagem de base do ambiente de execução usada para executar os binários compilados.
No momento, o repositório do Federated Compute da ODP é compatível com os seguintes tipos de cargas de trabalho:
- Workloads Java + Spring
- TaskAssignment, TaskManagement, Collector
- Java + Spring com cargas de trabalho do TensorFlow JNI
- ModelUpdater, Aggregator
- Workloads do Python
- TaskBuilder
Dependências
A lista a seguir mostra as dependências que o ODP usa para manter o determinismo e a disponibilidade:
- Bazel
- GitHub
- Maven
- PyPi
- Snapshots do Debian
- Registro do DockerHub
- Google Container Registry (GCR)
Cargas de trabalho deterministas
Todas as cargas de trabalho são compiladas usando o Bazel com conjuntos de ferramentas específicos da linguagem e imagens de contêiner criadas usando rules_oci. O arquivo WORKSPACE define todas as dependências com versões e hashes correspondentes.
Snapshots do Debian
Todas as imagens de carga de trabalho precisam ser criadas no dockerfile fornecido, que é baseado em um snapshot do Debian. Os snapshots do Debian fornecem um snapshot de repositório estável com:
- Cabeçalhos e bibliotecas do sistema
- Arquitetura do sistema
- linux_x86_64
- Debian
- Compilador C++
Cargas de trabalho do Java Spring
O
remotejdk_17 do
Bazel é usado para fornecer um Java hermético para compilação. Outras dependências do Java são gerenciadas e definidas no arquivo WORKSPACE.
As cargas de trabalho do Java Spring são compiladas em um arquivo JAR chamado
<service>_application.jar. O arquivo JAR contém:
- Arquivos de classe Java
META-INF/- Dados do manifesto do Bazel
build-data.properties- Dados de build do Bazel
BOOT-INF/- Dependências de jar empacotadas, geradas por rules_spring.
Camadas de imagem
A imagem da carga de trabalho do Java Spring consiste em duas camadas:
- Camada de imagem de base
- Imagem de base Java:
gcr.io/distroless/java17-debian11
- Imagem de base Java:
- Camada de carga de trabalho
binary_tar.tar<service>_application.jar
Configuração de imagem
- Ponto de entrada
java -jar <service>_application.jar
Cargas de trabalho do JNI Tensorflow
As cargas de trabalho do JNI Tensorflow são criadas com base nas cargas de trabalho do Java Spring. Uma cadeia de ferramentas hermética do Clang+LLVM Bazel é fornecida usando o Clang+LLVM 16 pré-criado com um sysroot fornecido pela imagem de snapshot do Debian para compilar código de máquina.
As cargas de trabalho JNI são compiladas em uma biblioteca compartilhada chamada libtensorflow.so junto com o <service>_application.jar.
Camadas de imagem
A imagem da carga de trabalho do TensorFlow JNI consiste em várias camadas:
- Camada de imagem de base
- Imagem de base Java:
gcr.io/distroless/java17-debian11
- Imagem de base Java:
- Camadas de dependência de pacotes Debian. As camadas são geradas usando arquivos deb
baixados do debian-snapshot e reempacotados como camadas de imagem
libc++1-16_amd64.tarlibc++abi1-16_amd64.tarlibc6_amd64.tarlibunwind-16_amd64.tarlibgcc-s1_amd64.targcc-13-base_amd64.tar
- Camada de carga de trabalho
binary_tar.tar<service>_application.jarlibtensorflow-jni.solibaggregation-jni.so
Configuração de imagem
- Rótulos (somente para imagens criadas para serem executadas no TEE)
"tee.launch_policy.allow_env_override": "FCP_OPTS"- Permite que a variável de ambiente
FCP_OPTSseja definida em espaço confidencial. A carga de trabalho vai consumirFCP_OPTSna inicialização para configurar os parâmetros obrigatórios. - A variável de ambiente
FCP_OPTSé definida quando a imagem é executada (em vez de criada) para manter o determinismo de build.
- Permite que a variável de ambiente
"tee.launch_policy.log_redirect": "always""tee.launch_policy.monitoring_memory_allow": "always"
- Ponto de entrada
java -Djava.library.path=. -jar <service>_application.jar
Workloads do Python
O rules_python do Bazel é usado para fornecer uma cadeia de ferramentas hermética do Python 3.10. Um arquivo requirements do pip bloqueado é usado para buscar dependências do pip de forma determinista. A imagem de snapshot do Debian garante que as distribuições deterministas sejam buscadas com base na compatibilidade da plataforma e fornece um conjunto de ferramentas C++ para compilar distribuições de origem.
As cargas de trabalho do Python serão empacotadas em um conjunto de pacotes pip baixados, uma distribuição do Python 3.10, o código-fonte do Python do ODP e um script de inicialização do Python.
<service>.runfiles/- A distribuição do Python é armazenada em
python_x86_64-unknown-linux-gnu/ - O código-fonte é armazenado em
com_google_ondevicepersonalization_federatedcompute/ - Os pacotes pip são armazenados em
pypi_<dependency_name>/
- A distribuição do Python é armazenada em
<service>.runfiles_manifest- Arquivo de manifesto para o diretório
<service>.runfiles/
- Arquivo de manifesto para o diretório
<service>- Script Python para executar a carga de trabalho do Python usando os runfiles
Camadas de imagem
A imagem da carga de trabalho do Python consiste em quatro camadas:
- Camada de imagem de base
- Imagem de base do Python: python:slim
- Camada de intérprete
interpreter_layer.jar<service>/<service>.runfiles/python_x86_64-unknown-linux-gnu/**
- Camada de pacotes
packages_layer.jar<service>/<service>.runfiles/**/site-packages/**
- Camada de carga de trabalho
app_tar_manifest.tar- Contém código-fonte, script de inicialização e manifesto.
<service>/<service>.runfiles_manifest<service>/<service><service>/<service>.runfiles/com_google_ondevicepersonalization_federatedcompute/**
- Contém código-fonte, script de inicialização e manifesto.
Configuração de imagem
- Ponto de entrada
/<service>/<service>
Criar imagens
Depois que as cargas de trabalho forem escolhidas, você poderá criar e publicar as imagens.
Pré-requisitos
Procedimento
As imagens precisam ser criadas no contêiner do Docker criado pelo dockerfile fornecido. Dois scripts são fornecidos para ajudar na criação das imagens deterministas finais.
- docker_run.sh
- O
docker_run.shvai criar a imagem do Docker com base no Dockerfile, montar o diretório de trabalho, montar o daemon do Docker do host e executar o Docker com o comando bash fornecido. Todas as variáveis transmitidas antes do comando bash serão tratadas como flags de execução do Docker.
- O
- build_images.sh
- O
build_images.shvai executarbazel buildpara todas as imagens e gerar os hashes de imagem para cada uma delas.
- O
Criar todas as imagens
./scripts/docker/docker_run.sh "./scripts/build_images.sh"
Os hashes de imagem esperados para cada versão podem ser encontrados em odp-federatedcompute GitHub releases.
Publicar imagens
A publicação é configurada usando as regras oci_push do Bazel. Para cada serviço, o repositório de destino precisa ser configurado para todos:
- agregador
- coletor
- model_updater
- task_assignment
- task_management
- task_scheduler
- task_builder
Publicar uma única imagem
Para publicar uma única imagem:
./scripts/docker/docker_run.sh "bazel run //shuffler/services/<servicename_no_underscore>:<servicename_with_underscore>_image_publish"
Imagens criadas
Todas as imagens criadas precisam ser armazenadas e hospedadas pelo criador, como em um registro de artefatos do GCP.