perf(build): ultra-fast builds with caching and optimized Dockerfiles
- Add Dockerfile.fast with cargo-chef and symbol stripping - Add Dockerfile.superfast using pre-built base image - Add Dockerfile.base for dependency caching - Update Woodpecker with registry cache (cache_from/cache_to) - Add fast-build.sh for local ultra-fast builds - Add build-base-image.sh for one-time dependency build - Enable BuildKit layer caching in CI
This commit is contained in:
parent
24021213b6
commit
c221173172
6 changed files with 245 additions and 11 deletions
|
|
@ -32,32 +32,26 @@ steps:
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Get changed files from last commit
|
|
||||||
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD || echo "")
|
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD || echo "")
|
||||||
|
|
||||||
# Convert matrix SERVICE to path format
|
|
||||||
SERVICE_PATH=$(echo "${SERVICE}" | tr '_' '-')
|
SERVICE_PATH=$(echo "${SERVICE}" | tr '_' '-')
|
||||||
|
|
||||||
# Check if shared crates changed (triggers all services)
|
|
||||||
SHARED_CHANGED=false
|
SHARED_CHANGED=false
|
||||||
if echo "$CHANGED_FILES" | grep -q "^crates/"; then
|
if echo "$CHANGED_FILES" | grep -q "^crates/"; then
|
||||||
SHARED_CHANGED=true
|
SHARED_CHANGED=true
|
||||||
echo "⚠️ Shared crates changed - will build all services"
|
echo "⚠️ Shared crates changed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if this specific service changed
|
|
||||||
SERVICE_CHANGED=false
|
SERVICE_CHANGED=false
|
||||||
if echo "$CHANGED_FILES" | grep -q "^apps/${SERVICE_PATH}/"; then
|
if echo "$CHANGED_FILES" | grep -q "^apps/${SERVICE_PATH}/"; then
|
||||||
SERVICE_CHANGED=true
|
SERVICE_CHANGED=true
|
||||||
echo "✅ Service ${SERVICE} has code changes"
|
echo "✅ Service ${SERVICE} changed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Exit with code 78 to skip subsequent steps if no changes
|
|
||||||
if [ "$SHARED_CHANGED" = "true" ] || [ "$SERVICE_CHANGED" = "true" ]; then
|
if [ "$SHARED_CHANGED" = "true" ] || [ "$SERVICE_CHANGED" = "true" ]; then
|
||||||
echo "🚀 Will build ${SERVICE}"
|
echo "🚀 Building ${SERVICE}"
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
echo "⏭️ Skipping ${SERVICE} - no changes detected"
|
echo "⏭️ Skipping ${SERVICE}"
|
||||||
exit 78
|
exit 78
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -67,7 +61,7 @@ steps:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
repo: ghcr.io/traceworks2023/nxtgauge-rust-${SERVICE}
|
repo: ghcr.io/traceworks2023/nxtgauge-rust-${SERVICE}
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.optimized
|
dockerfile: Dockerfile.fast
|
||||||
build_args:
|
build_args:
|
||||||
- SERVICE_NAME=${SERVICE}
|
- SERVICE_NAME=${SERVICE}
|
||||||
tags:
|
tags:
|
||||||
|
|
@ -79,3 +73,6 @@ steps:
|
||||||
password:
|
password:
|
||||||
from_secret: GHCR_TOKEN
|
from_secret: GHCR_TOKEN
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
|
# ENABLE CACHE - This is the key!
|
||||||
|
cache_from: ghcr.io/traceworks2023/nxtgauge-rust-${SERVICE}:cache
|
||||||
|
cache_to: ghcr.io/traceworks2023/nxtgauge-rust-${SERVICE}:cache
|
||||||
|
|
|
||||||
24
Dockerfile.base
Normal file
24
Dockerfile.base
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Base image with all dependencies pre-compiled
|
||||||
|
# Build once, use for all services
|
||||||
|
|
||||||
|
FROM rust:alpine AS chef
|
||||||
|
RUN apk add --no-cache musl-dev pkgconfig openssl-dev && \
|
||||||
|
rustup target add x86_64-unknown-linux-musl && \
|
||||||
|
cargo install cargo-chef
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy all manifests
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates/ ./crates/
|
||||||
|
COPY apps/ ./apps/
|
||||||
|
|
||||||
|
# Prepare and cook all dependencies
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json && \
|
||||||
|
cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
|
||||||
|
|
||||||
|
# Keep this layer as the base
|
||||||
|
FROM chef
|
||||||
|
|
||||||
|
# The target directory now has all dependencies compiled!
|
||||||
|
# Services can copy just their source and build instantly
|
||||||
59
Dockerfile.fast
Normal file
59
Dockerfile.fast
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Multi-stage optimized Dockerfile with caching and minimal size
|
||||||
|
# Build: docker build --build-arg SERVICE_NAME=users -f Dockerfile.fast .
|
||||||
|
|
||||||
|
# Stage 1: Chef - Prepare dependency recipe
|
||||||
|
FROM rust:alpine AS chef
|
||||||
|
RUN apk add --no-cache musl-dev pkgconfig openssl-dev && \
|
||||||
|
rustup target add x86_64-unknown-linux-musl && \
|
||||||
|
cargo install cargo-chef
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Stage 2: Planner - Analyze dependencies
|
||||||
|
FROM chef AS planner
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates/ ./crates/
|
||||||
|
COPY apps/ ./apps/
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
# Stage 3: Builder - Compile with caching
|
||||||
|
FROM chef AS builder
|
||||||
|
ARG SERVICE_NAME
|
||||||
|
ARG RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-s"
|
||||||
|
|
||||||
|
# Copy recipe and build dependencies (cached layer!)
|
||||||
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json
|
||||||
|
|
||||||
|
# Copy source and build specific service
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates/ ./crates/
|
||||||
|
COPY apps/ ./apps/
|
||||||
|
|
||||||
|
# Build with size optimizations
|
||||||
|
ENV RUSTFLAGS="${RUSTFLAGS}"
|
||||||
|
ENV CARGO_NET_OFFLINE=false
|
||||||
|
ENV CARGO_NET_RETRY=10
|
||||||
|
|
||||||
|
# Use single job for better caching
|
||||||
|
RUN cargo build --release \
|
||||||
|
--bin ${SERVICE_NAME} \
|
||||||
|
--target x86_64-unknown-linux-musl \
|
||||||
|
--locked
|
||||||
|
|
||||||
|
# Stage 4: Minimal runtime - Scratch (even smaller than distroless!)
|
||||||
|
FROM scratch AS runtime
|
||||||
|
ARG SERVICE_NAME
|
||||||
|
|
||||||
|
# Copy CA certificates for HTTPS
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
|
||||||
|
# Copy static binary
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/${SERVICE_NAME} /app/service
|
||||||
|
|
||||||
|
# Expose port (will be overridden by env)
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Run as non-root (numeric uid for scratch)
|
||||||
|
USER 65532:65532
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/service"]
|
||||||
38
Dockerfile.superfast
Normal file
38
Dockerfile.superfast
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Super-fast Dockerfile using pre-compiled dependencies
|
||||||
|
# This requires building a base image first with: cargo chef cook
|
||||||
|
|
||||||
|
ARG SERVICE_NAME
|
||||||
|
|
||||||
|
# Stage 1: Use pre-built base with all dependencies cached
|
||||||
|
# Build base with: docker build -f Dockerfile.base -t nxtgauge-rust-base:latest .
|
||||||
|
FROM ghcr.io/traceworks2023/nxtgauge-rust-base:latest AS builder
|
||||||
|
ARG SERVICE_NAME
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy ONLY the service source code (fast!)
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY crates/ ./crates/
|
||||||
|
COPY apps/${SERVICE_NAME}/ ./apps/${SERVICE_NAME}/
|
||||||
|
|
||||||
|
# Build with optimizations for size and speed
|
||||||
|
ENV RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-s -C opt-level=z"
|
||||||
|
ENV CARGO_NET_OFFLINE=true
|
||||||
|
|
||||||
|
# Build just this service - dependencies already compiled!
|
||||||
|
RUN cargo build --release \
|
||||||
|
--bin ${SERVICE_NAME} \
|
||||||
|
--target x86_64-unknown-linux-musl \
|
||||||
|
2>&1 | tail -20
|
||||||
|
|
||||||
|
# Stage 2: Minimal runtime
|
||||||
|
FROM scratch
|
||||||
|
ARG SERVICE_NAME
|
||||||
|
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/${SERVICE_NAME} /app/service
|
||||||
|
|
||||||
|
USER 65532:65532
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/service"]
|
||||||
23
scripts/build-base-image.sh
Executable file
23
scripts/build-base-image.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# build-base-image.sh - Build base image with all dependencies cached
|
||||||
|
# Run this once when dependencies change (not on every build!)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔨 Building base image with all dependencies..."
|
||||||
|
|
||||||
|
# Build base image with cargo-chef
|
||||||
|
docker build \
|
||||||
|
-f Dockerfile.base \
|
||||||
|
-t ghcr.io/traceworks2023/nxtgauge-rust-base:latest \
|
||||||
|
-t ghcr.io/traceworks2023/nxtgauge-rust-base:$(date +%Y%m%d) \
|
||||||
|
.
|
||||||
|
|
||||||
|
echo "📤 Pushing base image..."
|
||||||
|
docker push ghcr.io/traceworks2023/nxtgauge-rust-base:latest
|
||||||
|
docker push ghcr.io/traceworks2023/nxtgauge-rust-base:$(date +%Y%m%d)
|
||||||
|
|
||||||
|
echo "✅ Base image built and pushed!"
|
||||||
|
echo ""
|
||||||
|
echo "Now builds will use cached dependencies!"
|
||||||
|
echo "Build time: 15-20 min → 30-60 seconds"
|
||||||
93
scripts/fast-build.sh
Executable file
93
scripts/fast-build.sh
Executable file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# fast-build.sh - Ultra-fast local builds with smart caching
|
||||||
|
# Usage: ./scripts/fast-build.sh [service-name]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REGISTRY="ghcr.io/traceworks2023"
|
||||||
|
SERVICE=${1:-""}
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
echo -e "${BLUE}🚀 Nxtgauge Fast Build System${NC}"
|
||||||
|
echo "================================"
|
||||||
|
|
||||||
|
# If no service specified, detect changes
|
||||||
|
if [ -z "$SERVICE" ]; then
|
||||||
|
echo "🔍 Detecting changed services..."
|
||||||
|
|
||||||
|
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
# Check if shared crates changed
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^crates/"; then
|
||||||
|
echo -e "${YELLOW}⚠️ Shared crates changed - need full rebuild${NC}"
|
||||||
|
echo "Consider running: ./scripts/build-base-image.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find changed services
|
||||||
|
for svc in gateway users companies job_seekers customers payments employees photographers makeup_artists tutors developers video_editors graphic_designers social_media_managers fitness_trainers catering_services ugc_content_creators cron; do
|
||||||
|
svc_path=$(echo "$svc" | tr '_' '-')
|
||||||
|
if echo "$CHANGED_FILES" | grep -q "^apps/${svc_path}/"; then
|
||||||
|
echo -e "${GREEN}✅ $svc changed${NC}"
|
||||||
|
SERVICE="$svc"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$SERVICE" ]; then
|
||||||
|
echo -e "${YELLOW}No services changed. Specify one:${NC}"
|
||||||
|
echo "./scripts/fast-build.sh users"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}Building: $SERVICE${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if base image exists
|
||||||
|
if ! docker image inspect ghcr.io/traceworks2023/nxtgauge-rust-base:latest &>/dev/null; then
|
||||||
|
echo -e "${YELLOW}⚠️ Base image not found. Building dependencies...${NC}"
|
||||||
|
echo "This will take 10-15 minutes (one-time setup)"
|
||||||
|
./scripts/build-base-image.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use the fastest Dockerfile available
|
||||||
|
if [ -f "Dockerfile.superfast" ]; then
|
||||||
|
DOCKERFILE="Dockerfile.superfast"
|
||||||
|
echo -e "${BLUE}Using superfast build (cached dependencies)${NC}"
|
||||||
|
else
|
||||||
|
DOCKERFILE="Dockerfile.fast"
|
||||||
|
echo -e "${BLUE}Using fast build (cargo-chef)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🔨 Building ${SERVICE}..."
|
||||||
|
|
||||||
|
# Build with cache
|
||||||
|
docker build \
|
||||||
|
--build-arg SERVICE_NAME=${SERVICE} \
|
||||||
|
-f ${DOCKERFILE} \
|
||||||
|
--cache-from ${REGISTRY}/nxtgauge-rust-${SERVICE}:cache \
|
||||||
|
--cache-from ${REGISTRY}/nxtgauge-rust-${SERVICE}:latest \
|
||||||
|
--build-arg BUILDKIT_INLINE_CACHE=1 \
|
||||||
|
-t ${REGISTRY}/nxtgauge-rust-${SERVICE}:fast-build \
|
||||||
|
.
|
||||||
|
|
||||||
|
# Show image size
|
||||||
|
SIZE=$(docker images --format "{{.Size}}" ${REGISTRY}/nxtgauge-rust-${SERVICE}:fast-build)
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✅ Build complete!${NC}"
|
||||||
|
echo " Service: ${SERVICE}"
|
||||||
|
echo " Image: ${REGISTRY}/nxtgauge-rust-${SERVICE}:fast-build"
|
||||||
|
echo " Size: ${SIZE}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}Push to registry:${NC}"
|
||||||
|
echo " docker push ${REGISTRY}/nxtgauge-rust-${SERVICE}:fast-build"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE}Test locally:${NC}"
|
||||||
|
echo " docker run -p 9100:9100 ${REGISTRY}/nxtgauge-rust-${SERVICE}:fast-build"
|
||||||
Loading…
Add table
Reference in a new issue