464 lines
16 KiB
YAML
464 lines
16 KiB
YAML
# =============================================================================
|
|
# Agent Zero AI Stack — NUC Deployment (RKE2 Bare-Metal)
|
|
# =============================================================================
|
|
# Deploys: AgentZero (agent UI) on RKE2 cluster with Blue Jay profile
|
|
# Ollama: edge1 Pi 5 at 10.0.57.17:11434 (qwen2.5-coder:7b + nomic-embed-text)
|
|
# Target: RKE2 bare-metal cluster, namespace: agent-zero
|
|
# Profile: Blue Jay (21 tools, 3 prompts, 4 extensions, theme)
|
|
#
|
|
# Differences from LOCAL (WSL K3s):
|
|
# - Uses Longhorn StorageClass (not local-path)
|
|
# - Connects to edge1 Pi 5 Ollama (not workstation R9700)
|
|
# - NO Anthropic API key (free/local models only)
|
|
# - NO Piper TTS or Kiwix (edge1 handles TTS, no Wikipedia needed)
|
|
# - NO hostPath volumes — profile/tools/extensions loaded via ConfigMaps
|
|
# - Traefik IngressRoute for LAN access at agent-zero.iamworkin.lan
|
|
#
|
|
# ConfigMaps (defined in configmaps-bluejay.yaml):
|
|
# bluejay-tools 21 Python tool modules (~520K)
|
|
# bluejay-profile agent.json, agent.yaml, system_prompt.md (~20K)
|
|
# bluejay-prompts 3 prompt templates (~11K)
|
|
# flowercore-extensions 5 Python extension modules (~76K)
|
|
# bluejay-theme CSS theme (~7K)
|
|
#
|
|
# Apply: KUBECONFIG=~/.kube/rke2.yaml kubectl apply -f agent-zero-nuc.yaml
|
|
# =============================================================================
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: agent-zero
|
|
labels:
|
|
app.kubernetes.io/part-of: agent-zero-stack
|
|
|
|
# =============================================================================
|
|
# Persistent Volume Claims (Longhorn)
|
|
# =============================================================================
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: agent-zero-data
|
|
namespace: agent-zero
|
|
spec:
|
|
accessModes: [ReadWriteOnce]
|
|
storageClassName: longhorn
|
|
resources:
|
|
requests:
|
|
storage: 5Gi
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: agent-zero-knowledge
|
|
namespace: agent-zero
|
|
spec:
|
|
accessModes: [ReadWriteOnce]
|
|
storageClassName: longhorn
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
|
|
# =============================================================================
|
|
# RBAC — Give Agent Zero kubectl access to the cluster
|
|
# =============================================================================
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: agent-zero
|
|
namespace: agent-zero
|
|
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRoleBinding
|
|
metadata:
|
|
name: agent-zero-cluster-admin
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: ClusterRole
|
|
name: cluster-admin
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: agent-zero
|
|
namespace: agent-zero
|
|
|
|
# =============================================================================
|
|
# Agent Zero — AI Agent Web UI (NUC Edition, Blue Jay Profile)
|
|
# =============================================================================
|
|
# Connects to edge1 Pi 5 Ollama (free, local models only)
|
|
# Blue Jay profile with 21 tools, 3 prompts, 4 extensions
|
|
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: agent-zero
|
|
namespace: agent-zero
|
|
labels:
|
|
app: agent-zero
|
|
annotations:
|
|
agent-zero/deployment: "nuc"
|
|
agent-zero/profile: "bluejay"
|
|
agent-zero/ollama: "edge1 Pi 5 (10.0.57.17:11434)"
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: agent-zero
|
|
strategy:
|
|
type: Recreate
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: agent-zero
|
|
spec:
|
|
serviceAccountName: agent-zero
|
|
initContainers:
|
|
# Wait for edge1 Pi 5 Ollama to be reachable before starting Agent Zero.
|
|
- name: wait-for-ollama
|
|
image: busybox:1.37
|
|
command: ["sh", "-c"]
|
|
args:
|
|
- |
|
|
echo "Waiting for Ollama at edge1 (10.0.57.17:11434)..."
|
|
until wget -qO- --timeout=2 http://10.0.57.17:11434/api/tags >/dev/null 2>&1; do
|
|
echo "edge1 Ollama not ready, retrying in 5s..."
|
|
sleep 5
|
|
done
|
|
echo "edge1 Ollama is reachable!"
|
|
# Assemble the Blue Jay profile directory structure from ConfigMaps.
|
|
# ConfigMaps can't create nested dirs, so we copy into the workspace PVC.
|
|
- name: setup-bluejay
|
|
image: busybox:1.37
|
|
command: ["sh", "-c"]
|
|
args:
|
|
- |
|
|
echo "Setting up Blue Jay profile..."
|
|
# Profile root files
|
|
mkdir -p /a0/work/.bluejay/agents/bluejay/tools
|
|
mkdir -p /a0/work/.bluejay/agents/bluejay/prompts
|
|
cp /tmp/bluejay-profile/* /a0/work/.bluejay/agents/bluejay/
|
|
# Tools (split across 3 ConfigMaps to stay under K8s 262K annotation limit)
|
|
cp /tmp/bluejay-tools-a/* /a0/work/.bluejay/agents/bluejay/tools/
|
|
cp /tmp/bluejay-tools-b/* /a0/work/.bluejay/agents/bluejay/tools/
|
|
cp /tmp/bluejay-tools-c/* /a0/work/.bluejay/agents/bluejay/tools/
|
|
# Prompts
|
|
cp /tmp/bluejay-prompts/* /a0/work/.bluejay/agents/bluejay/prompts/
|
|
# Extensions
|
|
mkdir -p /a0/work/.bluejay/extensions/flowercore
|
|
cp /tmp/flowercore-extensions/* /a0/work/.bluejay/extensions/flowercore/
|
|
# Theme
|
|
mkdir -p /a0/work/.bluejay/theme
|
|
cp /tmp/bluejay-theme/* /a0/work/.bluejay/theme/
|
|
echo "Blue Jay profile ready:"
|
|
echo " Tools: $(ls /a0/work/.bluejay/agents/bluejay/tools/*.py | wc -l)"
|
|
echo " Prompts: $(ls /a0/work/.bluejay/agents/bluejay/prompts/*.md | wc -l)"
|
|
echo " Extensions: $(ls /a0/work/.bluejay/extensions/flowercore/*.py | wc -l)"
|
|
volumeMounts:
|
|
- name: workspace
|
|
mountPath: /a0/work
|
|
- name: bluejay-tools-a
|
|
mountPath: /tmp/bluejay-tools-a
|
|
- name: bluejay-tools-b
|
|
mountPath: /tmp/bluejay-tools-b
|
|
- name: bluejay-tools-c
|
|
mountPath: /tmp/bluejay-tools-c
|
|
- name: bluejay-profile
|
|
mountPath: /tmp/bluejay-profile
|
|
- name: bluejay-prompts
|
|
mountPath: /tmp/bluejay-prompts
|
|
- name: flowercore-extensions
|
|
mountPath: /tmp/flowercore-extensions
|
|
- name: bluejay-theme
|
|
mountPath: /tmp/bluejay-theme
|
|
containers:
|
|
- name: agent-zero
|
|
image: agent0ai/agent-zero:latest
|
|
command: ["/bin/bash", "-c"]
|
|
args:
|
|
- |
|
|
# Install kubectl if not cached
|
|
if [ -f /a0/work/kubectl ]; then
|
|
cp /a0/work/kubectl /usr/local/bin/kubectl
|
|
else
|
|
curl -sLO "https://dl.k8s.io/release/v1.32.0/bin/linux/amd64/kubectl" && \
|
|
chmod +x kubectl && mv kubectl /usr/local/bin/kubectl && \
|
|
cp /usr/local/bin/kubectl /a0/work/kubectl
|
|
fi
|
|
# Link Blue Jay profile from workspace into Agent Zero's expected path
|
|
ln -sfn /a0/work/.bluejay/agents/bluejay /a0/agents/bluejay
|
|
# Write model config BEFORE initialize.sh loads it
|
|
# The _model_config plugin reads config.json (NOT config.yaml)
|
|
# Default is OpenRouter; override to local Ollama on edge1
|
|
mkdir -p /a0/usr/plugins/_model_config
|
|
cat > /a0/usr/plugins/_model_config/config.json << 'MODELCFG'
|
|
{"allow_chat_override":true,"chat_model":{"provider":"ollama","name":"qwen2.5-coder:7b","api_base":"http://10.0.57.17:11434","ctx_length":2048,"ctx_history":0.7,"vision":false,"kwargs":{"temperature":0,"num_ctx":2048}},"utility_model":{"provider":"ollama","name":"qwen2.5-coder:7b","api_base":"http://10.0.57.17:11434","ctx_length":2048,"ctx_input":0.7,"kwargs":{"num_ctx":2048}},"embedding_model":{"provider":"ollama","name":"nomic-embed-text","api_base":"http://10.0.57.17:11434","kwargs":{}}}
|
|
MODELCFG
|
|
# Strip heredoc indentation
|
|
sed -i 's/^ //' /a0/usr/plugins/_model_config/config.json
|
|
# Run the original entrypoint
|
|
exec /exe/initialize.sh $BRANCH
|
|
ports:
|
|
- containerPort: 80
|
|
env:
|
|
# Agent identity
|
|
- name: AGENT_NAME
|
|
value: "Blue Jay (NUC)"
|
|
# Chat model — qwen2.5-coder:7b on edge1 Pi 5
|
|
- name: A0_SET_chat_model_provider
|
|
value: "ollama"
|
|
- name: A0_SET_chat_model_name
|
|
value: "qwen2.5-coder:7b"
|
|
- name: A0_SET_chat_model_api_base
|
|
value: "http://10.0.57.17:11434"
|
|
- name: A0_SET_chat_model_ctx_length
|
|
value: "2048"
|
|
- name: A0_SET_chat_model_kwargs
|
|
value: '{"temperature": 0, "num_ctx": 2048}'
|
|
# Utility model — same as chat (only one model available)
|
|
- name: A0_SET_util_model_provider
|
|
value: "ollama"
|
|
- name: A0_SET_util_model_name
|
|
value: "qwen2.5-coder:7b"
|
|
- name: A0_SET_util_model_api_base
|
|
value: "http://10.0.57.17:11434"
|
|
- name: A0_SET_util_model_kwargs
|
|
value: '{"num_ctx": 2048}'
|
|
# Embedding model — nomic on edge1
|
|
- name: A0_SET_embed_model_provider
|
|
value: "ollama"
|
|
- name: A0_SET_embed_model_name
|
|
value: "nomic-embed-text"
|
|
- name: A0_SET_embed_model_api_base
|
|
value: "http://10.0.57.17:11434"
|
|
# Browser model — disabled (no vision model on Pi)
|
|
- name: A0_SET_browser_model_provider
|
|
value: "ollama"
|
|
- name: A0_SET_browser_model_name
|
|
value: "qwen2.5-coder:7b"
|
|
- name: A0_SET_browser_model_api_base
|
|
value: "http://10.0.57.17:11434"
|
|
- name: A0_SET_browser_model_vision
|
|
value: "false"
|
|
# Agent profile — Blue Jay personality, tools, and system prompt
|
|
- name: A0_SET_agent_profile
|
|
value: "bluejay"
|
|
# Memory settings
|
|
- name: A0_SET_memory_memorize_enabled
|
|
value: "true"
|
|
- name: A0_SET_memory_memorize_consolidation
|
|
value: "true"
|
|
- name: A0_SET_memory_memorize_replace_threshold
|
|
value: "0.85"
|
|
- name: A0_SET_memory_recall_enabled
|
|
value: "true"
|
|
# Speech-to-text disabled (no GPU for Whisper)
|
|
- name: A0_SET_stt_model_size
|
|
value: "tiny"
|
|
# Print.Web — Thermal printer service on edge2
|
|
- name: PRINT_WEB_URL
|
|
value: "http://10.0.57.16:5200"
|
|
# Kubernetes
|
|
- name: KUBERNETES_SERVICE_HOST
|
|
value: "kubernetes.default.svc"
|
|
- name: KUBERNETES_SERVICE_PORT
|
|
value: "443"
|
|
volumeMounts:
|
|
- name: workspace
|
|
mountPath: /a0/work
|
|
- name: knowledge
|
|
mountPath: /a0/knowledge/custom/main
|
|
- name: flowercore-extensions
|
|
mountPath: /a0/extensions/flowercore
|
|
readOnly: true
|
|
- name: bluejay-theme
|
|
mountPath: /a0/webui/static/css/custom
|
|
readOnly: true
|
|
startupProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 80
|
|
initialDelaySeconds: 15
|
|
periodSeconds: 10
|
|
failureThreshold: 18
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /
|
|
port: 80
|
|
periodSeconds: 30
|
|
failureThreshold: 3
|
|
readinessProbe:
|
|
exec:
|
|
command:
|
|
- /bin/bash
|
|
- -c
|
|
- "curl -sf http://localhost:80/ > /dev/null && curl -sf --connect-timeout 3 http://10.0.57.17:11434/api/tags > /dev/null"
|
|
periodSeconds: 30
|
|
failureThreshold: 2
|
|
resources:
|
|
requests:
|
|
memory: "2Gi"
|
|
cpu: "1000m"
|
|
limits:
|
|
memory: "3Gi"
|
|
cpu: "2000m"
|
|
volumes:
|
|
- name: workspace
|
|
persistentVolumeClaim:
|
|
claimName: agent-zero-data
|
|
- name: knowledge
|
|
persistentVolumeClaim:
|
|
claimName: agent-zero-knowledge
|
|
- name: bluejay-tools-a
|
|
configMap:
|
|
name: bluejay-tools-a
|
|
- name: bluejay-tools-b
|
|
configMap:
|
|
name: bluejay-tools-b
|
|
- name: bluejay-tools-c
|
|
configMap:
|
|
name: bluejay-tools-c
|
|
- name: bluejay-profile
|
|
configMap:
|
|
name: bluejay-profile
|
|
- name: bluejay-prompts
|
|
configMap:
|
|
name: bluejay-prompts
|
|
- name: flowercore-extensions
|
|
configMap:
|
|
name: flowercore-extensions
|
|
- name: bluejay-theme
|
|
configMap:
|
|
name: bluejay-theme
|
|
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: agent-zero
|
|
namespace: agent-zero
|
|
spec:
|
|
type: ClusterIP
|
|
selector:
|
|
app: agent-zero
|
|
ports:
|
|
- port: 80
|
|
targetPort: 80
|
|
|
|
# =============================================================================
|
|
# Traefik IngressRoute — LAN access at agent-zero.iamworkin.lan
|
|
# =============================================================================
|
|
|
|
---
|
|
apiVersion: traefik.io/v1alpha1
|
|
kind: IngressRoute
|
|
metadata:
|
|
name: agent-zero
|
|
namespace: agent-zero
|
|
spec:
|
|
entryPoints:
|
|
- websecure
|
|
routes:
|
|
- match: Host(`agent-zero.iamworkin.lan`)
|
|
kind: Rule
|
|
services:
|
|
- name: agent-zero
|
|
port: 80
|
|
tls:
|
|
secretName: agent-zero-tls
|
|
|
|
# =============================================================================
|
|
# TLS Certificate via cert-manager (step-ca ACME)
|
|
# =============================================================================
|
|
|
|
---
|
|
apiVersion: cert-manager.io/v1
|
|
kind: Certificate
|
|
metadata:
|
|
name: agent-zero-tls
|
|
namespace: agent-zero
|
|
spec:
|
|
secretName: agent-zero-tls
|
|
issuerRef:
|
|
name: step-ca-acme
|
|
kind: ClusterIssuer
|
|
dnsNames:
|
|
- agent-zero.iamworkin.lan
|
|
duration: 720h
|
|
renewBefore: 240h
|
|
|
|
# =============================================================================
|
|
# NetworkPolicy — Restrict traffic
|
|
# =============================================================================
|
|
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: agent-zero-netpol
|
|
namespace: agent-zero
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app: agent-zero
|
|
policyTypes:
|
|
- Ingress
|
|
- Egress
|
|
ingress:
|
|
# Allow from Traefik
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: traefik-system
|
|
ports:
|
|
- port: 80
|
|
# Allow from monitoring (blackbox probe)
|
|
- from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: monitoring
|
|
ports:
|
|
- port: 80
|
|
egress:
|
|
# DNS
|
|
- to:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: kube-system
|
|
ports:
|
|
- port: 53
|
|
protocol: UDP
|
|
- port: 53
|
|
protocol: TCP
|
|
# Ollama on edge1
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.0.57.17/32
|
|
ports:
|
|
- port: 11434
|
|
# Print.Web on edge2
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.0.57.16/32
|
|
ports:
|
|
- port: 5200
|
|
# K8s API
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 10.0.56.11/32
|
|
ports:
|
|
- port: 6443
|
|
# Allow internet (for kubectl image pull, etc)
|
|
- to:
|
|
- ipBlock:
|
|
cidr: 0.0.0.0/0
|
|
except:
|
|
- 10.0.0.0/8
|
|
- 172.16.0.0/12
|
|
- 192.168.0.0/16
|