Descripción general de las Cloud TPU de Cloud en la GKE [versión preliminar pública]
Resumen
En este documento, se explica cómo administrar Cloud TPU en una configuración de Slices múltiples con GKE. Suponemos que estás familiarizado con el uso de únicas porciones de Cloud TPU en GKE y que tienes experiencia general con las porciones múltiples de Cloud TPU. Te recomendamos que modeles cada porción de Cloud TPU como un grupo de nodos y envíes trabajos que impliquen varios grupos de nodos mediante la API de JobSet.
Comentarios
Si tienes preguntas o comentarios, envíanos un correo electrónico a cloudtpu-multislice-preview@google.com.
Conceptos
- Reparación automática
- Cuando una sección encuentra un evento de mantenimiento, interrupción o falla de hardware, Google Kubernetes Engine creará una porción nueva. Si no hay capacidad suficiente para crear una porción nueva, esta no se completará hasta que el hardware esté disponible. Luego, se reiniciarán todas las porciones del entorno de varias porciones para que el entrenamiento pueda continuar. La secuencia de comandos de entrenamiento debe definir una secuencia de comandos de inicio que verifique los puntos de control y los cargue antes de reiniciar el entrenamiento.
- Redes del centro de datos (DCN)
- Una red de mayor capacidad de procesamiento y latencia más alta que conecte segmentos de Cloud TPU en una configuración de varias porciones.
- Conjunto de datos
- Los datos que usa un modelo para el entrenamiento o la inferencia.
- Host
- Un host es una computadora física que ejecuta VM. Un host puede ejecutarse como máximo en la VM a la vez. Cada VM tiene una Cloud TPU dedicada.
- Inferencia
- Cargar un modelo de aprendizaje automático previamente entrenado en un host y realizar predicciones sobre los datos
- Interchip Interconnect (ICI)
- Vínculos internos de alta velocidad y baja latencia que conectan las Cloud TPU dentro de un pod de Cloud TPU.
- Pod de Kubernetes
- Un grupo de uno o más contenedores, con almacenamiento compartido y recursos de red, y una especificación sobre cómo ejecutar los contenedores.
- Varios segmentos
- Dos o más porciones de Cloud TPU que se pueden comunicar a través de la red de centro de datos (DCN)
- Grupos de nodos
- En el contexto de GKE, un grupo de nodos es un grupo de nodos (VM) que el programador de Kubernetes puede programar con cargas de trabajo (por ejemplo, un trabajo de entrenamiento). Para obtener más información sobre la arquitectura y los conceptos del clúster de GKE, consulta Arquitectura del clúster.
- Recurso en cola
- Una representación de los recursos de Cloud TPU, que se usa para poner en cola y administrar una solicitud de un entorno de Cloud TPU de una sola porción o de varias porciones.
- Tensor
- Estructura de datos que se usa para representar datos multidimensionales en un modelo de aprendizaje automático.
- Unidad de procesamiento tensorial (TPU)
- El chip de aceleración de AA desarrollado por Google. Están diseñados para ofrecer el procesamiento más rápido y eficiente para las tareas clave de aprendizaje automático, como la multiplicación de matrices.
- pod de Cloud TPU
- Un conjunto de chips de Cloud TPU conectados por interfaces de red ICI dedicadas. Un pod de Cloud TPU te permite distribuir la carga de procesamiento en varias Cloud TPU.
- Porción de Cloud TPU
- Una subsección lógica de un pod de Cloud TPU que consta de chips de Cloud TPU. Todos los chips de una porción se comunican entre sí mediante la red ICI.
- VM de Cloud TPU
- Una máquina virtual que ejecuta Linux que tiene acceso a las Cloud TPU subyacentes. En el caso de las Cloud TPU v4, cada VM de Cloud TPU tiene acceso directo a 4 chips. A veces, las VM de Cloud TPU se denominan trabajadores.
Requisitos previos
En este documento, se supone que estás familiarizado con la descripción general de Cloud TPU multifragmento y la introducción a Cloud TPU en GKE.
Administra Cloud TPU de varias porciones en GKE
Aprovisiona la capacidad de Cloud TPU para usarla con múltiples secciones de GKE
En Implementa cargas de trabajo de Cloud TPU en GKE, se detalla cómo puedes reservar Cloud TPU. Sin embargo, si usas la capacidad de Cloud TPU directamente a través de la API de Cloud TPU y deseas usar la misma capacidad para GKE Multislice, comunícate con gke-tpu-support@google.com a fin de transferir parte de tu capacidad a GKE.
Crea grupos de nodos en un entorno de varias porciones
Para crear un entorno de varias secciones, debes agrupar varios grupos de nodos de GKE en un grupo de varias porciones. Debes crear un grupo de nodos independiente para cada porción que sea parte de tu entorno de varias secciones. Debes agregar cada grupo de nodos a un grupo de varias secciones. Los grupos de varios fragmentos son una forma de agrupar y administrar varios grupos de nodos. Los grupos de Slices múltiples se definen mediante la aplicación de etiquetas (pares nombre/valor) a cada grupo de nodos que será parte de tu entorno Multislice. En este instructivo, usamos etiquetas llamadas MultisliceGroup
y MultisliceGroupSize
, pero puedes usar el nombre que desees. Todos los grupos de nodos de un grupo de varias porciones deben tener las mismas etiquetas (pares nombre/valor) y la misma topología de Cloud TPU.
Puedes agregar la etiqueta de varios segmentos en el momento de la creación. En este ejemplo, usamos MultisliceGroup
y MultisliceGroupSize
para los nombres de etiquetas:
$ gcloud beta container node-pools create pool-name \
--region=cluster-region \
--cluster=cluster-name \
--node-locations=node-zone \
--machine-type=machine-type \
--tpu-topology=tpu-topology \
--node-labels=MultisliceGroup=multislice-group-name,MultisliceGroupSize=num-slices \
--num-nodes=num-nodes \
[--reservation-affinity=specific \
--reservation=reservation-name]
Notas:
Solo se puede realizar una operación de grupo de nodos a la vez, por lo que tendrás que esperar a que se cree o borre cada grupo de nodos para poder crear o borrar el siguiente.
No se admiten los entornos de varias secciones compuestos por porciones de host único. Por ejemplo, no puedes crear una multisección con varias
v4-16
,v5e
con topología (1,4) niv5e-8
.
También puedes agregar las etiquetas MultisliceGroup
y MultisliceGroupSize
a un grupo de nodos existente:
$ gcloud beta container node-pools update pool-name \
--region=cluster-region \
--cluster=cluster-name \
--node-labels=MultisliceGroup=multislice-group-name,MultisliceGroupSize=num-slices
Verifica el estado del grupo de nodos
Puedes verificar el estado de tus grupos de nodos con kubectl
:
$ kubectl get nodes -l MultisliceGroup=multislice-group-1 | grep "Ready" | wc -l
Notas:
- Debes conectarte al clúster de GKE antes de ejecutar el comando. Para obtener más información, consulta Accede a clústeres.
Cuando ejecutes el comando
get nodes
, es posible que veas el siguiente error:E0608 13:25:01.586645 21293 memcache.go:255] couldn't get resource list for http://metrics.k8s.io/v1beta1 : the server is currently unable to handle the request
Por lo general, este error significa que no tienes un grupo de nodos que no sea de Cloud TPU en el clúster de GKE. Agrega un grupo de nodos que no sea de Cloud TPU a tu clúster y vuelve a probar el comando.
Ejecuta cargas de trabajo de varios segmentos
API de JobSet
La API de JobSet es una API de código abierto para administrar un grupo de trabajos en conjunto. Es una gran opción para las cargas de trabajo de varias secciones; como se describe en la introducción a las Cloud TPU en GKE, usamos una sola IndexedJob
de Kubernetes para ejecutar cargas de trabajo en una sola porción de Cloud TPU. De manera similar, para los entornos de varias secciones, necesitamos que varios IndexedJobs de Kubernetes trabajen juntos y JobSet ayuda a lograrlo.
JobSet proporciona lo siguiente:
- Manejo de fallas: Recreación automática de todos los objetos Job secundarios en caso de que falle cualquiera de los objetos Job secundarios.
- Indexación de Slice: JobSet indexa cada objeto Job con un ID y lo agrega como parte de anotaciones/etiquetas en el Job y el Pod.
- Posición de la carga de trabajo: Se encarga automáticamente de asignar un solo trabajo a un dominio específico (en este caso, cada trabajo en un solo grupo de nodos).
- Creación de Headless-Service: Ciclo de vida administrado de un headless-svc para todos los objetos Job del JobSet.
Instalación
Instala la API de JobSet en tu clúster:
$ kubectl apply --server-side -f https://github.com/kubernetes-sigs/jobset/releases/download/v0.2.1/manifests.yaml
Verifica que el controlador de JobSet esté en ejecución:
$ kubectl get pods -n jobset-system
Ejecuta una carga de trabajo de varios segmentos
En el siguiente manifiesto YAML, se muestra cómo ejecutar una carga de trabajo de varias porciones en cuatro porciones de la versión 4-16. Copia el siguiente archivo YAML, guárdalo en un archivo y, luego, ejecuta kubectl apply -f <file-name.yaml>
:
apiVersion: jobset.x-k8s.io/v1alpha2
kind: JobSet
metadata:
name: multislice-job # JobSet name (${JOBSET_NAME})
annotations:
alpha.jobset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool # 1:1 job replica to node pool assignment
spec:
failurePolicy:
maxRestarts: 4 # The set will be restarted on failures up to 4 times.
replicatedJobs:
- name: slice # Part of the name of the child Jobs (<replicateJobName>)
replicas: 4 # Number of slices
template:
spec:
parallelism: 2 # Must be set to number of nodes in each node pool
completions: 2 # Must be set to number of nodes in each node pool
backoffLimit: 0 # Must be set to 0. Fail the job when any pod fails.
template:
spec:
affinity: # The affinity section is to make sure there is only one Multislice job running in a single Multislice Group. More on this below.
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: jobset.sigs.k8s.io/jobset-name
operator: In
values:
- multislice-job # JobSet name
topologyKey: MultisliceGroup
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: jobset.sigs.k8s.io/jobset-name
operator: NotIn
values:
- multislice-job # JobSet name
topologyKey: MultisliceGroup
namespaceSelector:
matchExpressions:
- key: jobset.sigs.k8s.io/jobset-name
operator: Exists
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
nodeSelector:
cloud.google.com/gke-tpu-accelerator: tpu-v4-podslice
cloud.google.com/gke-tpu-topology: 2x2x2
MultisliceGroupSize: "4"
containers:
- name: jax-tpu
image: python:3.8
ports:
- containerPort: 8471
- containerPort: 8080 # Port for MXLA coordinator
securityContext:
privileged: true
command:
- bash
- -c
- |
pip install "jax[tpu]" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html
python -c 'import jax; print("Global device count:", jax.device_count())'
resources:
limits:
google.com/tpu: 4 # Number of Cloud TPU VMs per worker
JobSet creará 4 trabajos indexados de Kubernetes (cantidad de segmentos). Los trabajos indexados de Kubernetes seguirán el patrón de nomenclatura:
<jobSetName>-<replicateJobName>-<job-index>
(en este caso, esmultislice-job-slice-{0,1,2,3}
). Los trabajos creados aparecerán en Google Cloud CLI enWorkloads
.JobSet es un servicio sin interfaz gráfica con el mismo nombre que JobSet (en este caso, es
multislice-job
).Cada trabajo indexado crea 2 (# de trabajadores) Pods de Kubernetes. Los trabajos siguen el patrón de nombres:
<jobSetName>-<replicateJobName>-<job-index>-<worker-index>-<5 letter suffix>
(en este caso, esmultislice-job-slice-{0,1,2,3}-{1,2}-{5 letter suffix}
).En la actualidad, las múltiples secciones en Google Kubernetes Engine solo admiten el entrenamiento síncrono de varios controladores. Para lograrlo,
parallelism
ycompletions
deben configurarse en la cantidad de nodos en cada grupo de nodos, ybackoff
debe establecerse en 0. La cantidad de nodos es dividida por la cantidad de núcleos/nodo. En el caso de las TPU v4, esto es ocho parav4-16
, configuraparallelism
ycompletions
en2
.La versión mínima de JAX es: v0.4.9.
Inserción de variables de entorno en GKE
El webhook de GKE inyecta de forma automática las variables de entorno a las especificaciones del Pod del trabajo indexado, que luego el Pod de Kubernetes que ejecuta la carga de trabajo de Multi Slice hereda. GKE inserta las siguientes variables de entorno:
Variable de entorno | Valor |
TPU_WORKER_ID
|
metadata.annotations['batch.kubernetes.io/job-completion-index']
|
TPU_WORKER_HOSTNAMES
|
Calcula la lista separada por comas de los índices de Pod del trabajo de la siguiente manera: <job-name>-0.<subdomain>,.......,<job-name>-<n-1>.<subdomain> |
MEGASCALE_NUM_SLICES
|
metadata.annotations['jobset.sigs.k8s.io/replicatedjob-replicas']
|
MEGASCALE_SLICE_ID
|
metadata.annotations['jobset.sigs.k8s.io/job-index']
|
MEGASCALE_COORDINATOR_ADDRESS
|
Generate the string using the jobSetName and the replicatedJobName:
<jobSetName>-<replicatedJobName>-0-0.<subdominio> |
Opcional: Desactiva hostNetwork en tus Pods de GKE
Usa hostNetwork: true
en la especificación de tu Pod para omitir toda la pila de red de Kubernetes y permite que tus Pods de Kubernetes usen la red del host directamente para la comunicación de VM a VM. Esto mejora el rendimiento de red entre porciones. Si deseas seguir usando podHostnames
para el descubrimiento de trabajadores con hostNetwork
, configura dnsPolicy: ClusterFirstWithHostNet
. Esto es importante cuando se reanuda automáticamente el trabajo de entrenamiento, y debe tener los mismos nombres para volver a cargar los mismos puntos de control.
Para desactivar hostNetworking
, quita las siguientes dos líneas de las especificaciones del Pod:
hostNetwork: true dnsPolicy: ClusterFirstWithHostNet
Organiza trabajos de varias secciones
Evita programar un interbloqueo
Si envías varios trabajos de varias porciones (JobSet A y B) del mismo MultisliceGroupSize
, podrías tener una situación en la que ambos objetos JobSet estén programados de manera parcial (por ejemplo, 5 de 8 trabajos secundarios están programados desde JobSet A y 3 de 8 trabajos secundarios están programados desde JobSet B). Esto hará que los grupos de nodos permanezcan inactivos y que el JobSet A y B estén colgados.
Para evitarlo, puedes aprovechar podAffinity
y podAntiAffinity
. En las cargas de trabajo de varias secciones, cambia topologyKey
para que sea la etiqueta (MultisliceGroup
) que representa un grupo de varias secciones en lugar de la etiqueta cloud.google.com/gke-nodepool
.
Por ejemplo:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: jobset.sigs.k8s.io/jobset-name
operator: In
values:
- multislice-job # JobSet name
topologyKey: MultisliceGroup
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: jobset.sigs.k8s.io/jobset-name
operator: NotIn
values:
- multislice-job # JobSet name
topologyKey: MultisliceGroup
namespaceSelector:
matchExpressions:
- key: jobset.sigs.k8s.io/jobset-name
operator: Exists
Observabilidad
Registros de Cloud Logging
Los Pods de GKE del JobSet se nombran con el siguiente patrón:
<jobSetName>-<replicateJobName>-<job-index>-<worker-index>-<5 letter suffix>
Puedes ver tus registros mediante el Explorador de registros de Cloud Logging con el siguiente filtro a fin de ver los registros de contenedores de tu carga de trabajo:
resource.type="k8s_container"
resource.labels.cluster_name=<cluster-name>
labels."k8s-pod/jobset_sigs_k8s_io/jobset-name"=<jobSetName>
Para filtrar los registros de la porción x
y el trabajador y
, usa el siguiente filtro:
resource.type="k8s_container"
resource.labels.cluster_name=<cluster-name>
labels."k8s-pod/jobset_sigs_k8s_io/jobset-name"=<jobSetName>
resource.labels.pod_name:<jobSetName>-<replicateJobName>-<job-index>-<worker-index>