Projetar jobs de GPU resilientes de longa duração

Neste tutorial, explicamos como projetar e implantar jobs resilientes do Cloud Run com GPU que podem ser executados por até 12 horas.

Embora o Cloud Run ofereça suporte a tempos limite de tarefas estendidos, ele não oferece um SLA para execução ininterrupta. Como as tarefas com GPU não são migradas em tempo real, a manutenção semanal de rotina as encerra no meio da execução. Consequentemente, qualquer tarefa estendida deve ser interrompida pelo menos uma vez. Para garantir que o job seja concluído sem desperdiçar recursos de computação, projete o aplicativo para criar checkpoints e retomar, em vez de depender de uma única execução ininterrupta.

Neste tutorial, mostramos como implementar o checkpoint, processar sinais da plataforma SIGTERM e configurar o número máximo de novas tentativas.

Objetivos

Neste tutorial, você vai:

  • Prepare um aplicativo Python que implemente pontos de verificação e processe sinais SIGTERM.
  • Crie um bucket do Cloud Storage para manter os checkpoints.
  • Crie e implante um job do Cloud Run com GPU ativada, um tempo limite de tarefa de 12 horas e uma política de repetição configurada.
  • Execute o job e entenda como monitorar eventos de preempção nos registros.

Custos

Neste documento, você vai usar os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços.

Novos usuários do Google Cloud podem estar qualificados para um teste sem custo financeiro.

Antes de começar

  1. Faça login na sua conta do Google Cloud . Se você começou a usar o Google Cloudagora, crie uma conta para avaliar o desempenho dos nossos produtos em situações reais. Clientes novos também recebem US $300 em créditos para executar, testar e implantar cargas de trabalho sem custos financeiros.
  2. Instale a CLI do Google Cloud.

  3. Ao usar um provedor de identidade (IdP) externo, primeiro faça login na CLI gcloud com sua identidade federada.

  4. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  5. Crie ou selecione um projeto do Google Cloud .

    Papéis necessários para selecionar ou criar um projeto

    • Selecionar um projeto: para selecionar um projeto, não é necessário ter um papel do IAM específico. É possível selecionar qualquer projeto em que você tenha um papel.
    • Criar um projeto: para criar um projeto, é preciso ter o papel de criador de projetos (roles/resourcemanager.projectCreator), que contém a permissão resourcemanager.projects.create. Saiba como conceder papéis.
    • Crie um projeto do Google Cloud :

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto do Google Cloud que você está criando.

    • Selecione o projeto do Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud .

  6. Verifique se o faturamento está ativado para o projeto do Google Cloud .

  7. Instale a CLI do Google Cloud.

  8. Ao usar um provedor de identidade (IdP) externo, primeiro faça login na CLI gcloud com sua identidade federada.

  9. Para inicializar a CLI gcloud, execute o seguinte comando:

    gcloud init
  10. Crie ou selecione um projeto do Google Cloud .

    Papéis necessários para selecionar ou criar um projeto

    • Selecionar um projeto: para selecionar um projeto, não é necessário ter um papel do IAM específico. É possível selecionar qualquer projeto em que você tenha um papel.
    • Criar um projeto: para criar um projeto, é preciso ter o papel de criador de projetos (roles/resourcemanager.projectCreator), que contém a permissão resourcemanager.projects.create. Saiba como conceder papéis.
    • Crie um projeto do Google Cloud :

      gcloud projects create PROJECT_ID

      Substitua PROJECT_ID por um nome para o projeto do Google Cloud que você está criando.

    • Selecione o projeto do Google Cloud que você criou:

      gcloud config set project PROJECT_ID

      Substitua PROJECT_ID pelo nome do projeto do Google Cloud .

  11. Verifique se o faturamento está ativado para o projeto do Google Cloud .

  12. Ative as APIs Cloud Run, Artifact Registry, Cloud Build e Cloud Storage, se ainda não estiverem ativadas:

    Funções necessárias para ativar APIs

    Para ativar APIs, você precisa da permissão serviceusage.services.enable. Se você criou o projeto, provavelmente já tem essa permissão por meio do papel de proprietário (roles/owner). Caso contrário, é possível conseguir essa permissão por meio do papel de administrador do Service Usage (roles/serviceusage.serviceUsageAdmin). Saiba como conceder papéis.

    gcloud services enable run.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com storage.googleapis.com
  13. Defina sua região como uma variável de ambiente:
    export REGION=REGION
  14. Crie uma conta de serviço para o job:
    gcloud iam service-accounts create gpu-job-sa
  15. Verifique se você tem [cota de GPU suficiente](/run/docs/configuring/jobs/gpu#request-quota) (por exemplo, Nvidia L4) na região escolhida.

Preparar o aplicativo

Para tornar um job de longa duração resiliente, seu aplicativo precisa:

  1. Salve periodicamente o progresso (checkpointing) em um local permanente, como um bucket do Cloud Storage montado.

    Você tem aproximadamente 10 segundos após SIGTERM antes que a tarefa seja encerrada. Esse tempo não é suficiente para gravar um checkpoint de modelo grande. Trate o gerenciador SIGTERM como uma limpeza de melhor esforço apenas de estado pequeno e use o checkpoint periódico como sua proteção principal. Escolha o intervalo de checkpoint com base na quantidade de trabalho que você pode perder, lembrando que checkpoints mais frequentes significam mais tempo sem realizar trabalho útil. O objetivo é alcançar o equilíbrio certo para seu caso de uso.

  2. Restaura o estado do último checkpoint na inicialização.

  3. Detecte o sinal SIGTERM, que o Cloud Run envia 10 segundos antes do processo de manutenção, e tente salvar um checkpoint final antes de sair.

O exemplo abaixo ilustra como implementar esses recursos.

Criar o código do aplicativo

  1. Crie um diretório para o projeto:

    mkdir gc-run-gpu-resilience
    cd gc-run-gpu-resilience
    
  2. Crie um arquivo chamado task.py e cole o script a seguir para simular um processo de treinamento ou processamento em lote que ofereça suporte a checkpoints e processamento de sinais:

    import os
    import sys
    import time
    import signal
    
    # The checkpoint directory will be mounted to this path
    CHECKPOINT_DIR = "/checkpoints"
    # Cloud Run sets these variables automatically
    JOB_NAME = os.environ.get("CLOUD_RUN_JOB", "local-job")
    TASK_INDEX = os.environ.get("CLOUD_RUN_TASK_INDEX", "0")
    CHECKPOINT_FILE = os.path.join(
        CHECKPOINT_DIR, f"{JOB_NAME}-task-{TASK_INDEX}-checkpoint.txt")
    TOTAL_STEPS = 100
    current_step = 0
    is_exiting = False
    
    def save_checkpoint(step):
        print(f"Saving checkpoint at step {step} to {CHECKPOINT_FILE}...")
        try:
            # Write to a temp file and rename to ensure atomicity
            temp_file = CHECKPOINT_FILE + ".tmp"
            with open(temp_file, "w") as f:
                f.write(str(step))
            os.rename(temp_file, CHECKPOINT_FILE)
            print("Checkpoint saved successfully.")
        except Exception as e:
            print(f"Failed to save checkpoint: {e}")
    
    def load_checkpoint():
        print(f"Looking for a checkpoint at {CHECKPOINT_FILE}...")
        global current_step
        if os.path.exists(CHECKPOINT_FILE):
            try:
                with open(CHECKPOINT_FILE, "r") as f:
                    current_step = int(f.read().strip())
                print(f"Resuming from checkpointed step {current_step}.")
            except Exception as e:
                print(f"Failed to load checkpoint: {e}. Starting from scratch.")
        else:
            print("No checkpoint found. Starting from scratch.")
    
    def sigterm_handler(signum, frame):
        global is_exiting
        print("Received SIGTERM signal from platform. Initiating graceful shutdown...")
        is_exiting = True
    
    # Register the signal handler for SIGTERM
    signal.signal(signal.SIGTERM, sigterm_handler)
    
    # Load existing checkpoint if any to resume work
    load_checkpoint()
    
    # Simulate work loop
    while current_step < TOTAL_STEPS and not is_exiting:
        print(f"Processing step {current_step}/{TOTAL_STEPS}...")
        # Simulate heavy GPU computation
        time.sleep(1)
        current_step += 1
        # Periodically save checkpoint (e.g., every 5 steps)
        if current_step % 5 == 0:
            save_checkpoint(current_step)
    
    if is_exiting:
        # Save final state before exiting due to preemption
        # Best-effort only: with ~10s before SIGKILL this will not complete for
        # large state. Periodic checkpointing (above) is what actually protects
        # you.
        save_checkpoint(current_step)
        # Note that save_checkpoint already writes-then-renames for atomicity,
        # which is good, but the reason it matters here is that a truncated
        # checkpoint from an interrupted final save must not clobber the last
        # good one.
        print("Graceful shutdown complete. Exiting with error code to trigger retry.")
        # Exit with a non-zero code to signal to Cloud Run that the task
        # was interrupted and needs to be retried from the checkpoint.
        sys.exit(143)
        # Note that this statement deliberately consumes of the configured retry
        # attempts.
    
    print("Task completed successfully.")
    sys.exit(0)
    
  3. Crie um Dockerfile para colocar o aplicativo em um contêiner:

Confira este exemplo para uma implementação mais detalhada e realista.

Criar um bucket do Cloud Storage para checkpoints

Crie um bucket do Cloud Storage para armazenar os arquivos de checkpoint e conceda à conta de serviço do job permissão para acessá-lo.

  1. Crie o bucket:

    gcloud storage buckets create gs://gpu-checkpoints-PROJECT_ID \
        --location REGION
    
  2. Conceda o papel de Administrador de objetos do Storage à conta de serviço do job:

    gcloud storage buckets add-iam-policy-binding gs://gpu-checkpoints-PROJECT_ID \
        --member="serviceAccount:gpu-job-sa@PROJECT_ID.iam.gserviceaccount.com" \
        --role="roles/storage.objectAdmin"
    

Implantar o job

Implante o job no Cloud Run:

gcloud beta run jobs deploy gpu-resilient-job \
    --source=. \
    --region REGION \
    --gpu 1 \
    --gpu-type nvidia-l4 \
    --no-gpu-zonal-redundancy \
    --task-timeout 168h \
    --max-retries 3 \
    --service-account gpu-job-sa@PROJECT_ID.iam.gserviceaccount.com \
    --add-volume=name=checkpoint-volume,type=cloud-storage,bucket=gpu-checkpoints-PROJECT_ID \
    --add-volume-mount=volume=checkpoint-volume,mount-path=/checkpoints

Observe as seguintes flags críticas:

  • --task-timeout 168h: permite que a tarefa seja executada por até 12 horas.
  • --max-retries 3: para jobs de GPU com um tempo limite maior que 1 hora, recomendamos definir a configuração padrão de 3 novas tentativas. Para evitar falhas silenciosas durante a manutenção da plataforma, recomendamos não definir 0 tentativas. Por fim, recomendamos que você inclua no orçamento pelo menos uma nova tentativa para falhas relacionadas à manutenção, além de novas tentativas para falhas de aplicativos em um período de execução de 12 horas.
  • --add-volume e --add-volume-mount: monta o bucket do Cloud Storage em /checkpoints para que o aplicativo possa persistir o estado entre as novas tentativas de tarefa.

Executar e monitorar o job

  1. Execute o job:

    gcloud run jobs execute gpu-resilient-job --region REGION
    

    Esse comando retorna uma mensagem semelhante a esta:

    Execution [gpu-resilient-job-abcde] has been successfully started.
    
    View details about this execution by running `gcloud run jobs executions
    describe gpu-resilient-job-abcde`.
    
    See logs for this execution at: https://console.cloud.google.com/logs/viewer?project=YOUR_PROJECT_ID&resource=cloud_run_job/job_name/my-job/location/my-region/execution_id/gpu-resilient-job-abcde
    

    A string gpu-resilient-job-abcde é o ID exclusivo gerado automaticamente para essa tentativa de execução específica. Você pode usá-lo para monitorar ou cancelar essa execução específica. A saída também inclui um link direto para a página da Análise de registros no console do Google Cloud para visualizar a saída ou os erros das tarefas do job.

  2. Monitorar os registros. Se um evento de manutenção ocorrer, o Cloud Run vai registrar um aviso. Você verá o seguinte:

    • Os registros do aplicativo: Received SIGTERM signal from platform. Initiating graceful shutdown... seguido por Saving checkpoint....
    • Um registro do sistema indicando preempção: Container instance preemption initiated by the platform due to underlying hardware maintenance. com o motivo INSTANCE_PREEMPTED.
    • O Cloud Run reinicia a tarefa automaticamente.
    • Os registros do aplicativo após a reinicialização: Resuming from checkpointed step X..

Limpar

Para evitar cobranças extras na sua conta do Google Cloud , exclua todos os recursos implantados com este guia de início rápido.

Excluir o repositório

O Cloud Run cobra apenas pelo tempo de execução do job. No entanto, é possível que você ainda receba cobranças pelo armazenamento da imagem do contêiner no Artifact Registry. Para excluir repositórios do Artifact Registry, siga as etapas em Excluir repositórios na documentação do Artifact Registry.

Excluir sua vaga

Os jobs do Cloud Run só geram custos quando uma tarefa de job está em execução. Para excluir o job do Cloud Run, siga uma destas etapas:

Console

Para excluir um job, siga estas etapas:

  1. No console do Google Cloud , acesse o Cloud Run:

    Acessar o Cloud Run

  2. Localize o job que você quer excluir na lista de jobs e clique na caixa de seleção para marcá-lo.

  3. Clique em Excluir. Isso encerra todas as execuções de jobs em andamento e todas as instâncias de contêiner em execução.

gcloud

Para excluir um job, execute o seguinte comando:

gcloud run jobs delete JOB_NAME

Substitua JOB_NAME pelo nome do job.

Excluir o projeto de teste

A exclusão do projeto do Google Cloud interrompe o faturamento de todos os recursos nele. Para liberar todos os recursos do Google Cloud no seu projeto, siga estas etapas:

    Exclua um projeto do Google Cloud :

    gcloud projects delete PROJECT_ID

Exclua o bucket do Cloud Storage:

gcloud storage buckets delete gs://gpu-checkpoints-PROJECT_ID

A seguir