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
This commit is contained in:
2026-01-04 00:53:44 +00:00
parent e1a02a6d69
commit 8a9d786b1a
7 changed files with 62 additions and 485 deletions
+15 -29
View File
@@ -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
-197
View File
@@ -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;'"]
+8 -44
View File
@@ -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
+5 -44
View File
@@ -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
+7 -46
View File
@@ -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
@@ -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:
+14 -38
View File
@@ -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