Add GHCR container image support with multi-arch builds and security scanning

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-08 17:37:44 +00:00
parent bf674e0da4
commit 9a757fd5df
5 changed files with 625 additions and 0 deletions

99
.dockerignore Normal file
View File

@@ -0,0 +1,99 @@
# Dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Testing
coverage
.nyc_output
*.test.ts
*.test.tsx
*.spec.ts
*.spec.tsx
__tests__
__mocks__
.vitest
# Next.js
.next
out
dist
build
# Production
/build
# Misc
.DS_Store
*.pem
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Local env files
.env
.env*.local
.env.development
.env.test
.env.production
# Vercel
.vercel
# TypeScript
*.tsbuildinfo
next-env.d.ts
# IDE
.vscode
.idea
*.swp
*.swo
*~
# Git
.git
.gitignore
.gitattributes
# Documentation
*.md
docs
README*
CHANGELOG*
LICENSE
# CI/CD
.github
.gitlab-ci.yml
azure-pipelines.yml
# Docker
Dockerfile*
docker-compose*
.dockerignore
# Development
.editorconfig
.prettierrc*
.eslintrc*
.eslintignore
# Storybook
.storybook
storybook-static
# E2E
e2e
playwright-report
test-results
# Temporary files
tmp
temp
.tmp
.cache

138
.github/workflows/container-build.yml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: Build and Push GHCR Images
on:
push:
branches:
- main
- develop
tags:
- 'v*.*.*'
pull_request:
branches:
- main
- develop
workflow_dispatch:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
permissions:
contents: read
packages: write
id-token: write
jobs:
build-and-push:
name: Build and Push Docker Images
runs-on: ubuntu-latest
strategy:
matrix:
include:
- image: nextjs-app
context: .
dockerfile: ./frontends/nextjs/Dockerfile
platforms: linux/amd64,linux/arm64
- image: dbal-daemon
context: ./dbal/production
dockerfile: ./dbal/production/build-config/Dockerfile
platforms: linux/amd64,linux/arm64
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platforms }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
VERSION=${{ steps.meta.outputs.version }}
- name: Generate artifact attestation
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
security-scan:
name: Security Scan Images
runs-on: ubuntu-latest
needs: build-and-push
if: github.event_name != 'pull_request'
strategy:
matrix:
image: [nextjs-app, dbal-daemon]
steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/${{ matrix.image }}:${{ github.ref_name }}
format: 'sarif'
output: 'trivy-results-${{ matrix.image }}.sarif'
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results-${{ matrix.image }}.sarif'
category: container-${{ matrix.image }}
publish-manifest:
name: Create Multi-Arch Manifest
runs-on: ubuntu-latest
needs: build-and-push
if: github.event_name != 'pull_request'
steps:
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest for all images
run: |
for image in nextjs-app dbal-daemon; do
docker manifest create \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/$image:${{ github.ref_name }} \
--amend ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/$image:${{ github.ref_name }}-amd64 \
--amend ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/$image:${{ github.ref_name }}-arm64
docker manifest push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/$image:${{ github.ref_name }}
done

110
docker-compose.ghcr.yml Normal file
View File

@@ -0,0 +1,110 @@
version: '3.9'
services:
# MetaBuilder Next.js App
nextjs-app:
image: ghcr.io/${GITHUB_REPOSITORY:-johndoe6345789/metabuilder}/nextjs-app:${IMAGE_TAG:-latest}
container_name: metabuilder-nextjs
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=file:/app/data/metabuilder.db
- DBAL_API_URL=http://dbal-daemon:8080
- DBAL_WS_URL=ws://dbal-daemon:50051
- NEXTAUTH_URL=http://localhost:3000
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-change-me-in-production}
volumes:
- metabuilder-data:/app/data
depends_on:
- dbal-daemon
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
networks:
- metabuilder-network
# DBAL Daemon (Production)
dbal-daemon:
image: ghcr.io/${GITHUB_REPOSITORY:-johndoe6345789/metabuilder}/dbal-daemon:${IMAGE_TAG:-latest}
container_name: metabuilder-dbal
ports:
- "8080:8080"
- "50051:50051"
environment:
- NODE_ENV=production
- DATABASE_URL=file:/app/data/metabuilder.db
- LOG_LEVEL=info
- ENABLE_METRICS=true
volumes:
- metabuilder-data:/app/data
- dbal-logs:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
networks:
- metabuilder-network
# Prometheus for metrics (optional)
prometheus:
image: prom/prometheus:latest
container_name: metabuilder-prometheus
ports:
- "9090:9090"
volumes:
- ./deployment/docker/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
restart: unless-stopped
networks:
- metabuilder-network
profiles:
- monitoring
# Grafana for visualization (optional)
grafana:
image: grafana/grafana:latest
container_name: metabuilder-grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
volumes:
- grafana-data:/var/lib/grafana
- ./deployment/docker/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
- ./deployment/docker/grafana/datasources:/etc/grafana/provisioning/datasources:ro
depends_on:
- prometheus
restart: unless-stopped
networks:
- metabuilder-network
profiles:
- monitoring
volumes:
metabuilder-data:
driver: local
dbal-logs:
driver: local
prometheus-data:
driver: local
grafana-data:
driver: local
networks:
metabuilder-network:
driver: bridge

189
docs/CONTAINER_IMAGES.md Normal file
View File

@@ -0,0 +1,189 @@
# MetaBuilder Container Images
MetaBuilder provides official container images hosted on GitHub Container Registry (GHCR) for easy deployment.
## Available Images
### 1. Next.js App (`ghcr.io/johndoe6345789/metabuilder/nextjs-app`)
The main MetaBuilder web application built with Next.js.
**Features:**
- Multi-architecture support (amd64, arm64)
- Standalone output for minimal image size
- Built-in health checks
- Non-root user for security
- DBAL types pre-generated
**Tags:**
- `latest` - Latest stable build from main branch
- `develop` - Latest development build
- `v*.*.*` - Semantic version tags
- `main-<sha>` - Specific commit from main branch
### 2. DBAL Daemon (`ghcr.io/johndoe6345789/metabuilder/dbal-daemon`)
The secure C++ DBAL daemon for production deployments.
**Features:**
- Multi-architecture support (amd64, arm64)
- Process isolation for security
- Connection pooling
- Row-level security enforcement
## Quick Start
### Using Docker Compose with GHCR Images
```bash
# Pull and start all services
docker compose -f docker-compose.ghcr.yml up -d
# With monitoring stack
docker compose -f docker-compose.ghcr.yml --profile monitoring up -d
# Stop services
docker compose -f docker-compose.ghcr.yml down
# View logs
docker compose -f docker-compose.ghcr.yml logs -f
```
### Running Individual Containers
```bash
# Run Next.js app
docker run -d \
--name metabuilder-nextjs \
-p 3000:3000 \
-e DATABASE_URL=file:/app/data/metabuilder.db \
-v metabuilder-data:/app/data \
ghcr.io/johndoe6345789/metabuilder/nextjs-app:latest
# Run DBAL daemon
docker run -d \
--name metabuilder-dbal \
-p 8080:8080 \
-p 50051:50051 \
-e DATABASE_URL=file:/app/data/metabuilder.db \
-v metabuilder-data:/app/data \
ghcr.io/johndoe6345789/metabuilder/dbal-daemon:latest
```
## Authentication
To pull images from GHCR, you need a GitHub Personal Access Token with `read:packages` scope:
```bash
# Login to GHCR
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Pull an image
docker pull ghcr.io/johndoe6345789/metabuilder/nextjs-app:latest
```
## Building Images Locally
```bash
# Build Next.js app
docker build -f frontends/nextjs/Dockerfile -t metabuilder/nextjs-app:local .
# Build with specific platform
docker buildx build \
--platform linux/amd64,linux/arm64 \
-f frontends/nextjs/Dockerfile \
-t metabuilder/nextjs-app:local .
```
## Environment Variables
### Next.js App
- `DATABASE_URL` - Database connection string
- `DBAL_API_URL` - DBAL daemon API URL (default: `http://localhost:8080`)
- `DBAL_WS_URL` - DBAL daemon WebSocket URL (default: `ws://localhost:50051`)
- `NEXTAUTH_SECRET` - NextAuth secret for session encryption
- `NODE_ENV` - Environment mode (production/development)
### DBAL Daemon
- `DATABASE_URL` - Database connection string
- `LOG_LEVEL` - Logging level (debug/info/warn/error)
- `ENABLE_METRICS` - Enable Prometheus metrics (true/false)
- `MAX_CONNECTIONS` - Maximum database connections
## Health Checks
Both images include health checks:
```bash
# Check Next.js app health
curl http://localhost:3000/api/health
# Check DBAL daemon health
curl http://localhost:8080/health
```
## Security
### Image Scanning
All images are automatically scanned for vulnerabilities using Trivy during the CI/CD pipeline. Results are available in the GitHub Security tab.
### Attestations
Build provenance attestations are generated for all images pushed to GHCR, ensuring supply chain security.
### Non-Root Users
All containers run as non-root users:
- Next.js app runs as user `nextjs` (UID 1001)
- DBAL daemon runs as user `dbal` (UID 1000)
## Monitoring
When using the monitoring profile:
- **Prometheus**: http://localhost:9090
- **Grafana**: http://localhost:3001 (admin/admin)
## Volumes
- `metabuilder-data` - Persistent database and application data
- `dbal-logs` - DBAL daemon logs
- `prometheus-data` - Prometheus metrics storage
- `grafana-data` - Grafana dashboards and settings
## Troubleshooting
### Container won't start
```bash
# Check logs
docker logs metabuilder-nextjs
docker logs metabuilder-dbal
# Check health status
docker inspect --format='{{json .State.Health}}' metabuilder-nextjs
```
### Permission issues
```bash
# Ensure volumes have correct permissions
docker volume inspect metabuilder-data
```
### Network connectivity
```bash
# Test network connectivity between containers
docker compose -f docker-compose.ghcr.yml exec nextjs-app curl http://dbal-daemon:8080/health
```
## CI/CD Integration
Images are automatically built and pushed on:
- Push to `main` or `develop` branches
- New version tags (`v*.*.*`)
- Manual workflow dispatch
See `.github/workflows/container-build.yml` for the complete workflow.
## Support
For issues related to container images, please open an issue in the MetaBuilder repository with:
- Image tag being used
- Docker/Podman version
- Platform (amd64/arm64)
- Container logs
- docker-compose.yml configuration (if applicable)

View File

@@ -0,0 +1,89 @@
# Multi-stage Dockerfile for MetaBuilder Next.js App
# Optimized for production deployment with GHCR
# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
# Install system dependencies
RUN apk add --no-cache libc6-compat
# Copy package files for all workspaces
COPY package*.json ./
COPY frontends/nextjs/package*.json ./frontends/nextjs/
COPY dbal/development/package*.json ./dbal/development/
COPY config/package*.json ./config/ 2>/dev/null || true
# Install dependencies
RUN npm ci
# Stage 2: Builder
FROM node:20-alpine AS builder
WORKDIR /app
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/frontends/nextjs/node_modules ./frontends/nextjs/node_modules
COPY --from=deps /app/dbal/development/node_modules ./dbal/development/node_modules
# Copy source code
COPY . .
# Generate DBAL types from YAML schemas
WORKDIR /app/dbal/development
RUN npx tsx ../shared/tools/codegen/generate-types.ts
# Generate Prisma Client
WORKDIR /app/frontends/nextjs
RUN npm run db:generate
# Build Next.js app
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build
# Stage 3: Production runner
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Install runtime dependencies
RUN apk add --no-cache \
tini \
curl \
dumb-init
# Create non-root user
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Copy necessary files from builder
COPY --from=builder /app/frontends/nextjs/public ./public
COPY --from=builder /app/frontends/nextjs/.next/standalone ./
COPY --from=builder /app/frontends/nextjs/.next/static ./.next/static
# Copy Prisma schema and generated client
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/node_modules/@prisma ./node_modules/@prisma
# Set proper permissions
RUN chown -R nextjs:nodejs /app
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
CMD curl -f http://localhost:3000/api/health || exit 1
# Use dumb-init to handle signals
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "server.js"]