Skip to content

Part 4: Ray Serve ​

Review baseline: Ray 2.58.0 · KubeRay 1.7.0 · 2026-09-12

Environment and Validation Scope ​

A small CPU response example was checked with Python 3.12 and ray[serve]==2.58.0. In this environment, the HAProxy module imported Jinja2 although the extra installation had not supplied it; explicitly adding Jinja2==3.1.6 fixed the import. Other environments may already have it through another dependency.

The ray[llm] extra adds large inference dependencies such as vLLM. It was not installed here, and no model weights, GPU, or EKS workload ran. Validation below covers Serve configuration, HTTP responses, and DeploymentHandle calls.

Deployments, Applications, and Request Paths ​

A Serve Deployment manages actor replicas; it is different from a Kubernetes Deployment. Several replica actors can fit in one Ray Pod, so replica and Pod counts are not interchangeable.

An Application contains one or more deployments and an ingress deployment. DeploymentHandles can connect preprocessing and inference without making every internal call traverse HTTP or creating a Kubernetes Service per deployment.

The Controller manages Serve control state and actor lifecycles. Proxies receive HTTP/gRPC traffic and forward it to deployments. The 2.58.0 default proxy location is EveryNode on nodes hosting replicas. HeadOnly and Disabled can be selected explicitly. An older architecture page's head-only default should not override the current API contract.

Distinguish caller queues at proxies/handles from ongoing requests assigned to replicas. Review synchronous/async handlers, blocking work, timeouts, and cancellation behavior in the application.

Small Local HTTP/Handle Example ​

This validates response APIs rather than model inference. The actual check used an available private-loopback port and confirmed HTTP 200 and double(4) == 8.

python
import requests
import ray
from ray import serve

try:
    ray.init(address="local", num_cpus=2, include_dashboard=False,
             object_store_memory=80 * 1024 * 1024)
    serve.start(proxy_location="HeadOnly",
                http_options={"host": "127.0.0.1", "port": 18080})

    @serve.deployment(num_replicas=1,
                      ray_actor_options={"num_cpus": 1},
                      max_ongoing_requests=2, max_queued_requests=4)
    class Echo:
        async def __call__(self, request):
            return {"echo": request.query_params.get("value", "")}
        def double(self, value):
            return value * 2

    handle = serve.run(Echo.bind(), name="echo", route_prefix="/echo")
    response = requests.get("http://127.0.0.1:18080/echo",
                            params={"value": "fixture"}, timeout=15)
    assert response.status_code == 200
    assert response.json() == {"echo": "fixture"}
    assert handle.double.remote(4).result(timeout_s=15) == 8
finally:
    serve.shutdown()
    ray.shutdown()

Run in a separate exercise process with port 18080 available. Ray logical resources and object-store size are not whole-process OS memory/CPU limits. serve.shutdown() stops the connected Serve instance; do not use this example's cleanup against a shared production cluster.

Replicas, Autoscaling, and Backpressure ​

Distinguish these verified 2.58.0 defaults:

ConfigurationValue or meaning
Default deploymentone replica, autoscaling unconfigured
num_replicas="auto"applies min 1, max 100, target ongoing 2
Direct AutoscalingConfig()min 1, max 1; omitting max restricts expansion
max_ongoing_requestsrequests sent to a replica without a response; default 5
max_queued_requestsqueue bound at each caller (proxy/handle); default -1, unlimited
Scaling delaydefault upscale 30 seconds/downscale 600 seconds; not actual readiness latency

The autoscaling target observes request load; it is distinct from max ongoing and a global queue bound. Exceeding queue limits can raise BackPressureError for handles or return HTTP 503 by default. Backpressure configuration can customize the HTTP response.

Tune min/max, measurement windows/delays, cold starts, model loading, batching, and real processing time together. Scaling to zero with min_replicas=0 does not eliminate restart latency. A desired replica count does not guarantee all replicas are ready.

Control Layers on EKS ​

  1. Serve adjusts deployment replica targets from request load and policy.
  2. Ray places actors/bundles; enabled Ray autoscaling and KubeRay can adjust worker Pod capacity.
  3. Kubernetes places Pods and a provisioner such as Karpenter supplies node capacity when required.

A pending actor does not automatically become one Pending Pod or one EC2 node. Existing Ray Pods can gain free capacity, or group bounds, placement, and quotas can prevent progress. Inspect demand and readiness at each layer.

HTTP/Handle requests reach Serve proxies and deployment replicas. Actor targets, Ray Pod capacity, and Kubernetes node provisioning are separate layers without a one-to-one actor/Pod/node mapping.

HTTP/Handle requests reach Serve proxies and deployment replicas. Actor targets, Ray Pod capacity, and Kubernetes node provisioning are separate layers without a one-to-one actor/Pod/node mapping.Open full screen ↗

GPU Inference and Ray Serve LLM ​

Ordinary GPU replicas use Ray resource settings such as ray_actor_options. Align devices, drivers, Pod limits, and Ray's structured-resource/rayStartParams precedence. As Part 2 explains, Pod limits are not always the only configured value.

APIs such as LLMConfig and build_openai_app provide a separate LLM configuration layer. The 2.58.0 documentation and package show vLLM and SGLang backends. Verified ray[llm] dependencies include vllm[audio]==0.26.0 and NIXL packages; this does not imply every SGLang dependency is installed too.

Distinguish model_loading_config, deployment_config, engine_kwargs, and server_cls. Check engine-specific fields and supported combinations rather than assuming every vllm serve CLI option transfers unchanged. Backends can differ in tensor-parallel option names and worker placement. Some APIs are beta, and older LLMServer/LLMRouter paths carry deprecation notices.

Validate model access, revision, weight downloads, engine/CUDA/driver compatibility, KV cache, and tensor/pipeline-parallel resources separately. OpenAI-compatible request format does not establish authentication, security, or identical feature coverage. The CPU Echo check does not prove LLM performance or compatibility.

RayService and Operational Updates ​

RayService is an option for declarative lifecycle management of Serve applications and RayClusters on EKS, not a universal requirement for every production deployment. Distinguish application configuration changes from cluster changes, and NewCluster from Gateway-based incremental upgrade strategies.

KubeRay 1.7's enabled incremental feature gate still requires Gateway APIs/implementation, spare capacity, readiness, and draining conditions. Test streaming and long-running requests against shutdown bounds. Do not describe every upgrade as guaranteed zero request loss.

Cluster-scoped startup settings such as HTTP options have dynamic-update limits. Deployment changes can be lightweight reconfiguration or actor replacement. Restarted/replaced replicas pay model initialization and state-recovery costs.

Access Control and Limits ​

Restrict API/dashboard/client entry points, model-artifact access, and application-user access separately. Ray cluster tokens and ClusterIP do not automatically implement authentication/authorization for every Serve endpoint. Review sensitive inputs, responses, prompts, and logs, and configure queue, timeout, and resource bounds.

The checks here cover native configuration/decorators and a tiny single-node HTTP/Handle application. Autoscaling load tests, GPU/LLM execution, multi-node failover, and RayService rollouts were not performed.

Primary Sources ​

Main Page · Quiz