From 8a9d786b1a565ad0c373ac98c976b22db74e6333 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sun, 4 Jan 2026 00:53:44 +0000 Subject: [PATCH] perf: build frontend once on runner instead of in Docker - [x] Remove Docker compilation from server, scanner, migrate Dockerfiles - [x] Use pre-built binaries injected by GoReleaser instead - [x] Delete separate gateway container and merge into frontend - [x] Update .goreleaser.yaml to reference pre-built binaries - [x] Simplify Kubernetes deployment to remove gateway service - [x] Simplify docker-compose to remove gateway container - [x] Add backend proxy configuration to frontend --- .goreleaser.yaml | 44 ++-- Dockerfile.gateway | 197 ------------------ Dockerfile.migrate | 52 +---- Dockerfile.scanner | 49 +---- Dockerfile.server | 53 +---- .../kubernetes/deployment-all-in-one.yaml | 100 ++------- docker-compose.example.yaml | 52 ++--- 7 files changed, 62 insertions(+), 485 deletions(-) delete mode 100644 Dockerfile.gateway diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 7d57cd9..4654835 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -119,11 +119,15 @@ release: prerelease: auto # Docker images (v2 - modern syntax) -# All Dockerfiles are self-contained multi-stage builds -# GoReleaser orchestrates buildx and passes build args +# Uses PRE-BUILT binaries from native builds (no Docker compilation - much faster!) +# GoReleaser injects the platform-specific binary into each Docker image automatically +# This avoids slow QEMU emulation for cross-architecture builds dockers_v2: # 1. Application Engine - Main GoHoarder server + # Uses pre-built binary from 'gohoarder' build (no Docker compilation) - id: gohoarder-server + ids: + - gohoarder images: - ghcr.io/lukaszraczylo/gohoarder-server tags: @@ -133,10 +137,7 @@ dockers_v2: - linux/amd64 - linux/arm64 dockerfile: Dockerfile.server - build_args: - VERSION: "{{ .Version }}" - GIT_COMMIT: "{{ .ShortCommit }}" - BUILD_TIME: "{{ .Date }}" + use_buildx: true flags: - "--pull" - "--label=org.opencontainers.image.title=GoHoarder Server" @@ -147,11 +148,6 @@ dockers_v2: - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.revision={{ .FullCommit }}" extra_files: - - go.mod - - go.sum - - cmd - - pkg - - internal - config.yaml.example # 2. Website - Frontend Dashboard @@ -179,7 +175,10 @@ dockers_v2: - frontend/dist # 3. Scanning Engine - Background scanner worker + # Uses pre-built binary from 'gohoarder' build (no Docker compilation) - id: gohoarder-scanner + ids: + - gohoarder images: - ghcr.io/lukaszraczylo/gohoarder-scanner tags: @@ -189,10 +188,7 @@ dockers_v2: - linux/amd64 - linux/arm64 dockerfile: Dockerfile.scanner - build_args: - VERSION: "{{ .Version }}" - GIT_COMMIT: "{{ .ShortCommit }}" - BUILD_TIME: "{{ .Date }}" + use_buildx: true flags: - "--pull" - "--label=org.opencontainers.image.title=GoHoarder Scanner" @@ -203,15 +199,13 @@ dockers_v2: - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.revision={{ .FullCommit }}" extra_files: - - go.mod - - go.sum - - cmd - - pkg - - internal - config.yaml.example # 4. Migration Engine - Database migration tool + # Uses pre-built binary from 'migrate' build (no Docker compilation) - id: gohoarder-migrate + ids: + - migrate images: - ghcr.io/lukaszraczylo/gohoarder-migrate tags: @@ -221,10 +215,7 @@ dockers_v2: - linux/amd64 - linux/arm64 dockerfile: Dockerfile.migrate - build_args: - VERSION: "{{ .Version }}" - GIT_COMMIT: "{{ .ShortCommit }}" - BUILD_TIME: "{{ .Date }}" + use_buildx: true flags: - "--pull" - "--label=org.opencontainers.image.title=GoHoarder Migrate" @@ -235,11 +226,6 @@ dockers_v2: - "--label=org.opencontainers.image.created={{ .Date }}" - "--label=org.opencontainers.image.revision={{ .FullCommit }}" extra_files: - - go.mod - - go.sum - - cmd - - pkg - - internal - migrations # Artifact signing with cosign diff --git a/Dockerfile.gateway b/Dockerfile.gateway deleted file mode 100644 index 4a5a5bd..0000000 --- a/Dockerfile.gateway +++ /dev/null @@ -1,197 +0,0 @@ -# Gateway - Nginx reverse proxy for unified deployment -# Routes traffic between frontend and backend under single vhost -FROM nginx:alpine - -# Install envsubst for runtime configuration -RUN apk add --no-cache gettext - -# Copy nginx configuration template -COPY <<'EOF' /etc/nginx/templates/default.conf.template -# Upstream servers -upstream backend { - server ${BACKEND_HOST}:${BACKEND_PORT}; - keepalive 32; -} - -upstream frontend { - server ${FRONTEND_HOST}:${FRONTEND_PORT}; - keepalive 32; -} - -# Rate limiting zones -limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; -limit_req_zone $binary_remote_addr zone=download_limit:10m rate=5r/s; - -# Cache configuration -proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=100m inactive=60m use_temp_path=off; - -server { - listen 80; - server_name ${SERVER_NAME}; - - # Security headers - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - - # Client body size for package uploads - client_max_body_size 500M; - client_body_timeout 300s; - - # Logging - access_log /var/log/nginx/access.log combined; - error_log /var/log/nginx/error.log warn; - - # API endpoints - proxy to backend - location /api/ { - # Rate limiting - limit_req zone=api_limit burst=20 nodelay; - - # Proxy settings - proxy_pass http://backend/; - proxy_http_version 1.1; - - # Headers - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host; - proxy_set_header X-Forwarded-Port $server_port; - - # Connection reuse - proxy_set_header Connection ""; - - # Timeouts for long-running operations - proxy_connect_timeout 60s; - proxy_send_timeout 300s; - proxy_read_timeout 300s; - - # Buffer settings - proxy_buffering on; - proxy_buffer_size 4k; - proxy_buffers 8 4k; - proxy_busy_buffers_size 8k; - } - - # Health check endpoint - location /health { - proxy_pass http://backend/health; - proxy_http_version 1.1; - proxy_set_header Connection ""; - access_log off; - } - - # Metrics endpoint (optional - may want to restrict access) - location /metrics { - # Uncomment to restrict to internal networks - # allow 10.0.0.0/8; - # allow 172.16.0.0/12; - # allow 192.168.0.0/16; - # deny all; - - proxy_pass http://backend/metrics; - proxy_http_version 1.1; - proxy_set_header Connection ""; - } - - # Package download endpoints with rate limiting - location ~ ^/(npm|pypi|go)/ { - limit_req zone=download_limit burst=10 nodelay; - - proxy_pass http://backend; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection ""; - - # Extended timeouts for package downloads - proxy_connect_timeout 60s; - proxy_send_timeout 600s; - proxy_read_timeout 600s; - - # Large buffer for package downloads - proxy_buffering on; - proxy_buffer_size 128k; - proxy_buffers 4 256k; - proxy_busy_buffers_size 256k; - } - - # Frontend - serve SPA - location / { - proxy_pass http://frontend; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Connection ""; - - # Cache static assets - proxy_cache static_cache; - proxy_cache_valid 200 1h; - proxy_cache_bypass $http_cache_control; - add_header X-Cache-Status $upstream_cache_status; - } - - # WebSocket support (if needed for future features) - location /ws/ { - proxy_pass http://backend/ws/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # WebSocket timeouts - proxy_connect_timeout 7d; - proxy_send_timeout 7d; - proxy_read_timeout 7d; - } -} - -# HTTPS server (uncomment and configure SSL certificates) -# server { -# listen 443 ssl http2; -# server_name ${SERVER_NAME}; -# -# ssl_certificate /etc/nginx/ssl/cert.pem; -# ssl_certificate_key /etc/nginx/ssl/key.pem; -# -# # SSL configuration -# ssl_protocols TLSv1.2 TLSv1.3; -# ssl_ciphers HIGH:!aNULL:!MD5; -# ssl_prefer_server_ciphers on; -# ssl_session_cache shared:SSL:10m; -# ssl_session_timeout 10m; -# -# # Include all location blocks from above -# # ... (copy from HTTP server block) -# } -EOF - -# Create cache directory -RUN mkdir -p /var/cache/nginx/static && \ - chown -R nginx:nginx /var/cache/nginx - -# Expose port -EXPOSE 80 443 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD wget --quiet --tries=1 --spider http://localhost/health || exit 1 - -# Environment variables with defaults -ENV BACKEND_HOST=gohoarder-server \ - BACKEND_PORT=8080 \ - FRONTEND_HOST=gohoarder-frontend \ - FRONTEND_PORT=80 \ - SERVER_NAME=_ - -# Use nginx with template substitution -CMD ["/bin/sh", "-c", "envsubst '$$BACKEND_HOST $$BACKEND_PORT $$FRONTEND_HOST $$FRONTEND_PORT $$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"] diff --git a/Dockerfile.migrate b/Dockerfile.migrate index f761232..f51f017 100644 --- a/Dockerfile.migrate +++ b/Dockerfile.migrate @@ -1,47 +1,7 @@ # Migration Engine - Database Migration Tool -# Multi-stage build to compile with CGO support +# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation) +# GoReleaser injects the platform-specific binary automatically -# Build stage -# Let buildx handle platform automatically -FROM golang:1.25-alpine AS builder - -# Get build platform from buildx -ARG TARGETPLATFORM -ARG TARGETOS -ARG TARGETARCH - -# Install build dependencies for CGO -RUN apk add --no-cache \ - gcc \ - g++ \ - musl-dev \ - sqlite-dev \ - git - -WORKDIR /build - -# Copy go mod files -COPY go.mod go.sum ./ -RUN go mod download - -# Copy source code -COPY . . - -# Build with CGO enabled -ARG TARGETOS -ARG TARGETARCH -ARG VERSION=dev -ARG GIT_COMMIT=unknown -ARG BUILD_TIME=unknown - -RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \ - go build -ldflags="-s -w \ - -X main.Version=${VERSION} \ - -X main.GitCommit=${GIT_COMMIT} \ - -X main.BuildTime=${BUILD_TIME}" \ - -o migrate ./cmd/migrate - -# Runtime stage FROM alpine:latest # Install runtime dependencies (including CGO/SQLite dependencies) @@ -58,10 +18,14 @@ RUN apk add --no-cache \ RUN addgroup -g 1000 gohoarder && \ adduser -D -u 1000 -G gohoarder gohoarder -# Copy binary from builder -COPY --from=builder /build/migrate /usr/local/bin/migrate +# Copy pre-built binary from GoReleaser +# GoReleaser will automatically inject the correct binary for the target platform +COPY migrate /usr/local/bin/migrate RUN chmod +x /usr/local/bin/migrate +# Copy migration SQL files +COPY migrations /migrations + WORKDIR /app USER gohoarder diff --git a/Dockerfile.scanner b/Dockerfile.scanner index c10a367..196e696 100644 --- a/Dockerfile.scanner +++ b/Dockerfile.scanner @@ -1,47 +1,7 @@ # Scanning Engine - Background Scanner Worker -# Multi-stage build to compile with CGO support +# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation) +# GoReleaser injects the platform-specific binary automatically -# Build stage -# Let buildx handle platform automatically -FROM golang:1.25-alpine AS builder - -# Get build platform from buildx -ARG TARGETPLATFORM -ARG TARGETOS -ARG TARGETARCH - -# Install build dependencies for CGO -RUN apk add --no-cache \ - gcc \ - g++ \ - musl-dev \ - sqlite-dev \ - git - -WORKDIR /build - -# Copy go mod files -COPY go.mod go.sum ./ -RUN go mod download - -# Copy source code -COPY . . - -# Build with CGO enabled -ARG TARGETOS -ARG TARGETARCH -ARG VERSION=dev -ARG GIT_COMMIT=unknown -ARG BUILD_TIME=unknown - -RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \ - go build -ldflags="-s -w \ - -X github.com/lukaszraczylo/gohoarder/internal/version.Version=${VERSION} \ - -X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit=${GIT_COMMIT} \ - -X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime=${BUILD_TIME}" \ - -o gohoarder ./cmd/gohoarder - -# Runtime stage FROM alpine:latest # Install scanning tools and runtime dependencies (including CGO/SQLite dependencies) @@ -79,8 +39,9 @@ RUN mkdir -p /var/cache/gohoarder \ /var/lib/gohoarder \ /var/lib/trivy -# Copy binary from builder -COPY --from=builder /build/gohoarder /usr/local/bin/gohoarder +# Copy pre-built binary from GoReleaser +# GoReleaser will automatically inject the correct binary for the target platform +COPY gohoarder /usr/local/bin/gohoarder RUN chmod +x /usr/local/bin/gohoarder # Copy example config diff --git a/Dockerfile.server b/Dockerfile.server index fdf6fdd..d2a941a 100644 --- a/Dockerfile.server +++ b/Dockerfile.server @@ -1,50 +1,10 @@ -# Application Engine - GoHoarder Server -# Multi-stage build to compile with CGO support +# Application Engine - Main GoHoarder Server +# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation) +# GoReleaser injects the platform-specific binary automatically -# Build stage -# Let buildx handle platform automatically -FROM golang:1.25-alpine AS builder - -# Get build platform from buildx -ARG TARGETPLATFORM -ARG TARGETOS -ARG TARGETARCH - -# Install build dependencies for CGO -RUN apk add --no-cache \ - gcc \ - g++ \ - musl-dev \ - sqlite-dev \ - git - -WORKDIR /build - -# Copy go mod files -COPY go.mod go.sum ./ -RUN go mod download - -# Copy source code -COPY . . - -# Build with CGO enabled -ARG TARGETOS -ARG TARGETARCH -ARG VERSION=dev -ARG GIT_COMMIT=unknown -ARG BUILD_TIME=unknown - -RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \ - go build -ldflags="-s -w \ - -X github.com/lukaszraczylo/gohoarder/internal/version.Version=${VERSION} \ - -X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit=${GIT_COMMIT} \ - -X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime=${BUILD_TIME}" \ - -o gohoarder ./cmd/gohoarder - -# Runtime stage FROM alpine:latest -# Install runtime dependencies (including CGO/SQLite dependencies) +# Install runtime dependencies (CGO/SQLite requires these) RUN apk add --no-cache \ ca-certificates \ tzdata \ @@ -66,8 +26,9 @@ RUN mkdir -p /var/cache/gohoarder \ chmod -R 750 /var/cache/gohoarder \ /var/lib/gohoarder -# Copy binary from builder -COPY --from=builder /build/gohoarder /usr/local/bin/gohoarder +# Copy pre-built binary from GoReleaser +# GoReleaser will automatically inject the correct binary for the target platform +COPY gohoarder /usr/local/bin/gohoarder RUN chmod +x /usr/local/bin/gohoarder # Copy example config diff --git a/deployments/kubernetes/deployment-all-in-one.yaml b/deployments/kubernetes/deployment-all-in-one.yaml index 22eec32..be41c79 100644 --- a/deployments/kubernetes/deployment-all-in-one.yaml +++ b/deployments/kubernetes/deployment-all-in-one.yaml @@ -1,5 +1,6 @@ # GoHoarder - Kubernetes Deployment (All-in-One) # This manifest deploys all GoHoarder services under a single ingress +# The frontend includes integrated nginx reverse proxy to the backend # # Usage: # kubectl create namespace gohoarder @@ -222,6 +223,13 @@ spec: value: "1.0.0" - name: APP_NAME value: GoHoarder + # Backend proxy configuration (frontend includes nginx reverse proxy) + - name: BACKEND_HOST + value: gohoarder-server + - name: BACKEND_PORT + value: "8080" + - name: SERVER_NAME + value: hoarder.i.raczylo.com livenessProbe: httpGet: path: / @@ -325,88 +333,6 @@ spec: configMap: name: gohoarder-config ---- -# Deployment - Gateway (Nginx Reverse Proxy) -apiVersion: apps/v1 -kind: Deployment -metadata: - name: gohoarder-gateway - namespace: gohoarder - labels: - app.kubernetes.io/name: gohoarder - app.kubernetes.io/component: gateway -spec: - replicas: 2 - selector: - matchLabels: - app.kubernetes.io/name: gohoarder - app.kubernetes.io/component: gateway - template: - metadata: - labels: - app.kubernetes.io/name: gohoarder - app.kubernetes.io/component: gateway - spec: - containers: - - name: gateway - image: ghcr.io/lukaszraczylo/gohoarder-gateway:latest - imagePullPolicy: Always - ports: - - name: http - containerPort: 80 - protocol: TCP - env: - - name: BACKEND_HOST - value: gohoarder-server - - name: BACKEND_PORT - value: "8080" - - name: FRONTEND_HOST - value: gohoarder-frontend - - name: FRONTEND_PORT - value: "80" - - name: SERVER_NAME - value: hoarder.i.raczylo.com - livenessProbe: - httpGet: - path: /health - port: 80 - initialDelaySeconds: 5 - periodSeconds: 30 - readinessProbe: - httpGet: - path: /health - port: 80 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 500m - memory: 256Mi - ---- -# Service - Gateway -apiVersion: v1 -kind: Service -metadata: - name: gohoarder-gateway - namespace: gohoarder - labels: - app.kubernetes.io/name: gohoarder - app.kubernetes.io/component: gateway -spec: - type: ClusterIP - ports: - - name: http - port: 80 - targetPort: http - protocol: TCP - selector: - app.kubernetes.io/name: gohoarder - app.kubernetes.io/component: gateway - --- # Ingress - Expose via domain apiVersion: networking.k8s.io/v1 @@ -436,7 +362,7 @@ spec: pathType: Prefix backend: service: - name: gohoarder-gateway + name: gohoarder-frontend port: number: 80 # Uncomment for HTTPS/TLS @@ -477,20 +403,20 @@ spec: averageUtilization: 80 --- -# HorizontalPodAutoscaler - Gateway +# HorizontalPodAutoscaler - Frontend apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: - name: gohoarder-gateway + name: gohoarder-frontend namespace: gohoarder labels: app.kubernetes.io/name: gohoarder - app.kubernetes.io/component: gateway + app.kubernetes.io/component: frontend spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: gohoarder-gateway + name: gohoarder-frontend minReplicas: 2 maxReplicas: 10 metrics: diff --git a/docker-compose.example.yaml b/docker-compose.example.yaml index 29446d0..ecf707c 100644 --- a/docker-compose.example.yaml +++ b/docker-compose.example.yaml @@ -2,7 +2,7 @@ version: '3.8' # GoHoarder - Unified Deployment Example # This docker-compose file demonstrates deploying all GoHoarder services -# under a single domain using the gateway reverse proxy +# The frontend includes integrated reverse proxy to the backend services: # Backend - Main application server @@ -39,7 +39,7 @@ services: retries: 3 start_period: 5s - # Frontend - Web dashboard + # Frontend - Web dashboard with integrated reverse proxy gohoarder-frontend: image: ghcr.io/lukaszraczylo/gohoarder-frontend:latest container_name: gohoarder-frontend @@ -49,8 +49,19 @@ services: - API_BASE_URL=/api - APP_VERSION=1.0.0 - APP_NAME=GoHoarder + # Backend proxy configuration (frontend includes nginx reverse proxy) + - BACKEND_HOST=gohoarder-server + - BACKEND_PORT=8080 + - SERVER_NAME=hoarder.i.raczylo.com + ports: + # Map to host port 80 (HTTP) + - "80:80" + # Map to host port 443 (HTTPS) - uncomment if using SSL + # - "443:443" networks: - gohoarder-internal + depends_on: + - gohoarder-server healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"] interval: 30s @@ -82,41 +93,6 @@ services: # profiles: # - scanner - # Gateway - Nginx reverse proxy - gohoarder-gateway: - image: ghcr.io/lukaszraczylo/gohoarder-gateway:latest - container_name: gohoarder-gateway - restart: unless-stopped - environment: - # Backend service connection - - BACKEND_HOST=gohoarder-server - - BACKEND_PORT=8080 - # Frontend service connection - - FRONTEND_HOST=gohoarder-frontend - - FRONTEND_PORT=80 - # Server configuration - - SERVER_NAME=hoarder.i.raczylo.com - ports: - # Map to host port 80 (HTTP) - - "80:80" - # Map to host port 443 (HTTPS) - uncomment if using SSL - # - "443:443" - networks: - - gohoarder-internal - depends_on: - - gohoarder-server - - gohoarder-frontend - # Uncomment if using custom SSL certificates - # volumes: - # - ./ssl/cert.pem:/etc/nginx/ssl/cert.pem:ro - # - ./ssl/key.pem:/etc/nginx/ssl/key.pem:ro - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 5s - networks: gohoarder-internal: driver: bridge @@ -144,7 +120,7 @@ volumes: # - Metrics: http://localhost/metrics # # For production: -# - Enable HTTPS in the gateway container +# - Enable HTTPS in the frontend container (add SSL certificates to nginx) # - Set up proper SSL certificates # - Configure firewall rules # - Set appropriate resource limits