Compare commits
10 Commits
20d0993b06
...
0c9f5b58e0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c9f5b58e0 | ||
|
|
567245a5b0 | ||
|
|
0493a7ef70 | ||
|
|
7ae458f768 | ||
|
|
59e270e3ca | ||
|
|
02c41b9f4e | ||
|
|
9420975f76 | ||
|
|
00331c5d95 | ||
|
|
b98075df82 | ||
|
|
b555203dc7 |
@ -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
|
||||
@ -75,12 +81,20 @@ jobs:
|
||||
-n sqdc-dashboard \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
- name: Deploy to Kubernetes
|
||||
- name: Install Kustomize
|
||||
run: |
|
||||
kubectl apply -f k8s/namespace.yaml k8s/deployment.yaml k8s/service.yaml k8s/ingress.yaml
|
||||
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||
sudo mv kustomize /usr/local/bin/
|
||||
|
||||
- name: Update deployment and verify
|
||||
- name: Deploy with Kustomize
|
||||
run: |
|
||||
kubectl set image deployment/sqdc-dashboard dashboard="$REGISTRY_URL/sortifal/pfee:$IMAGE_TAG" -n sqdc-dashboard
|
||||
kubectl rollout status deployment/sqdc-dashboard -n sqdc-dashboard --timeout=5m
|
||||
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-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
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
node_modules
|
||||
build
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
|
||||
@ -8,8 +8,8 @@ WORKDIR /app
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production
|
||||
# Install all dependencies (including dev) needed for build
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
@ -17,6 +17,9 @@ COPY . .
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Install only production dependencies for runtime
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Stage 2: Production image with Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
@ -43,4 +46,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
|
||||
|
||||
# Start both nginx and the API server
|
||||
CMD sh -c "node /app/server.js & nginx -g 'daemon off;'"
|
||||
CMD ["sh", "-c", "node /app/server.js & nginx -g 'daemon off;'"]
|
||||
|
||||
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
|
||||
@ -60,17 +55,6 @@ spec:
|
||||
volumes:
|
||||
- name: database
|
||||
persistentVolumeClaim:
|
||||
claimName: sqdc-dashboard-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: sqdc-dashboard-pvc
|
||||
namespace: sqdc-dashboard
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
storageClassName: standard
|
||||
claimName: sqdc-database-pvc
|
||||
imagePullSecrets:
|
||||
- name: registry-credentials
|
||||
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
|
||||
58
dashboard-sqdc/k8s/db-init-job.yaml
Normal file
58
dashboard-sqdc/k8s/db-init-job.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: sqdc-db-init
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-api
|
||||
spec:
|
||||
backoffLimit: 3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: sqdc-api-init
|
||||
spec:
|
||||
serviceAccountName: sqdc-db-init
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: db-init
|
||||
image: gitea.vidoks.fr/sortifal/pfee:latest
|
||||
imagePullPolicy: Always
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
echo "Starting database initialization..."
|
||||
|
||||
if [ ! -f /app/database/sqdc.db ]; then
|
||||
echo "Creating new database from schema..."
|
||||
sqlite3 /app/database/sqdc.db < /app/database/schema.sql
|
||||
|
||||
echo "Populating database with sample data..."
|
||||
python3 /app/database/populate_db.py
|
||||
|
||||
echo "✅ Database initialized successfully"
|
||||
else
|
||||
echo "✅ Database already exists, skipping initialization"
|
||||
fi
|
||||
|
||||
echo "Verifying database integrity..."
|
||||
sqlite3 /app/database/sqdc.db "SELECT COUNT(*) as table_count FROM sqlite_master WHERE type='table';"
|
||||
|
||||
echo "Database initialization complete"
|
||||
volumeMounts:
|
||||
- name: database
|
||||
mountPath: /app/database
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "200m"
|
||||
volumes:
|
||||
- name: database
|
||||
persistentVolumeClaim:
|
||||
claimName: sqdc-database-pvc
|
||||
imagePullSecrets:
|
||||
- name: registry-credentials
|
||||
7
dashboard-sqdc/k8s/db-init-sa.yaml
Normal file
7
dashboard-sqdc/k8s/db-init-sa.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: sqdc-db-init
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-api
|
||||
50
dashboard-sqdc/k8s/frontend-deployment.yaml
Normal file
50
dashboard-sqdc/k8s/frontend-deployment.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
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
|
||||
imagePullSecrets:
|
||||
- name: registry-credentials
|
||||
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,18 +4,26 @@ metadata:
|
||||
name: sqdc-dashboard-ingress
|
||||
namespace: sqdc-dashboard
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||
nginx.ingress.kubernetes.io/rewrite-target: /$2
|
||||
nginx.ingress.kubernetes.io/use-regex: "true"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: diwii.sortifal.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /api(/|$)(.*)
|
||||
pathType: ImplementationSpecific
|
||||
backend:
|
||||
service:
|
||||
name: sqdc-api
|
||||
port:
|
||||
number: 3001
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: sqdc-dashboard-service
|
||||
name: sqdc-frontend
|
||||
port:
|
||||
number: 80
|
||||
|
||||
@ -5,15 +5,24 @@ namespace: sqdc-dashboard
|
||||
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- pvc.yaml
|
||||
- db-init-sa.yaml
|
||||
- db-init-job.yaml
|
||||
- api-deployment.yaml
|
||||
- api-service.yaml
|
||||
- frontend-deployment.yaml
|
||||
- frontend-service.yaml
|
||||
- ingress.yaml
|
||||
- configmap.yaml
|
||||
|
||||
commonLabels:
|
||||
app: sqdc-dashboard
|
||||
managed-by: kustomize
|
||||
|
||||
# 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
|
||||
|
||||
13
dashboard-sqdc/k8s/pvc.yaml
Normal file
13
dashboard-sqdc/k8s/pvc.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: sqdc-database-pvc
|
||||
namespace: sqdc-dashboard
|
||||
labels:
|
||||
app: sqdc-api
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@ -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
|
||||
@ -1,8 +1,8 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
index index.html index.htm;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
@ -10,20 +10,13 @@ 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://localhost: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;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# React app - handle client-side routing
|
||||
# React app - handle client-side routing (must be last)
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
@ -31,12 +24,6 @@ server {
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# Static assets with caching
|
||||
location /static/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user