mirror of
https://github.com/vale981/ray
synced 2025-03-05 10:01:43 -05:00
[Dashboard] Make requests sent by the dashboard reverse proxy compatible (#14012)
This commit is contained in:
parent
ef96193b8b
commit
488f63efe3
11 changed files with 97 additions and 24 deletions
1
dashboard/client/.env.production.local
Normal file
1
dashboard/client/.env.production.local
Normal file
|
@ -0,0 +1 @@
|
|||
PUBLIC_URL="."
|
1
dashboard/client/.gitignore
vendored
1
dashboard/client/.gitignore
vendored
|
@ -16,7 +16,6 @@
|
|||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const base = window.location.origin;
|
||||
import { formatUrl } from "./service/requestHandlers";
|
||||
|
||||
type APIResponse<T> = {
|
||||
result: boolean;
|
||||
|
@ -7,12 +7,12 @@ type APIResponse<T> = {
|
|||
};
|
||||
// TODO(mitchellstern): Add JSON schema validation for the responses.
|
||||
const get = async <T>(path: string, params: { [key: string]: any }) => {
|
||||
const url = new URL(path, base);
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
url.searchParams.set(key, value);
|
||||
}
|
||||
const formattedParams = Object.entries(params)
|
||||
.map((pair) => pair.join("="))
|
||||
.join("&");
|
||||
const url = [formatUrl(path), formattedParams].filter((x) => x!!).join("?");
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
const response = await fetch(url);
|
||||
const json: APIResponse<T> = await response.json();
|
||||
|
||||
const { result, msg, data } = json;
|
||||
|
@ -323,10 +323,12 @@ export const checkProfilingStatus = (profilingId: string) =>
|
|||
profiling_id: profilingId,
|
||||
});
|
||||
|
||||
export const getProfilingResultURL = (profilingId: string) =>
|
||||
`${base}/speedscope/index.html#profileURL=${encodeURIComponent(
|
||||
`${base}/api/get_profiling_info?profiling_id=${profilingId}`,
|
||||
)}`;
|
||||
export const getProfilingResultURL = (profilingId: string) => {
|
||||
const uriComponent = encodeURIComponent(
|
||||
formatUrl(`/api/get_profiling_info?profiling_id=${profilingId}`),
|
||||
);
|
||||
return formatUrl(`/speedscope/index.html#profileURL=${uriComponent}`);
|
||||
};
|
||||
|
||||
export const launchKillActor = (
|
||||
actorId: string,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import axios from "axios";
|
||||
import { Actor } from "../type/actor";
|
||||
import { get } from "./requestHandlers";
|
||||
|
||||
export const getActors = () => {
|
||||
return axios.get<{
|
||||
return get<{
|
||||
result: boolean;
|
||||
message: string;
|
||||
data: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import axios from "axios";
|
||||
import { RayConfigRsp } from "../type/config";
|
||||
import { get } from "./requestHandlers";
|
||||
|
||||
export const getRayConfig = () => {
|
||||
return axios.get<RayConfigRsp>("api/ray_config");
|
||||
return get<RayConfigRsp>("api/ray_config");
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import axios from "axios";
|
||||
import { JobDetailRsp, JobListRsp } from "../type/job";
|
||||
import { get } from "./requestHandlers";
|
||||
|
||||
export const getJobList = () => {
|
||||
return axios.get<JobListRsp>("jobs?view=summary");
|
||||
return get<JobListRsp>("jobs?view=summary");
|
||||
};
|
||||
|
||||
export const getJobDetail = (id: string) => {
|
||||
return axios.get<JobDetailRsp>(`jobs/${id}`);
|
||||
return get<JobDetailRsp>(`jobs/${id}`);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import axios from "axios";
|
||||
import { get } from "./requestHandlers";
|
||||
|
||||
export const getLogDetail = async (url: string) => {
|
||||
if (window.location.pathname !== "/" && url !== "log_index") {
|
||||
|
@ -12,7 +12,7 @@ export const getLogDetail = async (url: string) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
const rsp = await axios.get(
|
||||
const rsp = await get(
|
||||
url === "log_index" ? url : `log_proxy?url=${encodeURIComponent(url)}`,
|
||||
);
|
||||
if (rsp.headers["content-type"]?.includes("html")) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import axios from "axios";
|
||||
import { NodeDetailRsp, NodeListRsp } from "../type/node";
|
||||
import { get } from "./requestHandlers";
|
||||
|
||||
export const getNodeList = async () => {
|
||||
return await axios.get<NodeListRsp>("nodes?view=summary");
|
||||
return await get<NodeListRsp>("nodes?view=summary");
|
||||
};
|
||||
|
||||
export const getNodeDetail = async (id: string) => {
|
||||
return await axios.get<NodeDetailRsp>(`nodes/${id}`);
|
||||
return await get<NodeDetailRsp>(`nodes/${id}`);
|
||||
};
|
||||
|
|
34
dashboard/client/src/service/requestHandlers.ts
Normal file
34
dashboard/client/src/service/requestHandlers.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* This utility file formats and sends HTTP requests such that
|
||||
* they fullfill the requirements expected by users of the dashboard.
|
||||
*
|
||||
* All HTTP requests should be sent using the helpers in this file.
|
||||
*
|
||||
* More HTTP Methods helpers should be added to this file when the need
|
||||
* arises.
|
||||
*/
|
||||
|
||||
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
|
||||
/**
|
||||
* This function formats URLs such that the user's browser
|
||||
* sets the HTTP request's Request URL relative to the path at
|
||||
* which the dashboard is served.
|
||||
* This works behind a reverse proxy.
|
||||
*
|
||||
* @param {String} url The URL to be hit
|
||||
* @return {String} The reverse proxy compatible URL
|
||||
*/
|
||||
export const formatUrl = (url: string): string => {
|
||||
if (url.startsWith("/")) {
|
||||
return url.slice(1);
|
||||
}
|
||||
return url;
|
||||
};
|
||||
|
||||
export const get = <T = any, R = AxiosResponse<T>>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<R> => {
|
||||
return axios.get<T, R>(formatUrl(url), config);
|
||||
};
|
|
@ -6,6 +6,6 @@ pillow==7.2.0
|
|||
alabaster>=0.7,<0.8,!=0.7.5
|
||||
commonmark==0.8.1
|
||||
recommonmark==0.5.0
|
||||
sphinx<2
|
||||
sphinx==3.0.4
|
||||
readthedocs-sphinx-ext<1.1
|
||||
sphinx-book-theme
|
||||
|
|
|
@ -169,6 +169,43 @@ More information on how to interpret the flamegraph is available at https://gith
|
|||
.. image:: https://raw.githubusercontent.com/ray-project/images/master/docs/dashboard/dashboard-profiling.png
|
||||
:align: center
|
||||
|
||||
Running Behind a Reverse Proxy
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
In ray 2.0.0, the dashboard works out-of-the-box when accessed via a reverse proxy. API requests no
|
||||
longer need to be proxied individually.
|
||||
|
||||
Always access the dashboard with a trailing ``/`` at the end of the URL.
|
||||
For example, if your proxy is set up to handle requests to ``/ray/dashboard``, view the dashboard at ``www.my-website.com/ray/dashboard/``.
|
||||
|
||||
The dashboard now sends HTTP requests with relative URL paths. Browsers will handle these requests as expected when the ``window.location.href`` ends in a trailing ``/``.
|
||||
|
||||
This is a peculiarity of how many browsers handle requests with relative URLs, despite what `MDN <https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL#examples_of_relative_urls>`_
|
||||
defines as the expected behavior.
|
||||
|
||||
Make your dashboard visible without a trailing ``/`` by including a rule in your reverse proxy that
|
||||
redirects the user's browser to ``/``, i.e. ``/ray/dashboard`` --> ``/ray/dashboard/``.
|
||||
|
||||
Below is an example with a `traefik <https://doc.traefik.io/traefik/getting-started/quick-start/>`_ TOML file that accomplishes this:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
[http]
|
||||
[http.routers]
|
||||
[http.routers.to-dashboard]
|
||||
rule = "PathPrefix(`/ray/dashboard`)"
|
||||
middlewares = ["test-redirectregex", "strip"]
|
||||
service = "dashboard"
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-redirectregex.redirectRegex]
|
||||
regex = "^(.*)/ray/dashboard$"
|
||||
replacement = "${1}/ray/dashboard/"
|
||||
[http.middlewares.strip.stripPrefix]
|
||||
prefixes = ["/ray/dashboard"]
|
||||
[http.services]
|
||||
[http.services.dashboard.loadBalancer]
|
||||
[[http.services.dashboard.loadBalancer.servers]]
|
||||
url = "http://localhost:8265"
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue