refactor: Split monolithic container into separate API and frontend deployments
ARCHITECTURE CHANGES:
- API service: Node.js server on port 3001 (Dockerfile.api)
- Frontend service: Nginx serving React on port 80 (Dockerfile.frontend)
- Each service has its own deployment, service, and replicas
- Ingress routes / to frontend and /api/ to API
KUBERNETES MANIFESTS:
- api-deployment.yaml: 2 replicas of Node.js API server
- api-service.yaml: ClusterIP service for API
- frontend-deployment.yaml: 2 replicas of Nginx frontend
- frontend-service.yaml: ClusterIP service for frontend
- Updated ingress.yaml: Routes traffic based on paths
- Updated kustomization.yaml: References new deployments
DOCKER IMAGES:
- Dockerfile.api: Minimal Node.js image for API (~200MB)
- Dockerfile.frontend: Nginx + React build (~50MB)
- Separate builds in workflow for independent versioning
NGINX CONFIGURATION:
- Removed API proxy (separate service now)
- Simplified config for static file serving only
BENEFITS:
- Independent scaling (can scale frontend/API separately)
- Smaller images with minimal base images
- API errors don't affect frontend availability
- Easier to update one service without affecting the other
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7ae458f768
commit
0493a7ef70
@ -39,12 +39,18 @@ jobs:
|
||||
- name: Login to Container Registry
|
||||
run: echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USER" --password-stdin
|
||||
|
||||
- name: Build and Push Docker image
|
||||
- name: Build and Push API image
|
||||
run: |
|
||||
docker build -t "$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG" -t "$REGISTRY_URL/sortifal/pfee:latest" .
|
||||
docker build -f Dockerfile.api -t "$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG" -t "$REGISTRY_URL/sortifal/pfee:latest" .
|
||||
docker push "$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG"
|
||||
docker push "$REGISTRY_URL/sortifal/pfee:latest"
|
||||
|
||||
- name: Build and Push Frontend image
|
||||
run: |
|
||||
docker build -f Dockerfile.frontend -t "$REGISTRY_URL/sortifal/pfee-frontend:$IMAGE_TAG" -t "$REGISTRY_URL/sortifal/pfee-frontend:latest" .
|
||||
docker push "$REGISTRY_URL/sortifal/pfee-frontend:$IMAGE_TAG"
|
||||
docker push "$REGISTRY_URL/sortifal/pfee-frontend:latest"
|
||||
|
||||
deploy:
|
||||
name: Deploy to Kubernetes
|
||||
runs-on: ubuntu-latest
|
||||
@ -84,9 +90,11 @@ jobs:
|
||||
run: |
|
||||
cd k8s
|
||||
kustomize edit set image gitea.vidoks.fr/sortifal/pfee="$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG"
|
||||
kustomize edit set image gitea.vidoks.fr/sortifal/pfee-frontend="$REGISTRY_URL/sortifal/pfee-frontend:$IMAGE_TAG"
|
||||
kubectl apply -k .
|
||||
|
||||
- name: Verify deployment
|
||||
run: |
|
||||
kubectl rollout status deployment/sqdc-dashboard -n sqdc-dashboard --timeout=5m
|
||||
kubectl rollout status deployment/sqdc-api -n sqdc-dashboard --timeout=5m
|
||||
kubectl rollout status deployment/sqdc-frontend -n sqdc-dashboard --timeout=5m
|
||||
kubectl get pods,svc,ingress -n sqdc-dashboard
|
||||
|
||||
24
dashboard-sqdc/Dockerfile.api
Normal file
24
dashboard-sqdc/Dockerfile.api
Normal file
@ -0,0 +1,24 @@
|
||||
# API Server Dockerfile
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install only production dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy database and server files
|
||||
COPY database ./database
|
||||
COPY server.js .
|
||||
|
||||
# Expose API port
|
||||
EXPOSE 3001
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3001/api/categories', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
|
||||
|
||||
# Start the API server
|
||||
CMD ["node", "server.js"]
|
||||
35
dashboard-sqdc/Dockerfile.frontend
Normal file
35
dashboard-sqdc/Dockerfile.frontend
Normal file
@ -0,0 +1,35 @@
|
||||
# Multi-stage build for React frontend with Nginx
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install all dependencies (including dev) needed for build
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Production image with Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy custom nginx configuration
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy built application from builder stage
|
||||
COPY --from=builder /app/build /usr/share/nginx/html
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@ -1,30 +1,25 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sqdc-dashboard
|
||||
name: sqdc-api
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-dashboard
|
||||
app: sqdc-api
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sqdc-dashboard
|
||||
app: sqdc-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sqdc-dashboard
|
||||
app: sqdc-api
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: registry-credentials
|
||||
containers:
|
||||
- name: dashboard
|
||||
- name: api
|
||||
image: gitea.vidoks.fr/sortifal/pfee:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
- containerPort: 3001
|
||||
name: api
|
||||
protocol: TCP
|
||||
@ -33,23 +28,23 @@ spec:
|
||||
value: "production"
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
path: /api/categories
|
||||
port: 3001
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
path: /api/categories
|
||||
port: 3001
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
16
dashboard-sqdc/k8s/api-service.yaml
Normal file
16
dashboard-sqdc/k8s/api-service.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sqdc-api
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-api
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 3001
|
||||
targetPort: 3001
|
||||
protocol: TCP
|
||||
name: api
|
||||
selector:
|
||||
app: sqdc-api
|
||||
48
dashboard-sqdc/k8s/frontend-deployment.yaml
Normal file
48
dashboard-sqdc/k8s/frontend-deployment.yaml
Normal file
@ -0,0 +1,48 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: sqdc-frontend
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-frontend
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: sqdc-frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sqdc-frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: gitea.vidoks.fr/sortifal/pfee-frontend:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 2
|
||||
16
dashboard-sqdc/k8s/frontend-service.yaml
Normal file
16
dashboard-sqdc/k8s/frontend-service.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sqdc-frontend
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-frontend
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: sqdc-frontend
|
||||
@ -4,7 +4,6 @@ metadata:
|
||||
name: sqdc-dashboard-ingress
|
||||
namespace: sqdc-dashboard
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
@ -16,6 +15,13 @@ spec:
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: sqdc-dashboard-service
|
||||
name: sqdc-frontend
|
||||
port:
|
||||
number: 80
|
||||
- path: /api/
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: sqdc-api
|
||||
port:
|
||||
number: 3001
|
||||
|
||||
@ -5,18 +5,21 @@ namespace: sqdc-dashboard
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- api-deployment.yaml
|
||||
- api-service.yaml
|
||||
- frontend-deployment.yaml
|
||||
- frontend-service.yaml
|
||||
- ingress.yaml
|
||||
- configmap.yaml
|
||||
# Note: PVC removed - using emptyDir for temporary storage
|
||||
|
||||
commonLabels:
|
||||
app: sqdc-dashboard
|
||||
managed-by: kustomize
|
||||
|
||||
# Image tag will be set via kustomize edit or --kustomize-replace during deployment
|
||||
# Image tags will be set via kustomize edit during deployment
|
||||
images:
|
||||
- name: gitea.vidoks.fr/sortifal/pfee
|
||||
newTag: latest
|
||||
newName: gitea.vidoks.fr/sortifal/pfee
|
||||
- name: gitea.vidoks.fr/sortifal/pfee-frontend
|
||||
newTag: latest
|
||||
newName: gitea.vidoks.fr/sortifal/pfee-frontend
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: sqdc-dashboard-service
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-dashboard
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: sqdc-dashboard
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
- name: api
|
||||
port: 3001
|
||||
targetPort: 3001
|
||||
protocol: TCP
|
||||
@ -10,19 +10,6 @@ server {
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
|
||||
|
||||
# API proxy
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3001/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
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;
|
||||
}
|
||||
|
||||
# Static assets with caching
|
||||
location /static/ {
|
||||
expires 1y;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user