towerops/k8s
Graham McIntire 03f364a956 feat(insights): LLM-powered insight enrichment
Adds an optional plain-language summary and recommended action to every
active insight. A new Oban cron worker runs every 5 minutes, picks up
unenriched insights, and asks the configured LLM to restate the finding
and suggest one specific action grounded in the structured metadata.

- Migration: adds llm_summary, recommended_action, llm_model,
  llm_enriched_at columns to preseem_insights
- Towerops.LLM context with swappable behaviour; real client uses Req
  with the standard Req.Test plug for test isolation
- MockClient + InsightPrompt module (builds chat messages, parses JSON
  with code-fence stripping and a raw-text fallback)
- Worker logs and skips on rate limits / missing key / parse errors so
  insights still render without an AI summary when the LLM is unavailable
- Optional towerops-llm k8s secret with example template; deployment.yaml
  references it as optional in both init and main containers
- UI renders summary + recommended action callout on /insights and on
  the org-level insights page
2026-05-09 16:41:48 -05:00
..
base-image Dockerfile: align Elixir/OTP/Debian versions with microwaveprop 2026-04-29 12:45:21 -05:00
certificate.yaml Remove www subdomain from certificate and IngressRoute 2026-01-02 15:06:21 -06:00
deployment.yaml feat(insights): LLM-powered insight enrichment 2026-05-09 16:41:48 -05:00
Dockerfile ci: cache slow runtime apt deps in a prebuilt base image 2026-05-05 11:19:49 -05:00
Dockerfile.base ci: cache slow runtime apt deps in a prebuilt base image 2026-05-05 11:19:49 -05:00
ingressroute.yaml Remove www subdomain from certificate and IngressRoute 2026-01-02 15:06:21 -06:00
kustomization.yaml fix: exclude cert-manager and traefik resources from FluxCD kustomization 2026-02-14 11:05:20 -06:00
namespace.yaml Add Kubernetes deployment manifests for towerops.net 2026-01-02 14:38:52 -06:00
poddisruptionbudget.yaml Add zero-downtime deployment configuration 2026-01-17 12:44:22 -06:00
README.md feat(insights): LLM-powered insight enrichment 2026-05-09 16:41:48 -05:00
service-headless.yaml cluster 2026-01-02 15:29:02 -06:00
service.yaml Add Kubernetes deployment manifests for towerops.net 2026-01-02 14:38:52 -06:00
towerops-llm-secret.example.yaml feat(insights): LLM-powered insight enrichment 2026-05-09 16:41:48 -05:00
traefik-security-headers-middleware.yaml fix prod crash with yaml parsing 2026-01-31 08:25:07 -06:00

Kubernetes Deployment

Secrets Management

Secrets are managed directly in the cluster and must be created before deploying the application.

Required secrets in the towerops namespace:

  • gitlab-registry - Docker registry credentials for pulling images
  • towerops-secrets - Application secrets (RELEASE_COOKIE, SECRET_KEY_BASE)
  • towerops-db - Database connection credentials
  • towerops-aws - AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION)

Optional secrets:

  • towerops-llm - DeepSeek API credentials for LLM-powered insight enrichment. Optional — when missing, insights still display without an AI summary. See k8s/towerops-llm-secret.example.yaml for the template, or create with:

    kubectl create secret generic towerops-llm \
      --from-literal=DEEPSEEK_API_KEY="<your-key>" \
      --from-literal=DEEPSEEK_MODEL="deepseek-v4-pro" \
      -n towerops
    kubectl rollout restart deployment/towerops -n towerops
    

For local development, the project root .envrc is used by direnv.

Deployment Timestamp

The application footer displays the deployment timestamp to track when the current version was deployed. This is automatically set by GitLab CI during deployment:

# GitLab CI sets this during deploy
- kubectl set env deployment/towerops DEPLOY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -n towerops

All pods in the deployment share the same timestamp (when the deployment was initiated), regardless of when individual pods were created. This is displayed in the footer as "Last deployed X ago · YYYY-MM-DD HH:MM:SS UTC".

For manual deployments without GitLab CI, set the timestamp:

kubectl set env deployment/towerops DEPLOY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -n towerops

Deploying

Apply all resources using kustomize:

kubectl apply -k k8s/

Or individually:

kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/service-headless.yaml
kubectl apply -f k8s/certificate.yaml
kubectl apply -f k8s/ingressroute.yaml