Dask and Ray Distributed Execution
Running Dask Workloads
To execute distributed Dask workloads as Flyte tasks, you define your task using the flyteplugins.dask.task.DaskTask plugin. This plugin requires a plugin_config of type flyteplugins.dask.task.Dask, which allows you to specify the configuration for your Dask cluster, including the scheduler and worker settings.
Here's a basic example of a Dask task that uses a default scheduler and a worker group with four workers:
import asyncio
import typing
from dask.distributed import Client
from flytekit import task, workflow
from flyteplugins.dask.task import Dask, DaskTask, Scheduler, WorkerGroup
dask_config = Dask()
@task(task_config=DaskTask(plugin_config=dask_config))
async def hello_dask_nested(n: int = 3) -> typing.List[int]:
print("running dask task")
client = Client()
futures = client.map(lambda x: x + 1, range(n))
res = client.gather(futures)
return res
@workflow
def dask_workflow(n: int = 3) -> typing.List[int]:
return hello_dask_nested(n=n)
The flyteplugins.dask.task.Dask object provides two primary configuration components: scheduler and workers.
- The
schedulerparameter accepts aflyteplugins.dask.task.Schedulerobject. This allows you to configure the Dask scheduler pod, including specifying a customimageand allocatingresources(CPU and memory). - The
workersparameter accepts aflyteplugins.dask.task.WorkerGroupobject. Here, you can define thenumber_of_workers, theirimage, andresources.
For instance, to specify custom images and resource requests for both the scheduler and workers, you would configure Dask as follows:
from flytekit.models.resources import Resources
from flyteplugins.dask.task import Dask, Scheduler, WorkerGroup
dask_config_with_resources = Dask()
Dask Task Considerations
- Image Requirements: Any custom images used for Dask scheduler and worker pods (specified via
Scheduler.imageandWorkerGroup.image) must havedask[distributed]installed. Additionally, the Python environment within these images should be consistent with the environment of the job runner pod. - Code Bundle Distribution: The
flyteplugins.dask.task.DaskTaskautomatically handles the distribution of your code bundle to the Dask scheduler and worker pods. This is managed byDownloadCodeBundleSchedulerPluginandDownloadCodeBundleWorkerPlugin, which register with the Dask client. This mechanism is active only when the task is executing within a Flyte cluster (flyte.ctx().is_in_cluster()is true) and a code bundle is present.
Running Ray Workloads
To execute distributed Ray jobs as Flyte tasks, you use the flyteplugins.ray.task.RayFunctionTask plugin. This plugin is configured via a plugin_config of type flyteplugins.ray.task.RayJobConfig, which defines the Ray cluster's head and worker node settings.
Here's an example of a Ray task that uses a single worker group with two replicas and a custom runtime environment:
import asyncio
import typing
import ray
from flytekit import task, workflow
from flyteplugins.ray.task import HeadNodeConfig, RayFunctionTask, RayJobConfig, WorkerNodeConfig
@ray.remote
def f(x):
return x * x
ray_config = RayJobConfig()
@task(task_config=RayFunctionTask(plugin_config=ray_config))
async def hello_ray_nested(n: int = 3) -> typing.List[int]:
print("running ray task")
futures = [f.remote(i) for i in range(n)]
res = ray.get(futures)
return res
@workflow
def ray_workflow(n: int = 3) -> typing.List[int]:
return hello_ray_nested(n=n)
The flyteplugins.ray.task.RayJobConfig object allows for detailed configuration of the Ray cluster:
head_node_config: An optionalflyteplugins.ray.task.HeadNodeConfigobject to configure the Ray head node. You can specifyray_start_params(parameters passed toray start), apod_template, and resourcerequestsandlimits.worker_node_config: A list offlyteplugins.ray.task.WorkerNodeConfigobjects, each defining a worker group. For each worker group, you can setgroup_name,replicas(number of instances),min_replicas,max_replicas(for autoscaling),ray_start_params, apod_template, and resourcerequestsandlimits.enable_autoscaling: A boolean to enable or disable autoscaling for the Ray cluster.runtime_env: A dictionary to specify the Ray runtime environment, such as Python package dependencies. This is deprecated in KubeRay versions 1.1.0 and later, whereruntime_env_yamlshould be used instead.address: An optional string specifying the address of the Ray cluster. If not provided, Ray might default to local execution or require specific environment setup for cluster execution.shutdown_after_job_finishes: A boolean indicating whether the Ray cluster should shut down immediately after the job completes.ttl_seconds_after_finished: An optional integer specifying the time-to-live for the Ray cluster after the job finishes, allowing for post-job inspection or cleanup.
For example, to configure a Ray job with specific resource limits for the head node and a worker group with autoscaling enabled:
from flytekit.models.resources import Resources
from flyteplugins.ray.task import HeadNodeConfig, RayJobConfig, WorkerNodeConfig
ray_config_with_resources = RayJobConfig()
head_node_config_instance = HeadNodeConfig()
head_node_config_instance.limits = Resources(cpu="2", memory="4Gi")
ray_config_with_resources.head_node_config = head_node_config_instance
worker_node_config_instance = WorkerNodeConfig()
worker_node_config_instance.group_name = "high-cpu-workers"
worker_node_config_instance.replicas = 1
worker_node_config_instance.min_replicas = 1
worker_node_config_instance.max_replicas = 5
worker_node_config_instance.limits = Resources(cpu="4", memory="8Gi")
ray_config_with_resources.worker_node_config = [worker_node_config_instance]
ray_config_with_resources.enable_autoscaling = True
Ray Task Considerations
- Runtime Environment: The
runtime_envparameter inRayJobConfigis deprecated in KubeRay versions 1.1.0 and later. For newer KubeRay deployments, useruntime_env_yamlfor specifying the Ray runtime environment. TheRayFunctionTaskhandles the conversion ofruntime_envtoruntime_env_yamlinternally ifruntime_envis provided. - Ray Initialization: The
RayFunctionTaskautomatically initializes Ray within the task'spremethod. Ifflyte.ctx().is_in_cluster()is true, it configures theruntime_envwith the current working directory and excludes specific files to ensure proper code distribution within the cluster. If Ray is already initialized, it will not re-initialize it.