diff --git a/.dockerignore b/.dockerignore index 0b2037df512..edd1df83c82 100644 --- a/.dockerignore +++ b/.dockerignore @@ -41,6 +41,9 @@ !docker/test-images/zipkin-ui/start-nginx !zipkin-lens/target/zipkin-lens-*.jar +!docker/test-images/zipkin-uiproxy/nginx.conf +!docker/test-images/zipkin-uiproxy/start-nginx + # Allow on-demand "mvn package". referenced in pom.xml must be added even if not built !zipkin/src/main/** !zipkin-collector/src/main/** diff --git a/.github/workflows/test_readme.yml b/.github/workflows/test_readme.yml index 4ef07381b98..170c5dd38e4 100644 --- a/.github/workflows/test_readme.yml +++ b/.github/workflows/test_readme.yml @@ -117,20 +117,32 @@ jobs: RELEASE_FROM_MAVEN_BUILD: true - name: docker/test-images/zipkin-ui/README.md run: | - build-bin/docker/docker_build openzipkin/zipkin-ui:test && - build-bin/docker/docker_test_image openzipkin/zipkin-ui:test + build-bin/docker/docker_build ${DOCKER_TAG} && + build-bin/docker/docker_test_image ${DOCKER_TAG} env: + DOCKER_TAG: openzipkin/zipkin-ui:test DOCKER_FILE: docker/test-images/zipkin-ui/Dockerfile RELEASE_FROM_MAVEN_BUILD: true + - name: docker/test-images/zipkin-uiproxy/README.md + run: | + build-bin/docker/docker_build ${DOCKER_TAG} && + build-bin/docker/docker_test_image ${DOCKER_TAG} + env: + DOCKER_TAG: openzipkin/zipkin-uiproxy:test + DOCKER_FILE: docker/test-images/zipkin-uiproxy/Dockerfile - name: docker/test-images/zipkin-activemq/README.md run: | - build-bin/docker/docker_build openzipkin/zipkin-activemq:test && - build-bin/docker/docker_test_image openzipkin/zipkin-activemq:test + build-bin/docker/docker_build ${DOCKER_TAG} && + build-bin/docker/docker_test_image ${DOCKER_TAG} + env: + DOCKER_TAG: openzipkin/zipkin-activemq:test + DOCKER_FILE: docker/test-images/zipkin-activemq/Dockerfile - name: docker/test-images/zipkin-cassandra/README.md run: | - build-bin/docker/docker_build openzipkin/zipkin-cassandra:test && - build-bin/docker/docker_test_image openzipkin/zipkin-cassandra:test + build-bin/docker/docker_build ${DOCKER_TAG} && + build-bin/docker/docker_test_image ${DOCKER_TAG} env: + DOCKER_TAG: openzipkin/zipkin-cassandra:test DOCKER_FILE: docker/test-images/zipkin-cassandra/Dockerfile - name: docker/test-images/zipkin-elasticsearch7/README.md run: | diff --git a/build-bin/docker-compose-zipkin-uiproxy.yml b/build-bin/docker-compose-zipkin-uiproxy.yml new file mode 100755 index 00000000000..bfd03180da7 --- /dev/null +++ b/build-bin/docker-compose-zipkin-uiproxy.yml @@ -0,0 +1,25 @@ +# +# Copyright The OpenZipkin Authors +# SPDX-License-Identifier: Apache-2.0 +# + +# uses 2.4 so we can use condition: service_healthy +version: "2.4" + +services: + zipkin: + # Use last build of Zipkin instead of adding a matrix build dependency + image: ghcr.io/openzipkin/zipkin-slim:master + container_name: zipkin + environment: + ZIPKIN_UI_BASEPATH: /admin/zipkin + # Use fixed service and container name 'sut; so our test script can copy/pasta + sut: + # This is the image just built. It is not in a remote repository. + image: openzipkin/zipkin-ui:test + container_name: sut + environment: + ZIPKIN_UI_BASEPATH: /admin/zipkin + depends_on: + zipkin: + condition: service_healthy diff --git a/build-bin/mlc_config.json b/build-bin/mlc_config.json index 138dac3a4bc..c15c8b617e7 100644 --- a/build-bin/mlc_config.json +++ b/build-bin/mlc_config.json @@ -15,6 +15,12 @@ { "pattern": "http://localhost:9411/zipkin" }, + { + "pattern": "http://localhost/admin/zipkin/" + }, + { + "pattern": "http://zipkin:9411" + }, { "pattern": "http://localhost:9411/zipkin?serviceName=backend" }, diff --git a/docker/examples/README.md b/docker/examples/README.md index 292ce546bb8..e441b0ab35f 100644 --- a/docker/examples/README.md +++ b/docker/examples/README.md @@ -150,7 +150,7 @@ Afterward, you can view traces that went through the backend via http://localhos ## UI -The docker-compose configuration can be extended to host the [UI](../test-images/zipkin-ui/README.md) on port 80 +The docker-compose configuration can be extended to [host the UI](../test-images/zipkin-ui/README.md) on port 80 using the `docker-compose-ui.yml` file. That file employs [docker-compose overrides](https://docs.docker.com/compose/extends/#multiple-compose-files) to add an NGINX container and relevant settings. @@ -165,7 +165,6 @@ This container doubles as a skeleton for creating proxy configuration around Zipkin like authentication, dealing with CORS with zipkin-js apps, or terminating SSL. - If you want to run the zipkin-ui standalone against a remote zipkin server, you need to set `ZIPKIN_BASE_URL` accordingly: @@ -175,6 +174,22 @@ $ docker run -d -p 80:80 \ openzipkin/zipkin-ui ``` +## UI Proxy + +The docker-compose configuration can be extended to [proxy the UI](../test-images/zipkin-uiproxy/README.md) on port 80 +using the `docker-compose-uiproxy.yml` file. That file employs +[docker-compose overrides](https://docs.docker.com/compose/extends/#multiple-compose-files) to add an NGINX container and relevant settings. + +To start the NGINX configuration, run: + +```bash +$ docker-compose -f docker-compose.yml -f docker-compose-uiproxy.yml up +``` + +This container helps verify the `ZIPKIN_UI_BASEPATH` variable by setting it to +"/admin/zipkin". This means when the compose configuration is up, you can +access Zipkin UI at http://localhost/admin/zipkin/ + ## Prometheus Zipkin comes with a built-in Prometheus metric exporter. The diff --git a/docker/examples/docker-compose-uiproxy.yml b/docker/examples/docker-compose-uiproxy.yml new file mode 100644 index 00000000000..a01658436f2 --- /dev/null +++ b/docker/examples/docker-compose-uiproxy.yml @@ -0,0 +1,33 @@ +# +# Copyright The OpenZipkin Authors +# SPDX-License-Identifier: Apache-2.0 +# + +# This file uses the version 2 docker-compose file format, described here: +# https://docs.docker.com/compose/compose-file/#version-2 +# +# It extends the default configuration from docker-compose.yml, hosting the +# ui on port 80 using NGINX + +version: '2.4' + +services: + zipkin-uiproxy: + image: ghcr.io/openzipkin/zipkin-uiproxy:${TAG:-latest} + container_name: zipkin-uiproxy + environment: + # This allows hitting the UI on the host by http://localhost/admin/zipkin + - ZIPKIN_UI_BASEPATH=/admin/zipkin + ports: + - 80:80 + depends_on: + zipkin: + condition: service_healthy + + zipkin: + extends: + file: docker-compose.yml + service: zipkin + environment: + # This must match what's set in zipkin-uiproxy + - ZIPKIN_UI_BASEPATH=/admin/zipkin diff --git a/docker/test-images/zipkin-ui/README.md b/docker/test-images/zipkin-ui/README.md index 444cebcd261..fc031ae57b9 100644 --- a/docker/test-images/zipkin-ui/README.md +++ b/docker/test-images/zipkin-ui/README.md @@ -2,6 +2,11 @@ The `zipkin-ui` testing image contains the static parts of the Zipkin UI served directly with NGINX. +Besides norms defined in [docker-alpine](https://github.com/openzipkin/docker-alpine), this accepts the +following environment variables: + +* `ZIPKIN_BASE_URL`: The proxied zipkin base URL. Defaults to http://zipkin:9411 + To build `openzipkin/zipkin-ui:test`, from the top-level of the repository, run: ```bash $ DOCKER_FILE=docker/test-images/zipkin-ui/Dockerfile build-bin/docker/docker_build openzipkin/zipkin-ui:test diff --git a/docker/test-images/zipkin-ui/nginx.conf b/docker/test-images/zipkin-ui/nginx.conf index e6e3bc5173f..7282ccb5c22 100644 --- a/docker/test-images/zipkin-ui/nginx.conf +++ b/docker/test-images/zipkin-ui/nginx.conf @@ -67,18 +67,21 @@ http { expires 1s; access_log off; error_log off; + # start-nginx overrides the base url to $ZIPKIN_UI_BASEPATH proxy_pass http://localhost:9411; } # accept UI config from the server location /zipkin/config.json { expires 10m; + # start-nginx overrides the base url to $ZIPKIN_BASE_URL proxy_pass http://localhost:9411; } # the UI looks for the api under the same relative path location /zipkin/api { expires off; + # start-nginx overrides the base url to $ZIPKIN_BASE_URL proxy_pass http://localhost:9411; } diff --git a/docker/test-images/zipkin-ui/start-nginx b/docker/test-images/zipkin-ui/start-nginx index 1d63f31840f..aba0807a47c 100755 --- a/docker/test-images/zipkin-ui/start-nginx +++ b/docker/test-images/zipkin-ui/start-nginx @@ -7,4 +7,5 @@ sed "s~proxy_pass http://localhost:9411~proxy_pass ${ZIPKIN_BASE_URL}~g" \ /etc/nginx/conf.d/zipkin.conf.template > /etc/nginx/nginx.conf +echo Starting NGINX exec nginx "$@" diff --git a/docker/test-images/zipkin-uiproxy/Dockerfile b/docker/test-images/zipkin-uiproxy/Dockerfile new file mode 100644 index 00000000000..9698f4032c8 --- /dev/null +++ b/docker/test-images/zipkin-uiproxy/Dockerfile @@ -0,0 +1,51 @@ +# +# Copyright The OpenZipkin Authors +# SPDX-License-Identifier: Apache-2.0 +# + +# Use latest version here: https://github.com/orgs/openzipkin/packages/container/package/alpine +# This is defined in many places because Docker has no "env" script functionality unless you use +# docker-compose: When updating, update everywhere. +ARG alpine_version=3.19.1 + +# java_version is used during the installation process to build or download the zipkin-lens jar. +# +# Use latest version here: https://github.com/orgs/openzipkin/packages/container/package/java +# This is defined in many places because Docker has no "env" script functionality unless you use +# docker-compose: When updating, update everywhere. +ARG java_version=21.0.2_p13 + +# We copy files from the context into a scratch container first to avoid a problem where docker and +# docker-compose don't share layer hashes https://github.com/docker/compose/issues/883 normally. +# COPY --from= works around the issue. +FROM scratch as scratch + +COPY build-bin/ /build-bin/ +COPY build-bin/docker/docker-healthcheck /docker-bin/ +COPY docker/test-images/zipkin-uiproxy/start-nginx /docker-bin/ +COPY docker/test-images/zipkin-uiproxy/nginx.conf /conf.d/zipkin.conf.template + +FROM ghcr.io/openzipkin/alpine:$alpine_version as zipkin-uiproxy +LABEL org.opencontainers.image.description="NGINX on Alpine Linux proxying the Zipkin UI with proxy_pass" +# Use latest from https://pkgs.alpinelinux.org/packages?name=nginx +ARG nginx_version=1.24.0 +LABEL nginx-version=$nginx_version + +ENV ZIPKIN_UI_BASEPATH=/zipkin +ENV ZIPKIN_BASE_URL=http://zipkin:9411 + +# Add HEALTHCHECK and ENTRYPOINT scripts into the default search path +COPY --from=scratch /docker-bin/* /usr/local/bin/ +# We use start period of 30s to avoid marking the container unhealthy on slow or contended CI hosts +HEALTHCHECK --interval=1s --start-period=30s --timeout=5s CMD ["docker-healthcheck"] +ENTRYPOINT ["start-nginx"] + +# Setup NGINX +COPY --from=scratch /conf.d/ /etc/nginx/conf.d/ +RUN apk add --update --no-cache nginx=~${nginx_version} && \ + mkdir -p /var/tmp/nginx && chown -R nginx:nginx /var/tmp/nginx + +# Usually, we read env set from pid 1 to get docker-healthcheck parameters. However, NGINX wipes the +# env, so we need to expose it in the Dockerfile instead. +ENV HEALTHCHECK_PORT=80 +EXPOSE 80 diff --git a/docker/test-images/zipkin-uiproxy/README.md b/docker/test-images/zipkin-uiproxy/README.md new file mode 100644 index 00000000000..f62d909a41d --- /dev/null +++ b/docker/test-images/zipkin-uiproxy/README.md @@ -0,0 +1,14 @@ +## zipkin-uiproxy Docker image + +The `zipkin-uiproxy` testing image proxies the Zipkin UI with NGINX. + +Besides norms defined in [docker-alpine](https://github.com/openzipkin/docker-alpine), this accepts the +following environment variables: + +* `ZIPKIN_UI_BASEPATH`: The path this proxy serves the UI under. Defaults to /zipkin +* `ZIPKIN_BASE_URL`: The proxied zipkin base URL. Defaults to http://zipkin:9411 + +To build `openzipkin/zipkin-ui:test`, from the top-level of the repository, run: +```bash +$ DOCKER_FILE=docker/test-images/zipkin-uiproxy/Dockerfile build-bin/docker/docker_build openzipkin/zipkin-uiproxy:test +``` diff --git a/docker/test-images/zipkin-uiproxy/nginx.conf b/docker/test-images/zipkin-uiproxy/nginx.conf new file mode 100644 index 00000000000..40164e685fb --- /dev/null +++ b/docker/test-images/zipkin-uiproxy/nginx.conf @@ -0,0 +1,71 @@ +user nginx nginx; +worker_processes 2; + +error_log /dev/stdout warn; +pid /var/run/nginx.pid; + +daemon off; + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /dev/stdout main; + + sendfile on; + + keepalive_timeout 65; + + gzip on; + gzip_types application/javascript application/json text/css; + + server_tokens off; + + server { + listen 80; + + root /var/www/html; + + index index.html; + + # Make site accessible from http://set-ip-address.xip.io + server_name localhost; + + charset utf-8; + + # start-nginx overrides /zipkin to $ZIPKIN_UI_BASEPATH + location /zipkin { + # start-nginx overrides the base url to $ZIPKIN_BASE_URL + proxy_pass http://localhost:9411/zipkin; + proxy_redirect default; + } + + # Pass through health check to the proxy for our docker HEALTHCHECK + location /health { + expires 1s; + access_log off; + error_log off; + # start-nginx overrides the base url to $ZIPKIN_BASE_URL + proxy_pass http://localhost:9411; + } + + + location = /favicon.ico { log_not_found off; access_log off; } + location = /robots.txt { access_log off; log_not_found off; } + + # Deny .htaccess file access + location ~ /\.ht { + deny all; + } + + } +} diff --git a/docker/test-images/zipkin-uiproxy/start-nginx b/docker/test-images/zipkin-uiproxy/start-nginx new file mode 100755 index 00000000000..1025bb33d9a --- /dev/null +++ b/docker/test-images/zipkin-uiproxy/start-nginx @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Copyright The OpenZipkin Authors +# SPDX-License-Identifier: Apache-2.0 +# + +sed -e "s~proxy_pass http://localhost:9411~proxy_pass ${ZIPKIN_BASE_URL}~g" \ + -e "s~location /zipkin~location ${ZIPKIN_UI_BASEPATH}~g" \ + /etc/nginx/conf.d/zipkin.conf.template > /etc/nginx/nginx.conf + +echo Starting NGINX +exec nginx "$@" diff --git a/zipkin-lens/README.md b/zipkin-lens/README.md index 2b39743df1c..846f55db17e 100644 --- a/zipkin-lens/README.md +++ b/zipkin-lens/README.md @@ -92,29 +92,29 @@ Firefox: https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/ ## Running behind a reverse proxy Since version `2.20`, Zipkin Lens supports running under an arbitrary context root. As a result, -it can be proxied under a different path than `/zipkin/` such as `/proxy/foo/myzipkin/`. +it can be proxied under a different path than `/zipkin` such as `/admin/zipkin`. -As an example, here is the configuration for Apache HTTPD acting as a reverse proxy +As an example, here is the configuration for Apache HTTP Server acting as a reverse proxy for a Zipkin instance running on the same host: ``` LoadModule proxy_module lib/httpd/modules/mod_proxy.so LoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so -ProxyPass "/proxy/foo/myzipkin" "http://localhost:9411/zipkin/" -ProxyPassReverse "/proxy/foo/myzipkin" "http://localhost:9411/zipkin/" +ProxyPass "/admin/zipkin" "http://localhost:9411/zipkin" +ProxyPassReverse "/admin/zipkin" "http://localhost:9411/zipkin" ``` For the reverse proxy configuration to work, Zipkin needs to be started with the `zipkin.ui.basepath` parameter pointing to the proxy path: ```bash -java -jar zipkin.jar --zipkin.ui.basepath=/proxy/foo/myzipkin +java -jar zipkin.jar --zipkin.ui.basepath=/admin/zipkin ``` or via docker ```bash -docker run -e ZIPKIN_UI_BASEPATH=/proxy/foo/myzipkin -p 9411:9411 openzipkin/zipkin +docker run -e ZIPKIN_UI_BASEPATH=/admin/zipkin -p 9411:9411 openzipkin/zipkin ``` ## Authentication / Authorization diff --git a/zipkin-lens/index.html b/zipkin-lens/index.html index 194d9e1599b..74d27522bd7 100644 --- a/zipkin-lens/index.html +++ b/zipkin-lens/index.html @@ -7,7 +7,7 @@ - + Zipkin diff --git a/zipkin-lens/src/img/zipkin-logo.png b/zipkin-lens/public/static/media/zipkin-logo.png similarity index 100% rename from zipkin-lens/src/img/zipkin-logo.png rename to zipkin-lens/public/static/media/zipkin-logo.png diff --git a/zipkin-lens/src/components/App/App.tsx b/zipkin-lens/src/components/App/App.tsx index c2333062713..18e975d4529 100644 --- a/zipkin-lens/src/components/App/App.tsx +++ b/zipkin-lens/src/components/App/App.tsx @@ -22,13 +22,12 @@ import { UiConfig, UiConfigConsumer } from '../UiConfig'; import configureStore from '../../store/configure-store'; import { theme } from '../../constants/color'; import AlertSnackbar from './AlertSnackbar'; +import { BASE_PATH } from '../../constants/api'; const App: React.FC = () => { useTitle('Zipkin'); const baseName = useMemo(() => { - return import.meta.env.DEV - ? '/zipkin' - : (import.meta.env.BASE_URL as string); + return import.meta.env.DEV ? '/zipkin' : BASE_PATH; }, []); return ( diff --git a/zipkin-lens/src/components/App/Layout.tsx b/zipkin-lens/src/components/App/Layout.tsx index 074e90da102..d5a072a6588 100644 --- a/zipkin-lens/src/components/App/Layout.tsx +++ b/zipkin-lens/src/components/App/Layout.tsx @@ -28,7 +28,6 @@ import TraceIdSearch from './TraceIdSearch'; import TraceJsonUploader from './TraceJsonUploader'; import { useUiConfig } from '../UiConfig'; import { darkTheme } from '../../constants/color'; -import logoSrc from '../../img/zipkin-logo.png'; const Layout: React.FC = ({ children }) => { const { t } = useTranslation(); @@ -116,7 +115,7 @@ const Toolbar = styled(MuiToolbar)` `; const Logo = styled.img.attrs({ - src: logoSrc, + src: './static/media/zipkin-logo.png', })` width: 42px; height: 42px; diff --git a/zipkin-lens/vite.config.ts b/zipkin-lens/vite.config.ts index d8b1b994eab..81e350cfbe4 100644 --- a/zipkin-lens/vite.config.ts +++ b/zipkin-lens/vite.config.ts @@ -39,6 +39,8 @@ export default defineConfig(():UserConfig => { outDir: 'build', // use the same path patterns as the original react-scripts lens build assetsDir: "static", + // uncomment to build with sourcemap, for troubleshooting zipkin-server + // sourcemap: true, rollupOptions: { output: { assetFileNames({name}):string { diff --git a/zipkin-server/src/main/java/zipkin2/server/internal/ui/ZipkinUiConfiguration.java b/zipkin-server/src/main/java/zipkin2/server/internal/ui/ZipkinUiConfiguration.java index 35ee4605821..8351ff9e373 100644 --- a/zipkin-server/src/main/java/zipkin2/server/internal/ui/ZipkinUiConfiguration.java +++ b/zipkin-server/src/main/java/zipkin2/server/internal/ui/ZipkinUiConfiguration.java @@ -162,6 +162,8 @@ static String maybeResource(String basePath, Resource resource) throws IOExcepti String content = StreamUtils.copyToString(stream, UTF_8); if (DEFAULT_BASEPATH.equals(basePath)) return content; + if (basePath.equals("/")) basePath = ""; + // relativize any href or src in index.html // TODO: see if vite config can make these relative by default! return content.replace("=\"" + DEFAULT_BASEPATH, "=\".") diff --git a/zipkin-server/src/test/java/zipkin2/server/internal/ui/ITZipkinUiConfiguration.java b/zipkin-server/src/test/java/zipkin2/server/internal/ui/ITZipkinUiConfiguration.java index 0b884ff40fb..d186acdc55f 100644 --- a/zipkin-server/src/test/java/zipkin2/server/internal/ui/ITZipkinUiConfiguration.java +++ b/zipkin-server/src/test/java/zipkin2/server/internal/ui/ITZipkinUiConfiguration.java @@ -31,7 +31,7 @@ properties = { "server.port=0", "spring.config.name=zipkin-server", - "zipkin.ui.base-path=/foozipkin", + "zipkin.ui.base-path=/admin/zipkin", "server.compression.enabled=true", "server.compression.min-response-size=128" }) @@ -115,7 +115,7 @@ class ITZipkinUiConfiguration { - + diff --git a/zipkin-server/src/test/java/zipkin2/server/internal/ui/ZipkinUiConfigurationTest.java b/zipkin-server/src/test/java/zipkin2/server/internal/ui/ZipkinUiConfigurationTest.java index 153db89d01d..708c36a9d79 100644 --- a/zipkin-server/src/test/java/zipkin2/server/internal/ui/ZipkinUiConfigurationTest.java +++ b/zipkin-server/src/test/java/zipkin2/server/internal/ui/ZipkinUiConfigurationTest.java @@ -142,13 +142,13 @@ class ZipkinUiConfigurationTest { } @Test void canOverrideProperty_basePath() { - context = createContextWithOverridenProperty("zipkin.ui.basepath:/foo/bar"); + context = createContextWithOverridenProperty("zipkin.ui.basepath:/admin/zipkin"); assertThat(serveIndex().contentUtf8()).isEqualTo(""" - + diff --git a/zipkin-server/src/test/resources/zipkin-lens/index.html b/zipkin-server/src/test/resources/zipkin-lens/index.html index 65a1ed127a4..ff8236f45f4 100644 --- a/zipkin-server/src/test/resources/zipkin-lens/index.html +++ b/zipkin-server/src/test/resources/zipkin-lens/index.html @@ -1,7 +1,7 @@ - +