Kubernetes Deployment
PhotoFlow runs on on-premise Kubernetes clusters with Helm-based deployments.
Namespace Organization
Namespace | Purpose | URL Pattern |
|---|
photoflow-dev | Development | dev.*.photoflow.vn |
photoflow-uat | User Acceptance Testing | uat.*.photoflow.vn |
photoflow-beta | Beta releases | beta.*.photoflow.vn |
photoflow | Production | *.photoflow.vn |
Helm # chart: helm/shin-service
PhotoFlow uses the shin-service Helm chart for consistent deployments.
Property | Value |
|---|
Chart Name | shin-service |
Version | 0.3.2 |
Registry | oci://registry.gitlab.com/nextera-devops/nera-helm-chart/helm/shin-service |
Supported Components
Component | Description | Port |
|---|
api | REST API (.NET) | 8080 |
grpc | gRPC service | 8081 |
nextjs | Next.js frontend | 3000 |
angular | Angular frontend | 80 |
background | Background worker | - |
Values Configuration
Base Values Structure
# values.yaml
replicaCount: 1
image:
repository: registry.shiningtree.co
pullPolicy: IfNotPresent
tag: "latest"
name: "" # Full image name (e.g., photo-flow/backend/api)
suffix: "" # Optional suffix for release name
service:
type: ClusterIP
port: 80
targetPort: 8080
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
probes:
liveness:
enabled: true
initialDelaySeconds: 30
path: /health
port: http
readiness:
enabled: true
initialDelaySeconds: 5
path: /health
port: http
Environment-Specific Values
Development (dev.yaml)
# devops/IaC/environments/dev.yaml
replicaCount: 1
image:
tag: dev
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: dev.api.photoflow.vn
paths:
- path: /
pathType: Prefix
tls:
- secretName: dev-api-tls
hosts:
- dev.api.photoflow.vn
configMap:
enabled: true
global:
ASPNETCORE_ENVIRONMENT: Development
LOG_LEVEL: Debug
Production (prod.yaml)
# devops/IaC/environments/prod.yaml
replicaCount: 3
image:
tag: latest
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
hosts:
- host: api.photoflow.vn
paths:
- path: /
pathType: Prefix
tls:
- secretName: api-tls
hosts:
- api.photoflow.vn
configMap:
enabled: true
global:
ASPNETCORE_ENVIRONMENT: Production
LOG_LEVEL: Warning
IaC Directory Structure
devops/IaC/
├── _global.yaml # Shared settings
├── _ingress.yaml # Ingress templates
├── _containers/
│ ├── api.yaml # API container config
│ ├── angular.yaml # Angular container config
│ └── background.yaml # Worker container config
└── environments/
├── dev.yaml # Development overrides
├── uat.yaml # UAT overrides
└── prod.yaml # Production settings
Container Configuration
# devops/IaC/_containers/api.yaml
image:
name: photo-flow/backend/pfl01y25-be-app
suffix: ""
service:
port: 80
targetPort: 8080
configMap:
components:
api:
ASPNETCORE_URLS: "http://+:8080"
SERVICE_TYPE: api
Deployment Commands
Manual Deployment
# Deploy to development
helm upgrade --install pfl-be-api \
oci://registry.gitlab.com/nextera-devops/nera-helm-chart/helm/shin-service \
--version 0.3.2 \
-n photoflow-dev \
-f devops/IaC/_global.yaml \
-f devops/IaC/_ingress.yaml \
-f devops/IaC/_containers/api.yaml \
-f devops/IaC/environments/dev.yaml \
--set image.tag=dev
# Verify deployment
kubectl get pods -n photoflow-dev -l app=pfl-be-api
Helm Operations
# List releases
helm list -n photoflow-dev
# Get release values
helm get values pfl-be-api -n photoflow-dev
# View release history
helm history pfl-be-api -n photoflow-dev
# Rollback to previous
helm rollback pfl-be-api -n photoflow-dev
Ingress Configuration
NGINX Ingress Controller
# Ingress annotations
ingress:
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/websocket-services: "pfl-be-api"
SignalR/WebSocket Support
# For SignalR connections
nginx.ingress.kubernetes.io/websocket-services: "pfl-be-api"
nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
SSL/TLS with cert-manager
tls:
- secretName: api-photoflow-tls
hosts:
- api.photoflow.vn
Resource Management
Default Resources
Component | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|
API | 250m | 500m | 256Mi | 512Mi |
Worker | 100m | 250m | 128Mi | 256Mi |
Frontend | 50m | 100m | 64Mi | 128Mi |
Production Resources
Component | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|
API | 500m | 1000m | 512Mi | 1Gi |
Worker | 250m | 500m | 256Mi | 512Mi |
Frontend | 100m | 200m | 128Mi | 256Mi |
Health Checks
Liveness Probe
Determines if container should be restarted:
probes:
liveness:
enabled: true
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
path: /health
port: http
Readiness Probe
Determines if container can receive traffic:
probes:
readiness:
enabled: true
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
path: /health
port: http
ConfigMaps and Secrets
ConfigMap Structure
configMap:
enabled: true
global:
ASPNETCORE_ENVIRONMENT: Production
LOG_LEVEL: Warning
DB_HOST: postgres.photoflow.svc.cluster.local
DB_PORT: "5432"
components:
api:
ASPNETCORE_URLS: "http://+:8080"
SignalR__Enabled: "true"
Secrets Management
# Create secret
kubectl create secret generic pfl-secrets \
-n photoflow \
--from-literal=DB_PASSWORD='secure-password' \
--from-literal=JWT_SECRET='jwt-secret-key'
# Reference in deployment
envFrom:
- secretRef:
name: pfl-secrets
Autoscaling
Horizontal Pod Autoscaler
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
Verify HPA
kubectl get hpa -n photoflow
kubectl describe hpa pfl-be-api -n photoflow
Monitoring
Service Monitor (Prometheus)
serviceMonitor:
enabled: true
interval: 30s
scrapeTimeout: 10s
additionalLabels:
release: prometheus
Pod Metrics
# View pod resource usage
kubectl top pods -n photoflow
# View pod logs
kubectl logs -f deployment/pfl-be-api -n photoflow
# Stream all replica logs
kubectl logs -f -l app=pfl-be-api -n photoflow --all-containers
Troubleshooting
Common Issues
Issue | Diagnosis | Solution |
|---|
Pod CrashLoopBackOff | Check logs | Fix application errors |
ImagePullBackOff | Registry auth | Verify imagePullSecrets |
Pending pods | Resource constraints | Check node resources |
502 Bad Gateway | Pod not ready | Check readiness probe |
Debug Commands
# Describe pod
kubectl describe pod <pod-name> -n photoflow
# Get events
kubectl get events -n photoflow --sort-by='.lastTimestamp'
# Exec into pod
kubectl exec -it <pod-name> -n photoflow -- /bin/sh
# Port forward for local debugging
kubectl port-forward svc/pfl-be-api 8080:80 -n photoflow
Last modified: 25 February 2026