Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/build-and-push-ui-images-standalone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: ./clients/ui
file: ./clients/ui/Dockerfile.standalone
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Expand Down
2 changes: 1 addition & 1 deletion clients/ui/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ COPY ${UI_SOURCE_CODE} /usr/src/app
# Install the dependencies and build
RUN npm cache clean --force
RUN npm ci --omit=optional
RUN npm run build:prod
RUN DEPLOYMENT_MODE=${DEPLOYMENT_MODE} STYLE_THEME=${STYLE_THEME} npm run build:prod

# BFF build stage
FROM ${GOLANG_BASE_IMAGE} AS bff-builder
Expand Down
68 changes: 68 additions & 0 deletions clients/ui/Dockerfile.standalone
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Source code for the repos
ARG UI_SOURCE_CODE=./frontend
ARG BFF_SOURCE_CODE=./bff

# Set the base images for the build stages
ARG NODE_BASE_IMAGE=node:22
ARG GOLANG_BASE_IMAGE=golang:1.24.6
ARG DISTROLESS_BASE_IMAGE=gcr.io/distroless/static:nonroot

# UI build stage
FROM ${NODE_BASE_IMAGE} AS ui-builder

ARG UI_SOURCE_CODE
ARG DEPLOYMENT_MODE
ARG STYLE_THEME

WORKDIR /usr/src/app

# Copy the source code to the container
COPY ${UI_SOURCE_CODE} /usr/src/app

# Install the dependencies and build
RUN npm cache clean --force
RUN npm ci --omit=optional
RUN DEPLOYMENT_MODE=${DEPLOYMENT_MODE} STYLE_THEME=${STYLE_THEME} npm run build:prod

# BFF build stage
FROM ${GOLANG_BASE_IMAGE} AS bff-builder

ARG BFF_SOURCE_CODE

ARG TARGETOS
ARG TARGETARCH

WORKDIR /usr/src/app

# Copy the Go Modules manifests
COPY ${BFF_SOURCE_CODE}/go.mod ${BFF_SOURCE_CODE}/go.sum ./

# Download dependencies
RUN go mod download

# Copy the go source files
COPY ${BFF_SOURCE_CODE}/cmd/ cmd/
COPY ${BFF_SOURCE_CODE}/internal/ internal/

# Build the Go application
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o bff ./cmd

# Install setup-envtest and download K8s binaries for mock mode
RUN go install sigs.k8s.io/controller-runtime/tools/setup-envtest@release-0.17
RUN setup-envtest use 1.29.0 --bin-dir /envtest-bin -p path

# Final stage
# Use distroless as minimal base image to package the application binary
FROM ${DISTROLESS_BASE_IMAGE}
WORKDIR /
COPY --from=bff-builder /usr/src/app/bff ./
COPY --from=ui-builder /usr/src/app/dist ./static/
# Copy envtest binaries for mock K8s mode
COPY --from=bff-builder /envtest-bin /envtest-bin
ENV ENVTEST_ASSETS_DIR=/envtest-bin
USER 65532:65532

# Expose port 8080
EXPOSE 8080

ENTRYPOINT ["/bff"]
4 changes: 2 additions & 2 deletions clients/ui/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ docker-build:

.PHONY: docker-build-standalone
docker-build-standalone:
$(CONTAINER_TOOL) build --build-arg DEPLOYMENT_MODE=standalone --build-arg STYLE_THEME=mui-theme -t ${IMG_UI_STANDALONE} .
$(CONTAINER_TOOL) build -f Dockerfile.standalone --build-arg DEPLOYMENT_MODE=standalone --build-arg STYLE_THEME=mui-theme -t ${IMG_UI_STANDALONE} .

.PHONY: docker-build-federated
docker-build-federated:
Expand All @@ -92,7 +92,7 @@ docker-buildx:

.PHONY: docker-buildx-standalone
docker-buildx-standalone:
docker buildx build --build-arg DEPLOYMENT_MODE=standalone --build-arg STYLE_THEME=mui-theme --platform ${PLATFORM} -t ${IMG_UI_STANDALONE} --push .
docker buildx build -f Dockerfile.standalone --build-arg DEPLOYMENT_MODE=standalone --build-arg STYLE_THEME=mui-theme --platform ${PLATFORM} -t ${IMG_UI_STANDALONE} --push .

.PHONY: docker-buildx-federated
docker-buildx-federated:
Expand Down
9 changes: 9 additions & 0 deletions clients/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ The following Makefile targets are used to build and push the Docker images the
* Command: `make docker-push-standalone`
* This command uses the `CONTAINER_TOOL` and `IMG_UI_STANDALONE` environment variables to push the image.

### Dockerfile Structure

The UI has two Dockerfiles:

* **`Dockerfile`**: Production image for Kubeflow and federated deployments. Minimal image without test dependencies.
* **`Dockerfile.standalone`**: Standalone mode image. Used for local development and docker compose.

The standalone image includes K8s test binaries to support the `--mock-k8s-client=true` flag at runtime.

## Deployments

For more information on how to deploy the Model Registry UI, please refer to the [Model registry UI] documentation.
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,27 @@ type TestEnvInput struct {
}

func SetupEnvTest(input TestEnvInput) (*envtest.Environment, kubernetes.Interface, error) {
projectRoot, err := getProjectRoot()
if err != nil {
input.Logger.Error("failed to find project root", slog.String("error", err.Error()))
input.Cancel()
os.Exit(1)
var binaryAssetsDir string

// Check for explicit envtest assets directory (used in Docker)
if envDir := os.Getenv("ENVTEST_ASSETS_DIR"); envDir != "" {
// Construct full path with OS/ARCH suffix
binaryAssetsDir = filepath.Join(envDir, "k8s",
fmt.Sprintf("1.29.0-%s-%s", runtime.GOOS, runtime.GOARCH))
} else {
// Fall back to project root detection (local development)
projectRoot, err := getProjectRoot()
if err != nil {
input.Logger.Error("failed to find project root", slog.String("error", err.Error()))
input.Cancel()
os.Exit(1)
}
binaryAssetsDir = filepath.Join(projectRoot, "bin", "k8s",
fmt.Sprintf("1.29.0-%s-%s", runtime.GOOS, runtime.GOARCH))
}

testEnv := &envtest.Environment{
BinaryAssetsDirectory: filepath.Join(projectRoot, "bin", "k8s", fmt.Sprintf("1.29.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
BinaryAssetsDirectory: binaryAssetsDir,
}

cfg, err := testEnv.Start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,15 @@ func (f *MockedStaticClientFactory) GetClient(_ context.Context) (k8s.Kubernetes
}

func (f *MockedStaticClientFactory) ExtractRequestIdentity(httpHeader http.Header) (*k8s.RequestIdentity, error) {
return f.realFactoryWithoutClient.ExtractRequestIdentity(httpHeader)
identity, err := f.realFactoryWithoutClient.ExtractRequestIdentity(httpHeader)
if err != nil {
// In mock mode, use default test user when header is missing
return &k8s.RequestIdentity{
UserID: DefaultTestUsers[0].UserName,
Groups: DefaultTestUsers[0].Groups,
}, nil
}
return identity, nil
}
func (f *MockedStaticClientFactory) ValidateRequestIdentity(identity *k8s.RequestIdentity) error {
return f.realFactoryWithoutClient.ValidateRequestIdentity(identity)
Expand Down
28 changes: 28 additions & 0 deletions docker-compose-local.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,40 @@
# - a MySQL database,
# - a model-registry server built from source connected to the MySQL database.
# - a model-catalog server built from source.
# - a model-registry-ui built from source (standalone mode)
# or
# - a PostgreSQL database,
# - a model-registry server built from source connected to the PostgreSQL database.
# - a model-catalog server built from source.
# - a model-registry-ui built from source (standalone mode)
version: '3.8'
services:
model-registry-ui:
build:
context: ./clients/ui
dockerfile: Dockerfile.standalone
args:
DEPLOYMENT_MODE: standalone
STYLE_THEME: mui-theme
command:
- --port=9000
- --mock-k8s-client=true
- --mock-mr-client=false
- --mock-mr-catalog-client=false
- --dev-mode=true
- --deployment-mode=standalone
- --log-level=DEBUG
container_name: model-registry-ui
ports:
- "9000:9000"
depends_on:
- model-registry
- model-catalog
profiles:
- postgres
extra_hosts:
- "localhost:host-gateway"

model-catalog:
build:
context: .
Expand Down
24 changes: 24 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,36 @@
# - a MySQL database,
# - a model-registry server connected to the MySQL database.
# - a model-catalog server
# - a model-registry-ui (standalone mode)
# or
# - a PostgreSQL database,
# - a model-registry server connected to the PostgreSQL database.
# - a model-catalog server
# - a model-registry-ui (standalone mode)
version: '3.8'
services:
model-registry-ui:
image: ghcr.io/kubeflow/model-registry/ui-standalone:latest
pull_policy: always
command:
- --port=9000
- --mock-k8s-client=true
- --mock-mr-client=false
- --mock-mr-catalog-client=false
- --dev-mode=true
- --deployment-mode=standalone
- --log-level=DEBUG
container_name: model-registry-ui
ports:
- "9000:9000"
depends_on:
- model-registry
- model-catalog
profiles:
- postgres
extra_hosts:
- "localhost:host-gateway"

model-catalog:
image: ghcr.io/kubeflow/model-registry/server:latest
pull_policy: always
Expand Down
Loading