Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Allow superset to be deployed under a prefixed URL #30134

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e3006c0
Use Flask.url_for when constructing redirect/resource paths.
martyngigg Sep 3, 2024
d007709
Introduce a BASE_PATH variable to the frontend.
martyngigg Sep 3, 2024
6270846
Add ASSET_BASE_URL and BASE_PATH arguments to Docker build.
martyngigg Sep 3, 2024
10d6e51
Add documentation on running Superset on a prefix path
martyngigg Sep 25, 2024
d2d729b
Fix SliceAdder create new chart link
martyngigg Oct 15, 2024
7a10e26
Fix resource export api link
martyngigg Oct 28, 2024
109fe29
Remove creating a test request context that hides the real request co…
martyngigg Jan 6, 2025
b5f52d4
Configure WEBDRIVER_BASEURL to go through the reverse proxy
martyngigg Jan 6, 2025
453fcee
Set thumbnail cache to redis cache for docker setup
martyngigg Jan 6, 2025
b7a91bd
Fix non-frontend route links in RightMenu when base prefix present.
martyngigg Jan 6, 2025
fa104bf
Use environment variables for base path in docker compose
martyngigg Jan 6, 2025
ece9189
Introduce an app_root variable to superset:create_app.
martyngigg Jan 16, 2025
422d12b
Pass application_root and static_assets_prefix through to frontend.
martyngigg Jan 17, 2025
4dfc11b
Set a default SUPERSET_APP_ROOT if not defined.
martyngigg Jan 20, 2025
d7adf61
Remove BASE_PATH from Dockerfile and docker-compose
martyngigg Jan 20, 2025
3617317
Set the default app_root to "/" to match Flask APPLICATION_ROOT
martyngigg Jan 20, 2025
c676994
Remove BASE_PATH from frontend.
martyngigg Jan 20, 2025
1ed1efe
Fix Dockerfile health check.
martyngigg Jan 20, 2025
8848824
Rewrite documentation on deploying Superset on a non-root prefix.
martyngigg Jan 20, 2025
00edad2
Fix mypy errors in app.py
martyngigg Jan 20, 2025
392e0fd
Fix backend formatting.
martyngigg Jan 20, 2025
e0eb2ed
Hit frontend code with prettier
martyngigg Jan 20, 2025
0774c7a
Add Apache license to Nginx template file.
martyngigg Jan 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ WORKDIR /app/superset-frontend

# Create necessary folders to avoid errors in subsequent steps
RUN mkdir -p /app/superset/static/assets \
/app/superset/translations
/app/superset/translations

# Mount package files and install dependencies if not in dev mode
# NOTE: we mount packages and plugins as they are referenced in package.json as workspaces
Expand All @@ -62,9 +62,9 @@ RUN --mount=type=bind,source=./superset-frontend/package.json,target=./package.j
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/root/.npm \
if [ "$DEV_MODE" = "false" ]; then \
npm ci; \
npm ci; \
else \
echo "Skipping 'npm ci' in dev mode"; \
echo "Skipping 'npm ci' in dev mode"; \
fi

# Runs the webpack build process
Expand All @@ -79,18 +79,18 @@ FROM superset-node-ci AS superset-node
RUN --mount=type=cache,target=/app/superset-frontend/.temp_cache \
--mount=type=cache,target=/root/.npm \
if [ "$DEV_MODE" = "false" ]; then \
echo "Running 'npm run ${BUILD_CMD}'"; \
npm run ${BUILD_CMD}; \
echo "Running 'npm run ${BUILD_CMD}'"; \
npm run ${BUILD_CMD}; \
else \
echo "Skipping 'npm run ${BUILD_CMD}' in dev mode"; \
echo "Skipping 'npm run ${BUILD_CMD}' in dev mode"; \
fi;

# Copy translation files
COPY superset/translations /app/superset/translations

# Build the frontend if not in dev mode
RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
npm run build-translation; \
npm run build-translation; \
fi; \
rm -rf /app/superset/translations/*/*/*.po; \
rm -rf /app/superset/translations/*/*/*.mo;
Expand Down Expand Up @@ -130,12 +130,12 @@ ARG INCLUDE_CHROMIUM="true"
ARG INCLUDE_FIREFOX="false"
RUN --mount=type=cache,target=/root/.cache/uv\
if [ "$INCLUDE_CHROMIUM" = "true" ] || [ "$INCLUDE_FIREFOX" = "true" ]; then \
uv pip install playwright && \
playwright install-deps && \
if [ "$INCLUDE_CHROMIUM" = "true" ]; then playwright install chromium; fi && \
if [ "$INCLUDE_FIREFOX" = "true" ]; then playwright install firefox; fi; \
uv pip install playwright && \
playwright install-deps && \
if [ "$INCLUDE_CHROMIUM" = "true" ]; then playwright install chromium; fi && \
if [ "$INCLUDE_FIREFOX" = "true" ]; then playwright install firefox; fi; \
else \
echo "Skipping browser installation"; \
echo "Skipping browser installation"; \
fi

######################################################################
Expand All @@ -150,7 +150,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \

COPY superset/translations/ /app/translations_mo/
RUN if [ "$BUILD_TRANSLATIONS" = "true" ]; then \
pybabel compile -d /app/translations_mo | true; \
pybabel compile -d /app/translations_mo | true; \
fi; \
rm -f /app/translations_mo/*/*/*.po; \
rm -f /app/translations_mo/*/*/*.json;
Expand All @@ -165,13 +165,13 @@ COPY --chmod=755 docker/entrypoints /app/docker/entrypoints
WORKDIR /app
# Set up necessary directories and user
RUN mkdir -p \
${SUPERSET_HOME} \
${PYTHONPATH} \
superset/static \
requirements \
superset-frontend \
apache_superset.egg-info \
requirements \
${SUPERSET_HOME} \
${PYTHONPATH} \
superset/static \
requirements \
superset-frontend \
apache_superset.egg-info \
requirements \
&& touch superset/static/version_info.json

# Copy required files for Python build
Expand All @@ -184,12 +184,12 @@ COPY --chmod=755 ./docker/entrypoints/run-server.sh /usr/bin/

# Some debian libs
RUN /app/docker/apt-install.sh \
curl \
libsasl2-dev \
libsasl2-modules-gssapi-mit \
libpq-dev \
libecpg-dev \
libldap2-dev
curl \
libsasl2-dev \
libsasl2-modules-gssapi-mit \
libpq-dev \
libecpg-dev \
libldap2-dev

# Copy compiled things from previous stages
COPY --from=superset-node /app/superset/static/assets superset/static/assets
Expand All @@ -203,7 +203,7 @@ RUN rm superset/translations/*/*/*.po
COPY --from=superset-node /app/superset/translations superset/translations
COPY --from=python-translation-compiler /app/translations_mo superset/translations

HEALTHCHECK CMD curl -f "http://localhost:${SUPERSET_PORT}/health"
HEALTHCHECK CMD curl -f "http://localhost:${SUPERSET_PORT}${SUPERSET_APP_ROOT}/health"
CMD ["/app/docker/entrypoints/run-server.sh"]
EXPOSE ${SUPERSET_PORT}

Expand Down
8 changes: 7 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ x-superset-volumes: &superset-volumes
- ./superset-frontend:/app/superset-frontend
- superset_home:/app/superset_home
- ./tests:/app/tests

x-common-build: &common-build
context: .
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
Expand All @@ -43,6 +42,11 @@ x-common-build: &common-build

services:
nginx:
env_file:
- path: docker/.env # default
required: true
- path: docker/.env-local # optional override
required: false
image: nginx:latest
container_name: superset_nginx
restart: unless-stopped
Expand All @@ -52,6 +56,8 @@ services:
- "host.docker.internal:host-gateway"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./docker/nginx/templates:/etc/nginx/templates:ro

redis:
image: redis:7
container_name: superset_cache
Expand Down
4 changes: 3 additions & 1 deletion docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ REDIS_HOST=redis
REDIS_PORT=6379

FLASK_DEBUG=true
SUPERSET_APP_ROOT="/myprefix"
FLASK_APP="superset.app:create_app(app_root=\"${SUPERSET_APP_ROOT:-/}\")"
SUPERSET_ENV=development
SUPERSET_LOAD_EXAMPLES=yes
SUPERSET_LOAD_EXAMPLES=no
CYPRESS_CONFIG=false
SUPERSET_PORT=8088
MAPBOX_API_KEY=''
Expand Down
35 changes: 1 addition & 34 deletions docker/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -90,38 +90,5 @@ http {

client_max_body_size 10m;

upstream superset_app {
server host.docker.internal:8088;
keepalive 100;
}

upstream superset_websocket {
server host.docker.internal:8080;
keepalive 100;
}

server {
listen 80 default_server;
server_name _;

location /ws {
proxy_pass http://superset_websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}

location / {
proxy_pass http://superset_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
port_in_redirect off;
proxy_connect_timeout 300;
}
}
include /etc/nginx/conf.d/superset.conf;
}
51 changes: 51 additions & 0 deletions docker/nginx/templates/superset.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

upstream superset_app {
server host.docker.internal:8088;
keepalive 100;
}

upstream superset_websocket {
server host.docker.internal:8080;
keepalive 100;
}

server {
listen 80 default_server;
server_name _;

location /ws {
proxy_pass http://superset_websocket;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}

location ${SUPERSET_APP_ROOT} {
proxy_pass http://superset_app;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
port_in_redirect off;
proxy_connect_timeout 300;
}

}
7 changes: 5 additions & 2 deletions docker/pythonpath_dev/superset_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"CACHE_REDIS_DB": REDIS_RESULTS_DB,
}
DATA_CACHE_CONFIG = CACHE_CONFIG
THUMBNAIL_CACHE_CONFIG = CACHE_CONFIG


class CeleryConfig:
Expand Down Expand Up @@ -100,9 +101,11 @@ class CeleryConfig:

FEATURE_FLAGS = {"ALERT_REPORTS": True}
ALERT_REPORTS_NOTIFICATION_DRY_RUN = True
WEBDRIVER_BASEURL = "http://superset:8088/" # When using docker compose baseurl should be http://superset_app:8088/ # noqa: E501
WEBDRIVER_BASEURL = f"http://superset_app{os.environ.get('SUPERSET_APP_ROOT', '/')}/" # When using docker compose baseurl should be http://superset_nginx{ENV{BASEPATH}}/ # noqa: E501
# The base URL for the email report hyperlinks.
WEBDRIVER_BASEURL_USER_FRIENDLY = WEBDRIVER_BASEURL
WEBDRIVER_BASEURL_USER_FRIENDLY = (
f"http://localhost:8888/{os.environ.get('SUPERSET_APP_ROOT', '/')}/"
)
SQLLAB_CTAS_NO_LIMIT = True

log_level_text = os.getenv("SUPERSET_LOG_LEVEL", "INFO")
Expand Down
31 changes: 31 additions & 0 deletions docs/docs/configuration/configuring-superset.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,37 @@ In case the reverse proxy is used for providing SSL encryption, an explicit defi
RequestHeader set X-Forwarded-Proto "https"
```

## Configuring the application root

Superset supports running the application under a non-root path. The Flask entrypoint
[`create_app()`](https://github.com/apache/superset/blob/master/superset/app.py#L29)
accepts an argument `app_root` that configures the prefix for the application.

To configure a prefix, e.g `/analytics`, pass the `app_root` argument to
`create_app` when calling flask run either through the `FLASK_APP`
environment variable:

```sh
FLASK_APP="superset:create_app(app_root='/analytics')"
```

or as part of the `--app` argument to `flask run`:

```sh
flask --app "superset.app:create_app(app_root='/analytics')"
```


### Docker builds

The [docker compose](/docs/installation/docker-compose-configuring-further) developer
configuration includes an additional environmental variable,
[`SUPERSET_APP_ROOT`](https://github.com/apache/superset/blob/master/docker/.env),
to simplify the process of setting up a non-default root path across the services.

In `docker/.env-local` set `SUPERSET_APP_ROOT` to the desired prefix and then bring the
services up with `docker compose up --detach`.

## Custom OAuth2 Configuration

Superset is built on Flask-AppBuilder (FAB), which supports many providers out of the box
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/configuration/networking-settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,4 @@ of your additional middleware classes.

For example, to use `AUTH_REMOTE_USER` from behind a proxy server like nginx, you have to add a
simple middleware class to add the value of `HTTP_X_PROXY_REMOTE_USER` (or any other custom header
from the proxy) to Gunicorn’s `REMOTE_USER` environment variable:
from the proxy) to Gunicorn’s `REMOTE_USER` environment variable.
Loading
Loading