Deploy to production: GitHub Actions + ghcr.io + Kubernetes

- Switch from Gitea to GitHub Container Registry (ghcr.io)
- Add GitHub Actions workflow with Tailscale connectivity
- Update k8s manifests for cloud nodes and Traefik ingress
- Configure for turbo.kazcloud.dev domain
- Test deployment with home page text change
This commit is contained in:
2026-03-29 08:46:27 -04:00
parent 2bb1dfa1e4
commit 69993a3bf5
14 changed files with 793 additions and 596 deletions

320
INITIAL_DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,320 @@
# Initial Deployment Guide
Follow these steps to deploy TurboVault to Kubernetes for the first time.
## Prerequisites
- ✅ Code pushed to GitHub
- ✅ PostgreSQL database ready (host, user, password)
- ✅ kubectl configured for k3s cluster (100.101.31.99:6443)
- ✅ Docker installed locally
- ✅ Gitea account at gitea.kazcloud.dev
---
## Step 1: Build and Push Initial Image
Since GitHub Actions hasn't run yet, build the first image manually:
```bash
# Build image
docker build -t gitea.kazcloud.dev/ryan/turbovault-app:v1.0.0 .
# Login to Gitea registry
docker login gitea.kazcloud.dev
# Username: ryankazokas
# Password: <your-gitea-token>
# Push image
docker push gitea.kazcloud.dev/ryan/turbovault-app:v1.0.0
# Also tag as latest
docker tag gitea.kazcloud.dev/ryan/turbovault-app:v1.0.0 \
gitea.kazcloud.dev/ryan/turbovault-app:latest
docker push gitea.kazcloud.dev/ryan/turbovault-app:latest
```
**Verify:** Check Gitea packages at `gitea.kazcloud.dev/ryankazokas/-/packages`
---
## Step 2: Configure Kubernetes Secrets
```bash
# Copy template
cp k8s/secrets.yaml.example k8s/secrets.yaml
# Generate Rails secret key
rails secret
# Copy the output
# Edit secrets file
nano k8s/secrets.yaml
```
**Add these values in `k8s/secrets.yaml`:**
```yaml
apiVersion: v1
kind: Secret
metadata:
name: turbovault-secrets
namespace: turbovault
type: Opaque
stringData:
SECRET_KEY_BASE: "<paste-output-from-rails-secret>"
DATABASE_PASSWORD: "your-postgres-password"
# Optional: IGDB integration
IGDB_CLIENT_ID: "your-igdb-client-id"
IGDB_CLIENT_SECRET: "your-igdb-client-secret"
# Optional: Email (for password resets)
SMTP_ADDRESS: "smtp.example.com"
SMTP_PORT: "587"
SMTP_USERNAME: "user@example.com"
SMTP_PASSWORD: "smtp-password"
```
---
## Step 3: Configure Database Connection
Edit `k8s/configmap.yaml`:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: turbovault-config
namespace: turbovault
data:
RAILS_ENV: "production"
DATABASE_HOST: "your-postgres-host"
DATABASE_NAME: "turbovault_production"
DATABASE_USERNAME: "turbovault"
RAILS_LOG_TO_STDOUT: "true"
RAILS_SERVE_STATIC_FILES: "true"
```
**Replace:**
- `your-postgres-host` - Your PostgreSQL server hostname/IP
- Database name and username if different
---
## Step 4: Deploy to Kubernetes
Run the automated deployment script:
```bash
./scripts/deploy-k8s.sh
```
**When prompted for registry credentials:**
- Registry: `gitea.kazcloud.dev`
- Username: `ryankazokas`
- Password: `<your-gitea-token>`
**The script will:**
1. Create namespace
2. Apply configmap
3. Apply secrets
4. Run database migration job
5. Deploy application
6. Create service
7. Create ingress
---
## Step 5: Verify Deployment
```bash
# Check all resources
kubectl get all -n turbovault
# Check pods are running
kubectl get pods -n turbovault
# View logs
kubectl logs -f -l app=turbovault -n turbovault
# Check migration job completed
kubectl logs job/turbovault-migrate -n turbovault
```
**Expected output:**
```
NAME READY STATUS RESTARTS AGE
pod/turbovault-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
pod/turbovault-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
```
---
## Step 6: Access Application
### Option A: Port Forward (Testing)
```bash
kubectl port-forward svc/turbovault-service 3000:80 -n turbovault
```
Visit: http://localhost:3000
### Option B: Ingress (Production)
Edit `k8s/ingress.yaml` with your domain and apply:
```bash
kubectl apply -f k8s/ingress.yaml
```
Visit: https://your-domain.com
---
## Step 7: Configure GitHub Secrets
Now set up automated deployments for future updates.
See [docs/GITHUB_SECRETS.md](docs/GITHUB_SECRETS.md) for detailed instructions.
**Required secrets (add at https://github.com/ryankazokas/turbovault-app/settings/secrets/actions):**
1. `GITEA_USERNAME` - Your Gitea username
2. `GITEA_TOKEN` - Gitea access token (Settings → Applications)
3. `TAILSCALE_CLIENT_ID` - Tailscale OAuth client ID
4. `TAILSCALE_CLIENT_SECRET` - Tailscale OAuth client secret
5. `KUBECONFIG` - Base64-encoded kubeconfig (`cat ~/.kube/config | base64 -w 0`)
---
## Step 8: Test Automated Deployment
After GitHub secrets are configured:
```bash
# Create a test tag
git tag v1.0.1
git push origin v1.0.1
```
Watch at: https://github.com/ryankazokas/turbovault-app/actions
GitHub Actions will:
1. Build Docker image
2. Push to Gitea registry
3. Connect via Tailscale
4. Deploy to Kubernetes
---
## Troubleshooting
### Pods in CrashLoopBackOff
```bash
# View logs
kubectl logs -l app=turbovault -n turbovault
# Common issues:
# - Database connection failed (check configmap/secrets)
# - Missing SECRET_KEY_BASE (check secrets)
# - Migration not run (check migration job logs)
```
### Migration Job Failed
```bash
# View migration logs
kubectl logs job/turbovault-migrate -n turbovault
# Re-run migration
kubectl delete job turbovault-migrate -n turbovault
kubectl apply -f k8s/migrate-job.yaml
```
### Can't Pull Image
```bash
# Check image pull secret
kubectl get secrets -n turbovault
# Re-run deploy script to create secret
./scripts/deploy-k8s.sh
```
### Database Connection Failed
```bash
# Test from pod
kubectl exec -it deployment/turbovault -n turbovault -- \
rails runner "puts ActiveRecord::Base.connection.execute('SELECT 1').first"
# Check environment variables
kubectl exec -it deployment/turbovault -n turbovault -- env | grep DATABASE
```
---
## Next Steps
After successful initial deployment:
1. ✅ Application is running in Kubernetes
2. ✅ GitHub Actions configured for automated deployments
3. 🚀 **Daily workflow:** Just push tags to deploy!
```bash
# Make changes
git add .
git commit -m "Feature: new functionality"
git push
# Deploy
git tag v1.0.2
git push origin v1.0.2
# GitHub Actions automatically builds and deploys! ✅
```
---
## Quick Reference
```bash
# View status
kubectl get all -n turbovault
# View logs
kubectl logs -f -l app=turbovault -n turbovault
# Restart deployment
kubectl rollout restart deployment/turbovault -n turbovault
# Rollback deployment
kubectl rollout undo deployment/turbovault -n turbovault
# Delete everything (start over)
kubectl delete namespace turbovault
```
---
## Files You Modified
Keep these files safe (they're gitignored):
- `k8s/secrets.yaml` - Contains sensitive data (SECRET_KEY_BASE, passwords)
- `~/.kube/config` - Your Kubernetes access
**DO NOT** commit these to git!
---
**Need help?** Check logs first:
```bash
kubectl describe pod -l app=turbovault -n turbovault
kubectl logs -l app=turbovault -n turbovault
```