mirror of
https://github.com/ryankazokas/turbovault-app.git
synced 2026-04-16 22:12:53 +00:00
Moving to github
This commit is contained in:
306
k8s/README.md
Normal file
306
k8s/README.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# TurboVault Kubernetes Deployment
|
||||
|
||||
This directory contains Kubernetes manifests for deploying TurboVault to your k3s cluster.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes cluster (k3s, k8s, or any other)
|
||||
- `kubectl` configured to access your cluster
|
||||
- Docker registry (Docker Hub, GitHub Container Registry, or private registry)
|
||||
- PostgreSQL database (external or in-cluster)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Build and Push Docker Image
|
||||
|
||||
**Option A: Use GitHub Actions (Recommended)**
|
||||
|
||||
Push a tag and GitHub Actions will build and push automatically:
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
Image will be at: `ghcr.io/your-username/turbovault:v1.0.0`
|
||||
|
||||
**Option B: Build Locally**
|
||||
|
||||
```bash
|
||||
# Build the image
|
||||
docker build -t ghcr.io/your-username/turbovault:latest .
|
||||
|
||||
# Login to GitHub Container Registry
|
||||
echo $GITHUB_TOKEN | docker login ghcr.io -u your-username --password-stdin
|
||||
|
||||
# Push to registry
|
||||
docker push ghcr.io/your-username/turbovault:latest
|
||||
```
|
||||
|
||||
### 1.5. Create Registry Secret (if using private registry)
|
||||
|
||||
**For Public GitHub Container Registry:** No secret needed!
|
||||
|
||||
**For Private Registry:**
|
||||
|
||||
```bash
|
||||
kubectl create secret docker-registry registry-secret \
|
||||
--docker-server=your-registry.com \
|
||||
--docker-username=your-username \
|
||||
--docker-password=your-token \
|
||||
--docker-email=your-email@example.com \
|
||||
--namespace=turbovault
|
||||
```
|
||||
|
||||
Then uncomment `imagePullSecrets` in `deployment.yaml` and `migrate-job.yaml`.
|
||||
|
||||
### 2. Configure Secrets
|
||||
|
||||
```bash
|
||||
# Copy the example secrets file
|
||||
cp k8s/secrets.yaml.example k8s/secrets.yaml
|
||||
|
||||
# Edit with your actual values
|
||||
nano k8s/secrets.yaml
|
||||
|
||||
# Generate a SECRET_KEY_BASE
|
||||
rails secret
|
||||
# Copy the output to secrets.yaml
|
||||
```
|
||||
|
||||
### 3. Update Configuration
|
||||
|
||||
Edit `k8s/deployment.yaml` and update:
|
||||
- `image: your-registry/turbovault:latest` (line 28)
|
||||
- Database configuration in `k8s/configmap.yaml`
|
||||
- Domain in `k8s/ingress.yaml`
|
||||
|
||||
### 4. Deploy to Kubernetes
|
||||
|
||||
```bash
|
||||
# Create namespace
|
||||
kubectl apply -f k8s/namespace.yaml
|
||||
|
||||
# Create ConfigMap
|
||||
kubectl apply -f k8s/configmap.yaml
|
||||
|
||||
# Create Secrets
|
||||
kubectl apply -f k8s/secrets.yaml
|
||||
|
||||
# Run database migrations
|
||||
kubectl apply -f k8s/migrate-job.yaml
|
||||
|
||||
# Wait for migration to complete
|
||||
kubectl wait --for=condition=complete --timeout=300s job/turbovault-migrate -n turbovault
|
||||
|
||||
# Deploy application
|
||||
kubectl apply -f k8s/deployment.yaml
|
||||
|
||||
# Create service
|
||||
kubectl apply -f k8s/service.yaml
|
||||
|
||||
# Create ingress (for external access)
|
||||
kubectl apply -f k8s/ingress.yaml
|
||||
```
|
||||
|
||||
### 5. Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check pods
|
||||
kubectl get pods -n turbovault
|
||||
|
||||
# Check logs
|
||||
kubectl logs -f deployment/turbovault -n turbovault
|
||||
|
||||
# Check service
|
||||
kubectl get svc -n turbovault
|
||||
|
||||
# Check ingress
|
||||
kubectl get ingress -n turbovault
|
||||
```
|
||||
|
||||
## Database Setup
|
||||
|
||||
### Option 1: External PostgreSQL
|
||||
|
||||
Update `k8s/configmap.yaml` with your external PostgreSQL details:
|
||||
|
||||
```yaml
|
||||
DATABASE_HOST: "your-postgres-host"
|
||||
DATABASE_PORT: "5432"
|
||||
DATABASE_NAME: "turbovault_production"
|
||||
DATABASE_USERNAME: "turbovault"
|
||||
```
|
||||
|
||||
And add the password to `k8s/secrets.yaml`:
|
||||
|
||||
```yaml
|
||||
DATABASE_PASSWORD: "your-secure-password"
|
||||
```
|
||||
|
||||
### Option 2: In-Cluster PostgreSQL
|
||||
|
||||
Deploy PostgreSQL in your cluster:
|
||||
|
||||
```bash
|
||||
# Using Helm
|
||||
helm repo add bitnami https://charts.bitnami.com/bitnami
|
||||
helm install postgres bitnami/postgresql \
|
||||
--namespace turbovault \
|
||||
--set auth.database=turbovault_production \
|
||||
--set auth.username=turbovault \
|
||||
--set auth.password=changeme
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Required
|
||||
|
||||
- `DATABASE_HOST` - PostgreSQL host
|
||||
- `DATABASE_PASSWORD` - PostgreSQL password
|
||||
- `SECRET_KEY_BASE` - Rails secret key (generate with `rails secret`)
|
||||
|
||||
### Optional
|
||||
|
||||
- `IGDB_CLIENT_ID` - IGDB API client ID (for game metadata)
|
||||
- `IGDB_CLIENT_SECRET` - IGDB API client secret
|
||||
- `SMTP_ADDRESS` - SMTP server for emails
|
||||
- `SMTP_PORT` - SMTP port
|
||||
- `SMTP_USERNAME` - SMTP username
|
||||
- `SMTP_PASSWORD` - SMTP password
|
||||
|
||||
## Scaling
|
||||
|
||||
Scale the deployment:
|
||||
|
||||
```bash
|
||||
kubectl scale deployment turbovault --replicas=3 -n turbovault
|
||||
```
|
||||
|
||||
## Updating
|
||||
|
||||
### Deploy New Version
|
||||
|
||||
```bash
|
||||
# Option 1: Use GitHub Actions (Recommended)
|
||||
git tag v2.0.0
|
||||
git push origin v2.0.0
|
||||
# Wait for build to complete in Actions tab
|
||||
|
||||
# Option 2: Build locally
|
||||
docker build -t ghcr.io/username/turbovault:v2.0.0 .
|
||||
docker push ghcr.io/username/turbovault:v2.0.0
|
||||
|
||||
# Update deployment image
|
||||
kubectl set image deployment/turbovault turbovault=ghcr.io/username/turbovault:v2.0.0 -n turbovault
|
||||
|
||||
# Run migrations if needed
|
||||
kubectl delete job turbovault-migrate -n turbovault
|
||||
kubectl apply -f k8s/migrate-job.yaml
|
||||
kubectl wait --for=condition=complete --timeout=300s job/turbovault-migrate -n turbovault
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Pods Not Starting
|
||||
|
||||
```bash
|
||||
# Check pod status
|
||||
kubectl describe pod -l app=turbovault -n turbovault
|
||||
|
||||
# Check logs
|
||||
kubectl logs -l app=turbovault -n turbovault
|
||||
```
|
||||
|
||||
### Database Connection Issues
|
||||
|
||||
```bash
|
||||
# Test database connection
|
||||
kubectl run -it --rm debug --image=postgres:15 --restart=Never -n turbovault -- \
|
||||
psql -h postgres-service -U turbovault -d turbovault_production
|
||||
```
|
||||
|
||||
### Migration Failures
|
||||
|
||||
```bash
|
||||
# Check migration job logs
|
||||
kubectl logs job/turbovault-migrate -n turbovault
|
||||
|
||||
# Re-run migrations
|
||||
kubectl delete job turbovault-migrate -n turbovault
|
||||
kubectl apply -f k8s/migrate-job.yaml
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Check Application Health
|
||||
|
||||
```bash
|
||||
# Via kubectl
|
||||
kubectl port-forward svc/turbovault-service 3000:80 -n turbovault
|
||||
|
||||
# Visit http://localhost:3000/up in your browser
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All pods
|
||||
kubectl logs -f -l app=turbovault -n turbovault
|
||||
|
||||
# Specific pod
|
||||
kubectl logs -f turbovault-xxxxx-xxxxx -n turbovault
|
||||
|
||||
# Previous logs (if pod crashed)
|
||||
kubectl logs --previous turbovault-xxxxx-xxxxx -n turbovault
|
||||
```
|
||||
|
||||
## Backup
|
||||
|
||||
### Database Backup
|
||||
|
||||
```bash
|
||||
# Backup database
|
||||
kubectl exec -it postgres-xxxxx -n turbovault -- \
|
||||
pg_dump -U turbovault turbovault_production > backup.sql
|
||||
|
||||
# Restore database
|
||||
kubectl exec -i postgres-xxxxx -n turbovault -- \
|
||||
psql -U turbovault turbovault_production < backup.sql
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Use secrets management** - Consider using Sealed Secrets or External Secrets Operator
|
||||
2. **Enable TLS** - Uncomment TLS section in `ingress.yaml`
|
||||
3. **Network policies** - Restrict pod-to-pod communication
|
||||
4. **Resource limits** - Already configured in deployment.yaml
|
||||
5. **Regular updates** - Keep dependencies and images up to date
|
||||
|
||||
### Sealed Secrets (Recommended)
|
||||
|
||||
```bash
|
||||
# Install Sealed Secrets controller
|
||||
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.18.0/controller.yaml
|
||||
|
||||
# Create sealed secret
|
||||
kubeseal --format yaml < k8s/secrets.yaml > k8s/sealed-secrets.yaml
|
||||
|
||||
# Apply sealed secret (safe to commit)
|
||||
kubectl apply -f k8s/sealed-secrets.yaml
|
||||
```
|
||||
|
||||
## Clean Up
|
||||
|
||||
Remove TurboVault from cluster:
|
||||
|
||||
```bash
|
||||
kubectl delete namespace turbovault
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- GitHub Issues: https://github.com/yourusername/turbovault/issues
|
||||
- Documentation: https://github.com/yourusername/turbovault
|
||||
15
k8s/configmap.yaml
Normal file
15
k8s/configmap.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: turbovault-config
|
||||
namespace: turbovault
|
||||
data:
|
||||
RAILS_ENV: "production"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
RAILS_SERVE_STATIC_FILES: "true"
|
||||
RAILS_MAX_THREADS: "5"
|
||||
# Update these values for your environment
|
||||
DATABASE_HOST: "postgres-service" # Your PostgreSQL service name or external host
|
||||
DATABASE_PORT: "5432"
|
||||
DATABASE_NAME: "turbovault_production"
|
||||
DATABASE_USERNAME: "turbovault"
|
||||
133
k8s/deployment.yaml
Normal file
133
k8s/deployment.yaml
Normal file
@@ -0,0 +1,133 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: turbovault
|
||||
namespace: turbovault
|
||||
labels:
|
||||
app: turbovault
|
||||
spec:
|
||||
replicas: 2
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: turbovault
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: turbovault
|
||||
spec:
|
||||
# Pull images from container registry
|
||||
# For private registries, uncomment and create secret:
|
||||
# imagePullSecrets:
|
||||
# - name: registry-secret
|
||||
containers:
|
||||
- name: turbovault
|
||||
# UPDATE THIS: Replace with your registry path
|
||||
# Examples:
|
||||
# - GitHub Container Registry: ghcr.io/username/turbovault:latest
|
||||
# - Docker Hub: docker.io/username/turbovault:latest
|
||||
# - Private registry: registry.example.com/turbovault:latest
|
||||
image: ghcr.io/username/turbovault:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
protocol: TCP
|
||||
env:
|
||||
# Load from ConfigMap
|
||||
- name: RAILS_ENV
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: RAILS_ENV
|
||||
- name: RAILS_LOG_TO_STDOUT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: RAILS_LOG_TO_STDOUT
|
||||
- name: RAILS_SERVE_STATIC_FILES
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: RAILS_SERVE_STATIC_FILES
|
||||
- name: RAILS_MAX_THREADS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: RAILS_MAX_THREADS
|
||||
- name: DATABASE_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_HOST
|
||||
- name: DATABASE_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_PORT
|
||||
- name: DATABASE_NAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_NAME
|
||||
- name: DATABASE_USERNAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_USERNAME
|
||||
# Load from Secrets
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turbovault-secrets
|
||||
key: DATABASE_PASSWORD
|
||||
- name: SECRET_KEY_BASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turbovault-secrets
|
||||
key: SECRET_KEY_BASE
|
||||
- name: IGDB_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turbovault-secrets
|
||||
key: IGDB_CLIENT_ID
|
||||
optional: true
|
||||
- name: IGDB_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turbovault-secrets
|
||||
key: IGDB_CLIENT_SECRET
|
||||
optional: true
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /up
|
||||
port: 3000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /up
|
||||
port: 3000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: storage
|
||||
mountPath: /app/storage
|
||||
volumes:
|
||||
- name: storage
|
||||
emptyDir: {} # Replace with PersistentVolumeClaim for production
|
||||
27
k8s/ingress.yaml
Normal file
27
k8s/ingress.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: turbovault-ingress
|
||||
namespace: turbovault
|
||||
annotations:
|
||||
# Update these based on your ingress controller
|
||||
# nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
# cert-manager.io/cluster-issuer: "letsencrypt-prod"
|
||||
spec:
|
||||
ingressClassName: nginx # Or traefik, depending on your setup
|
||||
rules:
|
||||
- host: turbovault.example.com # Update with your domain
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: turbovault-service
|
||||
port:
|
||||
number: 80
|
||||
# Uncomment for TLS/HTTPS
|
||||
# tls:
|
||||
# - hosts:
|
||||
# - turbovault.example.com
|
||||
# secretName: turbovault-tls
|
||||
63
k8s/migrate-job.yaml
Normal file
63
k8s/migrate-job.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: turbovault-migrate
|
||||
namespace: turbovault
|
||||
labels:
|
||||
app: turbovault
|
||||
job: migrate
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: turbovault
|
||||
job: migrate
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
# For private registries, uncomment and create secret:
|
||||
# imagePullSecrets:
|
||||
# - name: registry-secret
|
||||
containers:
|
||||
- name: migrate
|
||||
# UPDATE THIS: Replace with your registry path (same as deployment.yaml)
|
||||
image: ghcr.io/username/turbovault:latest
|
||||
command: ["bundle", "exec", "rails", "db:migrate"]
|
||||
env:
|
||||
# Load from ConfigMap
|
||||
- name: RAILS_ENV
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: RAILS_ENV
|
||||
- name: DATABASE_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_HOST
|
||||
- name: DATABASE_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_PORT
|
||||
- name: DATABASE_NAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_NAME
|
||||
- name: DATABASE_USERNAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: turbovault-config
|
||||
key: DATABASE_USERNAME
|
||||
# Load from Secrets
|
||||
- name: DATABASE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turbovault-secrets
|
||||
key: DATABASE_PASSWORD
|
||||
- name: SECRET_KEY_BASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: turbovault-secrets
|
||||
key: SECRET_KEY_BASE
|
||||
backoffLimit: 3
|
||||
7
k8s/namespace.yaml
Normal file
7
k8s/namespace.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: turbovault
|
||||
labels:
|
||||
name: turbovault
|
||||
app: turbovault
|
||||
26
k8s/secrets.yaml.example
Normal file
26
k8s/secrets.yaml.example
Normal file
@@ -0,0 +1,26 @@
|
||||
# TurboVault Secrets Template
|
||||
# Copy this file to secrets.yaml and update with your actual values
|
||||
# DO NOT commit secrets.yaml to version control!
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: turbovault-secrets
|
||||
namespace: turbovault
|
||||
type: Opaque
|
||||
stringData:
|
||||
# Database password
|
||||
DATABASE_PASSWORD: "changeme"
|
||||
|
||||
# Rails master key (generate with: rails secret)
|
||||
SECRET_KEY_BASE: "changeme-use-rails-secret-to-generate"
|
||||
|
||||
# IGDB API credentials (optional, for game metadata)
|
||||
IGDB_CLIENT_ID: "your_igdb_client_id"
|
||||
IGDB_CLIENT_SECRET: "your_igdb_client_secret"
|
||||
|
||||
# Email configuration (optional, for password resets)
|
||||
SMTP_ADDRESS: "smtp.example.com"
|
||||
SMTP_PORT: "587"
|
||||
SMTP_USERNAME: "your_smtp_username"
|
||||
SMTP_PASSWORD: "your_smtp_password"
|
||||
16
k8s/service.yaml
Normal file
16
k8s/service.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: turbovault-service
|
||||
namespace: turbovault
|
||||
labels:
|
||||
app: turbovault
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 3000
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: turbovault
|
||||
Reference in New Issue
Block a user