diff --git a/python/ray/serve/api.py b/python/ray/serve/api.py index 767e1c588..4f7a09c6f 100644 --- a/python/ray/serve/api.py +++ b/python/ray/serve/api.py @@ -106,6 +106,7 @@ class Client: self._shutdown = False self._http_config: HTTPOptions = ray.get( controller.get_http_config.remote()) + self._root_url = ray.get(self._controller.get_root_url.remote()) # Each handle has the overhead of long poll client, therefore cached. self.handle_cache = WeakValueDictionary() @@ -122,7 +123,7 @@ class Client: @property def root_url(self): - return self._http_config.root_url + return self._root_url def __del__(self): if not self._detached: @@ -384,6 +385,10 @@ def start( - "NoServer" or None: disable HTTP server. - num_cpus (int): The number of CPU cores to reserve for each internal Serve HTTP proxy actor. Defaults to 0. + - root_url (str): The URL where the Deployment will be located at. + If no `root_url` is provided, Serve will check the environment + variable `RAY_SERVE_ROOT_URL` on the Ray cluster which is + serving the Deployment. dedicated_cpu (bool): Whether to reserve a CPU core for the internal Serve controller actor. Defaults to False. """ diff --git a/python/ray/serve/config.py b/python/ray/serve/config.py index 70c981a96..4b1d34d49 100644 --- a/python/ray/serve/config.py +++ b/python/ray/serve/config.py @@ -1,14 +1,12 @@ import inspect from enum import Enum -import os from typing import Any, List, Optional import pydantic from pydantic import BaseModel, PositiveInt, validator, NonNegativeFloat from ray import cloudpickle as cloudpickle -from ray.serve.constants import (DEFAULT_HTTP_HOST, DEFAULT_HTTP_PORT, - SERVE_ROOT_URL_ENV_KEY) +from ray.serve.constants import DEFAULT_HTTP_HOST, DEFAULT_HTTP_PORT class BackendConfig(BaseModel): @@ -167,15 +165,6 @@ class HTTPOptions(pydantic.BaseModel): return DeploymentMode.NoServer return v - @validator("root_url", always=True) - def fill_default_root_url(cls, v, values): - if v == "": - if SERVE_ROOT_URL_ENV_KEY in os.environ: - return os.environ[SERVE_ROOT_URL_ENV_KEY] - else: - return f"http://{values['host']}:{values['port']}" - return v - class Config: validate_assignment = True extra = "forbid" diff --git a/python/ray/serve/controller.py b/python/ray/serve/controller.py index 746473fee..ada039e23 100644 --- a/python/ray/serve/controller.py +++ b/python/ray/serve/controller.py @@ -2,6 +2,7 @@ import asyncio import json import time from collections import defaultdict +import os from typing import Dict, List, Optional, Tuple, Any import ray @@ -19,7 +20,7 @@ from ray.serve.common import ( ReplicaTag, ) from ray.serve.config import BackendConfig, HTTPOptions, ReplicaConfig -from ray.serve.constants import CONTROL_LOOP_PERIOD_S +from ray.serve.constants import CONTROL_LOOP_PERIOD_S, SERVE_ROOT_URL_ENV_KEY from ray.serve.endpoint_state import EndpointState from ray.serve.http_state import HTTPState from ray.serve.storage.kv_store import RayInternalKVStore @@ -179,6 +180,16 @@ class ServeController: """Return the HTTP proxy configuration.""" return self.http_state.get_config() + def get_root_url(self): + """Return the root url for the serve instance.""" + http_config = self.get_http_config() + if http_config.root_url == "": + if SERVE_ROOT_URL_ENV_KEY in os.environ: + return os.environ[SERVE_ROOT_URL_ENV_KEY] + else: + return f"http://{http_config.host}:{http_config.port}" + return http_config.root_url + async def shutdown(self) -> List[GoalId]: """Shuts down the serve instance completely.""" async with self.write_lock: diff --git a/python/ray/serve/tests/test_standalone.py b/python/ray/serve/tests/test_standalone.py index 99fe2f9a1..929adf329 100644 --- a/python/ray/serve/tests/test_standalone.py +++ b/python/ray/serve/tests/test_standalone.py @@ -289,6 +289,7 @@ def test_http_root_url(ray_shutdown): f.deploy() assert f.url == root_url + "/f" serve.shutdown() + ray.shutdown() del os.environ[SERVE_ROOT_URL_ENV_KEY] port = new_port() @@ -297,6 +298,15 @@ def test_http_root_url(ray_shutdown): assert f.url != root_url + "/f" assert f.url == f"http://127.0.0.1:{port}/f" serve.shutdown() + ray.shutdown() + + ray.init(runtime_env={"env_vars": {SERVE_ROOT_URL_ENV_KEY: root_url}}) + port = new_port() + serve.start(http_options=dict(port=port)) + f.deploy() + assert f.url == root_url + "/f" + serve.shutdown() + ray.shutdown() @pytest.mark.skipif(sys.platform == "win32", reason="Failing on Windows")