feat(infra): add production-grade k3s Kubernetes setup for single VM
Complete K8s deployment alternative to Docker Compose, targeting
~50 beta users on a Standard_D8s_v5 Azure VM (8 vCPU, 32 GB RAM).
setup-k8s.sh (6 phases):
1. Pre-flight: verify docker phases 1-5 ran, disk/RAM checks
2. Install k3s: Docker runtime, NodePort range 1024-32767
3. Build images: docker compose build + tag as bytelyst/<svc>
4. Config: namespaces, ConfigMap (3 copies), Secrets (JWT + blob keys), Ollama
5. Deploy: infra -> platform -> dashboards -> products (ordered)
6. Health check: 32 endpoints + kubectl pod status
K8s manifests (18 files):
- 4 namespaces (infra, platform, dashboards, products)
- 6 infra (cosmos StatefulSet+PVC, azurite StatefulSet+PVC,
mailpit, loki StatefulSet+PVC, grafana+PVC, ollama external)
- 3 platform (Deployment+Service+NodePort each)
- 2 dashboards (Deployment+Service+NodePort each)
- 10 backends + 9 webs (all with readiness+liveness probes,
resource limits, product-specific NEXT_PUBLIC_* env vars)
Design decisions:
- k3s --docker: reuses existing Docker images, no containerd import
- Same ports as Docker Compose (NodePort with extended range)
- ConfigMap replaces .env.ecosystem, copied to 3 app namespaces
- Blob storage keys injected at deploy time via Secret (not in YAML)
- Cross-namespace DNS: <svc>.<ns>.svc for service discovery
- Ollama as Endpoints+Service pointing to host node IP
- Resource limits: ~19 Gi total, fits in 32 GB with 13 GB headroom
- Teardown: --teardown flag deletes namespaces, keeps k3s
This commit is contained in:
parent
7d0c469858
commit
8a568932b4
@ -1,18 +1,30 @@
|
||||
# ByteLyst Single-VM Kubernetes Deployment (k3s)
|
||||
|
||||
> Deploy the ByteLyst ecosystem on Kubernetes using **k3s** — a lightweight, certified K8s distribution
|
||||
> that runs on a single VM with ~512 MB overhead.
|
||||
|
||||
**Status:** Planning — see design decisions below.
|
||||
> Deploy the **entire ByteLyst ecosystem** (30 services, 10 products) on Kubernetes
|
||||
> using **k3s** — a lightweight, CNCF-certified K8s distribution.
|
||||
> Production-grade for ~50 beta users on a single Azure VM.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Step 1: Run Docker setup phases 1-5 (system deps, Gitea, repos, packages)
|
||||
cd /opt/bytelyst/learning_ai_common_plat/docs/devops/single_azure_vm
|
||||
sudo ./docker/setup.sh --resume # Runs phases 1-5 (skip 6-8)
|
||||
|
||||
# Step 2: Deploy to Kubernetes
|
||||
sudo ./k8s/setup-k8s.sh # 6 phases: preflight → k3s → images → config → deploy → health
|
||||
|
||||
# Step 3: Verify
|
||||
/opt/bytelyst/check-health-k8s.sh # 32 health checks
|
||||
kubectl get pods -A # All pods
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Same VM as the Docker Compose approach:
|
||||
- **Azure VM:** Ubuntu 24.04 LTS, **Standard_D8s_v5** (8 vCPU, 32 GB RAM)
|
||||
- **Disk:** 128 GB+
|
||||
- **Docker images:** Built by `docker/setup.sh` phases 1-5 (reused, not rebuilt)
|
||||
- **Azure VM:** Ubuntu 24.04 LTS, **Standard_D8s_v5** (8 vCPU, 32 GB RAM, 128 GB disk)
|
||||
- **Docker setup phases 1-5 completed** (system deps, Gitea, repos, packages built + published)
|
||||
|
||||
## Why k3s?
|
||||
|
||||
@ -59,252 +71,156 @@ Ubuntu 24.04 VM
|
||||
└── Gitea (Docker container — :3300, used for build-time only)
|
||||
```
|
||||
|
||||
## Manifest Structure (planned)
|
||||
## File Structure
|
||||
|
||||
```
|
||||
k8s/
|
||||
├── README.md # This file
|
||||
├── setup-k8s.sh # Bootstrap script (installs k3s, applies manifests)
|
||||
├── setup-k8s.sh # Bootstrap script (6 phases)
|
||||
├── namespaces.yaml # 4 namespaces
|
||||
├── config/
|
||||
│ ├── configmap.yaml # Shared env vars (replaces .env.ecosystem)
|
||||
│ └── secrets.yaml # JWT_SECRET, COSMOS_KEY, etc.
|
||||
│ └── secrets.yaml # JWT_SECRET template (generated at deploy)
|
||||
├── infra/
|
||||
│ ├── cosmos-emulator.yaml # StatefulSet + Service + PVC
|
||||
│ ├── azurite.yaml # StatefulSet + Service + PVC
|
||||
│ ├── mailpit.yaml # Deployment + Service
|
||||
│ ├── loki.yaml # StatefulSet + Service + PVC
|
||||
│ └── grafana.yaml # Deployment + Service + PVC
|
||||
│ ├── cosmos-emulator.yaml # StatefulSet + Service + PVC + NodePort
|
||||
│ ├── azurite.yaml # StatefulSet + Service + PVC + NodePort
|
||||
│ ├── mailpit.yaml # Deployment + Service + NodePort
|
||||
│ ├── loki.yaml # StatefulSet + Service + PVC + NodePort
|
||||
│ ├── grafana.yaml # Deployment + Service + PVC + NodePort
|
||||
│ └── ollama-external.yaml # Service + Endpoints → host Ollama
|
||||
├── platform/
|
||||
│ ├── platform-service.yaml # Deployment + Service
|
||||
│ ├── extraction-service.yaml # Deployment + Service
|
||||
│ └── mcp-server.yaml # Deployment + Service
|
||||
│ ├── platform-service.yaml # Deployment + Service + NodePort (:4003)
|
||||
│ ├── extraction-service.yaml # Deployment + Service + NodePort (:4005)
|
||||
│ └── mcp-server.yaml # Deployment + Service + NodePort (:4007)
|
||||
├── dashboards/
|
||||
│ ├── admin-web.yaml # Deployment + Service
|
||||
│ └── tracker-web.yaml # Deployment + Service
|
||||
├── products/
|
||||
│ ├── _backend-template.yaml # Helm-like template (for reference)
|
||||
│ ├── peakpulse-backend.yaml
|
||||
│ ├── chronomind-backend.yaml
|
||||
│ ├── ... (8 more backends)
|
||||
│ ├── lysnrai-dashboard.yaml
|
||||
│ ├── chronomind-web.yaml
|
||||
│ └── ... (7 more web apps)
|
||||
└── ingress/
|
||||
└── ingress.yaml # Traefik IngressRoute rules
|
||||
│ ├── admin-web.yaml # Deployment + Service + NodePort (:3001)
|
||||
│ └── tracker-web.yaml # Deployment + Service + NodePort (:3003)
|
||||
└── products/
|
||||
├── backends.yaml # 10 backend Deployments + Services + NodePorts
|
||||
└── webs.yaml # 9 web Deployments + Services + NodePorts
|
||||
```
|
||||
|
||||
## Setup Phases
|
||||
|
||||
| Phase | Duration | What happens |
|
||||
|-------|----------|--------------|
|
||||
| 1. Pre-flight | ~10s | Verify Docker phases 1-5 completed, check disk/RAM |
|
||||
| 2. Install k3s | ~2 min | k3s with Docker runtime, NodePort range 1024-32767 |
|
||||
| 3. Build images | ~15 min | Docker compose build + tag as `bytelyst/<service>:latest` |
|
||||
| 4. Generate config | ~30s | Namespaces, ConfigMap (3 copies), Secrets (JWT), Ollama endpoint |
|
||||
| 5. Deploy | ~5 min | Apply manifests: infra → platform → dashboards → products |
|
||||
| 6. Health check | ~1 min | 32 endpoint checks + kubectl pod status |
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### 1. Image Source: Import from Docker
|
||||
### k3s with Docker Runtime
|
||||
k3s installed with `--docker` flag — reuses existing Docker daemon and images.
|
||||
No `containerd` import step needed. Same images used by Docker Compose work directly.
|
||||
|
||||
k3s uses containerd, not Docker. We import the Docker-built images:
|
||||
### 4-Namespace Isolation
|
||||
- **bytelyst-infra** — Cosmos emulator, Azurite, Mailpit, Loki, Grafana
|
||||
- **bytelyst-platform** — platform-service, extraction-service, mcp-server
|
||||
- **bytelyst-dashboards** — admin-web, tracker-web
|
||||
- **bytelyst-products** — 10 backends + 9 web apps
|
||||
|
||||
```bash
|
||||
# Build images with Docker (phases 1-7 from docker/setup.sh)
|
||||
docker save platform-service:latest | k3s ctr images import -
|
||||
ConfigMap + Secrets are copied to all 3 app namespaces by the setup script.
|
||||
|
||||
# Or build directly with nerdctl (k3s-native)
|
||||
nerdctl build -t platform-service:latest -f services/platform-service/Dockerfile .
|
||||
```
|
||||
### Cross-Namespace DNS
|
||||
K8s DNS: `<service>.<namespace>.svc.cluster.local`
|
||||
- Backends reach Cosmos: `cosmos-emulator.bytelyst-infra.svc:8081`
|
||||
- Webs reach backends: `flowmonk-backend.bytelyst-products.svc:4017`
|
||||
- Everything reaches platform: `platform-service.bytelyst-platform.svc:4003`
|
||||
|
||||
**Decision:** Import from Docker first (simpler), migrate to nerdctl later.
|
||||
### Ollama as External Service
|
||||
Ollama stays on the host (systemd). A headless Service + Endpoints in `bytelyst-infra`
|
||||
points to the node's internal IP. Pods reach it as `ollama.bytelyst-infra.svc:11434`.
|
||||
Setup script auto-detects the node IP.
|
||||
|
||||
### 2. Cosmos Emulator: StatefulSet with PVC
|
||||
### NodePort for External Access
|
||||
All services use the **same ports** as Docker Compose (e.g., `:4003`, `:3002`, `:3030`).
|
||||
k3s is configured with `--kube-apiserver-arg=service-node-port-range=1024-32767`.
|
||||
|
||||
The Cosmos emulator needs persistent storage and specific env vars.
|
||||
Use a `StatefulSet` (not Deployment) for stable network identity:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: cosmos-emulator
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: cosmos-emulator
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: cosmos
|
||||
image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
- containerPort: 1234
|
||||
env:
|
||||
- name: AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE
|
||||
value: "true"
|
||||
- name: ENABLE_EXPLORER
|
||||
value: "true"
|
||||
resources:
|
||||
limits:
|
||||
memory: "3Gi"
|
||||
cpu: "2"
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: cosmos-data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
```
|
||||
|
||||
### 3. Ollama: Host Network
|
||||
|
||||
Ollama stays as a systemd service on the host. Pods reach it via `hostNetwork`
|
||||
or a manually created Endpoints + Service pointing to the node IP:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
ports:
|
||||
- port: 11434
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: bytelyst-products
|
||||
subsets:
|
||||
- addresses:
|
||||
- ip: 172.17.0.1 # Host IP (node's internal IP)
|
||||
ports:
|
||||
- port: 11434
|
||||
```
|
||||
|
||||
### 4. ConfigMap replaces .env.ecosystem
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bytelyst-config
|
||||
namespace: bytelyst-platform
|
||||
data:
|
||||
COSMOS_ENDPOINT: "http://cosmos-emulator.bytelyst-infra.svc:8081"
|
||||
COSMOS_DATABASE: "bytelyst"
|
||||
DB_PROVIDER: "cosmos"
|
||||
PLATFORM_SERVICE_URL: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
EXTRACTION_SERVICE_URL: "http://extraction-service.bytelyst-platform.svc:4005"
|
||||
```
|
||||
|
||||
Note: K8s DNS uses `<service>.<namespace>.svc` format for cross-namespace access.
|
||||
|
||||
### 5. Secrets for sensitive values
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: bytelyst-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
JWT_SECRET: "<generated>"
|
||||
COSMOS_KEY: "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
||||
AZURE_BLOB_ACCOUNT_KEY: "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
|
||||
```
|
||||
|
||||
### 6. Health Checks → Readiness/Liveness Probes
|
||||
|
||||
Every backend gets K8s-native probes:
|
||||
|
||||
```yaml
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4003
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4003
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
```
|
||||
|
||||
### 7. Resource Limits
|
||||
### Resource Limits (tuned for 32 GB VM, 50 beta users)
|
||||
|
||||
| Service type | CPU request | CPU limit | Memory request | Memory limit |
|
||||
|-------------|------------|-----------|---------------|-------------|
|
||||
| Backend | 100m | 500m | 256Mi | 512Mi |
|
||||
| Web app | 100m | 500m | 256Mi | 512Mi |
|
||||
| Platform service | 200m | 1000m | 384Mi | 768Mi |
|
||||
| Cosmos emulator | 1000m | 2000m | 2Gi | 3Gi |
|
||||
| Ollama | (host) | (host) | (host) | (host) |
|
||||
| Backend (×10) | 100m | 500m | 256Mi | 512Mi |
|
||||
| Web app (×9) | 100m | 500m | 256Mi | 512Mi |
|
||||
| Platform (×3) | 200m | 1000m | 384Mi | 768Mi |
|
||||
| Cosmos emulator | 500m | 2000m | 2Gi | 3Gi |
|
||||
| Grafana | 100m | 500m | 128Mi | 256Mi |
|
||||
| Mailpit / Loki | 50-100m | 500m | 64-128Mi | 512Mi |
|
||||
| k3s overhead | — | — | — | ~512Mi |
|
||||
| Ollama (host) | — | — | — | ~3Gi |
|
||||
| **Total** | | | **~10 Gi** | **~19 Gi** |
|
||||
|
||||
## Implementation Phases
|
||||
Fits comfortably in 32 GB with ~13 GB headroom.
|
||||
|
||||
### Phase A: Foundation (Day 1)
|
||||
- [ ] Install k3s on VM
|
||||
- [ ] Create 4 namespaces
|
||||
- [ ] Deploy ConfigMap + Secrets
|
||||
- [ ] Deploy cosmos-emulator + azurite (StatefulSets)
|
||||
- [ ] Verify: `kubectl get pods -A` shows infra running
|
||||
### Readiness + Liveness Probes
|
||||
Every service gets both:
|
||||
- **Readiness:** `GET /health` every 10s (traffic only when ready)
|
||||
- **Liveness:** `GET /health` every 30s (auto-restart on failure)
|
||||
- Backends: `initialDelaySeconds: 15`, Web apps: `initialDelaySeconds: 15`
|
||||
- Cosmos emulator: `initialDelaySeconds: 60` (slow startup)
|
||||
|
||||
### Phase B: Platform (Day 1-2)
|
||||
- [ ] Import platform-service Docker image
|
||||
- [ ] Deploy platform-service (Deployment + Service)
|
||||
- [ ] Verify: `kubectl exec` + `curl http://platform-service:4003/health`
|
||||
- [ ] Deploy extraction-service + mcp-server
|
||||
- [ ] Deploy admin-web + tracker-web
|
||||
|
||||
### Phase C: Products (Day 2-3)
|
||||
- [ ] Template: create one backend manifest, verify it works
|
||||
- [ ] Replicate for all 10 backends
|
||||
- [ ] Create web app manifests (9 services)
|
||||
- [ ] Verify: all 30 services running
|
||||
|
||||
### Phase D: Networking (Day 3)
|
||||
- [ ] Set up Traefik IngressRoute for external access
|
||||
- [ ] Configure NodePort services for direct port access
|
||||
- [ ] Create Ollama external service endpoint
|
||||
- [ ] Verify: health check script works against K8s services
|
||||
|
||||
### Phase E: Operations (Day 4+)
|
||||
- [ ] `kubectl scale deployment/flowmonk-backend --replicas=2` — test scaling
|
||||
- [ ] `kubectl rollout restart deployment/platform-service` — test rolling update
|
||||
- [ ] `kubectl top pods` — resource usage monitoring
|
||||
- [ ] Set up HorizontalPodAutoscaler for one service
|
||||
- [ ] Practice: `kubectl logs`, `kubectl exec`, `kubectl describe`
|
||||
|
||||
## Useful Commands (cheat sheet)
|
||||
## Operations Cheat Sheet
|
||||
|
||||
```bash
|
||||
# Cluster status
|
||||
kubectl get nodes
|
||||
kubectl get pods -A # All namespaces
|
||||
kubectl get pods -n bytelyst-products # Product namespace
|
||||
# ── Cluster status ─────────────────────────────────
|
||||
kubectl get nodes # Node health
|
||||
kubectl get pods -A # All pods
|
||||
kubectl top pods -A # Resource usage (CPU/memory)
|
||||
|
||||
# Deploy / update
|
||||
kubectl apply -f k8s/ # Apply all manifests
|
||||
kubectl apply -f k8s/products/ # Apply product manifests
|
||||
kubectl rollout restart deployment/flowmonk-backend -n bytelyst-products
|
||||
# ── Deploy / update ────────────────────────────────
|
||||
kubectl apply -f k8s/products/ # Re-apply product manifests
|
||||
kubectl rollout restart deploy/flowmonk-backend -n bytelyst-products # Rolling restart
|
||||
|
||||
# Debugging
|
||||
kubectl logs deployment/platform-service -n bytelyst-platform -f
|
||||
kubectl describe pod <pod-name> -n bytelyst-platform
|
||||
kubectl exec -it deployment/platform-service -n bytelyst-platform -- sh
|
||||
# ── Scaling (for load testing) ─────────────────────
|
||||
kubectl scale deploy/platform-service --replicas=2 -n bytelyst-platform
|
||||
kubectl autoscale deploy/flowmonk-backend --min=1 --max=3 --cpu-percent=70 -n bytelyst-products
|
||||
|
||||
# Scaling
|
||||
kubectl scale deployment/flowmonk-backend --replicas=2 -n bytelyst-products
|
||||
kubectl autoscale deployment/flowmonk-backend --min=1 --max=3 --cpu-percent=70
|
||||
# ── Debugging ──────────────────────────────────────
|
||||
kubectl logs deploy/platform-service -n bytelyst-platform -f # Stream logs
|
||||
kubectl describe pod <name> -n bytelyst-platform # Pod events
|
||||
kubectl exec -it deploy/platform-service -n bytelyst-platform -- sh # Shell into pod
|
||||
|
||||
# Resource monitoring
|
||||
kubectl top pods -n bytelyst-products
|
||||
kubectl top nodes
|
||||
# ── Teardown ───────────────────────────────────────
|
||||
sudo ./setup-k8s.sh --teardown # Delete all namespaces (keep k3s)
|
||||
/usr/local/bin/k3s-uninstall.sh # Uninstall k3s completely
|
||||
```
|
||||
|
||||
## Migration from Docker Compose
|
||||
## Port Map (same as Docker Compose)
|
||||
|
||||
Both approaches can coexist on the same VM:
|
||||
1. `docker/setup.sh` builds images and publishes packages (phases 1-5)
|
||||
2. `docker compose down` stops the compose stack
|
||||
3. `setup-k8s.sh` imports images into k3s and applies manifests
|
||||
4. Both share the same Gitea registry and Ollama instance
|
||||
| Service | Port | Health check |
|
||||
|---------|------|-------------|
|
||||
| Gitea (npm) | 3300 | `http://localhost:3300/api/v1/version` |
|
||||
| Ollama (LLM) | 11434 | `http://localhost:11434/api/version` |
|
||||
| Cosmos Explorer | 1234 | `http://localhost:1234` |
|
||||
| Azurite (Blob) | 10000 | `http://localhost:10000/devstoreaccount1?comp=list` |
|
||||
| Mailpit UI | 8025 | `http://localhost:8025` |
|
||||
| Loki | 3100 | `http://localhost:3100/ready` |
|
||||
| Grafana | 3000 | `http://localhost:3000/api/health` |
|
||||
| platform-service | 4003 | `/health` |
|
||||
| extraction-service | 4005 | `/health` |
|
||||
| mcp-server | 4007 | `/health` |
|
||||
| admin-web | 3001 | `/` |
|
||||
| tracker-web | 3003 | `/` |
|
||||
| Backends | 4010-4019 | `/health` |
|
||||
| Web apps | 3002, 3030, 3035, 3040, 3045, 3050, 3055, 3060, 3070 | `/` |
|
||||
|
||||
## Switching Between Docker Compose and K8s
|
||||
|
||||
Both approaches coexist on the same VM:
|
||||
|
||||
```bash
|
||||
# Docker → K8s
|
||||
cd /opt/bytelyst/learning_ai_common_plat
|
||||
docker compose -f docker-compose.ecosystem.yml down # Stop compose stack
|
||||
sudo ../docs/devops/single_azure_vm/k8s/setup-k8s.sh # Deploy to k3s
|
||||
|
||||
# K8s → Docker
|
||||
sudo ./setup-k8s.sh --teardown # Remove k8s resources
|
||||
sudo ../docker/setup.sh --phase=7 # Re-deploy via compose
|
||||
```
|
||||
|
||||
Both share: Gitea registry (Docker container), Ollama (systemd), and built Docker images.
|
||||
|
||||
77
docs/devops/single_azure_vm/k8s/config/configmap.yaml
Normal file
77
docs/devops/single_azure_vm/k8s/config/configmap.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
# Shared configuration for all ByteLyst services.
|
||||
# Generated by setup-k8s.sh phase 4 — this is the TEMPLATE.
|
||||
# The setup script replaces JWT_SECRET with a random value at deploy time.
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bytelyst-config
|
||||
namespace: bytelyst-platform
|
||||
labels:
|
||||
app.kubernetes.io/part-of: bytelyst
|
||||
data:
|
||||
# Cosmos DB Emulator
|
||||
COSMOS_ENDPOINT: "http://cosmos-emulator.bytelyst-infra.svc:8081"
|
||||
COSMOS_KEY: "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
||||
COSMOS_DATABASE: "bytelyst"
|
||||
DB_PROVIDER: "cosmos"
|
||||
|
||||
# Azure Blob Storage (Azurite) — keys in bytelyst-secrets
|
||||
STORAGE_PROVIDER: "azure"
|
||||
AZURE_BLOB_ACCOUNT_NAME: "devstoreaccount1"
|
||||
AZURE_BLOB_PUBLIC_ENDPOINT: "http://localhost:10000/devstoreaccount1"
|
||||
|
||||
# Email (Mailpit)
|
||||
EMAIL_PROVIDER: "smtp"
|
||||
EMAIL_FROM_ADDRESS: "noreply@bytelyst.local"
|
||||
EMAIL_FROM_NAME: "ByteLyst"
|
||||
SMTP_HOST: "mailpit.bytelyst-infra.svc"
|
||||
SMTP_PORT: "1025"
|
||||
SMTP_SECURE: "false"
|
||||
SMTP_USER: ""
|
||||
SMTP_PASSWORD: ""
|
||||
|
||||
# Stripe (test placeholders)
|
||||
STRIPE_SECRET_KEY: "sk_test_placeholder"
|
||||
STRIPE_WEBHOOK_SECRET: "whsec_placeholder"
|
||||
STRIPE_PRICE_PRO: "price_placeholder"
|
||||
STRIPE_PRICE_ENTERPRISE: "price_placeholder"
|
||||
|
||||
# Extraction Service
|
||||
PYTHON_SIDECAR_URL: "http://localhost:4006"
|
||||
DEFAULT_MODEL_ID: "gemini-2.5-flash"
|
||||
GEMINI_API_KEY: "placeholder"
|
||||
EXTRACTION_QUEUE_BACKEND: "file"
|
||||
EXTRACTION_QUEUE_FILE: ".data/extraction-jobs.json"
|
||||
|
||||
# Cross-service URLs (K8s DNS: <service>.<namespace>.svc)
|
||||
PLATFORM_SERVICE_URL: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
EXTRACTION_SERVICE_URL: "http://extraction-service.bytelyst-platform.svc:4005"
|
||||
MCP_SERVER_URL: "http://mcp-server.bytelyst-platform.svc:4007"
|
||||
|
||||
# Telemetry
|
||||
TELEMETRY_ENABLED: "true"
|
||||
RATE_LIMIT_STORE_MODE: "datastore"
|
||||
|
||||
# Event Bus
|
||||
EVENT_BUS_BACKEND: "file"
|
||||
EVENT_BUS_FILE: ".data/platform-events.json"
|
||||
|
||||
# Field Encryption
|
||||
FIELD_ENCRYPT_KEY_PROVIDER: "memory"
|
||||
|
||||
# Product Identity
|
||||
DEFAULT_PRODUCT_ID: "lysnrai"
|
||||
|
||||
# Webhooks (disabled)
|
||||
WEBHOOK_INVITATION_REDEEMED_URL: ""
|
||||
WEBHOOK_REFERRAL_STATUS_URL: ""
|
||||
WEBHOOK_WAITLIST_JOINED_URL: ""
|
||||
|
||||
# Notifications (disabled)
|
||||
TELEGRAM_BOT_TOKEN: ""
|
||||
TELEGRAM_DEFAULT_CHAT_ID: ""
|
||||
SLACK_WEBHOOK_URL: ""
|
||||
SLACK_DEFAULT_CHANNEL: ""
|
||||
|
||||
# Ollama (host service via ExternalName)
|
||||
OLLAMA_URL: "http://ollama.bytelyst-infra.svc:11434"
|
||||
14
docs/devops/single_azure_vm/k8s/config/secrets.yaml
Normal file
14
docs/devops/single_azure_vm/k8s/config/secrets.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
# Sensitive configuration — JWT_SECRET is replaced by setup-k8s.sh at deploy time.
|
||||
# Cosmos and Azurite keys are well-known emulator keys (public, safe to commit).
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: bytelyst-secrets
|
||||
namespace: bytelyst-platform
|
||||
labels:
|
||||
app.kubernetes.io/part-of: bytelyst
|
||||
type: Opaque
|
||||
stringData:
|
||||
JWT_SECRET: "REPLACE_ME_AT_DEPLOY_TIME"
|
||||
AZURE_BLOB_ACCOUNT_KEY: "REPLACE_ME_AT_DEPLOY_TIME"
|
||||
AZURE_BLOB_CONNECTION_STRING: "REPLACE_ME_AT_DEPLOY_TIME"
|
||||
80
docs/devops/single_azure_vm/k8s/dashboards/admin-web.yaml
Normal file
80
docs/devops/single_azure_vm/k8s/dashboards/admin-web.yaml
Normal file
@ -0,0 +1,80 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: admin-web
|
||||
namespace: bytelyst-dashboards
|
||||
labels:
|
||||
app: admin-web
|
||||
tier: dashboard
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: admin-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: admin-web
|
||||
tier: dashboard
|
||||
spec:
|
||||
containers:
|
||||
- name: admin-web
|
||||
image: bytelyst/admin-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3001
|
||||
name: http
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3001"
|
||||
- name: PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3001
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3001
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: admin-web
|
||||
namespace: bytelyst-dashboards
|
||||
spec:
|
||||
selector:
|
||||
app: admin-web
|
||||
ports:
|
||||
- name: http
|
||||
port: 3001
|
||||
targetPort: 3001
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: admin-web-external
|
||||
namespace: bytelyst-dashboards
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: admin-web
|
||||
ports:
|
||||
- name: http
|
||||
port: 3001
|
||||
targetPort: 3001
|
||||
nodePort: 3001
|
||||
80
docs/devops/single_azure_vm/k8s/dashboards/tracker-web.yaml
Normal file
80
docs/devops/single_azure_vm/k8s/dashboards/tracker-web.yaml
Normal file
@ -0,0 +1,80 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: tracker-web
|
||||
namespace: bytelyst-dashboards
|
||||
labels:
|
||||
app: tracker-web
|
||||
tier: dashboard
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: tracker-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: tracker-web
|
||||
tier: dashboard
|
||||
spec:
|
||||
containers:
|
||||
- name: tracker-web
|
||||
image: bytelyst/tracker-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3003
|
||||
name: http
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3003"
|
||||
- name: PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3003
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3003
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tracker-web
|
||||
namespace: bytelyst-dashboards
|
||||
spec:
|
||||
selector:
|
||||
app: tracker-web
|
||||
ports:
|
||||
- name: http
|
||||
port: 3003
|
||||
targetPort: 3003
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tracker-web-external
|
||||
namespace: bytelyst-dashboards
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: tracker-web
|
||||
ports:
|
||||
- name: http
|
||||
port: 3003
|
||||
targetPort: 3003
|
||||
nodePort: 3003
|
||||
80
docs/devops/single_azure_vm/k8s/infra/azurite.yaml
Normal file
80
docs/devops/single_azure_vm/k8s/infra/azurite.yaml
Normal file
@ -0,0 +1,80 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: azurite
|
||||
namespace: bytelyst-infra
|
||||
labels:
|
||||
app: azurite
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: azurite
|
||||
selector:
|
||||
matchLabels:
|
||||
app: azurite
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: azurite
|
||||
spec:
|
||||
containers:
|
||||
- name: azurite
|
||||
image: mcr.microsoft.com/azure-storage/azurite:latest
|
||||
command: ["azurite", "--blobHost", "0.0.0.0", "--queueHost", "0.0.0.0", "--tableHost", "0.0.0.0", "-l", "/data"]
|
||||
ports:
|
||||
- containerPort: 10000
|
||||
name: blob
|
||||
- containerPort: 10001
|
||||
name: queue
|
||||
- containerPort: 10002
|
||||
name: table
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 10000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: azurite-data
|
||||
mountPath: /data
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: azurite-data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: azurite
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
selector:
|
||||
app: azurite
|
||||
ports:
|
||||
- name: blob
|
||||
port: 10000
|
||||
targetPort: 10000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: azurite-external
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: azurite
|
||||
ports:
|
||||
- name: blob
|
||||
port: 10000
|
||||
targetPort: 10000
|
||||
nodePort: 10000
|
||||
93
docs/devops/single_azure_vm/k8s/infra/cosmos-emulator.yaml
Normal file
93
docs/devops/single_azure_vm/k8s/infra/cosmos-emulator.yaml
Normal file
@ -0,0 +1,93 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: cosmos-emulator
|
||||
namespace: bytelyst-infra
|
||||
labels:
|
||||
app: cosmos-emulator
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: cosmos-emulator
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cosmos-emulator
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cosmos-emulator
|
||||
spec:
|
||||
containers:
|
||||
- name: cosmos
|
||||
image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
name: api
|
||||
- containerPort: 1234
|
||||
name: explorer
|
||||
env:
|
||||
- name: AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE
|
||||
value: "true"
|
||||
- name: ENABLE_EXPLORER
|
||||
value: "true"
|
||||
- name: AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE
|
||||
value: "0.0.0.0"
|
||||
- name: PROTOCOL
|
||||
value: "http"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "2Gi"
|
||||
limits:
|
||||
cpu: "2000m"
|
||||
memory: "3Gi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /_explorer/emulator.pem
|
||||
port: 8081
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 15
|
||||
timeoutSeconds: 5
|
||||
volumeMounts:
|
||||
- name: cosmos-data
|
||||
mountPath: /tmp/cosmos/appdata
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: cosmos-data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cosmos-emulator
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
selector:
|
||||
app: cosmos-emulator
|
||||
ports:
|
||||
- name: api
|
||||
port: 8081
|
||||
targetPort: 8081
|
||||
- name: explorer
|
||||
port: 1234
|
||||
targetPort: 1234
|
||||
---
|
||||
# NodePort for external access to Cosmos Explorer
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cosmos-emulator-external
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: cosmos-emulator
|
||||
ports:
|
||||
- name: explorer
|
||||
port: 1234
|
||||
targetPort: 1234
|
||||
nodePort: 1234
|
||||
89
docs/devops/single_azure_vm/k8s/infra/grafana.yaml
Normal file
89
docs/devops/single_azure_vm/k8s/infra/grafana.yaml
Normal file
@ -0,0 +1,89 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: bytelyst-infra
|
||||
labels:
|
||||
app: grafana
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: grafana
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: grafana
|
||||
spec:
|
||||
containers:
|
||||
- name: grafana
|
||||
image: grafana/grafana:10.4.0
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
env:
|
||||
- name: GF_SECURITY_ADMIN_USER
|
||||
value: "admin"
|
||||
- name: GF_SECURITY_ADMIN_PASSWORD
|
||||
value: "bytelyst"
|
||||
- name: GF_USERS_ALLOW_SIGN_UP
|
||||
value: "false"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "256Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/health
|
||||
port: 3000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: grafana-data
|
||||
mountPath: /var/lib/grafana
|
||||
volumes:
|
||||
- name: grafana-data
|
||||
persistentVolumeClaim:
|
||||
claimName: grafana-data
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: grafana-data
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
selector:
|
||||
app: grafana
|
||||
ports:
|
||||
- name: http
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: grafana-external
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: grafana
|
||||
ports:
|
||||
- name: http
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
nodePort: 3000
|
||||
77
docs/devops/single_azure_vm/k8s/infra/loki.yaml
Normal file
77
docs/devops/single_azure_vm/k8s/infra/loki.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: loki
|
||||
namespace: bytelyst-infra
|
||||
labels:
|
||||
app: loki
|
||||
spec:
|
||||
replicas: 1
|
||||
serviceName: loki
|
||||
selector:
|
||||
matchLabels:
|
||||
app: loki
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: loki
|
||||
spec:
|
||||
containers:
|
||||
- name: loki
|
||||
image: grafana/loki:2.9.0
|
||||
args: ["-config.file=/etc/loki/local-config.yaml"]
|
||||
ports:
|
||||
- containerPort: 3100
|
||||
name: http
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 3100
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
volumeMounts:
|
||||
- name: loki-data
|
||||
mountPath: /loki
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: loki-data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: loki
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
selector:
|
||||
app: loki
|
||||
ports:
|
||||
- name: http
|
||||
port: 3100
|
||||
targetPort: 3100
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: loki-external
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: loki
|
||||
ports:
|
||||
- name: http
|
||||
port: 3100
|
||||
targetPort: 3100
|
||||
nodePort: 3100
|
||||
69
docs/devops/single_azure_vm/k8s/infra/mailpit.yaml
Normal file
69
docs/devops/single_azure_vm/k8s/infra/mailpit.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mailpit
|
||||
namespace: bytelyst-infra
|
||||
labels:
|
||||
app: mailpit
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mailpit
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mailpit
|
||||
spec:
|
||||
containers:
|
||||
- name: mailpit
|
||||
image: axllent/mailpit:latest
|
||||
ports:
|
||||
- containerPort: 1025
|
||||
name: smtp
|
||||
- containerPort: 8025
|
||||
name: ui
|
||||
resources:
|
||||
requests:
|
||||
cpu: "50m"
|
||||
memory: "64Mi"
|
||||
limits:
|
||||
cpu: "200m"
|
||||
memory: "128Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8025
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mailpit
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
selector:
|
||||
app: mailpit
|
||||
ports:
|
||||
- name: smtp
|
||||
port: 1025
|
||||
targetPort: 1025
|
||||
- name: ui
|
||||
port: 8025
|
||||
targetPort: 8025
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mailpit-external
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: mailpit
|
||||
ports:
|
||||
- name: ui
|
||||
port: 8025
|
||||
targetPort: 8025
|
||||
nodePort: 8025
|
||||
26
docs/devops/single_azure_vm/k8s/infra/ollama-external.yaml
Normal file
26
docs/devops/single_azure_vm/k8s/infra/ollama-external.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
# Ollama runs on the host as a systemd service, not in K8s.
|
||||
# This ExternalName service lets pods reach it via K8s DNS:
|
||||
# ollama.bytelyst-infra.svc:11434
|
||||
#
|
||||
# On the VM, the node's internal IP is used. The setup script
|
||||
# patches the Endpoints IP at deploy time.
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: bytelyst-infra
|
||||
spec:
|
||||
ports:
|
||||
- port: 11434
|
||||
targetPort: 11434
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: bytelyst-infra
|
||||
subsets:
|
||||
- addresses:
|
||||
- ip: NODE_IP_PLACEHOLDER
|
||||
ports:
|
||||
- port: 11434
|
||||
27
docs/devops/single_azure_vm/k8s/namespaces.yaml
Normal file
27
docs/devops/single_azure_vm/k8s/namespaces.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: bytelyst-infra
|
||||
labels:
|
||||
app.kubernetes.io/part-of: bytelyst
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: bytelyst-platform
|
||||
labels:
|
||||
app.kubernetes.io/part-of: bytelyst
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: bytelyst-dashboards
|
||||
labels:
|
||||
app.kubernetes.io/part-of: bytelyst
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: bytelyst-products
|
||||
labels:
|
||||
app.kubernetes.io/part-of: bytelyst
|
||||
@ -0,0 +1,85 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: extraction-service
|
||||
namespace: bytelyst-platform
|
||||
labels:
|
||||
app: extraction-service
|
||||
tier: platform
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: extraction-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: extraction-service
|
||||
tier: platform
|
||||
spec:
|
||||
containers:
|
||||
- name: extraction-service
|
||||
image: bytelyst/extraction-service:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4005
|
||||
name: http
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4005"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: bytelyst-config
|
||||
- secretRef:
|
||||
name: bytelyst-secrets
|
||||
resources:
|
||||
requests:
|
||||
cpu: "200m"
|
||||
memory: "384Mi"
|
||||
limits:
|
||||
cpu: "1000m"
|
||||
memory: "768Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4005
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4005
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: extraction-service
|
||||
namespace: bytelyst-platform
|
||||
spec:
|
||||
selector:
|
||||
app: extraction-service
|
||||
ports:
|
||||
- name: http
|
||||
port: 4005
|
||||
targetPort: 4005
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: extraction-service-external
|
||||
namespace: bytelyst-platform
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: extraction-service
|
||||
ports:
|
||||
- name: http
|
||||
port: 4005
|
||||
targetPort: 4005
|
||||
nodePort: 4005
|
||||
85
docs/devops/single_azure_vm/k8s/platform/mcp-server.yaml
Normal file
85
docs/devops/single_azure_vm/k8s/platform/mcp-server.yaml
Normal file
@ -0,0 +1,85 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mcp-server
|
||||
namespace: bytelyst-platform
|
||||
labels:
|
||||
app: mcp-server
|
||||
tier: platform
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mcp-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mcp-server
|
||||
tier: platform
|
||||
spec:
|
||||
containers:
|
||||
- name: mcp-server
|
||||
image: bytelyst/mcp-server:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4007
|
||||
name: http
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4007"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: bytelyst-config
|
||||
- secretRef:
|
||||
name: bytelyst-secrets
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4007
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4007
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mcp-server
|
||||
namespace: bytelyst-platform
|
||||
spec:
|
||||
selector:
|
||||
app: mcp-server
|
||||
ports:
|
||||
- name: http
|
||||
port: 4007
|
||||
targetPort: 4007
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mcp-server-external
|
||||
namespace: bytelyst-platform
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: mcp-server
|
||||
ports:
|
||||
- name: http
|
||||
port: 4007
|
||||
targetPort: 4007
|
||||
nodePort: 4007
|
||||
@ -0,0 +1,87 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: platform-service
|
||||
namespace: bytelyst-platform
|
||||
labels:
|
||||
app: platform-service
|
||||
tier: platform
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: platform-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: platform-service
|
||||
tier: platform
|
||||
spec:
|
||||
containers:
|
||||
- name: platform-service
|
||||
image: bytelyst/platform-service:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4003
|
||||
name: http
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4003"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: bytelyst-config
|
||||
- secretRef:
|
||||
name: bytelyst-secrets
|
||||
resources:
|
||||
requests:
|
||||
cpu: "200m"
|
||||
memory: "384Mi"
|
||||
limits:
|
||||
cpu: "1000m"
|
||||
memory: "768Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4003
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4003
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
timeoutSeconds: 5
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: platform-service
|
||||
namespace: bytelyst-platform
|
||||
spec:
|
||||
selector:
|
||||
app: platform-service
|
||||
ports:
|
||||
- name: http
|
||||
port: 4003
|
||||
targetPort: 4003
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: platform-service-external
|
||||
namespace: bytelyst-platform
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: platform-service
|
||||
ports:
|
||||
- name: http
|
||||
port: 4003
|
||||
targetPort: 4003
|
||||
nodePort: 4003
|
||||
778
docs/devops/single_azure_vm/k8s/products/backends.yaml
Normal file
778
docs/devops/single_azure_vm/k8s/products/backends.yaml
Normal file
@ -0,0 +1,778 @@
|
||||
# All 10 product backends — Deployment + ClusterIP Service + NodePort Service
|
||||
# Each backend: Fastify 5 + TypeScript, /health endpoint, shared ConfigMap + Secrets
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── PeakPulse Backend (:4010) ─────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: peakpulse-backend
|
||||
namespace: bytelyst-products
|
||||
labels: &pp-labels
|
||||
app: peakpulse-backend
|
||||
tier: backend
|
||||
product: peakpulse
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: peakpulse-backend
|
||||
template:
|
||||
metadata:
|
||||
labels: *pp-labels
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/peakpulse-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4010
|
||||
env: &backend-env-4010
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4010"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: &backend-envfrom
|
||||
- configMapRef:
|
||||
name: bytelyst-config
|
||||
- secretRef:
|
||||
name: bytelyst-secrets
|
||||
resources: &backend-resources
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe: &probe-4010
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4010
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4010
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: peakpulse-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: peakpulse-backend
|
||||
ports:
|
||||
- port: 4010
|
||||
targetPort: 4010
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: peakpulse-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: peakpulse-backend
|
||||
ports:
|
||||
- port: 4010
|
||||
targetPort: 4010
|
||||
nodePort: 4010
|
||||
|
||||
---
|
||||
# ── ChronoMind Backend (:4011) ────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: chronomind-backend
|
||||
namespace: bytelyst-products
|
||||
labels: &cm-labels
|
||||
app: chronomind-backend
|
||||
tier: backend
|
||||
product: chronomind
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: chronomind-backend
|
||||
template:
|
||||
metadata:
|
||||
labels: *cm-labels
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/chronomind-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4011
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4011"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4011
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4011
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: chronomind-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: chronomind-backend
|
||||
ports:
|
||||
- port: 4011
|
||||
targetPort: 4011
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: chronomind-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: chronomind-backend
|
||||
ports:
|
||||
- port: 4011
|
||||
targetPort: 4011
|
||||
nodePort: 4011
|
||||
|
||||
---
|
||||
# ── JarvisJr Backend (:4012) ──────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jarvisjr-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: jarvisjr-backend
|
||||
tier: backend
|
||||
product: jarvisjr
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jarvisjr-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: jarvisjr-backend
|
||||
tier: backend
|
||||
product: jarvisjr
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/jarvisjr-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4012
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4012"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4012
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4012
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jarvisjr-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: jarvisjr-backend
|
||||
ports:
|
||||
- port: 4012
|
||||
targetPort: 4012
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jarvisjr-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: jarvisjr-backend
|
||||
ports:
|
||||
- port: 4012
|
||||
targetPort: 4012
|
||||
nodePort: 4012
|
||||
|
||||
---
|
||||
# ── NomGap Backend (:4013) ────────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nomgap-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: nomgap-backend
|
||||
tier: backend
|
||||
product: nomgap
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nomgap-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nomgap-backend
|
||||
tier: backend
|
||||
product: nomgap
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/nomgap-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4013
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4013"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4013
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4013
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nomgap-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: nomgap-backend
|
||||
ports:
|
||||
- port: 4013
|
||||
targetPort: 4013
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nomgap-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: nomgap-backend
|
||||
ports:
|
||||
- port: 4013
|
||||
targetPort: 4013
|
||||
nodePort: 4013
|
||||
|
||||
---
|
||||
# ── MindLyst Backend (:4014) ──────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mindlyst-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: mindlyst-backend
|
||||
tier: backend
|
||||
product: mindlyst
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mindlyst-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mindlyst-backend
|
||||
tier: backend
|
||||
product: mindlyst
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/mindlyst-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4014
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4014"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4014
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4014
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mindlyst-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: mindlyst-backend
|
||||
ports:
|
||||
- port: 4014
|
||||
targetPort: 4014
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mindlyst-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: mindlyst-backend
|
||||
ports:
|
||||
- port: 4014
|
||||
targetPort: 4014
|
||||
nodePort: 4014
|
||||
|
||||
---
|
||||
# ── LysnrAI Backend (:4015) ──────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: lysnrai-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: lysnrai-backend
|
||||
tier: backend
|
||||
product: lysnrai
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: lysnrai-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: lysnrai-backend
|
||||
tier: backend
|
||||
product: lysnrai
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/lysnrai-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4015
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4015"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4015
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4015
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: lysnrai-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: lysnrai-backend
|
||||
ports:
|
||||
- port: 4015
|
||||
targetPort: 4015
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: lysnrai-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: lysnrai-backend
|
||||
ports:
|
||||
- port: 4015
|
||||
targetPort: 4015
|
||||
nodePort: 4015
|
||||
|
||||
---
|
||||
# ── NoteLett Backend (:4016) ──────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notelett-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: notelett-backend
|
||||
tier: backend
|
||||
product: notelett
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notelett-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notelett-backend
|
||||
tier: backend
|
||||
product: notelett
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/notelett-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4016
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4016"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4016
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4016
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notelett-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: notelett-backend
|
||||
ports:
|
||||
- port: 4016
|
||||
targetPort: 4016
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notelett-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: notelett-backend
|
||||
ports:
|
||||
- port: 4016
|
||||
targetPort: 4016
|
||||
nodePort: 4016
|
||||
|
||||
---
|
||||
# ── FlowMonk Backend (:4017) ─────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: flowmonk-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: flowmonk-backend
|
||||
tier: backend
|
||||
product: flowmonk
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: flowmonk-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: flowmonk-backend
|
||||
tier: backend
|
||||
product: flowmonk
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/flowmonk-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4017
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4017"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4017
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4017
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: flowmonk-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: flowmonk-backend
|
||||
ports:
|
||||
- port: 4017
|
||||
targetPort: 4017
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: flowmonk-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: flowmonk-backend
|
||||
ports:
|
||||
- port: 4017
|
||||
targetPort: 4017
|
||||
nodePort: 4017
|
||||
|
||||
---
|
||||
# ── ActionTrail Backend (:4018) ───────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: actiontrail-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: actiontrail-backend
|
||||
tier: backend
|
||||
product: actiontrail
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: actiontrail-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: actiontrail-backend
|
||||
tier: backend
|
||||
product: actiontrail
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/actiontrail-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4018
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4018"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4018
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4018
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: actiontrail-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: actiontrail-backend
|
||||
ports:
|
||||
- port: 4018
|
||||
targetPort: 4018
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: actiontrail-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: actiontrail-backend
|
||||
ports:
|
||||
- port: 4018
|
||||
targetPort: 4018
|
||||
nodePort: 4018
|
||||
|
||||
---
|
||||
# ── LocalMemGPT Backend (:4019) ──────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: localmemgpt-backend
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: localmemgpt-backend
|
||||
tier: backend
|
||||
product: localmemgpt
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: localmemgpt-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: localmemgpt-backend
|
||||
tier: backend
|
||||
product: localmemgpt
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: bytelyst/localmemgpt-backend:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 4019
|
||||
env:
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "4019"
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: OLLAMA_URL
|
||||
value: "http://ollama.bytelyst-infra.svc:11434"
|
||||
envFrom: *backend-envfrom
|
||||
resources: *backend-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4019
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 4019
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: localmemgpt-backend
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: localmemgpt-backend
|
||||
ports:
|
||||
- port: 4019
|
||||
targetPort: 4019
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: localmemgpt-backend-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: localmemgpt-backend
|
||||
ports:
|
||||
- port: 4019
|
||||
targetPort: 4019
|
||||
nodePort: 4019
|
||||
710
docs/devops/single_azure_vm/k8s/products/webs.yaml
Normal file
710
docs/devops/single_azure_vm/k8s/products/webs.yaml
Normal file
@ -0,0 +1,710 @@
|
||||
# All 9 product web apps — Deployment + ClusterIP Service + NodePort Service
|
||||
# Each web: Next.js 16, product-specific NEXT_PUBLIC_* env vars
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── LysnrAI Dashboard (:3002) ─────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: lysnrai-dashboard
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: lysnrai-dashboard
|
||||
tier: web
|
||||
product: lysnrai
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: lysnrai-dashboard
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: lysnrai-dashboard
|
||||
tier: web
|
||||
product: lysnrai
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/lysnrai-dashboard:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3002
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3002"
|
||||
- name: PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
- name: ACTIONTRAIL_SERVICE_URL
|
||||
value: "http://actiontrail-backend.bytelyst-products.svc:4018"
|
||||
- name: NEXT_PUBLIC_PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
- name: NEXT_PUBLIC_PRODUCT_ID
|
||||
value: "lysnrai"
|
||||
resources: &web-resources
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3002
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3002
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: lysnrai-dashboard
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: lysnrai-dashboard
|
||||
ports:
|
||||
- port: 3002
|
||||
targetPort: 3002
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: lysnrai-dashboard-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: lysnrai-dashboard
|
||||
ports:
|
||||
- port: 3002
|
||||
targetPort: 3002
|
||||
nodePort: 3002
|
||||
|
||||
---
|
||||
# ── ChronoMind Web (:3030) ───────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: chronomind-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: chronomind-web
|
||||
tier: web
|
||||
product: chronomind
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: chronomind-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: chronomind-web
|
||||
tier: web
|
||||
product: chronomind
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/chronomind-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3030
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3030"
|
||||
- name: NEXT_PUBLIC_BACKEND_URL
|
||||
value: "http://chronomind-backend.bytelyst-products.svc:4011"
|
||||
- name: NEXT_PUBLIC_PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3030
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3030
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: chronomind-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: chronomind-web
|
||||
ports:
|
||||
- port: 3030
|
||||
targetPort: 3030
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: chronomind-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: chronomind-web
|
||||
ports:
|
||||
- port: 3030
|
||||
targetPort: 3030
|
||||
nodePort: 3030
|
||||
|
||||
---
|
||||
# ── JarvisJr Web (:3035) ─────────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jarvisjr-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: jarvisjr-web
|
||||
tier: web
|
||||
product: jarvisjr
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: jarvisjr-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: jarvisjr-web
|
||||
tier: web
|
||||
product: jarvisjr
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/jarvisjr-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3035
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3035"
|
||||
- name: NEXT_PUBLIC_PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3035
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3035
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jarvisjr-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: jarvisjr-web
|
||||
ports:
|
||||
- port: 3035
|
||||
targetPort: 3035
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jarvisjr-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: jarvisjr-web
|
||||
ports:
|
||||
- port: 3035
|
||||
targetPort: 3035
|
||||
nodePort: 3035
|
||||
|
||||
---
|
||||
# ── FlowMonk Web (:3040) ─────────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: flowmonk-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: flowmonk-web
|
||||
tier: web
|
||||
product: flowmonk
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: flowmonk-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: flowmonk-web
|
||||
tier: web
|
||||
product: flowmonk
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/flowmonk-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3040
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3040"
|
||||
- name: NEXT_PUBLIC_API_URL
|
||||
value: "http://flowmonk-backend.bytelyst-products.svc:4017"
|
||||
- name: NEXT_PUBLIC_PLATFORM_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003/api"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3040
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3040
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: flowmonk-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: flowmonk-web
|
||||
ports:
|
||||
- port: 3040
|
||||
targetPort: 3040
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: flowmonk-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: flowmonk-web
|
||||
ports:
|
||||
- port: 3040
|
||||
targetPort: 3040
|
||||
nodePort: 3040
|
||||
|
||||
---
|
||||
# ── NoteLett Web (:3045) ─────────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notelett-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: notelett-web
|
||||
tier: web
|
||||
product: notelett
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notelett-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notelett-web
|
||||
tier: web
|
||||
product: notelett
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/notelett-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3045
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3045"
|
||||
- name: NEXT_PUBLIC_NOTES_API_URL
|
||||
value: "http://notelett-backend.bytelyst-products.svc:4016/api"
|
||||
- name: NEXT_PUBLIC_PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003/api"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3045
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3045
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notelett-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: notelett-web
|
||||
ports:
|
||||
- port: 3045
|
||||
targetPort: 3045
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notelett-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: notelett-web
|
||||
ports:
|
||||
- port: 3045
|
||||
targetPort: 3045
|
||||
nodePort: 3045
|
||||
|
||||
---
|
||||
# ── MindLyst Web (:3050) ─────────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mindlyst-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: mindlyst-web
|
||||
tier: web
|
||||
product: mindlyst
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mindlyst-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mindlyst-web
|
||||
tier: web
|
||||
product: mindlyst
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/mindlyst-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3050
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3050"
|
||||
- name: NEXT_PUBLIC_PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3050
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3050
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mindlyst-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: mindlyst-web
|
||||
ports:
|
||||
- port: 3050
|
||||
targetPort: 3050
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mindlyst-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: mindlyst-web
|
||||
ports:
|
||||
- port: 3050
|
||||
targetPort: 3050
|
||||
nodePort: 3050
|
||||
|
||||
---
|
||||
# ── NomGap Web (:3055) ───────────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nomgap-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: nomgap-web
|
||||
tier: web
|
||||
product: nomgap
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nomgap-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nomgap-web
|
||||
tier: web
|
||||
product: nomgap
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/nomgap-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3055
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3055"
|
||||
- name: NEXT_PUBLIC_NOMGAP_API_URL
|
||||
value: "http://nomgap-backend.bytelyst-products.svc:4013/api"
|
||||
- name: NEXT_PUBLIC_PLATFORM_SERVICE_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003/api"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3055
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3055
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nomgap-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: nomgap-web
|
||||
ports:
|
||||
- port: 3055
|
||||
targetPort: 3055
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nomgap-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: nomgap-web
|
||||
ports:
|
||||
- port: 3055
|
||||
targetPort: 3055
|
||||
nodePort: 3055
|
||||
|
||||
---
|
||||
# ── ActionTrail Web (:3060) ──────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: actiontrail-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: actiontrail-web
|
||||
tier: web
|
||||
product: actiontrail
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: actiontrail-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: actiontrail-web
|
||||
tier: web
|
||||
product: actiontrail
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/actiontrail-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3060
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3060"
|
||||
- name: NEXT_PUBLIC_API_URL
|
||||
value: "http://actiontrail-backend.bytelyst-products.svc:4018"
|
||||
- name: NEXT_PUBLIC_PLATFORM_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3060
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3060
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: actiontrail-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: actiontrail-web
|
||||
ports:
|
||||
- port: 3060
|
||||
targetPort: 3060
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: actiontrail-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: actiontrail-web
|
||||
ports:
|
||||
- port: 3060
|
||||
targetPort: 3060
|
||||
nodePort: 3060
|
||||
|
||||
---
|
||||
# ── LocalMemGPT Web (:3070) ──────────────────────────────────────────
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: localmemgpt-web
|
||||
namespace: bytelyst-products
|
||||
labels:
|
||||
app: localmemgpt-web
|
||||
tier: web
|
||||
product: localmemgpt
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: localmemgpt-web
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: localmemgpt-web
|
||||
tier: web
|
||||
product: localmemgpt
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: bytelyst/localmemgpt-web:latest
|
||||
imagePullPolicy: Never
|
||||
ports:
|
||||
- containerPort: 3070
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: "production"
|
||||
- name: PORT
|
||||
value: "3070"
|
||||
- name: NEXT_PUBLIC_BACKEND_URL
|
||||
value: "http://localmemgpt-backend.bytelyst-products.svc:4019"
|
||||
- name: NEXT_PUBLIC_PLATFORM_URL
|
||||
value: "http://platform-service.bytelyst-platform.svc:4003"
|
||||
resources: *web-resources
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3070
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 3070
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: localmemgpt-web
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
selector:
|
||||
app: localmemgpt-web
|
||||
ports:
|
||||
- port: 3070
|
||||
targetPort: 3070
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: localmemgpt-web-ext
|
||||
namespace: bytelyst-products
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: localmemgpt-web
|
||||
ports:
|
||||
- port: 3070
|
||||
targetPort: 3070
|
||||
nodePort: 3070
|
||||
580
docs/devops/single_azure_vm/k8s/setup-k8s.sh
Executable file
580
docs/devops/single_azure_vm/k8s/setup-k8s.sh
Executable file
@ -0,0 +1,580 @@
|
||||
#!/usr/bin/env bash
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ByteLyst Single-VM Kubernetes Setup (k3s)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# Deploys the ByteLyst ecosystem on Kubernetes using k3s.
|
||||
#
|
||||
# PREREQUISITE: Run ../docker/setup.sh phases 1-5 first to install
|
||||
# system deps, Gitea, clone repos, build + publish @bytelyst/* packages.
|
||||
# This script handles k8s-specific deployment only.
|
||||
#
|
||||
# What this script does:
|
||||
# Phase 1: Pre-flight checks (verify docker phases ran)
|
||||
# Phase 2: Install k3s (lightweight K8s)
|
||||
# Phase 3: Build Docker images + import into k3s containerd
|
||||
# Phase 4: Generate K8s secrets (JWT, etc.)
|
||||
# Phase 5: Apply K8s manifests (namespaces → config → infra → platform → products)
|
||||
# Phase 6: Health check
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./setup-k8s.sh # Full install
|
||||
# sudo ./setup-k8s.sh --phase=N # Run single phase (1-6)
|
||||
# sudo ./setup-k8s.sh --status # Show phase status
|
||||
# sudo ./setup-k8s.sh --reset # Clear markers, start fresh
|
||||
# sudo ./setup-k8s.sh --teardown # Remove all K8s resources
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configuration ────────────────────────────────────────────────────
|
||||
INSTALL_DIR="/opt/bytelyst"
|
||||
K8S_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
STATE_DIR="${INSTALL_DIR}/.setup-state-k8s"
|
||||
COMPOSE_FILE="docker-compose.ecosystem.yml"
|
||||
|
||||
# Well-known emulator keys (public, safe to embed)
|
||||
COSMOS_EMULATOR_KEY="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
|
||||
|
||||
# Image prefix for k3s imports
|
||||
IMAGE_PREFIX="bytelyst"
|
||||
|
||||
# Services that need Docker image builds (not pre-built infra)
|
||||
BUILD_SERVICES=(
|
||||
platform-service extraction-service mcp-server
|
||||
admin-web tracker-web
|
||||
peakpulse-backend chronomind-backend jarvisjr-backend nomgap-backend
|
||||
mindlyst-backend lysnrai-backend notelett-backend flowmonk-backend
|
||||
actiontrail-backend localmemgpt-backend
|
||||
lysnrai-dashboard chronomind-web jarvisjr-web flowmonk-web notelett-web
|
||||
mindlyst-web nomgap-web actiontrail-web localmemgpt-web
|
||||
)
|
||||
|
||||
# Namespaces that need the shared ConfigMap + Secrets
|
||||
CONFIG_NAMESPACES=(bytelyst-platform bytelyst-dashboards bytelyst-products)
|
||||
|
||||
# ── Helpers ──────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||
log() { echo -e "${BLUE}[k8s]${NC} $*"; }
|
||||
ok() { echo -e "${GREEN} ✓ $*${NC}"; }
|
||||
warn() { echo -e "${YELLOW} ⚠ $*${NC}"; }
|
||||
fail() { echo -e "${RED} ✗ $*${NC}" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
mark_phase_done() { date -Iseconds > "${STATE_DIR}/phase${1}.done"; }
|
||||
is_phase_done() { [ -f "${STATE_DIR}/phase${1}.done" ]; }
|
||||
reset_markers() { rm -f "${STATE_DIR}"/phase*.done; log "Phase markers cleared."; }
|
||||
|
||||
last_completed_phase() {
|
||||
local last=0
|
||||
for i in 1 2 3 4 5 6; do
|
||||
if is_phase_done "$i"; then last=$i; else break; fi
|
||||
done
|
||||
echo "$last"
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PHASE 1: Pre-flight Checks
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
phase1_preflight() {
|
||||
log "Phase 1: Pre-flight checks..."
|
||||
|
||||
# Verify docker setup phases 1-5 ran
|
||||
local docker_state="${INSTALL_DIR}/.setup-state"
|
||||
for p in 1 2 3 4 5; do
|
||||
if [ ! -f "${docker_state}/phase${p}.done" ]; then
|
||||
fail "Docker phase ${p} not completed. Run first: sudo ../docker/setup.sh --resume"
|
||||
fi
|
||||
done
|
||||
ok "Docker phases 1-5 completed"
|
||||
|
||||
# Verify repos exist
|
||||
local plat_dir="${INSTALL_DIR}/learning_ai_common_plat"
|
||||
[ -d "$plat_dir" ] || fail "Missing ${plat_dir}. Run docker/setup.sh phases 1-5 first."
|
||||
ok "Repos cloned"
|
||||
|
||||
# Verify Docker is available (needed for image builds)
|
||||
command -v docker &>/dev/null || fail "Docker not found. Run docker/setup.sh phase 1."
|
||||
ok "Docker available"
|
||||
|
||||
# Pre-flight: disk + memory
|
||||
local disk_gb mem_gb
|
||||
disk_gb=$(df -BG / | awk 'NR==2 {gsub(/G/,"",$4); print $4}')
|
||||
mem_gb=$(free -g | awk '/^Mem:/ {print $2}')
|
||||
log " Disk: ${disk_gb} GB free, RAM: ${mem_gb} GB total"
|
||||
[ "${disk_gb:-0}" -ge 20 ] || warn "Low disk (${disk_gb} GB). Recommend 40+ GB free."
|
||||
[ "${mem_gb:-0}" -ge 16 ] || warn "Low RAM (${mem_gb} GB). Recommend 32 GB."
|
||||
|
||||
ok "Phase 1 complete. Pre-flight passed."
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PHASE 2: Install k3s
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
phase2_k3s() {
|
||||
log "Phase 2: Installing k3s..."
|
||||
|
||||
if command -v kubectl &>/dev/null && kubectl cluster-info &>/dev/null 2>&1; then
|
||||
ok "k3s already installed and running"
|
||||
else
|
||||
# Install k3s with:
|
||||
# --docker: use Docker as container runtime (reuse existing images)
|
||||
# --disable=traefik: we manage our own ingress
|
||||
# --write-kubeconfig-mode=644: allow non-root kubectl
|
||||
# --kube-apiserver-arg: extend NodePort range to use our service ports
|
||||
log " Installing k3s (Docker runtime, extended NodePort range)..."
|
||||
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="\
|
||||
--docker \
|
||||
--disable=traefik \
|
||||
--write-kubeconfig-mode=644 \
|
||||
--kube-apiserver-arg=service-node-port-range=1024-32767" \
|
||||
sh -
|
||||
|
||||
# Wait for k3s to be ready
|
||||
log " Waiting for k3s node to be Ready..."
|
||||
local retries=30
|
||||
while [ $retries -gt 0 ]; do
|
||||
if kubectl get nodes 2>/dev/null | grep -q " Ready"; then
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
retries=$((retries - 1))
|
||||
done
|
||||
|
||||
if [ $retries -eq 0 ]; then
|
||||
fail "k3s node did not become Ready within 150 seconds."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify
|
||||
kubectl get nodes
|
||||
ok "Phase 2 complete. k3s is running."
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PHASE 3: Build Docker Images + Tag for k3s
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
phase3_images() {
|
||||
log "Phase 3: Building Docker images for k3s..."
|
||||
|
||||
local plat_dir="${INSTALL_DIR}/learning_ai_common_plat"
|
||||
|
||||
# Restore Gitea token for Docker builds
|
||||
if [ -z "${GITEA_NPM_TOKEN:-}" ] && [ -f "${INSTALL_DIR}/.gitea_token" ]; then
|
||||
GITEA_NPM_TOKEN=$(cat "${INSTALL_DIR}/.gitea_token")
|
||||
export GITEA_NPM_TOKEN
|
||||
fi
|
||||
[ -n "${GITEA_NPM_TOKEN:-}" ] || fail "GITEA_NPM_TOKEN not set. Run docker/setup.sh phase 2."
|
||||
|
||||
# Detect Docker host IP for Gitea access during builds
|
||||
local docker_host_ip
|
||||
docker_host_ip=$(ip -4 addr show docker0 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' || echo "172.17.0.1")
|
||||
export GITEA_NPM_HOST="${docker_host_ip}"
|
||||
export DOCKER_BUILDKIT=1
|
||||
export COMPOSE_DOCKER_CLI_BUILD=1
|
||||
|
||||
# Stop Ollama to free RAM during builds
|
||||
if systemctl is-active --quiet ollama 2>/dev/null; then
|
||||
log " Stopping Ollama to free RAM for builds..."
|
||||
systemctl stop ollama 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Build images using docker compose (same Dockerfiles as docker approach)
|
||||
local env_file="${plat_dir}/.env.ecosystem"
|
||||
if [ ! -f "$env_file" ]; then
|
||||
# Generate minimal env for compose config parsing
|
||||
log " Generating temporary .env.ecosystem for compose builds..."
|
||||
cat > "$env_file" <<-ENV
|
||||
COSMOS_KEY=${COSMOS_EMULATOR_KEY}
|
||||
JWT_SECRET=build-time-placeholder
|
||||
COSMOS_ENDPOINT=http://localhost:8081
|
||||
COSMOS_DATABASE=bytelyst
|
||||
DB_PROVIDER=cosmos
|
||||
ENV
|
||||
fi
|
||||
|
||||
local build_ok=0 build_fail=0
|
||||
local total=${#BUILD_SERVICES[@]}
|
||||
local idx=0
|
||||
mkdir -p "${STATE_DIR}/builds"
|
||||
|
||||
for svc in "${BUILD_SERVICES[@]}"; do
|
||||
idx=$((idx + 1))
|
||||
log " [${idx}/${total}] Building ${svc}..."
|
||||
|
||||
local log_file="${STATE_DIR}/builds/${svc}.log"
|
||||
if docker compose -f "${plat_dir}/${COMPOSE_FILE}" --env-file "$env_file" \
|
||||
build "$svc" > "$log_file" 2>&1; then
|
||||
|
||||
# Tag for k3s: compose builds as <project>-<svc>, we tag as bytelyst/<svc>
|
||||
local compose_image
|
||||
compose_image=$(docker compose -f "${plat_dir}/${COMPOSE_FILE}" --env-file "$env_file" \
|
||||
images --format json 2>/dev/null | jq -r ".[] | select(.Service==\"${svc}\") | .Repository + \":\" + .Tag" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$compose_image" ] && [ "$compose_image" != ":" ]; then
|
||||
docker tag "$compose_image" "${IMAGE_PREFIX}/${svc}:latest" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
build_ok=$((build_ok + 1))
|
||||
ok " [${idx}/${total}] ${svc} — built"
|
||||
else
|
||||
build_fail=$((build_fail + 1))
|
||||
warn " [${idx}/${total}] ${svc} — FAILED (see ${log_file})"
|
||||
fi
|
||||
done
|
||||
|
||||
# Restart Ollama
|
||||
if command -v ollama &>/dev/null; then
|
||||
log " Restarting Ollama..."
|
||||
systemctl start ollama 2>/dev/null || nohup ollama serve > /var/log/ollama.log 2>&1 &
|
||||
fi
|
||||
|
||||
# Prune build cache
|
||||
docker builder prune -f --filter "until=1h" > /dev/null 2>&1 || true
|
||||
|
||||
log " Built: ${build_ok}, Failed: ${build_fail}"
|
||||
[ "$build_fail" -eq 0 ] || warn " ${build_fail} images failed to build. Fix and re-run: sudo ./setup-k8s.sh --phase=3"
|
||||
|
||||
ok "Phase 3 complete. ${build_ok}/${total} images ready."
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PHASE 4: Generate K8s Secrets + Patch ConfigMap
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
phase4_config() {
|
||||
log "Phase 4: Generating K8s configuration..."
|
||||
|
||||
# Apply namespaces first
|
||||
log " Creating namespaces..."
|
||||
kubectl apply -f "${K8S_DIR}/namespaces.yaml"
|
||||
|
||||
# Generate random JWT secret
|
||||
local jwt_secret
|
||||
jwt_secret=$(openssl rand -base64 32)
|
||||
|
||||
# Patch the secrets template with real JWT secret
|
||||
local secrets_file="${K8S_DIR}/config/secrets.yaml"
|
||||
log " Generating secrets (JWT_SECRET)..."
|
||||
|
||||
# Well-known Azurite emulator key (public, safe)
|
||||
local azurite_key="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="
|
||||
local azurite_conn="DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=${azurite_key};BlobEndpoint=http://azurite.bytelyst-infra.svc:10000/devstoreaccount1;"
|
||||
|
||||
# Apply secrets to all namespaces that need them
|
||||
for ns in "${CONFIG_NAMESPACES[@]}"; do
|
||||
kubectl create secret generic bytelyst-secrets \
|
||||
--namespace="$ns" \
|
||||
--from-literal=JWT_SECRET="$jwt_secret" \
|
||||
--from-literal=AZURE_BLOB_ACCOUNT_KEY="$azurite_key" \
|
||||
--from-literal=AZURE_BLOB_CONNECTION_STRING="$azurite_conn" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
done
|
||||
ok "Secrets applied to ${#CONFIG_NAMESPACES[@]} namespaces"
|
||||
|
||||
# Detect node IP for Ollama external service
|
||||
local node_ip
|
||||
node_ip=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}' 2>/dev/null || echo "172.17.0.1")
|
||||
log " Node IP for Ollama: ${node_ip}"
|
||||
|
||||
# Patch Ollama endpoint with actual node IP
|
||||
sed "s/NODE_IP_PLACEHOLDER/${node_ip}/" "${K8S_DIR}/infra/ollama-external.yaml" \
|
||||
| kubectl apply -f -
|
||||
ok "Ollama external service configured (${node_ip}:11434)"
|
||||
|
||||
# Apply ConfigMap to all namespaces that need it
|
||||
for ns in "${CONFIG_NAMESPACES[@]}"; do
|
||||
sed "s/namespace: bytelyst-platform/namespace: ${ns}/" "${K8S_DIR}/config/configmap.yaml" \
|
||||
| kubectl apply -f -
|
||||
done
|
||||
ok "ConfigMap applied to ${#CONFIG_NAMESPACES[@]} namespaces"
|
||||
|
||||
ok "Phase 4 complete."
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PHASE 5: Apply K8s Manifests (ordered deployment)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
phase5_deploy() {
|
||||
log "Phase 5: Deploying services to k3s..."
|
||||
|
||||
# ── 5a: Infrastructure ──────────────────────────────────────────────
|
||||
log " Deploying infrastructure..."
|
||||
for f in "${K8S_DIR}"/infra/*.yaml; do
|
||||
local name
|
||||
name=$(basename "$f" .yaml)
|
||||
[ "$name" = "ollama-external" ] && continue # Already applied in phase 4
|
||||
log " Applying ${name}..."
|
||||
kubectl apply -f "$f"
|
||||
done
|
||||
ok "Infrastructure deployed"
|
||||
|
||||
# Wait for Cosmos emulator (everything depends on it)
|
||||
log " Waiting for Cosmos emulator to be Ready (this can take 2-3 minutes)..."
|
||||
kubectl wait --for=condition=Ready pod -l app=cosmos-emulator \
|
||||
-n bytelyst-infra --timeout=300s 2>/dev/null || warn "Cosmos emulator not ready yet"
|
||||
|
||||
# ── 5b: Platform services ───────────────────────────────────────────
|
||||
log " Deploying platform services..."
|
||||
kubectl apply -f "${K8S_DIR}/platform/"
|
||||
ok "Platform services deployed"
|
||||
|
||||
# Wait for platform-service
|
||||
log " Waiting for platform-service..."
|
||||
kubectl wait --for=condition=Ready pod -l app=platform-service \
|
||||
-n bytelyst-platform --timeout=120s 2>/dev/null || warn "platform-service not ready yet"
|
||||
|
||||
# ── 5c: Dashboards ─────────────────────────────────────────────────
|
||||
log " Deploying dashboards..."
|
||||
kubectl apply -f "${K8S_DIR}/dashboards/"
|
||||
ok "Dashboards deployed"
|
||||
|
||||
# ── 5d: Product services ────────────────────────────────────────────
|
||||
log " Deploying product backends + web apps..."
|
||||
kubectl apply -f "${K8S_DIR}/products/"
|
||||
ok "Product services deployed"
|
||||
|
||||
# ── Summary ─────────────────────────────────────────────────────────
|
||||
log " Waiting 30s for services to stabilize..."
|
||||
sleep 30
|
||||
|
||||
echo ""
|
||||
log " Pod status across all namespaces:"
|
||||
kubectl get pods -A -l app.kubernetes.io/part-of=bytelyst 2>/dev/null || \
|
||||
kubectl get pods -A 2>/dev/null | grep -E "bytelyst-"
|
||||
echo ""
|
||||
|
||||
ok "Phase 5 complete. All manifests applied."
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PHASE 6: Health Check
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
phase6_verify() {
|
||||
log "Phase 6: Verifying service health..."
|
||||
|
||||
# Create reusable health check script
|
||||
cat > "${INSTALL_DIR}/check-health-k8s.sh" <<'HEALTH'
|
||||
#!/usr/bin/env bash
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
|
||||
check() {
|
||||
local name="$1" url="$2"
|
||||
if curl -sf --connect-timeout 3 "$url" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN} ✓ ${name}${NC} ${url}"
|
||||
else
|
||||
echo -e "${RED} ✗ ${name}${NC} ${url}"
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "═══ K8s Cluster ═══"
|
||||
echo -n " Nodes: "; kubectl get nodes --no-headers 2>/dev/null | wc -l | tr -d ' '
|
||||
echo -n " Pods: "; kubectl get pods -A --no-headers 2>/dev/null | wc -l | tr -d ' '
|
||||
echo -n " Ready: "; kubectl get pods -A --no-headers 2>/dev/null | grep -c "Running" || echo 0
|
||||
|
||||
echo ""
|
||||
echo "═══ Infrastructure ═══"
|
||||
check "Gitea (npm)" "http://localhost:3300/api/v1/version"
|
||||
check "Ollama (LLM)" "http://localhost:11434/api/version"
|
||||
check "Cosmos Explorer" "http://localhost:1234"
|
||||
check "Azurite (Blob)" "http://localhost:10000/devstoreaccount1?comp=list"
|
||||
check "Mailpit" "http://localhost:8025"
|
||||
check "Loki" "http://localhost:3100/ready"
|
||||
check "Grafana" "http://localhost:3000/api/health"
|
||||
|
||||
echo ""
|
||||
echo "═══ Platform Services ═══"
|
||||
check "platform-service" "http://localhost:4003/health"
|
||||
check "extraction-service" "http://localhost:4005/health"
|
||||
check "mcp-server" "http://localhost:4007/health"
|
||||
|
||||
echo ""
|
||||
echo "═══ Dashboards ═══"
|
||||
check "admin-web" "http://localhost:3001"
|
||||
check "tracker-web" "http://localhost:3003"
|
||||
|
||||
echo ""
|
||||
echo "═══ Product Backends ═══"
|
||||
check "peakpulse" "http://localhost:4010/health"
|
||||
check "chronomind" "http://localhost:4011/health"
|
||||
check "jarvisjr" "http://localhost:4012/health"
|
||||
check "nomgap" "http://localhost:4013/health"
|
||||
check "mindlyst" "http://localhost:4014/health"
|
||||
check "lysnrai" "http://localhost:4015/health"
|
||||
check "notelett" "http://localhost:4016/health"
|
||||
check "flowmonk" "http://localhost:4017/health"
|
||||
check "actiontrail" "http://localhost:4018/health"
|
||||
check "localmemgpt" "http://localhost:4019/health"
|
||||
|
||||
echo ""
|
||||
echo "═══ Product Web Apps ═══"
|
||||
check "lysnrai-dashboard" "http://localhost:3002"
|
||||
check "chronomind-web" "http://localhost:3030"
|
||||
check "jarvisjr-web" "http://localhost:3035"
|
||||
check "flowmonk-web" "http://localhost:3040"
|
||||
check "notelett-web" "http://localhost:3045"
|
||||
check "mindlyst-web" "http://localhost:3050"
|
||||
check "nomgap-web" "http://localhost:3055"
|
||||
check "actiontrail-web" "http://localhost:3060"
|
||||
check "localmemgpt-web" "http://localhost:3070"
|
||||
|
||||
echo ""
|
||||
echo "═══ K8s Quick Commands ═══"
|
||||
echo " kubectl get pods -A # All pods"
|
||||
echo " kubectl top pods -A # Resource usage"
|
||||
echo " kubectl logs deploy/<name> -n <ns> -f # Stream logs"
|
||||
echo " kubectl rollout restart deploy/<name> # Rolling restart"
|
||||
echo ""
|
||||
HEALTH
|
||||
chmod +x "${INSTALL_DIR}/check-health-k8s.sh"
|
||||
|
||||
# Run it
|
||||
bash "${INSTALL_DIR}/check-health-k8s.sh"
|
||||
|
||||
ok "Phase 6 complete."
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# TEARDOWN: Remove all K8s resources
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
teardown() {
|
||||
log "Tearing down all ByteLyst K8s resources..."
|
||||
for ns in bytelyst-products bytelyst-dashboards bytelyst-platform bytelyst-infra; do
|
||||
log " Deleting namespace: ${ns}"
|
||||
kubectl delete namespace "$ns" --ignore-not-found=true 2>/dev/null || true
|
||||
done
|
||||
reset_markers
|
||||
ok "Teardown complete. k3s itself is still running (uninstall with: /usr/local/bin/k3s-uninstall.sh)"
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
run_phase() {
|
||||
local n="$1"
|
||||
case "$n" in
|
||||
1) phase1_preflight ;;
|
||||
2) phase2_k3s ;;
|
||||
3) phase3_images ;;
|
||||
4) phase4_config ;;
|
||||
5) phase5_deploy ;;
|
||||
6) phase6_verify ;;
|
||||
*) fail "Unknown phase: $n" ;;
|
||||
esac
|
||||
mark_phase_done "$n"
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Usage: sudo ./setup-k8s.sh [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --resume Auto-resume from last completed phase"
|
||||
echo " --phase=N Run ONLY phase N (1-6)"
|
||||
echo " --reset Clear phase markers"
|
||||
echo " --status Show completed phases"
|
||||
echo " --teardown Remove all K8s resources"
|
||||
echo " -h, --help Show this help"
|
||||
echo ""
|
||||
echo "Phases:"
|
||||
echo " 1 Pre-flight checks (verify docker phases ran)"
|
||||
echo " 2 Install k3s"
|
||||
echo " 3 Build Docker images + tag for k3s"
|
||||
echo " 4 Generate K8s config (Secrets + ConfigMap)"
|
||||
echo " 5 Apply manifests (infra → platform → dashboards → products)"
|
||||
echo " 6 Health check"
|
||||
echo ""
|
||||
echo "PREREQUISITE: Run ../docker/setup.sh (phases 1-5) first."
|
||||
}
|
||||
|
||||
main() {
|
||||
local mode="full" start_phase=1 only_phase=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--resume)
|
||||
mode="resume" ;;
|
||||
--phase=*)
|
||||
mode="single"
|
||||
only_phase="${arg#*=}" ;;
|
||||
--reset)
|
||||
reset_markers; exit 0 ;;
|
||||
--status)
|
||||
echo "K8s phase completion:"
|
||||
for i in 1 2 3 4 5 6; do
|
||||
if is_phase_done "$i"; then
|
||||
echo " Phase $i: DONE ($(cat "${STATE_DIR}/phase${i}.done"))"
|
||||
else
|
||||
echo " Phase $i: pending"
|
||||
fi
|
||||
done
|
||||
exit 0 ;;
|
||||
--teardown)
|
||||
teardown; exit 0 ;;
|
||||
-h|--help)
|
||||
usage; exit 0 ;;
|
||||
*)
|
||||
warn "Unknown option: $arg"; usage; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
exec > >(tee -a "${INSTALL_DIR}/setup-k8s.log") 2>&1
|
||||
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ ByteLyst K8s Deployment (k3s on single VM) ║"
|
||||
echo "║ 30 services · 4 namespaces · production-grade ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
log "Log file: ${INSTALL_DIR}/setup-k8s.log"
|
||||
|
||||
[ "$(id -u)" -eq 0 ] || fail "This script must be run as root (sudo)."
|
||||
|
||||
local start_time
|
||||
start_time=$(date +%s)
|
||||
|
||||
if [ "$mode" = "single" ]; then
|
||||
log "Running ONLY phase ${only_phase}..."
|
||||
run_phase "$only_phase"
|
||||
ok "Phase ${only_phase} complete."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$mode" = "resume" ]; then
|
||||
local last
|
||||
last=$(last_completed_phase)
|
||||
if [ "$last" -ge 6 ]; then
|
||||
ok "All phases completed. Use --reset to start over."
|
||||
exit 0
|
||||
fi
|
||||
start_phase=$((last + 1))
|
||||
log "Resuming from phase ${start_phase}."
|
||||
fi
|
||||
|
||||
for n in 1 2 3 4 5 6; do
|
||||
[ "$n" -ge "$start_phase" ] || continue
|
||||
run_phase "$n"
|
||||
done
|
||||
|
||||
local elapsed=$(( $(date +%s) - start_time ))
|
||||
local minutes=$(( elapsed / 60 ))
|
||||
local seconds=$(( elapsed % 60 ))
|
||||
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ K8s deployment complete in ${minutes}m ${seconds}s ║"
|
||||
echo "║ ║"
|
||||
echo "║ Health check: /opt/bytelyst/check-health-k8s.sh ║"
|
||||
echo "║ All pods: kubectl get pods -A ║"
|
||||
echo "║ Pod resources: kubectl top pods -A ║"
|
||||
echo "║ Stream logs: kubectl logs deploy/<name> -n <ns> -f ║"
|
||||
echo "║ Scale up: kubectl scale deploy/<name> --replicas=2 ║"
|
||||
echo "║ Teardown: sudo ./setup-k8s.sh --teardown ║"
|
||||
echo "║ ║"
|
||||
echo "║ Grafana: http://localhost:3000 (admin / bytelyst) ║"
|
||||
echo "║ Mailpit: http://localhost:8025 ║"
|
||||
echo "║ Gitea: http://localhost:3300 ║"
|
||||
echo "║ Ollama: http://localhost:11434 ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue
Block a user