rdev/docs/operations/deployment.md
jordan c59d348040 chore: prepare for composable monorepo template implementation
This commit captures the current state before implementing the composable
monorepo template system. Key changes included:

Infrastructure:
- Add CockroachDB provisioner adapter for database provisioning
- Add Redis provisioner adapter for cache provisioning
- Add build events system with PostgreSQL storage
- Add WebSocket endpoint for real-time build progress

Code agent improvements:
- Fix Claude Code adapter to use default allowed tools instead of dangerously-skip-permissions
- Add context-aware stream closing for cancellation support
- Improve parser tests for edge cases

Build system:
- Add build event constants and metrics
- Remove deprecated git_operations.go (replaced by pod_git_operations.go)
- Add rollback logic for multi-step provisioning operations

Documentation:
- Add composable-monorepo feature documentation
- Add DNS/Cloudflare service documentation
- Update deployment and troubleshooting guides

Cookbooks:
- Add fullstack-app cookbook
- Refactor landing-test with shared library

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 11:39:28 -07:00

404 lines
7.5 KiB
Markdown

# Deployment Guide
This guide covers deploying rdev API to the k3s cluster.
## Prerequisites
```bash
# REQUIRED: Set kubeconfig before any kubectl command
export KUBECONFIG=~/.kube/orchard9-k3sf.yaml
```
- k3s cluster (orchard9-k3sf)
- kubectl configured with correct kubeconfig
- PostgreSQL database
- Container registry access (ghcr.io/orchard9)
## Quick Deploy
```bash
# Release + deploy (recommended)
./scripts/release.sh v0.10.1 "Description of changes" --deploy
# Or manual deploy
kubectl apply -f deployments/k8s/base/rdev-api.yaml
kubectl rollout restart -n rdev deployment/rdev-api
# Verify deployment
kubectl -n rdev get pods
kubectl -n rdev get svc
```
## Configuration
### Environment Variables
| Variable | Description | Required | Default |
|----------|-------------|----------|---------|
| `PORT` | HTTP server port | No | 8080 |
| `POSTGRES_HOST` | Database host | Yes | - |
| `POSTGRES_PORT` | Database port | No | 5432 |
| `POSTGRES_USER` | Database user | Yes | - |
| `POSTGRES_PASSWORD` | Database password | Yes | - |
| `POSTGRES_DB` | Database name | No | rdev |
| `RDEV_NAMESPACE` | K8s namespace for pods | No | default |
| `RATE_LIMIT_RPS` | Requests per second | No | 10 |
| `CONCURRENT_COMMANDS` | Max concurrent commands | No | 5 |
### Secrets
Create a secret for database credentials:
```bash
kubectl -n rdev create secret generic rdev-api-secrets \
--from-literal=postgres-password=your-password
```
Or use the manifest:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: rdev-api-secrets
namespace: rdev
type: Opaque
stringData:
postgres-password: your-secure-password
```
### ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: rdev-api-config
namespace: rdev
data:
POSTGRES_HOST: "postgres.databases.svc"
POSTGRES_DB: "rdev"
RDEV_NAMESPACE: "rdev"
RATE_LIMIT_RPS: "10"
CONCURRENT_COMMANDS: "5"
```
## Kubernetes Manifests
### Namespace
```yaml
apiVersion: v1
kind: Namespace
metadata:
name: rdev
labels:
app.kubernetes.io/name: rdev
```
### Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rdev-api
namespace: rdev
spec:
replicas: 2
selector:
matchLabels:
app: rdev-api
template:
metadata:
labels:
app: rdev-api
spec:
serviceAccountName: rdev-api
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: rdev-api
image: your-registry/rdev-api:latest
ports:
- containerPort: 8080
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: rdev-api-secrets
key: postgres-password
envFrom:
- configMapRef:
name: rdev-api-config
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
```
### Service
```yaml
apiVersion: v1
kind: Service
metadata:
name: rdev-api
namespace: rdev
spec:
selector:
app: rdev-api
ports:
- port: 80
targetPort: 8080
```
### Ingress
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rdev-api
namespace: rdev
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
rules:
- host: rdev.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rdev-api
port:
number: 80
tls:
- hosts:
- rdev.example.com
secretName: rdev-tls
```
### RBAC
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: rdev-api
namespace: rdev
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: rdev-api-role
namespace: rdev
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: rdev-api-binding
namespace: rdev
subjects:
- kind: ServiceAccount
name: rdev-api
roleRef:
kind: Role
name: rdev-api-role
apiGroup: rbac.authorization.k8s.io
```
### Pod Disruption Budget
```yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: rdev-api-pdb
namespace: rdev
spec:
minAvailable: 1
selector:
matchLabels:
app: rdev-api
```
### Network Policy
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: rdev-api-policy
namespace: rdev
spec:
podSelector:
matchLabels:
app: rdev-api
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: databases
ports:
- protocol: TCP
port: 5432
```
## Database Setup
### Create Database
```sql
CREATE DATABASE rdev;
CREATE USER rdev_user WITH PASSWORD 'secure-password';
GRANT ALL PRIVILEGES ON DATABASE rdev TO rdev_user;
```
### Migrations
Migrations run automatically on startup. To run manually:
```bash
# Connect to pod
kubectl -n rdev exec -it deployment/rdev-api -- sh
# Check migration status
psql $DATABASE_URL -c "SELECT * FROM schema_migrations;"
```
## Scaling
### Horizontal Pod Autoscaler
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: rdev-api-hpa
namespace: rdev
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: rdev-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
```
## Upgrading
### Rolling Update
```bash
# Update image
kubectl -n rdev set image deployment/rdev-api \
rdev-api=your-registry/rdev-api:new-version
# Watch rollout
kubectl -n rdev rollout status deployment/rdev-api
```
### Rollback
```bash
# Rollback to previous version
kubectl -n rdev rollout undo deployment/rdev-api
# Rollback to specific revision
kubectl -n rdev rollout undo deployment/rdev-api --to-revision=2
```
## Health Checks
### Liveness
```bash
curl http://rdev-api/health
```
Returns `200 OK` if the service is running.
### Readiness
```bash
curl http://rdev-api/ready
```
Returns `200 OK` if database and K8s are connected.
## Troubleshooting
### Pod Not Starting
```bash
# Check pod events
kubectl -n rdev describe pod -l app=rdev-api
# Check logs
kubectl -n rdev logs -l app=rdev-api
```
### Database Connection Failed
1. Check secret is mounted correctly
2. Verify database host is reachable
3. Check network policy allows egress
### K8s API Errors
1. Verify ServiceAccount has correct RBAC
2. Check namespace configuration
3. Verify API server connectivity