diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..964c45e5 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,300 @@ +name: Build and Push Docker Images + +on: + push: + branches: + - main + tags: + - 'v*' + pull_request: + branches: + - main + workflow_dispatch: + inputs: + push_to_registry: + description: 'Push images to registry' + required: false + default: 'true' + type: boolean + +env: + REGISTRY_IMAGE_DOCKERHUB: rustfs/rustfs + REGISTRY_IMAGE_GHCR: ghcr.io/${{ github.repository }} + +jobs: + # Skip duplicate job runs + skip-check: + permissions: + actions: write + contents: read + runs-on: ubuntu-latest + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@v5 + with: + concurrent_skipping: 'same_content_newer' + cancel_others: true + paths_ignore: '["*.md", "docs/**"]' + + # Build RustFS binary for different platforms + build-binary: + needs: skip-check + if: needs.skip-check.outputs.should_skip != 'true' + strategy: + matrix: + include: + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + arch: amd64 + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + arch: arm64 + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + target: ${{ matrix.target }} + components: rustfmt, clippy + + - name: Install cross-compilation dependencies + run: | + sudo apt-get update + sudo apt-get install -y musl-tools + if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then + sudo apt-get install -y gcc-aarch64-linux-gnu + fi + + - name: Install protoc + uses: arduino/setup-protoc@v3 + with: + version: "31.1" + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install flatc + uses: Nugine/setup-flatc@v1 + with: + version: "25.2.10" + + - name: Cache cargo dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-${{ matrix.target }}- + ${{ runner.os }}-cargo- + + - name: Build RustFS binary + run: | + export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc + cargo build --release --target ${{ matrix.target }} --bin rustfs + + - name: Upload binary artifact + uses: actions/upload-artifact@v4 + with: + name: rustfs-${{ matrix.arch }} + path: target/${{ matrix.target }}/release/rustfs + retention-days: 1 + + # Build and push Docker images + build-images: + needs: [skip-check, build-binary] + if: needs.skip-check.outputs.should_skip != 'true' + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + matrix: + image-type: [production, ubuntu, rockylinux, devenv] + platform: [linux/amd64, linux/arm64] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download binary artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts + + - name: Setup binary files + run: | + mkdir -p target/x86_64-unknown-linux-musl/release + mkdir -p target/aarch64-unknown-linux-musl/release + cp artifacts/rustfs-amd64/rustfs target/x86_64-unknown-linux-musl/release/ + cp artifacts/rustfs-arm64/rustfs target/aarch64-unknown-linux-musl/release/ + chmod +x target/*/release/rustfs + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Login to Docker Hub + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set Dockerfile and context + id: dockerfile + run: | + case "${{ matrix.image-type }}" in + production) + echo "dockerfile=Dockerfile" >> $GITHUB_OUTPUT + echo "context=." >> $GITHUB_OUTPUT + echo "suffix=" >> $GITHUB_OUTPUT + ;; + ubuntu) + echo "dockerfile=.docker/Dockerfile.ubuntu22.04" >> $GITHUB_OUTPUT + echo "context=." >> $GITHUB_OUTPUT + echo "suffix=-ubuntu22.04" >> $GITHUB_OUTPUT + ;; + rockylinux) + echo "dockerfile=.docker/Dockerfile.rockylinux9.3" >> $GITHUB_OUTPUT + echo "context=." >> $GITHUB_OUTPUT + echo "suffix=-rockylinux9.3" >> $GITHUB_OUTPUT + ;; + devenv) + echo "dockerfile=.docker/Dockerfile.devenv" >> $GITHUB_OUTPUT + echo "context=." >> $GITHUB_OUTPUT + echo "suffix=-devenv" >> $GITHUB_OUTPUT + ;; + esac + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.REGISTRY_IMAGE_DOCKERHUB }} + ${{ env.REGISTRY_IMAGE_GHCR }} + tags: | + type=ref,event=branch,suffix=${{ steps.dockerfile.outputs.suffix }} + type=ref,event=pr,suffix=${{ steps.dockerfile.outputs.suffix }} + type=semver,pattern={{version}},suffix=${{ steps.dockerfile.outputs.suffix }} + type=semver,pattern={{major}}.{{minor}},suffix=${{ steps.dockerfile.outputs.suffix }} + type=semver,pattern={{major}},suffix=${{ steps.dockerfile.outputs.suffix }} + type=raw,value=latest,suffix=${{ steps.dockerfile.outputs.suffix }},enable={{is_default_branch}} + flavor: | + latest=false + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ${{ steps.dockerfile.outputs.context }} + file: ${{ steps.dockerfile.outputs.dockerfile }} + platforms: ${{ matrix.platform }} + push: ${{ github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=${{ matrix.image-type }}-${{ matrix.platform }} + cache-to: type=gha,mode=max,scope=${{ matrix.image-type }}-${{ matrix.platform }} + build-args: | + BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} + VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} + REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }} + + # Create multi-arch manifests + create-manifest: + needs: [skip-check, build-images] + if: needs.skip-check.outputs.should_skip != 'true' && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + runs-on: ubuntu-latest + strategy: + matrix: + image-type: [production, ubuntu, rockylinux, devenv] + steps: + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set image suffix + id: suffix + run: | + case "${{ matrix.image-type }}" in + production) echo "suffix=" >> $GITHUB_OUTPUT ;; + ubuntu) echo "suffix=-ubuntu22.04" >> $GITHUB_OUTPUT ;; + rockylinux) echo "suffix=-rockylinux9.3" >> $GITHUB_OUTPUT ;; + devenv) echo "suffix=-devenv" >> $GITHUB_OUTPUT ;; + esac + + - name: Create and push manifest + run: | + # Set tag based on ref + if [[ $GITHUB_REF == refs/tags/* ]]; then + TAG=${GITHUB_REF#refs/tags/} + else + TAG="main" + fi + + SUFFIX="${{ steps.suffix.outputs.suffix }}" + + # Docker Hub manifest + docker buildx imagetools create -t ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX} \ + ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-amd64 \ + ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-arm64 + + # GitHub Container Registry manifest + docker buildx imagetools create -t ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX} \ + ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-amd64 \ + ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-arm64 + + # Create latest tag for main branch + if [[ $GITHUB_REF == refs/heads/main ]]; then + docker buildx imagetools create -t ${REGISTRY_IMAGE_DOCKERHUB}:latest${SUFFIX} \ + ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-amd64 \ + ${REGISTRY_IMAGE_DOCKERHUB}:${TAG}${SUFFIX}-linux-arm64 + + docker buildx imagetools create -t ${REGISTRY_IMAGE_GHCR}:latest${SUFFIX} \ + ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-amd64 \ + ${REGISTRY_IMAGE_GHCR}:${TAG}${SUFFIX}-linux-arm64 + fi + + # Security scanning + security-scan: + needs: [skip-check, build-images] + if: needs.skip-check.outputs.should_skip != 'true' + runs-on: ubuntu-latest + strategy: + matrix: + image-type: [production] + steps: + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY_IMAGE_GHCR }}:main + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: 'trivy-results.sarif' diff --git a/Dockerfile.multi-stage b/Dockerfile.multi-stage new file mode 100644 index 00000000..63a7f1d3 --- /dev/null +++ b/Dockerfile.multi-stage @@ -0,0 +1,119 @@ +# Multi-stage Dockerfile for RustFS +# Supports cross-compilation for amd64 and arm64 architectures +ARG TARGETPLATFORM +ARG BUILDPLATFORM + +# Build stage +FROM --platform=$BUILDPLATFORM rust:1.85-bookworm AS builder + +# Install required build dependencies +RUN apt-get update && apt-get install -y \ + wget \ + git \ + curl \ + unzip \ + gcc \ + pkg-config \ + libssl-dev \ + lld \ + musl-tools \ + && rm -rf /var/lib/apt/lists/* + +# Install cross-compilation tools for ARM64 +RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ + apt-get update && \ + apt-get install -y gcc-aarch64-linux-gnu && \ + rm -rf /var/lib/apt/lists/*; \ + fi + +# Install protoc +RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-linux-x86_64.zip \ + && unzip protoc-31.1-linux-x86_64.zip -d protoc3 \ + && mv protoc3/bin/* /usr/local/bin/ && chmod +x /usr/local/bin/protoc \ + && mv protoc3/include/* /usr/local/include/ && rm -rf protoc-31.1-linux-x86_64.zip protoc3 + +# Install flatc +RUN wget https://github.com/google/flatbuffers/releases/download/v25.2.10/Linux.flatc.binary.g++-13.zip \ + && unzip Linux.flatc.binary.g++-13.zip \ + && mv flatc /usr/local/bin/ && chmod +x /usr/local/bin/flatc && rm -rf Linux.flatc.binary.g++-13.zip + +# Set up Rust targets based on platform +RUN case "$TARGETPLATFORM" in \ + "linux/amd64") rustup target add x86_64-unknown-linux-musl ;; \ + "linux/arm64") rustup target add aarch64-unknown-linux-musl ;; \ + *) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \ + esac + +# Set up environment for cross-compilation +ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc + +WORKDIR /usr/src/rustfs + +# Copy Cargo files for dependency caching +COPY Cargo.toml Cargo.lock ./ +COPY */Cargo.toml ./*/ + +# Create dummy main.rs files for dependency compilation +RUN find . -name "Cargo.toml" -not -path "./Cargo.toml" | \ + xargs -I {} dirname {} | \ + xargs -I {} sh -c 'mkdir -p {}/src && echo "fn main() {}" > {}/src/main.rs' + +# Build dependencies only (cache layer) +RUN case "$TARGETPLATFORM" in \ + "linux/amd64") cargo build --release --target x86_64-unknown-linux-musl ;; \ + "linux/arm64") cargo build --release --target aarch64-unknown-linux-musl ;; \ + esac + +# Copy source code +COPY . . + +# Generate protobuf code +RUN cargo run --bin gproto + +# Build the actual application +RUN case "$TARGETPLATFORM" in \ + "linux/amd64") \ + cargo build --release --target x86_64-unknown-linux-musl --bin rustfs && \ + cp target/x86_64-unknown-linux-musl/release/rustfs /usr/local/bin/rustfs \ + ;; \ + "linux/arm64") \ + cargo build --release --target aarch64-unknown-linux-musl --bin rustfs && \ + cp target/aarch64-unknown-linux-musl/release/rustfs /usr/local/bin/rustfs \ + ;; \ + esac + +# Runtime stage - minimal Alpine image +FROM alpine:latest + +# Install runtime dependencies +RUN apk add --no-cache \ + ca-certificates \ + tzdata \ + && rm -rf /var/cache/apk/* + +# Create rustfs user and group +RUN addgroup -g 1000 rustfs && \ + adduser -D -s /bin/sh -u 1000 -G rustfs rustfs + +WORKDIR /app + +# Create data directories +RUN mkdir -p /data/rustfs{0,1,2,3} && \ + chown -R rustfs:rustfs /data /app + +# Copy binary from builder stage +COPY --from=builder /usr/local/bin/rustfs /app/rustfs +RUN chmod +x /app/rustfs + +# Switch to non-root user +USER rustfs + +# Expose ports +EXPOSE 9000 9001 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:9000/health || exit 1 + +# Set default command +CMD ["/app/rustfs"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..5ba1d936 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,221 @@ +version: '3.8' + +services: + # RustFS main service + rustfs: + image: rustfs/rustfs:latest + container_name: rustfs-server + build: + context: . + dockerfile: Dockerfile.multi-stage + args: + TARGETPLATFORM: linux/amd64 + ports: + - "9000:9000" # S3 API port + - "9001:9001" # Console port + environment: + - RUSTFS_VOLUMES=/data/rustfs0,/data/rustfs1,/data/rustfs2,/data/rustfs3 + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001 + - RUSTFS_ACCESS_KEY=rustfsadmin + - RUSTFS_SECRET_KEY=rustfsadmin + - RUSTFS_LOG_LEVEL=info + - RUSTFS_OBS_ENDPOINT=http://otel-collector:4317 + volumes: + - rustfs_data_0:/data/rustfs0 + - rustfs_data_1:/data/rustfs1 + - rustfs_data_2:/data/rustfs2 + - rustfs_data_3:/data/rustfs3 + - ./logs:/app/logs + networks: + - rustfs-network + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + depends_on: + - otel-collector + + # Development environment + rustfs-dev: + image: rustfs/rustfs:devenv + container_name: rustfs-dev + build: + context: . + dockerfile: .docker/Dockerfile.devenv + ports: + - "9010:9000" + - "9011:9001" + environment: + - RUSTFS_VOLUMES=/data/rustfs0,/data/rustfs1 + - RUSTFS_ADDRESS=0.0.0.0:9000 + - RUSTFS_CONSOLE_ENABLE=true + - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001 + - RUSTFS_ACCESS_KEY=devadmin + - RUSTFS_SECRET_KEY=devadmin + - RUSTFS_LOG_LEVEL=debug + volumes: + - .:/root/s3-rustfs + - rustfs_dev_data:/data + networks: + - rustfs-network + restart: unless-stopped + profiles: + - dev + + # OpenTelemetry Collector + otel-collector: + image: otel/opentelemetry-collector-contrib:latest + container_name: otel-collector + command: + - --config=/etc/otelcol-contrib/otel-collector.yml + volumes: + - ./.docker/observability/otel-collector.yml:/etc/otelcol-contrib/otel-collector.yml:ro + ports: + - "4317:4317" # OTLP gRPC receiver + - "4318:4318" # OTLP HTTP receiver + - "8888:8888" # Prometheus metrics + - "8889:8889" # Prometheus exporter metrics + networks: + - rustfs-network + restart: unless-stopped + profiles: + - observability + + # Jaeger for tracing + jaeger: + image: jaegertracing/all-in-one:latest + container_name: jaeger + ports: + - "16686:16686" # Jaeger UI + - "14250:14250" # Jaeger gRPC + environment: + - COLLECTOR_OTLP_ENABLED=true + networks: + - rustfs-network + restart: unless-stopped + profiles: + - observability + + # Prometheus for metrics + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - "9090:9090" + volumes: + - ./.docker/observability/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--storage.tsdb.retention.time=200h' + - '--web.enable-lifecycle' + networks: + - rustfs-network + restart: unless-stopped + profiles: + - observability + + # Grafana for visualization + grafana: + image: grafana/grafana:latest + container_name: grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + - ./.docker/observability/grafana/provisioning:/etc/grafana/provisioning:ro + - ./.docker/observability/grafana/dashboards:/var/lib/grafana/dashboards:ro + networks: + - rustfs-network + restart: unless-stopped + profiles: + - observability + + # MinIO for S3 API testing + minio: + image: minio/minio:latest + container_name: minio-test + ports: + - "9020:9000" + - "9021:9001" + environment: + - MINIO_ROOT_USER=minioadmin + - MINIO_ROOT_PASSWORD=minioadmin + volumes: + - minio_data:/data + command: server /data --console-address ":9001" + networks: + - rustfs-network + restart: unless-stopped + profiles: + - testing + + # Redis for caching (optional) + redis: + image: redis:7-alpine + container_name: redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + networks: + - rustfs-network + restart: unless-stopped + profiles: + - cache + + # NGINX reverse proxy (optional) + nginx: + image: nginx:alpine + container_name: nginx-proxy + ports: + - "80:80" + - "443:443" + volumes: + - ./.docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./.docker/nginx/ssl:/etc/nginx/ssl:ro + networks: + - rustfs-network + restart: unless-stopped + profiles: + - proxy + depends_on: + - rustfs + +networks: + rustfs-network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + +volumes: + rustfs_data_0: + driver: local + rustfs_data_1: + driver: local + rustfs_data_2: + driver: local + rustfs_data_3: + driver: local + rustfs_dev_data: + driver: local + prometheus_data: + driver: local + grafana_data: + driver: local + minio_data: + driver: local + redis_data: + driver: local diff --git a/docs/docker-build.md b/docs/docker-build.md new file mode 100644 index 00000000..47262176 --- /dev/null +++ b/docs/docker-build.md @@ -0,0 +1,426 @@ +# RustFS Docker Build and Deployment Guide + +This document describes how to build and deploy RustFS using Docker, including the automated GitHub Actions workflow for building and pushing images to Docker Hub and GitHub Container Registry. + +## 🚀 Quick Start + +### Using Pre-built Images + +```bash +# Pull and run the latest RustFS image +docker run -d \ + --name rustfs \ + -p 9000:9000 \ + -p 9001:9001 \ + -v rustfs_data:/data \ + -e RUSTFS_VOLUMES=/data/rustfs0,/data/rustfs1,/data/rustfs2,/data/rustfs3 \ + -e RUSTFS_ACCESS_KEY=rustfsadmin \ + -e RUSTFS_SECRET_KEY=rustfsadmin \ + -e RUSTFS_CONSOLE_ENABLE=true \ + rustfs/rustfs:latest +``` + +### Using Docker Compose + +```bash +# Basic deployment +docker-compose up -d + +# Development environment +docker-compose --profile dev up -d + +# With observability stack +docker-compose --profile observability up -d + +# Full stack with all services +docker-compose --profile dev --profile observability --profile testing up -d +``` + +## 📦 Available Images + +Our GitHub Actions workflow builds multiple image variants: + +### Image Registries + +- **Docker Hub**: `rustfs/rustfs` +- **GitHub Container Registry**: `ghcr.io/rustfs/s3-rustfs` + +### Image Variants + +| Variant | Tag Suffix | Description | Use Case | +|---------|------------|-------------|----------| +| Production | *(none)* | Minimal Alpine-based runtime | Production deployment | +| Ubuntu | `-ubuntu22.04` | Ubuntu 22.04 based build environment | Development/Testing | +| Rocky Linux | `-rockylinux9.3` | Rocky Linux 9.3 based build environment | Enterprise environments | +| Development | `-devenv` | Full development environment | Development/Debugging | + +### Supported Architectures + +All images support multi-architecture: +- `linux/amd64` (x86_64) +- `linux/arm64` (aarch64) + +### Tag Examples + +```bash +# Latest production image +rustfs/rustfs:latest +rustfs/rustfs:main + +# Specific version +rustfs/rustfs:v1.0.0 +rustfs/rustfs:v1.0.0-ubuntu22.04 + +# Development environment +rustfs/rustfs:latest-devenv +rustfs/rustfs:main-devenv +``` + +## 🔧 GitHub Actions Workflow + +The Docker build workflow (`.github/workflows/docker.yml`) automatically: + +1. **Builds cross-platform binaries** for `amd64` and `arm64` +2. **Creates Docker images** for all variants +3. **Pushes to registries** (Docker Hub and GitHub Container Registry) +4. **Creates multi-arch manifests** for seamless platform selection +5. **Performs security scanning** using Trivy + +### Workflow Triggers + +- **Push to main branch**: Builds and pushes `main` and `latest` tags +- **Tag push** (`v*`): Builds and pushes version tags +- **Pull requests**: Builds images without pushing +- **Manual trigger**: Workflow dispatch with options + +### Required Secrets + +Configure these secrets in your GitHub repository: + +```bash +# Docker Hub credentials +DOCKERHUB_USERNAME=your-dockerhub-username +DOCKERHUB_TOKEN=your-dockerhub-access-token + +# GitHub token is automatically available +GITHUB_TOKEN=automatically-provided +``` + +## 🏗️ Building Locally + +### Prerequisites + +- Docker with BuildKit enabled +- Docker Compose (optional) + +### Build Commands + +```bash +# Build production image for local platform +docker build -t rustfs:local . + +# Build multi-stage production image +docker build -f Dockerfile.multi-stage -t rustfs:multi-stage . + +# Build specific variant +docker build -f .docker/Dockerfile.ubuntu22.04 -t rustfs:ubuntu . + +# Build for specific platform +docker build --platform linux/amd64 -t rustfs:amd64 . +docker build --platform linux/arm64 -t rustfs:arm64 . + +# Build multi-platform image +docker buildx build --platform linux/amd64,linux/arm64 -t rustfs:multi . +``` + +### Build with Docker Compose + +```bash +# Build all services +docker-compose build + +# Build specific service +docker-compose build rustfs + +# Build development environment +docker-compose build rustfs-dev +``` + +## 🚀 Deployment Options + +### 1. Single Container + +```bash +docker run -d \ + --name rustfs \ + --restart unless-stopped \ + -p 9000:9000 \ + -p 9001:9001 \ + -v /data/rustfs:/data \ + -e RUSTFS_VOLUMES=/data/rustfs0,/data/rustfs1,/data/rustfs2,/data/rustfs3 \ + -e RUSTFS_ADDRESS=0.0.0.0:9000 \ + -e RUSTFS_CONSOLE_ENABLE=true \ + -e RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001 \ + -e RUSTFS_ACCESS_KEY=rustfsadmin \ + -e RUSTFS_SECRET_KEY=rustfsadmin \ + rustfs/rustfs:latest +``` + +### 2. Docker Compose Profiles + +```bash +# Production deployment +docker-compose up -d + +# Development with debugging +docker-compose --profile dev up -d + +# With monitoring stack +docker-compose --profile observability up -d + +# Complete testing environment +docker-compose --profile dev --profile observability --profile testing up -d +``` + +### 3. Kubernetes Deployment + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rustfs +spec: + replicas: 3 + selector: + matchLabels: + app: rustfs + template: + metadata: + labels: + app: rustfs + spec: + containers: + - name: rustfs + image: rustfs/rustfs:latest + ports: + - containerPort: 9000 + - containerPort: 9001 + env: + - name: RUSTFS_VOLUMES + value: "/data/rustfs0,/data/rustfs1,/data/rustfs2,/data/rustfs3" + - name: RUSTFS_ADDRESS + value: "0.0.0.0:9000" + - name: RUSTFS_CONSOLE_ENABLE + value: "true" + - name: RUSTFS_CONSOLE_ADDRESS + value: "0.0.0.0:9001" + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: rustfs-data +``` + +## ⚙️ Configuration + +### Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `RUSTFS_VOLUMES` | Comma-separated list of data volumes | Required | +| `RUSTFS_ADDRESS` | Server bind address | `0.0.0.0:9000` | +| `RUSTFS_CONSOLE_ENABLE` | Enable web console | `false` | +| `RUSTFS_CONSOLE_ADDRESS` | Console bind address | `0.0.0.0:9001` | +| `RUSTFS_ACCESS_KEY` | S3 access key | `rustfsadmin` | +| `RUSTFS_SECRET_KEY` | S3 secret key | `rustfsadmin` | +| `RUSTFS_LOG_LEVEL` | Log level | `info` | +| `RUSTFS_OBS_ENDPOINT` | Observability endpoint | `""` | +| `RUSTFS_TLS_PATH` | TLS certificates path | `""` | + +### Volume Mounts + +- **Data volumes**: `/data/rustfs{0,1,2,3}` - RustFS data storage +- **Logs**: `/app/logs` - Application logs +- **Config**: `/etc/rustfs/` - Configuration files +- **TLS**: `/etc/ssl/rustfs/` - TLS certificates + +### Ports + +- **9000**: S3 API endpoint +- **9001**: Web console (if enabled) +- **9002**: Admin API (if enabled) +- **50051**: gRPC API (if enabled) + +## 🔍 Monitoring and Observability + +### Health Checks + +The Docker images include built-in health checks: + +```bash +# Check container health +docker ps --filter "name=rustfs" --format "table {{.Names}}\t{{.Status}}" + +# View health check logs +docker inspect rustfs --format='{{json .State.Health}}' +``` + +### Metrics and Tracing + +When using the observability profile: + +- **Prometheus**: http://localhost:9090 +- **Grafana**: http://localhost:3000 (admin/admin) +- **Jaeger**: http://localhost:16686 +- **OpenTelemetry Collector**: http://localhost:8888/metrics + +### Log Collection + +```bash +# View container logs +docker logs rustfs -f + +# Export logs +docker logs rustfs > rustfs.log 2>&1 +``` + +## 🛠️ Development + +### Development Environment + +```bash +# Start development container +docker-compose --profile dev up -d rustfs-dev + +# Access development container +docker exec -it rustfs-dev bash + +# Mount source code for live development +docker run -it --rm \ + -v $(pwd):/root/s3-rustfs \ + -p 9000:9000 \ + rustfs/rustfs:devenv \ + bash +``` + +### Building from Source in Container + +```bash +# Use development image for building +docker run --rm \ + -v $(pwd):/root/s3-rustfs \ + -w /root/s3-rustfs \ + rustfs/rustfs:ubuntu22.04 \ + cargo build --release --bin rustfs +``` + +## 🔐 Security + +### Security Scanning + +The workflow includes Trivy security scanning: + +```bash +# Run security scan locally +docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ + -v $HOME/Library/Caches:/root/.cache/ \ + aquasec/trivy:latest image rustfs/rustfs:latest +``` + +### Security Best Practices + +1. **Use non-root user**: Images run as `rustfs` user (UID 1000) +2. **Minimal base images**: Alpine Linux for production +3. **Security updates**: Regular base image updates +4. **Secret management**: Use Docker secrets or environment files +5. **Network security**: Use Docker networks and proper firewall rules + +## 📝 Troubleshooting + +### Common Issues + +1. **Build failures**: Check build logs and ensure all dependencies are installed +2. **Permission issues**: Ensure proper volume permissions for UID 1000 +3. **Network connectivity**: Verify port mappings and network configuration +4. **Resource limits**: Ensure sufficient memory and CPU for compilation + +### Debug Commands + +```bash +# Check container status +docker ps -a + +# View container logs +docker logs rustfs --tail 100 + +# Access container shell +docker exec -it rustfs sh + +# Check resource usage +docker stats rustfs + +# Inspect container configuration +docker inspect rustfs +``` + +## 🔄 CI/CD Integration + +### GitHub Actions + +The provided workflow can be customized: + +```yaml +# Override image names +env: + REGISTRY_IMAGE_DOCKERHUB: myorg/rustfs + REGISTRY_IMAGE_GHCR: ghcr.io/myorg/rustfs +``` + +### GitLab CI + +```yaml +build: + stage: build + image: docker:latest + services: + - docker:dind + script: + - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA +``` + +### Jenkins Pipeline + +```groovy +pipeline { + agent any + stages { + stage('Build') { + steps { + script { + docker.build("rustfs:${env.BUILD_ID}") + } + } + } + stage('Push') { + steps { + script { + docker.withRegistry('https://registry.hub.docker.com', 'dockerhub-credentials') { + docker.image("rustfs:${env.BUILD_ID}").push() + } + } + } + } + } +} +``` + +## 📚 Additional Resources + +- [Docker Official Documentation](https://docs.docker.com/) +- [Docker Compose Reference](https://docs.docker.com/compose/) +- [GitHub Actions Documentation](https://docs.github.com/en/actions) +- [RustFS Configuration Guide](../README.md) +- [Kubernetes Deployment Guide](./kubernetes.md)