Depurar jobs do Ray concluídos com o servidor de histórico do Ray

O servidor de histórico do Ray permite acessar o painel do Ray e os registros dele depois que um cluster do Ray é encerrado.

Este documento descreve como configurar e implantar o servidor de histórico do Ray em clusters do Google Kubernetes Engine (GKE) que executam cargas de trabalho do Ray. Este documento também explica como acessar dados do RayCluster encerrado usando um painel local do Ray.

Por padrão, o painel do Ray e os registros dele só existem enquanto o cluster do Ray está em execução. Ao executar jobs em clusters Ray temporários, os dados de depuração são perdidos assim que o cluster é encerrado. Anteriormente, a preservação desses dados para depuração exigia a manutenção de clusters ociosos em execução, o que consumia recursos de computação desnecessários.

O servidor de histórico do Ray mantém esses dados, o que permite encerrar clusters imediatamente após a conclusão de um job para otimizar o uso de recursos. Os desenvolvedores podem continuar acessando o painel, revisando registros e resolvendo problemas depois que os recursos de computação são liberados.

Quando configurado, o servidor de histórico do Ray atua como back-end do painel do Ray. Para mais informações sobre como usar o painel, consulte Painel do Ray.

Custo

O servidor de histórico do Ray usa o Cloud Storage. Para mais informações, consulte Preços do Cloud Storage.

As imagens de contêiner do servidor de histórico do Ray são armazenadas no Artifact Registry. Para mais informações, consulte Preços do Artifact Registry.

Antes de começar

Antes de começar, verifique se você realizou as tarefas a seguir:

  • Ativar a API Google Kubernetes Engine.
  • Ativar a API Google Kubernetes Engine
  • Se você quiser usar a Google Cloud CLI para essa tarefa, instale e, em seguida, inicialize a CLI gcloud. Se você instalou a CLI gcloud anteriormente, instale a versão mais recente executando o comando gcloud components update. Talvez as versões anteriores da CLI gcloud não sejam compatíveis com a execução dos comandos neste documento.
  • Instale e atualize o Helm.
  • Verifique se o bucket do Cloud Storage foi criado.

Requisitos e limitações

O servidor de histórico do Ray exige uma versão mínima do KubeRay v1.6 e usa a versão v2.55 do Ray.

Este documento pressupõe que você esteja familiarizado com os seguintes conceitos e operações:

Configurar o cluster do GKE

Nesta seção, você configura as variáveis necessárias e o cluster do GKE.

Configure as variáveis de ambiente

As variáveis de ambiente a seguir são usadas neste documento:

export LOCATION="LOCATION"
export PROJECT_NAME="PROJECT_NAME"
export PROJECT_NUMBER="PROJECT_NUMBER"
export GKE_CLUSTER_NAME="GKE_CLUSTER_NAME"
export GCS_BUCKET="GCS_BUCKET"
export GCP_SA="GCP_SA"
export RAY_JOB="RAY_JOB"
export NAMESPACE="NAMESPACE"
  • LOCATION: região ou zona do cluster
  • PROJECT_NAME: Google Cloud nome do projeto
  • PROJECT_NUMBER: Google Cloud número do projeto
  • GKE_CLUSTER_NAME: nome do cluster do GKE
  • GCS_BUCKET: nome do bucket do Cloud Storage
  • GCP_SA: nome da conta de serviço
  • RAY_JOB: nome do job do Ray
  • NAMESPACE: namespace em que o servidor de histórico do Ray reside em um cluster do GKE

Criar um cluster do GKE

O cluster do GKE precisa ter a Federação de Identidade da Carga de Trabalho para GKE ativada para acessar o Cloud Storage.

gcloud

Crie um cluster padrão com a Federação de Identidade da Carga de Trabalho para GKE ativada.

gcloud container clusters create GKE_CLUSTER_NAME \
  --location=LOCATION
  --workload-pool=PROJECT_NAME.svc.id.goog

Defina o contexto kubectl para o cluster do GKE:

gcloud container clusters get-credentials GKE_CLUSTER_NAME \
  --location=LOCATION

Configurar o armazenamento com a federação de identidade da carga de trabalho e as contas de serviço

Configure as permissões necessárias para o acesso ao bucket do Cloud Storage. Para mais informações, consulte Federação de identidade da carga de trabalho.

O Cloud Storage é usado para armazenar todos os registros e eventos necessários emitidos pelo Ray e também é usado para reconstruir o painel do Ray. O bucket do Cloud Storage precisa ser criado com a configuração:

  --uniform-bucket-level-access

Para criar a conta de serviço do Kubernetes, execute o comando a seguir:

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ray-history-server
  namespace: NAMESPACE
automountServiceAccountToken: true
EOF

Vincule o papel roles/storage.objectUser à conta de serviço do Kubernetes:

gcloud storage buckets add-iam-policy-binding gs://GCS_BUCKET \
  --member "principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_NAME.svc.id.goog
/subject/ns/NAMESPACE/sa/ray-history-server"  --role "roles/storage.objectUser"

Criar imagens do servidor de histórico do Ray

Para criar a imagem personalizada, siga as etapas descritas na documentação do KubeRay

Instalar o KubeRay

Adicione e atualize o repositório do KubeRay:

helm repo add kuberay https://ray-project.github.io/kuberay-helm/
helm repo update

Para instalar o operador KubeRay, execute o comando helm install:

helm install kuberay-operator kuberay/kuberay-operator

Configurar o servidor de histórico do Ray

Configurar papéis do RBAC para o servidor de histórico do Ray

Prepare os papéis de cluster e do RBAC necessários para os componentes do servidor de histórico do Ray:

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: raycluster-reader
rules:
- apiGroups: ["ray.io"]
  resources: ["rayclusters"]
  verbs: ["list", "get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: historyserver
  namespace: NAMESPACE
subjects:
- kind: ServiceAccount
  name: ray-history-server
  namespace: NAMESPACE
roleRef:
  kind: ClusterRole
  name: raycluster-reader
EOF

Implantar o servidor de histórico do Ray

Crie um arquivo YAML HISTORY_SERVER_FILE_NAME com o seguinte manifesto:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: historyserver-demo
 labels:
   app: historyserver
spec:
 replicas: 1
 selector:
   matchLabels:
     app: historyserver
 template:
   metadata:
     labels:
       app: historyserver
   spec:
     serviceAccountName: ray-history-server
     containers:
     - name: historyserver
       env:
         - name: GCS_BUCKET
           value: "GCS_BUCKET"
       image: historyserver:v0.1.0
       imagePullPolicy: IfNotPresent
       command:
       - historyserver
       - --runtime-class-name=gcs
       - --ray-root-dir=log
       ports:
       - containerPort: 8080
       resources:
         limits:
           cpu: "500m"

Aplique o HISTORY_SERVER_FILE_NAME usando kubectl:

kubectl apply -f HISTORY_SERVER_FILE_NAME

Adicione um manifesto de serviço SERVICE_FILE_NAME para o servidor de histórico do Ray:

apiVersion: v1
kind: Service
metadata:
 name: historyserver
 labels:
   app: historyserver
spec:
 selector:
   app: historyserver
 ports:
 - protocol: TCP
   name: http
   port: 30080
   targetPort: 8080
 type: ClusterIP

Aplique o manifesto de serviço usando kubectl:

kubectl apply -f SERVICE_FILE_NAME

Implantar um job do Ray com um cluster do Ray temporário

O componente coletor do servidor de histórico do Ray reside em cada um dos pods do RayCluster e processa a coleta dos registros e eventos necessários, exportando-os para o Cloud Storage.

Adicione as seguintes variáveis de ambiente exigidas pelo servidor de histórico do Ray:

  • RAY_enable_core_worker_ray_event_to_aggregator e RAY_DASHBOARD_AGGREGATOR_AGENT_EVENTS_EXPORT_ADDR ativam a API de exportação de eventos do Ray.
  • RAY_DASHBOARD_AGGREGATOR_AGENT_PUBLISHER_HTTP_ENDPOINT_EXPOSABLE_EVENT_TYPES lista os tipos de eventos que o servidor de histórico do Ray precisa coletar.
  • GCS_BUCKET informa ao coletor qual bucket do Cloud Storage usar.

Observação:os comandos do RayCluster são usados para configurar o servidor de histórico do Ray e recuperar o node_id como parte da configuração do contêiner coletor. Os comandos também ajudam a garantir que os registros sejam salvos durante a reinicialização ou o encerramento.

  • O campo role informa ao coletor a qual nó do Ray o coletor pertence.
  • O campo runtime-class-name determina o cliente de armazenamento.
  • O campo ray-cluster-name define o nome do RayCluster.
  • O campo ray-root informa ao servidor de histórico do Ray o diretório raiz dos registros.
  • O campo events-port informa ao servidor de histórico do Ray de qual porta os eventos vêm.

O snippet a seguir mostra um exemplo de manifesto do RayJob:

apiVersion: ray.io/v1
kind: RayJob
metadata:
  name: RAY_JOB
  namespace: NAMESPACE
spec:
  entrypoint: "python -c 'import ray; ray.init(); print(ray.cluster_resources())'"
  shutdownAfterJobFinishes: true
  rayClusterSpec:
    headGroupSpec:
      rayStartParams:
        dashboard-host: 0.0.0.0
      template:
        spec:
          serviceAccountName: ray-history-server
          containers:
          - name: ray-head
            image: rayproject/ray:2.53.0
            env:
            - name: RAY_enable_ray_event
              value: "true"
            - name: RAY_enable_core_worker_ray_event_to_aggregator
              value: "true"
            - name: RAY_DASHBOARD_AGGREGATOR_AGENT_EVENTS_EXPORT_ADDR
              value: "http://localhost:8084/v1/events"
            - name: RAY_DASHBOARD_AGGREGATOR_AGENT_PUBLISHER_HTTP_ENDPOINT_EXPOSABLE_EVENT_TYPES
              value: "TASK_DEFINITION_EVENT,TASK_LIFECYCLE_EVENT,ACTOR_TASK_DEFINITION_EVENT,TASK_PROFILE_EVENT,DRIVER_JOB_DEFINITION_EVENT,DRIVER_JOB_LIFECYCLE_EVENT,ACTOR_DEFINITION_EVENT,ACTOR_LIFECYCLE_EVENT,NODE_DEFINITION_EVENT,NODE_LIFECYCLE_EVENT"
            command:
            - /bin/sh
            - -c
            - 'echo "=========================================="; [ -d "/tmp/ray/session_latest" ] && dest="/tmp/ray/prev-logs/$(basename $(readlink /tmp/ray/session_latest))/$(cat /tmp/ray/raylet_node_id)" && echo "dst is $dest" && mkdir -p "$dest" && mv /tmp/ray/session_latest/logs "$dest/logs"; echo "========================================="'
            # This hook retrieves and persists the node_id for the collector
            lifecycle:
              postStart:
                exec:
                  command:
                  - /bin/sh
                  - -lc
                  - --
                  - |
                    GetNodeId(){
                      while true;
                      do
                        nodeid=$(ps -ef | grep raylet | grep node_id | grep -v grep | grep -oP '(?<=--node_id=)[^ ]*')
                        if [ -n "$nodeid" ]; then
                          echo "$(date) raylet started: ${nodeid}" >> /tmp/ray/init.log
                          echo $nodeid > /tmp/ray/raylet_node_id
                          break
                        else
                          sleep 1
                        fi
                      done
                    }
                    GetNodeId
            volumeMounts:
            - name: ray-dir
              mountPath: /tmp/ray
          - name: collector
            image: COLLECTOR_IMAGE
            env:
            - name: GCS_BUCKET
              value: "GCS_BUCKET"
            command:
            - collector
            - --role=Head
            - --runtime-class-name=gcs
            - --ray-cluster-name=RAY_JOB
            - --ray-root-dir=log
            - --events-port=8084
            volumeMounts:
            - name: ray-dir
              mountPath: /tmp/ray
          volumes:
          - name: ray-dir
            emptyDir: {}
    workerGroupSpecs:
    - groupName: cpu
      replicas: 1
      template:
        spec:
          serviceAccountName: ray-history-server
          containers:
          - name: ray-worker
            image: rayproject/ray:2.53.0
            env:
            - name: RAY_enable_ray_event
              value: "true"
            - name: RAY_enable_core_worker_ray_event_to_aggregator
              value: "true"
            - name: RAY_DASHBOARD_AGGREGATOR_AGENT_EVENTS_EXPORT_ADDR
              value: "http://localhost:8084/v1/events"
            - name: RAY_DASHBOARD_AGGREGATOR_AGENT_PUBLISHER_HTTP_ENDPOINT_EXPOSABLE_EVENT_TYPES
              value: "TASK_DEFINITION_EVENT,TASK_LIFECYCLE_EVENT,ACTOR_TASK_DEFINITION_EVENT,TASK_PROFILE_EVENT,DRIVER_JOB_DEFINITION_EVENT,DRIVER_JOB_LIFECYCLE_EVENT,ACTOR_DEFINITION_EVENT,ACTOR_LIFECYCLE_EVENT,NODE_DEFINITION_EVENT,NODE_LIFECYCLE_EVENT"
            command:
            - /bin/sh
            - -c
            - 'echo "=========================================="; [ -d "/tmp/ray/session_latest" ] && dest="/tmp/ray/prev-logs/$(basename $(readlink /tmp/ray/session_latest))/$(cat /tmp/ray/raylet_node_id)" && echo "dst is $dest" && mkdir -p "$dest" && mv /tmp/ray/session_latest/logs "$dest/logs"; echo "========================================="'
            lifecycle:
              postStart:
                exec:
                  command:
                  - /bin/sh
                  - -lc
                  - --
                  - |
                    GetNodeId(){
                      while true;
                      do
                        nodeid=$(ps -ef | grep raylet | grep node_id | grep -v grep | grep -oP '(?<=--node_id=)[^ ]*')
                        if [ -n "$nodeid" ]; then
                          echo $nodeid > /tmp/ray/raylet_node_id
                          break
                        else
                          sleep 1
                        fi
                      done
                    }
                    GetNodeId
            volumeMounts:
            - name: ray-dir
              mountPath: /tmp/ray
          - name: collector
            image: COLLECTOR_IMAGE
            env:
            - name: GCS_BUCKET
              value: "GCS_BUCKET"
            command:
            - collector
            - --role=Worker
            - --runtime-class-name=gcs
            - --ray-cluster-name=RAY_JOB
            - --ray-root-dir=log
            - --events-port=8084
            volumeMounts:
            - name: ray-dir
              mountPath: /tmp/ray
          volumes:
          - name: ray-dir
            emptyDir: {}

Acessar RayClusters encerrados usando o painel local do Ray

Encaminhe o serviço historyserver para que ele possa ser acessado pelo painel local do Ray:

kubectl port-forward svc/historyserver 8080:30080

Iniciar o painel local do Ray

Instale o Ray localmente. Use a versão 2.55+.

pip uninstall -y ray
pip install -U "ray[default]==2.55.0"

Para mais informações, consulte Versões do Ray.

o painel do Ray, execute o comando ray start:

ray start --head --num-cpus=1 --proxy-server-url=http://localhost:8080

Configurar o RayCluster para o painel do Ray

É necessário encontrar e selecionar os cookies para que o painel do Ray saiba qual RayCluster procurar.

Para selecionar um cluster histórico, primeiro receba a lista de todos os clusters do Ray e as sessões deles.

No navegador, liste as sessões do cluster do Ray navegando até o seguinte URL:

http://localhost:8265/clusters

O resultado da chamada de endpoint será semelhante ao seguinte:

[
 {
  "name": "ratjob",
  "namespace": "default",
  "sessionName": "session_2026-03-20_10-50-19_089740_1",
  "createTime": "2026-03-20T10:50:19Z",
  "createTimeStamp": 1774003819
 },
 {
  "name": "ray-cluster-hs",
  "namespace": "default",
  "sessionName": "session_2026-03-18_17-11-25_410478_1",
  "createTime": "2026-03-18T17:11:25Z",
  "createTimeStamp": 1773853885
 },
 {
  "name": "raycluster-historyserver",
  "namespace": "default",
  "sessionName": "session_2026-02-20_13-03-16_320452_1",
  "createTime": "2026-02-20T13:03:16Z",
  "createTimeStamp": 1771592596
 },
]

Copie uma sessão do cluster do Ray e navegue até esse endpoint no navegador:

http://localhost:8265/enter_cluster/default/raycluster-historyserver/SESSION_ID

Os cookies são definidos quando o endpoint é carregado.

Uma solicitação bem-sucedida produz uma saída como a seguinte:

{
 "name": "ratjob",
 "namespace": "default",
 "result": "success",
 "session": "session_2026-03-20_10-41-12_950419_1"
}

É possível acessar o registro usando o seguinte endpoint do painel:

http://localhost:8265

Usando o exemplo do RayJob, o painel do Ray é semelhante aos exemplos a seguir.

Página de status do job do Ray usando o servidor de histórico do Ray como back-end: Status do RayJob encerrado pelo servidor de histórico

Página de registro do job do Ray usando o servidor de histórico do Ray como back-end: Registro de RayJob encerrado pelo servidor de histórico

A seguir