From 61178ebb7beefc5cefcafaa5c87f6a62465193a4 Mon Sep 17 00:00:00 2001 From: dex-bot Date: Mon, 27 Apr 2026 08:33:18 +0000 Subject: [PATCH] feat(CUB-64): Docker runtime setup for development & deployment - Backend Dockerfile: added curl install for health check (not in aspnet base image) - Frontend Dockerfile: multi-stage Angular build with nginx serving - Frontend nginx.conf: SPA routing, API proxy, SignalR WebSocket support, health endpoint - Frontend .dockerignore: excludes node_modules, dist, .angular, etc. - docker-compose.dev.yml: added PostgreSQL service, fixed frontend context path, renamed web service from control-center-web to extrudex-web, added DB env vars, proper service dependencies with health checks - deploy.sh: updated service list to include PostgreSQL port --- backend/Dockerfile | 3 +++ deploy.sh | 5 ++-- docker-compose.dev.yml | 40 +++++++++++++++++++++++++++---- frontend/.dockerignore | 10 ++++++++ frontend/Dockerfile | 30 +++++++++++++++++++++++ frontend/nginx.conf | 54 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile create mode 100644 frontend/nginx.conf diff --git a/backend/Dockerfile b/backend/Dockerfile index b604978..23aacef 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -17,6 +17,9 @@ RUN dotnet publish Extrudex.csproj \ FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS runtime WORKDIR /app +# Install curl for health check (not included in aspnet base image) +RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* + # Non-root user for security RUN adduser --disabled-password --gecos "" appuser USER appuser diff --git a/deploy.sh b/deploy.sh index d17960c..d00c1e6 100755 --- a/deploy.sh +++ b/deploy.sh @@ -18,13 +18,14 @@ echo "📦 Building and starting services..." $COMPOSE_CMD -f docker-compose.dev.yml up -d --build echo "⏳ Waiting for services to become healthy..." -sleep 10 +sleep 15 echo "✅ Deployment complete!" echo "" echo "Services running:" +echo " • PostgreSQL: localhost:5433" echo " • Extrudex API: http://localhost:5080" -echo " • Control Center Web: http://localhost:5081" +echo " • Extrudex Web: http://localhost:5081" echo "" echo "To view logs:" echo " $COMPOSE_CMD -f docker-compose.dev.yml logs -f" diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 2859dff..a0a3d49 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,6 +1,25 @@ -version: '3.8' - services: + extrudex-db: + image: postgres:16-alpine + container_name: extrudex-db + environment: + POSTGRES_USER: extrudex + POSTGRES_PASSWORD: changeme + POSTGRES_DB: extrudex + ports: + - "5433:5432" + volumes: + - extrudex-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U extrudex"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + restart: unless-stopped + networks: + - extrudex-network + extrudex-api: build: context: ./backend @@ -11,6 +30,14 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=http://+:8080 + - EXTRUDEX_DB_HOST=extrudex-db + - EXTRUDEX_DB_PORT=5432 + - EXTRUDEX_DB_NAME=extrudex + - EXTRUDEX_DB_USER=extrudex + - EXTRUDEX_DB_PASSWORD=changeme + depends_on: + extrudex-db: + condition: service_healthy restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] @@ -21,11 +48,11 @@ services: networks: - extrudex-network - control-center-web: + extrudex-web: build: - context: ../Control-Center/frontend + context: ./frontend dockerfile: Dockerfile - container_name: control-center-web + container_name: extrudex-web ports: - "5081:80" depends_on: @@ -35,6 +62,9 @@ services: networks: - extrudex-network +volumes: + extrudex-db-data: + networks: extrudex-network: driver: bridge \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..2803aa5 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,10 @@ +node_modules +dist +.git +.angular +.vscode +*.md +.editorconfig +.prettierrc +src/test.ts +**/*.spec.ts \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..489494c --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,30 @@ +# ── Stage 1: Build the Angular application ─────────────────── +FROM node:22-alpine AS build +WORKDIR /app + +# Copy package files first for better layer caching +COPY package.json package-lock.json ./ +RUN npm ci + +# Copy source and build +COPY . . +RUN npx ng build --configuration production + +# ── Stage 2: Serve static files with nginx ─────────────────── +FROM nginx:alpine + +# Remove default nginx config +RUN rm /etc/nginx/conf.d/default.conf + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built Angular artifacts from build stage +COPY --from=build /app/dist/frontend/browser /usr/share/nginx/html + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD wget -qO- http://localhost:80/health || exit 1 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/frontend/nginx.conf b/frontend/nginx.conf new file mode 100644 index 0000000..dfcf5af --- /dev/null +++ b/frontend/nginx.conf @@ -0,0 +1,54 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; + gzip_min_length 256; + + # Angular SPA — fallback to index.html for client-side routing + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets aggressively + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Proxy API requests to backend + # Uses resolver so nginx doesn't crash if backend isn't available at startup + resolver 127.0.0.11 valid=30s ipv6=off; + set $backend "extrudex-api:8080"; + + location /api/ { + proxy_pass http://$backend; + 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; + } + + # SignalR WebSocket support + location /hubs/ { + proxy_pass http://$backend; + 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; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "ok"; + add_header Content-Type text/plain; + } +} \ No newline at end of file