Files
OpenShell/deploy/docker/Dockerfile.gateway
T
Piotr Mlocek 18fb7af4e2 perf(docker): move version ARG below cached layers to fix cache invalidation (#385)
The OPENSHELL_CARGO_VERSION build arg contains a git commit hash that
changes on every build (e.g. 0.0.7-dev.11+g085b131ae). Declaring this
ARG near the top of each Dockerfile invalidated every layer below it --
including expensive dependency installs, toolchain setup, and the Rust
dependency pre-build step -- on every single commit.

Move the ARG declaration to just before the RUN that actually uses it so
upstream layers stay cached. This recovers ~5-6 minutes per build on the
gateway image (from ~9m back toward ~2-3m) and similarly improves cluster,
CLI, and Python wheel builds.

Also removes unused OPENSHELL_IMAGE_TAG ARG from cli-macos, python-wheels,
and python-wheels-macos Dockerfiles.
2026-03-16 18:56:30 -07:00

108 lines
5.0 KiB
Docker

# syntax=docker/dockerfile:1.4
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# OpenShell Gateway Docker image
# Multi-stage build with cross-compilation support for multi-arch
# Stage 1: Rust builder (runs on build platform, cross-compiles for target)
FROM --platform=$BUILDPLATFORM rust:1.88-slim AS builder
ARG TARGETARCH
ARG BUILDARCH
ARG CARGO_TARGET_CACHE_SCOPE=default
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
cmake g++ make protobuf-compiler curl && rm -rf /var/lib/apt/lists/*
# Install cross-compilation toolchain, sccache, + Rust target (no-ops for native builds)
COPY deploy/docker/cross-build.sh /usr/local/bin/
RUN . cross-build.sh && install_cross_toolchain && install_sccache && add_rust_target
ARG SCCACHE_MEMCACHED_ENDPOINT
WORKDIR /build
# Copy dependency manifests first for better caching
COPY Cargo.toml Cargo.lock ./
COPY crates/openshell-cli/Cargo.toml crates/openshell-cli/Cargo.toml
COPY crates/openshell-core/Cargo.toml crates/openshell-core/Cargo.toml
COPY crates/openshell-providers/Cargo.toml crates/openshell-providers/Cargo.toml
COPY crates/openshell-router/Cargo.toml crates/openshell-router/Cargo.toml
COPY crates/openshell-sandbox/Cargo.toml crates/openshell-sandbox/Cargo.toml
COPY crates/openshell-server/Cargo.toml crates/openshell-server/Cargo.toml
COPY crates/openshell-bootstrap/Cargo.toml crates/openshell-bootstrap/Cargo.toml
# Create dummy source files to build dependencies
RUN mkdir -p crates/openshell-cli/src crates/openshell-core/src crates/openshell-providers/src crates/openshell-router/src crates/openshell-sandbox/src crates/openshell-server/src crates/openshell-bootstrap/src && \
echo "fn main() {}" > crates/openshell-cli/src/main.rs && \
echo "fn main() {}" > crates/openshell-sandbox/src/main.rs && \
echo "fn main() {}" > crates/openshell-server/src/main.rs && \
touch crates/openshell-core/src/lib.rs && \
touch crates/openshell-providers/src/lib.rs && \
touch crates/openshell-router/src/lib.rs && \
touch crates/openshell-bootstrap/src/lib.rs
# Copy proto files needed for build
COPY proto/ proto/
# Build dependencies only (cached unless Cargo.toml/lock changes).
# sccache uses memcached in CI (SCCACHE_MEMCACHED_ENDPOINT) or the local
# disk cache mount for local dev builds. The cargo-target mount gives cargo
# a persistent target/ dir for true incremental rebuilds on source changes.
RUN --mount=type=cache,id=cargo-registry-gateway-${TARGETARCH},sharing=locked,target=/usr/local/cargo/registry \
--mount=type=cache,id=cargo-target-gateway-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-gateway-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && cargo_cross_build --release -p openshell-server 2>/dev/null || true
# Copy actual source code
COPY crates/ crates/
# Touch source files to ensure they're rebuilt (not the cached dummy).
# Touch build.rs and proto files to force proto code regeneration when the
# cargo target cache mount retains stale OUT_DIR artifacts from prior builds.
RUN touch crates/openshell-server/src/main.rs \
crates/openshell-core/build.rs \
proto/*.proto
# Build the actual application
# Declare version ARG here (not earlier) so the git-hash-bearing value does not
# invalidate the expensive dependency-build layers above on every commit.
ARG OPENSHELL_CARGO_VERSION
RUN --mount=type=cache,id=cargo-registry-gateway-${TARGETARCH},sharing=locked,target=/usr/local/cargo/registry \
--mount=type=cache,id=cargo-target-gateway-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-gateway-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && \
if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \
sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \
fi && \
cargo_cross_build --release -p openshell-server && \
cp "$(cross_output_dir release)/openshell-server" /build/openshell-server
# Stage 2: Runtime (uses target platform)
# NVIDIA hardened Ubuntu base for supply chain consistency.
FROM nvcr.io/nvidia/base/ubuntu:noble-20251013 AS runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates && rm -rf /var/lib/apt/lists/*
RUN useradd --create-home --user-group openshell
WORKDIR /app
COPY --from=builder /build/openshell-server /usr/local/bin/
# Copy migrations to the build-time manifest directory expected by sqlx
RUN mkdir -p /build/crates/openshell-server
COPY crates/openshell-server/migrations /build/crates/openshell-server/migrations
USER openshell
EXPOSE 8080
# Health checks are handled by Kubernetes liveness/readiness probes (tcpSocket).
# No Docker HEALTHCHECK is needed since this image runs inside a k3s cluster.
ENTRYPOINT ["openshell-server"]
CMD ["--port", "8080"]