[INFRA-003] infra: Set up Kubernetes deployment
This commit is contained in:
parent
ef87ab98ff
commit
82f2735529
12 changed files with 535 additions and 3 deletions
|
|
@ -11237,8 +11237,20 @@
|
|||
"description": "Create Kubernetes manifests for production deployment",
|
||||
"owner": "devops",
|
||||
"estimated_hours": 8,
|
||||
"status": "todo",
|
||||
"files_involved": [],
|
||||
"status": "completed",
|
||||
"files_involved": [
|
||||
"k8s/namespace.yaml",
|
||||
"k8s/configmap.yaml",
|
||||
"k8s/secrets.yaml.example",
|
||||
"k8s/ingress.yaml",
|
||||
"k8s/backend-api/deployment.yaml",
|
||||
"k8s/backend-api/service.yaml",
|
||||
"k8s/frontend/deployment.yaml",
|
||||
"k8s/frontend/service.yaml",
|
||||
"k8s/chat-server/deployment.yaml",
|
||||
"k8s/chat-server/service.yaml",
|
||||
"k8s/README.md"
|
||||
],
|
||||
"implementation_steps": [
|
||||
{
|
||||
"step": 1,
|
||||
|
|
@ -11258,7 +11270,15 @@
|
|||
"Unit tests",
|
||||
"Integration tests"
|
||||
],
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"completed_at": "2025-12-25T21:32:06.116304",
|
||||
"validation": {
|
||||
"yaml_syntax": "All manifests validated",
|
||||
"manifests_created": "Complete Kubernetes deployment manifests for all services",
|
||||
"services": "Backend API, Frontend, Chat Server, Stream Server",
|
||||
"components": "Deployments, Services, ConfigMaps, Secrets, Ingress, Namespace",
|
||||
"documentation": "k8s/README.md with deployment instructions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "INFRA-004",
|
||||
|
|
|
|||
130
k8s/README.md
Normal file
130
k8s/README.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# Kubernetes Deployment Manifests
|
||||
|
||||
This directory contains Kubernetes manifests for deploying Veza Platform to production.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
k8s/
|
||||
├── namespace.yaml # Namespace definition
|
||||
├── configmap.yaml # Configuration values
|
||||
├── secrets.yaml.example # Example secrets (DO NOT COMMIT REAL SECRETS)
|
||||
├── ingress.yaml # Ingress configuration
|
||||
├── backend-api/
|
||||
│ ├── deployment.yaml # Backend API deployment
|
||||
│ └── service.yaml # Backend API service
|
||||
├── frontend/
|
||||
│ ├── deployment.yaml # Frontend deployment
|
||||
│ └── service.yaml # Frontend service
|
||||
├── chat-server/
|
||||
│ ├── deployment.yaml # Chat server deployment
|
||||
│ └── service.yaml # Chat server service
|
||||
└── stream-server/
|
||||
└── (see veza-stream-server/k8s/production/)
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes cluster 1.24+
|
||||
- kubectl configured
|
||||
- Docker images built and pushed to registry
|
||||
- Secrets configured (see secrets.yaml.example)
|
||||
|
||||
## Deployment Steps
|
||||
|
||||
### 1. Create Namespace
|
||||
|
||||
```bash
|
||||
kubectl apply -f k8s/namespace.yaml
|
||||
```
|
||||
|
||||
### 2. Create Secrets
|
||||
|
||||
```bash
|
||||
# Copy example and fill in real values
|
||||
cp k8s/secrets.yaml.example k8s/secrets.yaml
|
||||
# Edit secrets.yaml with real values
|
||||
kubectl create secret generic veza-secrets \
|
||||
--from-env-file=k8s/secrets.yaml \
|
||||
-n veza-production
|
||||
```
|
||||
|
||||
### 3. Create ConfigMap
|
||||
|
||||
```bash
|
||||
kubectl apply -f k8s/configmap.yaml
|
||||
```
|
||||
|
||||
### 4. Deploy Services
|
||||
|
||||
```bash
|
||||
# Backend API
|
||||
kubectl apply -f k8s/backend-api/
|
||||
|
||||
# Frontend
|
||||
kubectl apply -f k8s/frontend/
|
||||
|
||||
# Chat Server
|
||||
kubectl apply -f k8s/chat-server/
|
||||
|
||||
# Stream Server (if separate)
|
||||
kubectl apply -f veza-stream-server/k8s/production/
|
||||
```
|
||||
|
||||
### 5. Create Ingress
|
||||
|
||||
```bash
|
||||
kubectl apply -f k8s/ingress.yaml
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# Check pods
|
||||
kubectl get pods -n veza-production
|
||||
|
||||
# Check services
|
||||
kubectl get svc -n veza-production
|
||||
|
||||
# Check ingress
|
||||
kubectl get ingress -n veza-production
|
||||
|
||||
# View logs
|
||||
kubectl logs -f deployment/veza-backend-api -n veza-production
|
||||
```
|
||||
|
||||
## Scaling
|
||||
|
||||
```bash
|
||||
# Scale backend API
|
||||
kubectl scale deployment veza-backend-api --replicas=5 -n veza-production
|
||||
|
||||
# Scale frontend
|
||||
kubectl scale deployment veza-frontend --replicas=3 -n veza-production
|
||||
```
|
||||
|
||||
## Updates
|
||||
|
||||
```bash
|
||||
# Update image
|
||||
kubectl set image deployment/veza-backend-api \
|
||||
backend-api=veza-backend-api:v1.1.0 \
|
||||
-n veza-production
|
||||
|
||||
# Rollout status
|
||||
kubectl rollout status deployment/veza-backend-api -n veza-production
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# Describe pod
|
||||
kubectl describe pod <pod-name> -n veza-production
|
||||
|
||||
# Get events
|
||||
kubectl get events -n veza-production --sort-by='.lastTimestamp'
|
||||
|
||||
# Port forward for debugging
|
||||
kubectl port-forward deployment/veza-backend-api 8080:8080 -n veza-production
|
||||
```
|
||||
|
||||
98
k8s/backend-api/deployment.yaml
Normal file
98
k8s/backend-api/deployment.yaml
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: veza-backend-api
|
||||
namespace: veza-production
|
||||
labels:
|
||||
app: veza-backend-api
|
||||
component: api
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 3
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: veza-backend-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: veza-backend-api
|
||||
version: v1.0.0
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/path: "/metrics"
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
containers:
|
||||
- name: backend-api
|
||||
image: veza-backend-api:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: APP_ENV
|
||||
value: "production"
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: veza-secrets
|
||||
key: database-url
|
||||
- name: REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: veza-secrets
|
||||
key: redis-url
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: veza-secrets
|
||||
key: jwt-secret
|
||||
- name: CORS_ALLOWED_ORIGINS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: veza-config
|
||||
key: cors-allowed-origins
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
limits:
|
||||
cpu: "2000m"
|
||||
memory: "2Gi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "sleep 15"]
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
17
k8s/backend-api/service.yaml
Normal file
17
k8s/backend-api/service.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: veza-backend-api
|
||||
namespace: veza-production
|
||||
labels:
|
||||
app: veza-backend-api
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: veza-backend-api
|
||||
|
||||
90
k8s/chat-server/deployment.yaml
Normal file
90
k8s/chat-server/deployment.yaml
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: veza-chat-server
|
||||
namespace: veza-production
|
||||
labels:
|
||||
app: veza-chat-server
|
||||
component: chat
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 3
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: veza-chat-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: veza-chat-server
|
||||
version: v1.0.0
|
||||
annotations:
|
||||
prometheus.io/scrape: "true"
|
||||
prometheus.io/port: "8081"
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
containers:
|
||||
- name: chat-server
|
||||
image: veza-chat-server:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8081
|
||||
protocol: TCP
|
||||
- name: websocket
|
||||
containerPort: 8082
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: RUST_LOG
|
||||
value: "info"
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: veza-secrets
|
||||
key: database-url
|
||||
- name: JWT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: veza-secrets
|
||||
key: jwt-secret
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
limits:
|
||||
cpu: "2000m"
|
||||
memory: "2Gi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8081
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8081
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
lifecycle:
|
||||
preStop:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "sleep 15"]
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
21
k8s/chat-server/service.yaml
Normal file
21
k8s/chat-server/service.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: veza-chat-server
|
||||
namespace: veza-production
|
||||
labels:
|
||||
app: veza-chat-server
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
protocol: TCP
|
||||
- name: websocket
|
||||
port: 8082
|
||||
targetPort: 8082
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: veza-chat-server
|
||||
|
||||
10
k8s/configmap.yaml
Normal file
10
k8s/configmap.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: veza-config
|
||||
namespace: veza-production
|
||||
data:
|
||||
api-url: "http://veza-backend-api:8080"
|
||||
cors-allowed-origins: "https://app.veza.com,https://veza.com"
|
||||
app-env: "production"
|
||||
|
||||
68
k8s/frontend/deployment.yaml
Normal file
68
k8s/frontend/deployment.yaml
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: veza-frontend
|
||||
namespace: veza-production
|
||||
labels:
|
||||
app: veza-frontend
|
||||
component: web
|
||||
version: v1.0.0
|
||||
spec:
|
||||
replicas: 3
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: veza-frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: veza-frontend
|
||||
version: v1.0.0
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: veza-frontend:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: VITE_API_URL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: veza-config
|
||||
key: api-url
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "256Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
17
k8s/frontend/service.yaml
Normal file
17
k8s/frontend/service.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: veza-frontend
|
||||
namespace: veza-production
|
||||
labels:
|
||||
app: veza-frontend
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 80
|
||||
protocol: TCP
|
||||
selector:
|
||||
app: veza-frontend
|
||||
|
||||
38
k8s/ingress.yaml
Normal file
38
k8s/ingress.yaml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: veza-ingress
|
||||
namespace: veza-production
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: nginx
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- app.veza.com
|
||||
- api.veza.com
|
||||
secretName: veza-tls
|
||||
rules:
|
||||
- host: app.veza.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: veza-frontend
|
||||
port:
|
||||
number: 80
|
||||
- host: api.veza.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: veza-backend-api
|
||||
port:
|
||||
number: 8080
|
||||
|
||||
8
k8s/namespace.yaml
Normal file
8
k8s/namespace.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: veza-production
|
||||
labels:
|
||||
name: veza-production
|
||||
environment: production
|
||||
|
||||
15
k8s/secrets.yaml.example
Normal file
15
k8s/secrets.yaml.example
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Example secrets file - DO NOT COMMIT REAL SECRETS
|
||||
# Copy this file to secrets.yaml and fill in real values
|
||||
# Then use: kubectl create secret generic veza-secrets --from-env-file=secrets.yaml -n veza-production
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: veza-secrets
|
||||
namespace: veza-production
|
||||
type: Opaque
|
||||
stringData:
|
||||
database-url: "postgresql://user:password@postgres:5432/veza?sslmode=require"
|
||||
redis-url: "redis://redis:6379/0"
|
||||
jwt-secret: "your-jwt-secret-key-min-32-chars-long"
|
||||
|
||||
Loading…
Reference in a new issue