Skip to content

Commit 678e695

Browse files
committed
Expand deployment docs and streamline README links
1 parent 0e0202c commit 678e695

File tree

2 files changed

+139
-60
lines changed

2 files changed

+139
-60
lines changed

README.md

Lines changed: 12 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -548,67 +548,19 @@ For platform-specific guides, see:
548548
and
549549
[Deploying to Hugging Face Spaces](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-to-hugging-face-spaces).
550550
and
551-
[Deploying to Azure Web Apps](#deploying-to-azure-web-apps).
552-
553-
### Deploying to Azure Web Apps
554-
555-
If your app gets stuck on the dashboard `Loading...` screen on Azure, the most common
556-
issue is that the dashboard HTML is returned, but Dash endpoints such as
557-
`/_dash-layout`, `/_dash-dependencies`, or `/_dash-component-suites/*` are not mounted
558-
on the same Flask server and base path.
559-
560-
Use a single WSGI app (`app = db.flask_server()`) and run it with `gunicorn`.
561-
Also avoid re-building the model/explainer inside a Flask request handler.
562-
563-
**dashboard.py**:
564-
```python
565-
import os
566-
from explainerdashboard import ClassifierExplainer, ExplainerDashboard
567-
568-
# Build/load these once at startup:
569-
# model = ...
570-
# X_test = ...
571-
# y_test = ...
572-
573-
explainer = ClassifierExplainer(model, X_test, y_test)
574-
575-
# If your app is mounted under a subpath, e.g. "/dashboard/",
576-
# set APP_BASE_PATH=/dashboard/ in Azure App Settings.
577-
base_path = os.getenv("APP_BASE_PATH", "/")
578-
if not base_path.startswith("/"):
579-
base_path = "/" + base_path
580-
if not base_path.endswith("/"):
581-
base_path = base_path + "/"
582-
583-
db = ExplainerDashboard(
584-
explainer,
585-
url_base_pathname=base_path,
586-
routes_pathname_prefix=base_path,
587-
requests_pathname_prefix=base_path,
588-
title="Model Explainer",
589-
)
590-
591-
app = db.flask_server()
592-
```
593-
594-
**requirements.txt** should include at least:
595-
```txt
596-
explainerdashboard
597-
gunicorn
598-
```
599-
600-
Set your Azure Web App startup command to:
601-
602-
```bash
603-
gunicorn --bind=0.0.0.0:${PORT:-8000} dashboard:app
604-
```
605-
606-
Troubleshooting checklist:
551+
[Deploying to Azure Web Apps](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-to-azure-web-apps),
552+
[Deploying behind reverse proxies and path prefixes](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-behind-reverse-proxies-and-path-prefixes),
553+
[Deploying to Google Cloud Run](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-to-google-cloud-run),
554+
[Deploying to Kubernetes (Ingress)](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-to-kubernetes-ingress),
555+
[Deploying in Databricks notebooks](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-in-databricks-notebooks),
556+
and
557+
[Deploying in Kaggle notebooks](https://explainerdashboard.readthedocs.io/en/latest/deployment.html#deploying-in-kaggle-notebooks).
607558

608-
1. Open browser dev tools and check network requests to `/_dash-layout` and `/_dash-dependencies`.
609-
2. If those requests return `404`, fix your base path prefixes (or set `APP_BASE_PATH=/`).
610-
3. If initial page load is very slow, make sure model training and data loading are not done per request.
611-
4. Check App Service logs for worker timeouts and increase plan/resources if needed.
559+
For a concise production pattern, expose a single WSGI app and run with `gunicorn`:
560+
`app = db.flask_server()` and `gunicorn --bind=0.0.0.0:${PORT:-8000} dashboard:app`.
561+
If your deployment runs behind a proxy/path prefix and you see `Loading...`, check
562+
the deployment docs above and align `url_base_pathname`, `routes_pathname_prefix`,
563+
and `requests_pathname_prefix`.
612564

613565
It can be helpful to store your `explainer` and dashboard layout to disk, and
614566
then reload, e.g.:

docs/source/deployment.rst

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,133 @@ on ``ExplainerDashboard``.
179179

180180
.. highlight:: python
181181

182+
Deploying behind reverse proxies and path prefixes
183+
==================================================
184+
185+
For any platform behind a reverse proxy or ingress (Azure, Kubernetes, AWS ALB, etc),
186+
the key requirement is that Dash base paths and proxy routing agree.
187+
188+
Use a shared base path (for example ``/dashboard/``) for all three settings::
189+
190+
db = ExplainerDashboard(
191+
explainer,
192+
url_base_pathname="/dashboard/",
193+
routes_pathname_prefix="/dashboard/",
194+
requests_pathname_prefix="/dashboard/",
195+
)
196+
app = db.flask_server()
197+
198+
Quick checks:
199+
200+
1. The page is served from the same base path as Dash API calls.
201+
2. ``/_dash-layout`` and ``/_dash-dependencies`` return ``200`` (not ``404``).
202+
3. ``/_dash-component-suites/*`` assets are reachable.
203+
4. The app binds to ``0.0.0.0:$PORT`` in production.
204+
205+
Deploying to Google Cloud Run
206+
=============================
207+
208+
Cloud Run works well with ``gunicorn`` and a containerized app.
209+
210+
**dashboard.py**::
211+
212+
from explainerdashboard import ExplainerDashboard
213+
214+
db = ExplainerDashboard.from_config("dashboard.yaml")
215+
app = db.flask_server()
216+
217+
**Dockerfile**::
218+
219+
FROM python:3.11-slim
220+
221+
WORKDIR /app
222+
COPY requirements.txt .
223+
RUN pip install --no-cache-dir -r requirements.txt
224+
COPY . .
225+
226+
CMD ["sh", "-c", "gunicorn dashboard:app --bind 0.0.0.0:${PORT}"]
227+
228+
Notes:
229+
230+
1. Keep startup fast (load prebuilt explainer, do not retrain on request).
231+
2. If served under a path prefix, set the three Dash pathname prefixes as above.
232+
233+
Deploying to Kubernetes (Ingress)
234+
=================================
235+
236+
For Kubernetes with ingress path routing, avoid path rewrites unless your Dash prefixes
237+
match the rewritten path exactly.
238+
239+
Example deployment env var:
240+
241+
.. code-block:: yaml
242+
243+
env:
244+
- name: APP_BASE_PATH
245+
value: /dashboard/
246+
247+
Example app setup:
248+
249+
.. highlight:: python
250+
251+
::
252+
253+
import os
254+
from explainerdashboard import ExplainerDashboard
255+
256+
base_path = os.getenv("APP_BASE_PATH", "/")
257+
db = ExplainerDashboard(
258+
explainer,
259+
url_base_pathname=base_path,
260+
routes_pathname_prefix=base_path,
261+
requests_pathname_prefix=base_path,
262+
)
263+
app = db.flask_server()
264+
265+
If you see ``Loading...`` with ``404`` responses on ``/_dash-*``, fix ingress
266+
path rules and Dash prefixes first.
267+
268+
.. highlight:: python
269+
270+
Deploying in Databricks notebooks
271+
=================================
272+
273+
For model exploration in Databricks, run the dashboard in notebook context and make
274+
sure all Dash pathname prefixes match the Databricks proxy path.
275+
276+
Example::
277+
278+
from explainerdashboard import ExplainerDashboard
279+
280+
port = 8050
281+
# Replace this with your workspace-specific Databricks proxy base path:
282+
base_path = f"/driver-proxy/o/<org-id>/<cluster-id>/{port}/"
283+
284+
db = ExplainerDashboard(
285+
explainer,
286+
mode="external",
287+
url_base_pathname=base_path,
288+
routes_pathname_prefix=base_path,
289+
requests_pathname_prefix=base_path,
290+
)
291+
db.run(port=port)
292+
293+
If you get ``Loading...``, check network requests for ``/_dash-layout`` and
294+
``/_dash-dependencies``; ``404`` usually means proxy prefix mismatch.
295+
296+
Deploying in Kaggle notebooks
297+
=============================
298+
299+
Kaggle is best suited for interactive exploration in notebook sessions, not durable
300+
hosting of a long-running web app.
301+
302+
Recommended pattern:
303+
304+
1. Run in notebook mode (``mode="inline"`` or ``mode="external"``).
305+
2. Keep it lightweight for notebook resources (for example lower ``plot_sample``,
306+
disable expensive tabs such as ``shap_interaction``).
307+
3. Share results with ``db.save_html("dashboard.html")`` when you need a portable artifact.
308+
182309
Deploying to Hugging Face Spaces
183310
================================
184311

0 commit comments

Comments
 (0)