# Build all images
make docker-all
# Build specific component
make docker-scheduler
make docker-webhook
make docker-agent# Build with specific version
make VERSION=v1.0.0 docker-all
# Build for different platform
make DOCKER_PLATFORM=linux/arm64 docker
# Build with custom registry
make REGISTRY=registry.in docker-allThe Dockerfile uses modern best practices for optimal builds:
Two stages minimize final image size:
- Builder stage: Full Go toolchain (golang:alpine)
- Runtime stage: Minimal distroless image (~2MB)
# Dependencies cached separately from source code
COPY go.mod go.sum ./
RUN go mod download
# Source copied after dependencies
COPY . .Benefits:
- Dependencies only re-download when go.mod/go.sum change
- Faster rebuilds during development
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build ...Features:
- Persistent cache across builds
- Shared cache between different builds
- 10-100x faster rebuilds
CGO_ENABLED=0 # Static binary, no C dependencies
-ldflags="-w -s" # Strip debug info and symbol table
-trimpath # Remove file system paths from binary
-installsuffix cgo # Better cachingResult:
- Smaller binary size (~50% reduction)
- No external dependencies
- Reproducible builds
ARG VERSION=dev
ARG COMMIT=unknown
ARG BUILD_DATE=unknown
-ldflags="-X main.version=${VERSION} \
-X main.commit=${COMMIT} \
-X main.date=${BUILD_DATE}"Usage:
# Binaries include version info
./scheduler --version# Minimal distroless base image
FROM gcr.io/distroless/static-debian12:nonroot
# Non-root user
USER 65532:65532Benefits:
- No shell or package manager (reduced attack surface)
- Non-root execution
- CVE-free base (regularly updated)
- Minimal dependencies
LABEL org.opencontainers.image.title="GPU Scheduler"
org.opencontainers.image.source="https://github.com/restack/gpu-scheduler"
org.opencontainers.image.licenses="MIT"Enables:
- Container registry metadata
- Image provenance tracking
- License compliance
Approximate sizes:
| Component | Size |
|---|---|
| Scheduler | ~72MB |
| Webhook | ~12MB |
| Agent | ~38MB |
Compare to typical Go apps with full base image: ~50-200MB
# Build and load into kind
make dev-docker
# Deploy to kind cluster
make deploy
# View logs
make logs# Rebuild specific component
make docker-scheduler
# Reload in kind
kind load docker-image ghcr.io/restack/gpu-scheduler:dev
# Restart deployment
kubectl rollout restart deployment/gpu-scheduler# Push all images
make docker-push-all
# Push specific component
make docker-push-scheduler
# Push with version tag
make VERSION=v1.0.0 docker-push-allBuild for multiple architectures:
# Create buildx builder
docker buildx create --name multiarch --use
# Build for multiple platforms
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg CMD_PATH=cmd/scheduler \
--push \
-t ghcr.io/restack/gpu-scheduler:latest .Or use the Makefile:
# Set platform
export DOCKER_PLATFORM=linux/amd64,linux/arm64
make docker-all# Check image size
docker images ghcr.io/restack/gpu-scheduler:dev
# Inspect layers
docker history ghcr.io/restack/gpu-scheduler:dev
# Check metadata
docker inspect ghcr.io/restack/gpu-scheduler:devFor debugging, use non-distroless base:
# Replace final stage with debug variant
FROM gcr.io/distroless/base-debian12:debug
# Or use full Alpine
FROM alpine:3.19# Distroless doesn't have shell, but you can exec into it
docker run -it --entrypoint /bin/sh alpine:3.19
# Then copy binary and test- name: Build and push images
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
make VERSION=${{ github.ref_name }} docker-push-allbuild:
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- make VERSION=$CI_COMMIT_TAG docker-push-allExclude unnecessary files from build context:
.git/
.github/
docs/
*.md
.cache/
.config/
bin/
coverage.*
# Good: Specific version
FROM golang:1.24-alpine3.21
# Bad: Latest (unpredictable)
FROM golang:latest- Structure Dockerfile for optimal caching
- Use BuildKit cache mounts
- Order layers from least to most frequently changed
# Scan for vulnerabilities
trivy image ghcr.io/restack/gpu-scheduler:dev
# Or use Docker Scout
docker scout cves ghcr.io/restack/gpu-scheduler:dev- Update base images regularly
- Rebuild weekly for security patches
- Use automated dependency updates (Dependabot)
# Clear build cache
docker builder prune
# Verify go.mod/go.sum
go mod tidy# Check what's taking space
docker history ghcr.io/restack/gpu-scheduler:dev --human --no-trunc
# Ensure using distroless final stage
# Check .dockerignore excludes unnecessary files# Enable BuildKit
export DOCKER_BUILDKIT=1
# Or in docker-compose
export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1# Install qemu for emulation
docker run --privileged --rm tonistiigi/binfmt --install all
# Verify
docker buildx ls- Use cache mounts: 10-100x faster rebuilds
- Order layers properly: Dependencies before source
- Minimize context size: Good .dockerignore
- Parallel builds: Build all components concurrently
- Registry caching: Pull before building