Kubernetes Deployment
Overview
Vulcan can be deployed to Kubernetes using the provided example manifests. This guide covers deployment, configuration, and best practices for running Vulcan in a Kubernetes environment.
Prerequisites
- Kubernetes cluster (1.21+)
- kubectl configured with cluster access
- PostgreSQL database (can be in-cluster or external)
- Ingress controller (nginx recommended)
- Persistent storage provisioner (for database if in-cluster)
- SSL/TLS certificates (cert-manager recommended)
Architecture
Components
- Vulcan Web Application - Rails application pods
- PostgreSQL Database - Data persistence layer
- Ingress Controller - External access and SSL termination
- ConfigMaps - Application configuration
- Secrets - Sensitive credentials
- Services - Internal networking
- PersistentVolumeClaims - Database storage
Quick Start
1. Create Namespace
bash
kubectl create namespace vulcan
2. Configure Secrets
Create comprehensive secrets for your deployment:
yaml
# k8s-vulcan-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: vulcansecrets
namespace: vulcan
type: Opaque
stringData:
postgresql-password: "secure_database_password"
key-base: "$(openssl rand -hex 64)"
cipher-password: "$(openssl rand -hex 32)"
cipher-salt: "$(openssl rand -hex 32)"
ldap-password: "ldap_service_account_password"
oidc-client-secret: "your_oidc_client_secret"
Generate secure values:
bash
# Generate SECRET_KEY_BASE
echo "key-base: $(openssl rand -hex 64)"
# Generate cipher keys
echo "cipher-password: $(openssl rand -hex 32)"
echo "cipher-salt: $(openssl rand -hex 32)"
Apply the secret:
bash
kubectl apply -f k8s-vulcan-secrets.yaml
3. Create ConfigMap
yaml
# k8s-vulcan-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: vulcan-config
namespace: vulcan
data:
vulcan-config.yml: |
production:
welcome_text: "Welcome to Vulcan"
contact_email: "admin@example.com"
app_url: "https://vulcan.example.com"
smtp:
enabled: true
settings:
address: smtp.example.com
port: 587
domain: example.com
authentication: plain
enable_starttls_auto: true
local_login:
enabled: true
email_confirmation: false
session_timeout: 60
user_registration:
enabled: true
project_create_permission:
enabled: true
Apply the ConfigMap:
bash
kubectl apply -f k8s-vulcan-config.yaml
4. Deploy Vulcan
Complete deployment with all production settings:
yaml
# k8s-vulcan-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vulcan-web
namespace: vulcan
labels:
app: vulcan-web
spec:
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: vulcan-web
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: vulcan-web
spec:
automountServiceAccountToken: false # Security best practice
containers:
- name: vulcan-web
image: mitre/vulcan:v2.2.1
imagePullPolicy: Always
ports:
- containerPort: 3000
name: vulcan-web
resources:
requests:
cpu: "500m"
memory: 1Gi
limits:
cpu: "1000m"
memory: 2Gi
volumeMounts:
- name: config-volume
mountPath: /app/config/vulcan.yml
subPath: vulcan-config.yml
env:
# Database Configuration
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: vulcansecrets
key: postgresql-password
- name: DATABASE_URL
value: postgres://vulcan:$(DATABASE_PASSWORD)@postgresql:5432/vulcan_production
# Rails Configuration
- name: RAILS_ENV
value: production
- name: RAILS_SERVE_STATIC_FILES
value: "true"
- name: RAILS_LOG_TO_STDOUT
value: "true"
- name: SECRET_KEY_BASE
valueFrom:
secretKeyRef:
name: vulcansecrets
key: key-base
# Security Keys
- name: CIPHER_PASSWORD
valueFrom:
secretKeyRef:
name: vulcansecrets
key: cipher-password
- name: CIPHER_SALT
valueFrom:
secretKeyRef:
name: vulcansecrets
key: cipher-salt
# Application Configuration
- name: VULCAN_WELCOME_TEXT
value: "Welcome to Vulcan - Kubernetes Deployment"
- name: VULCAN_CONTACT_EMAIL
value: "vulcan-admin@example.com"
- name: VULCAN_APP_URL
value: "https://vulcan.example.com"
- name: VULCAN_SESSION_TIMEOUT
value: "10"
# LDAP Configuration (if using)
- name: VULCAN_ENABLE_LDAP
value: "true"
- name: VULCAN_LDAP_HOST
value: "ldap.example.com"
- name: VULCAN_LDAP_PORT
value: "636"
- name: VULCAN_LDAP_TITLE
value: "Corporate LDAP"
- name: VULCAN_LDAP_ATTRIBUTE
value: "sAMAccountName"
- name: VULCAN_LDAP_ENCRYPTION
value: "simple_tls"
- name: VULCAN_LDAP_BIND_DN
value: "CN=vulcan.svcacct,OU=Service Accounts,DC=example,DC=com"
- name: VULCAN_LDAP_ADMIN_PASS
valueFrom:
secretKeyRef:
name: vulcansecrets
key: ldap-password
- name: VULCAN_LDAP_BASE
value: "DC=example,DC=com"
# OIDC Configuration (if using)
- name: VULCAN_ENABLE_OIDC
value: "false"
- name: VULCAN_OIDC_ISSUER_URL
value: "https://your-idp.example.com"
- name: VULCAN_OIDC_CLIENT_ID
value: "vulcan-kubernetes"
- name: VULCAN_OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: vulcansecrets
key: oidc-client-secret
# Health Checks
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
successThreshold: 1
startupProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
volumes:
- name: config-volume
configMap:
name: vulcan-config
---
apiVersion: v1
kind: Service
metadata:
name: vulcan
namespace: vulcan
spec:
selector:
app: vulcan
ports:
- protocol: TCP
port: 80
targetPort: 3000
4. Configure Ingress
yaml
# k8s-vulcan-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vulcan
namespace: vulcan
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- vulcan.example.com
secretName: vulcan-tls
rules:
- host: vulcan.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vulcan
port:
number: 80
Configuration
ConfigMap for Non-Sensitive Settings
yaml
# k8s-vulcan-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: vulcan-config
namespace: vulcan
data:
VULCAN_ENABLE_OIDC: "true"
VULCAN_OIDC_ISSUER_URL: "https://your-domain.okta.com"
VULCAN_OIDC_CLIENT_ID: "your-client-id"
VULCAN_CONTACT_EMAIL: "vulcan-admin@example.com"
VULCAN_APP_URL: "https://vulcan.example.com"
Database Setup
Option 1: External Database
Use a managed database service (RDS, Cloud SQL, etc.) and provide the connection string in the secret.
Option 2: In-Cluster PostgreSQL
Deploy PostgreSQL using Helm:
bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install postgres bitnami/postgresql \
--namespace vulcan \
--set auth.database=vulcan \
--set auth.username=vulcan \
--set persistence.size=10Gi
Persistent Storage
For file uploads, configure a PersistentVolume:
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vulcan-uploads
namespace: vulcan
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
Mount in deployment:
yaml
volumeMounts:
- name: uploads
mountPath: /app/public/uploads
volumes:
- name: uploads
persistentVolumeClaim:
claimName: vulcan-uploads
Database Backup
Automated Backup CronJob
yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: vulcan-db-backup
namespace: vulcan
spec:
schedule: "0 2 * * *" # Daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: postgres-backup
image: postgres:15
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
command:
- /bin/bash
- -c
- |
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump -h postgres -U vulcan vulcan > /backup/vulcan_$DATE.sql
# Upload to S3 or other storage
# aws s3 cp /backup/vulcan_$DATE.sql s3://backups/vulcan/
volumeMounts:
- name: backup
mountPath: /backup
volumes:
- name: backup
emptyDir: {}
restartPolicy: OnFailure
Scaling
Horizontal Pod Autoscaler
yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: vulcan-hpa
namespace: vulcan
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: vulcan
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Monitoring
Prometheus Metrics
Add annotations for Prometheus scraping:
yaml
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
Health Checks
Vulcan provides health endpoints:
/health
- Basic health check/readiness
- Database connectivity check
Security Best Practices
- Network Policies: Restrict pod-to-pod communication
- Pod Security: Run as non-root user
- Secrets Management: Use sealed-secrets or external-secrets
- RBAC: Limit service account permissions
- Image Security: Scan images regularly
- Resource Limits: Always set resource requests/limits
Example Network Policy
yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: vulcan-netpol
namespace: vulcan
spec:
podSelector:
matchLabels:
app: vulcan
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 3000
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
Troubleshooting
Common Issues
Database Connection Failed
bashkubectl logs -n vulcan deployment/vulcan kubectl describe pod -n vulcan vulcan-xxx
Asset Compilation Issues
bashkubectl exec -n vulcan deployment/vulcan -- rails assets:precompile
Migration Pending
bashkubectl exec -n vulcan deployment/vulcan -- rails db:migrate
Debug Commands
bash
# Get pod status
kubectl get pods -n vulcan
# View logs
kubectl logs -n vulcan -l app=vulcan --tail=100
# Execute shell in pod
kubectl exec -it -n vulcan deployment/vulcan -- bash
# Check events
kubectl get events -n vulcan --sort-by='.lastTimestamp'
Production Checklist
- [ ] TLS/SSL configured
- [ ] Database backups configured
- [ ] Monitoring/alerting set up
- [ ] Resource limits defined
- [ ] Network policies in place
- [ ] Secrets properly managed
- [ ] High availability (multiple replicas)
- [ ] Persistent storage for uploads
- [ ] Ingress configured with rate limiting
- [ ] Pod disruption budgets set
Example Files
Complete example manifests are available in the repository:
- k8s-vulcan-deployment-example.yaml
- k8s-vulcan-config-example.yml
- k8s-vulcan-secrets-example.yaml
- k8s-vulcan-ingress-example.yaml
Next Steps
- Docker Deployment - Container basics
- Environment Variables - Full configuration reference
- Authentication Setup - Configure SSO