Ray: Computación distribuida para todos, parte 2

entrega de mi serie de dos partes sobre la biblioteca Ray, un marco de Python creado por AnyScale para computación distribuida y paralela. La parte 1 cubrió cómo paralelizar trabajos de Python con uso intensivo de CPU en su PC local distribuyendo la carga de trabajo entre todos los núcleos disponibles, lo que resultó en marcadas mejoras en el tiempo de ejecución. Dejaré un enlace a la Parte 1 al final de este artículo.

Esta parte trata un tema similar, excepto que llevamos la distribución de cargas de trabajo de Python al siguiente nivel usando Ray para paralelizarlas en clústeres de múltiples servidores en la nube.

Si llegó a esto sin haber leído la Parte 1, el TL;DR de Ray es que es un marco informático distribuido de código abierto diseñado para facilitar la escala de programas Python desde una computadora portátil a un clúster con cambios mínimos de código. Con suerte, eso por sí solo debería ser suficiente para despertar su interés. En mi propia prueba, en mi PC de escritorio, tomé un programa Python sencillo y relativamente simple que encuentra números primos y reduje su tiempo de ejecución en un factor de 10 agregando solo cuatro líneas de código.

¿Dónde se pueden ejecutar clústeres de Ray?

Los grupos de rayos se pueden configurar en lo siguiente:

AWS y GCP Cloud, aunque también existen integraciones no oficiales para otros proveedores, como Azure AnyScale, una plataforma totalmente administrada desarrollada por los creadores de Ray. Kubernetes también se puede utilizar a través del proyecto KubeRay con soporte oficial.

Requisitos previos

Para seguir mi proceso, necesitarás configurar algunas cosas de antemano. Usaré AWS para mi demostración, ya que tengo una cuenta allí; sin embargo, espero que la configuración para otros proveedores y plataformas de nube sea muy similar. Deberías tener:

Credenciales configuradas para ejecutar comandos de Cloud CLI desde el proveedor elegido. Una VPC predeterminada y al menos una subred pública asociada a ella que tenga una dirección IP accesible públicamente. Un archivo de par de claves SSH (.pem) que puede descargar en su sistema local para que Ray (y usted) puedan conectarse a los nodos de su clúster. Tiene cuotas suficientes para satisfacer la cantidad solicitada de nodos y vCPU en cualquier clúster que configure.

Si desea realizar algunas pruebas locales de su código Ray antes de implementarlo en un clúster, también deberá instalar la biblioteca Ray. Podemos hacerlo usando pip.

$ pip instalar rayo

Ejecutaré todo desde un shell WSL2 Ubuntu en mi escritorio de Windows.

Para verificar que Ray se haya instalado correctamente, debería poder utilizar su intérprete de línea de comandos. En una ventana de terminal, escriba el siguiente comando.

$ ray –help Uso: ray [OPCIONES] COMANDO [ARGS]… Opciones: –logging-level TEXT El umbral del nivel de registro, options=['debug', 'info', 'warning', 'error', 'critical'], default='info' –logging-format TEXT El formato de registro. default="%%(asctime)st%%(levelname)s %%(filename)s:%%(lineno)s — %%(message)s" –version Muestra la versión y sale. –help Muestra este mensaje y sale. Comandos: adjuntar Crea o adjunta una sesión SSH a un clúster de Ray. check-open-ports Verifique los puertos abiertos en el clúster de Ray local. cluster-dump Obtiene datos de registro de uno o más nodos. … … …

Si no ve esto, algo salió mal y debe volver a verificar el resultado de su comando de instalación.

Suponiendo que todo esté bien, estamos listos para comenzar.

Sin embargo, un último punto importante. La creación de recursos, como clústeres de computación, en un proveedor de nube como AWS generará costos, por lo que es esencial que lo tenga en cuenta. La buena noticia es que Ray tiene un comando incorporado que derribará cualquier infraestructura que cree, pero para estar seguro, debe verificar que ningún servicio no utilizado y potencialmente costoso quede “encendido” por error.

Nuestro código Python de ejemplo

El primer paso es modificar nuestro código Ray existente de la Parte 1 para ejecutarlo en un clúster. Aquí está el código original para su referencia. Recuerde que estamos intentando contar el número de números primos dentro de un rango numérico específico.

import math import time # —————————————– # Cambio No. 1 # —————————————– import ray ray.init() def is_prime(n: int) -> bool: if n < 2: return False if n == 2: return True if n % 2 == 0: return False r = int(math.isqrt(n)) + 1 for i in range(3, r, 2): if n % i == 0: return False return True # —————————————– # Cambio No. 2 # —————————————– @ray.remote(num_cpus=1) # bucle de Python puro → 1 CPU por tarea def count_primes(a: int, b: int) -> int: c = 0 for n in range(a, b): if is_prime(n): c += 1 return c if __name__ == "__main__": A, B = 10_000_000, 20_000_000 total_cpus = int(ray.cluster_resources().get("CPU", 1)) # Iniciar "grueso"; podemos barrer estos fragmentos posteriores = max(4, total_cpus * 2) step = (B – A) // fragmentos print(f"nodes={len(ray.nodes())}, CPUs~{total_cpus}, fragmentos={chunks}") t0 = time.time() refs =[]para i en rango(fragmentos): s = A + i * paso e = s + paso si i < fragmentos – 1 else B # —————————————– # Cambio No. 3 # —————————————– refs.append(count_primes.remote(s, e)) # —————————————– # Cambio No. 4 # —————————————– total = sum(ray.get(refs)) print(f"total={total}, tiempo={tiempo.tiempo() – t0:.2f}s")

¿Qué modificaciones se necesitan para ejecutarlo en un clúster? La respuesta es que sólo se requiere un pequeño cambio.

Cambie ray.init() a ray.init(dirección=auto)

Esa es una de las bellezas de Ray. El mismo código se ejecuta casi sin modificaciones en su PC local y en cualquier otro lugar donde desee ejecutarlo, incluidos grandes clústeres de nubes con múltiples servidores.

Configurando nuestro cluster

En la nube, un clúster de Ray consta de un nodo principal y uno o más nodos trabajadores. En AWS, todos estos nodos son simplemente instancias EC2. Los clústeres de Ray pueden tener un tamaño fijo o escalarse automáticamente hacia arriba o hacia abajo según los recursos solicitados por las aplicaciones que se ejecutan en el clúster. El nodo principal se inicia primero y los nodos trabajadores se configuran con la dirección del nodo principal para formar el clúster. Si el escalado automático está habilitado, los nodos trabajadores aumentan o disminuyen automáticamente según la carga de la aplicación y se reducirán después de un período especificado por el usuario (5 minutos de forma predeterminada).

Ray usa archivos YAML para configurar clústeres. Un archivo YAML es simplemente un archivo de texto sin formato con una sintaxis similar a JSON que se utiliza para la configuración del sistema.

Aquí está el archivo YAML que usaré para configurar mi clúster. Descubrí que la instancia EC2 más cercana a mi PC de escritorio, en términos de número de núcleos de CPU y rendimiento, era una c7g.8xlarge. Para simplificar, hago que el nodo principal sea del mismo tipo de servidor que todos los trabajadores, pero puedes mezclar y combinar diferentes tipos de EC2 si lo deseas.

nombre_clúster: proveedor ray_test: tipo: aws región: eu-west-1 zona_disponibilidad: eu-west-1a auth: # Para las AMI de Amazon Linux, el usuario SSH es 'ec2-user'. # Si cambia a una AMI de Ubuntu, cámbielo a 'ubuntu'. ssh_user: ec2-user ssh_private_key: ~/.ssh/ray-autoscaler_eu-west-1.pem max_workers: 10 idle_timeout_minutos: 10 head_node_type: head_node available_node_types: head_node: node_config: InstanceType: c7g.8xlarge ImageId: ami-06687e45b21b1fca9 Nombre clave: ray-autoscaler_eu-west-1 nodo_trabajador: min_workers: 5 max_workers: 5 node_config: Tipo de instancia: c7g.8xlarge ID de imagen: ami-06687e45b21b1fca9 Nombre clave: ray-autoscaler_eu-west-1 Opciones de mercado de instancia: Tipo de mercado: spot # ========================== # Comandos de configuración (ejecutar en el cabezal + trabajadores) # ========================== setup_commands: – | set -euo pipefail have_cmd() { comando -v "$1" >/dev/null 2>&1; } have_pip_py() { python3 -c 'importar importlib.util, sys; sys.exit(0 if importlib.util.find_spec("pip") else 1)' } # 1) Asegúrese de que Python 3 esté presente si ! have_cmd python3; entonces si have_cmd dnf; luego sudo dnf install -y python3 elif have_cmd yum; luego sudo yum install -y python3 elif have_cmd apt-get; luego sudo apt-get update -y sudo apt-get install -y python3 else echo "No se encontró ningún administrador de paquetes compatible para instalar python3". >&2 salida 1 fi fi # 2) Asegúrese de que exista pip si ! tener_pip_py; luego python3 -m surepip –upgrade >/dev/null 2>&1 || cierto fi si! have_pip_py; entonces si have_cmd dnf; luego sudo dnf install -y python3-pip || verdadero elif have_cmd yum; luego sudo yum install -y python3-pip || verdadero elif have_cmd apt-get; luego sudo apt-get update -y || verdadero sudo apt-get install -y python3-pip || cierto fi fi si! have_pip_py; luego curl -fsS https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py python3 /tmp/get-pip.py fi # 3) Actualice las herramientas de empaquetado e instale Ray python3 -m pip install -U pip setuptools wheel python3 -m pip install -U "ray[predeterminado]"

Aquí hay una breve explicación de cada sección crítica de YAML.

cluster_name: asigna un nombre al clúster, lo que permite a Ray rastrearlo y administrarlo por separado de los demás. proveedor: especifica qué nube usar (AWS aquí), junto con la región y la zona de disponibilidad para lanzar instancias. autenticación: define cómo Ray se conecta a las instancias a través de SSH: el nombre de usuario y la clave privada utilizada para la autenticación. max_workers: establece el número máximo de nodos trabajadores que Ray puede escalar cuando se necesita más computación. idle_timeout_minutos: le dice a Ray cuánto tiempo esperar antes de finalizar automáticamente los nodos trabajadores inactivos. available_node_types: describe los diferentes tipos de nodos (cabezal y trabajadores), sus tamaños de instancia, imágenes AMI y límites de escala. head_node_type: identifica cuál de los tipos de nodo actúa como controlador del clúster (el nodo principal). setup_commands: enumera los comandos de shell que se ejecutan una vez en cada nodo cuando se crea por primera vez, generalmente para instalar software o configurar el entorno.

Para iniciar la creación del clúster, use este comando ray desde la terminal.

$ rayo arriba -y ray_test.yaml

Ray hará lo suyo, creará toda la infraestructura necesaria y, después de unos minutos, debería ver algo como esto en la ventana de su terminal.

… … … Próximos pasos Para agregar otro nodo a este clúster de Ray, ejecute ray start –address='10.0.9.248:6379' Para conectarse a este clúster de Ray: import ray ray.init() Para enviar un trabajo de Ray utilizando la CLI de trabajos de Ray: RAY_ADDRESS='http://10.0.9.248:8265' envío de trabajo de Ray –working-dir. — python my_script.py Consulte https://docs.ray.io/en/latest/cluster/running-applications/job-submission/index.html para obtener más información sobre cómo enviar trabajos de Ray al clúster de Ray. Para finalizar el tiempo de ejecución de Ray, ejecute ray stop. Para ver el estado del clúster, use ray status. Para monitorear y depurar Ray, vea el panel en 10.0.9.248:8265. Si falla la conexión al panel, verifique la configuración de su firewall y la configuración de red. Conexión compartida al 108.130.38.255 cerrada. Nuevo estado: actualizado Comandos útiles: Para terminar el clúster: ray down /mnt/c/Users/thoma/ray_test.yaml Para recuperar la dirección IP del cabezal del clúster: ray get-head-ip /mnt/c/Users/thoma/ray_test.yaml Para reenviar el panel Ray Dashboard del clúster a la máquina local: ray Dashboard /mnt/c/Users/thoma/ray_test.yaml Para enviar un trabajo al clúster, reenvíe el puerto de Ray Dashboard en otra terminal y ejecute: envío de trabajo de ray –address http://localhost: –working-dir . — python my_script.py Para conectarse a una terminal en el cabezal del clúster para depuración: ray adjunto /mnt/c/Users/thoma/ray_test.yaml Para monitorear el escalado automático: ray exec /mnt/c/Users/thoma/ray_test.yaml 'tail -n 100 -f /tmp/ray/session_latest/logs/monitor*'

Ejecutar un trabajo de Ray en un clúster

En esta etapa, el clúster se ha creado y estamos listos para enviarle nuestro trabajo de Ray. Para darle al clúster algo más sustancial con qué trabajar, aumenté el rango para la búsqueda principal en mi código de 10.000.000 a 20.000.000 a 10.000.000–60.000.000. En mi escritorio local, Ray ejecutó esto en 18 segundos.

Esperé un momento a que todos los nodos del clúster se inicializaran por completo y luego ejecuté el código en el clúster con este comando.

$ ray ejecutivo ray_test.yaml 'python3 ~/ray_test.py'

Aquí está mi salida.

(base) tom@tpr-desktop:/mnt/c/Users/thoma$ ray exec ray_test2.yaml 'python3 ~/primes_ray.py' 2025-11-01 13:44:22,983 INFORMACIÓN util.py:389 – establecer el máximo de trabajadores para el tipo de nodo principal en 0 Configuración del proveedor en caché cargada Si tiene problemas con el proveedor de la nube, intente volver a ejecutar el comando con –no-config-cache. IP obtenida: 52.213.155.130 Advertencia: Se agregó permanentemente '52.213.155.130' (ED25519) a la lista de hosts conocidos. 2025-11-01 13:44:26,469 INFORMACIÓN trabajador.py:1832 – Conexión al clúster Ray existente en la dirección: 10.0.5.86:6379… 2025-11-01 13:44:26,477 INFORMACIÓN trabajador.py:2003 – Conectado al clúster Ray. Vea el panel en http://10.0.5.86:8265 nodes=6, CPUs~192, chunks=384 (escalador automático +2s) Consejo: use `ray status` para ver el estado detallado del clúster. Para deshabilitar estos mensajes, configure RAY_SCHEDULER_EVENTS=0. (escalador automático +2s) Ningún tipo de nodo disponible puede satisfacer las solicitudes de recursos {'CPU': 1.0}*160. Agregue tipos de nodos adecuados a este clúster para resolver este problema. total=2897536, tiempo=5,71s Conexión compartida al 52.213.155.130 cerrada.

Como puede ver, el tiempo necesario para ejecutarse en el clúster fue de poco más de 5 segundos. Entonces, cinco nodos trabajadores ejecutaron el mismo trabajo en menos de un tercio del tiempo que tomaba en mi PC local. No está nada mal.

Cuando haya terminado con su clúster, ejecute el siguiente comando de Ray para derribarlo.

$ rayo hacia abajo -y ray_test.yaml

Como mencioné antes, siempre debes verificar tu cuenta para asegurarte de que este comando haya funcionado como se esperaba.

Resumen

Este artículo, el segundo de una serie de dos partes, demuestra cómo ejecutar código Python con uso intensivo de CPU en clústeres basados ​​en la nube utilizando la biblioteca Ray. Al distribuir la carga de trabajo entre todas las vCPU disponibles, Ray garantiza que nuestro código ofrezca rendimiento y tiempos de ejecución rápidos.

Describí y mostré cómo crear un clúster usando un archivo YAML y cómo utilizar la interfaz de línea de comandos de Ray para enviar código para su ejecución en el clúster.

Utilizando AWS como plataforma de ejemplo, tomé el código Ray Python, que se había estado ejecutando en mi PC local y lo ejecuté, casi sin cambios, en un clúster EC2 de 6 nodos. Esto mostró mejoras de rendimiento significativas (3 veces) durante el tiempo de ejecución fuera del clúster.

Finalmente, mostré cómo utilizar la herramienta de línea de comandos de ray para derribar la infraestructura del clúster de AWS que Ray había creado.

Si aún no ha leído mi primer artículo de esta serie, haga clic en el enlace a continuación para verlo.

Tenga en cuenta que, aparte de ser un usuario ocasional de sus servicios, no tengo ninguna afiliación con AnyScale o AWS ni con ninguna otra organización mencionada en este artículo.