168 lines
5.1 KiB
YAML
168 lines
5.1 KiB
YAML
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: hermes
|
|
namespace: platform-engineer
|
|
labels:
|
|
app: hermes
|
|
spec:
|
|
replicas: 1 # MUST be 1 — Hermes' /opt/data is single-writer.
|
|
strategy:
|
|
type: Recreate # never run two pods against the same PVC
|
|
selector:
|
|
matchLabels:
|
|
app: hermes
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: hermes
|
|
spec:
|
|
serviceAccountName: platform-engineer
|
|
# No imagePullSecrets — using the public stock Hermes image from Docker Hub.
|
|
|
|
# Pin to the powerful amd64 node (image is linux/amd64; the NUC has 24 GiB).
|
|
nodeSelector:
|
|
kubernetes.io/arch: amd64
|
|
affinity:
|
|
nodeAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
preference:
|
|
matchExpressions:
|
|
- key: hardware
|
|
operator: In
|
|
values: ["high-memory"]
|
|
podAntiAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
podAffinityTerm:
|
|
labelSelector:
|
|
matchLabels:
|
|
app: hermes
|
|
topologyKey: kubernetes.io/hostname
|
|
|
|
initContainers:
|
|
# Download kubectl + helm into a shared emptyDir so the stock Hermes image
|
|
# (which doesn't ship kubectl) can still drive the cluster. Avoids building
|
|
# and pushing a custom image through a slow / size-capped registry.
|
|
- name: install-tools
|
|
image: curlimages/curl:8.12.1
|
|
command: ["sh", "-c"]
|
|
args:
|
|
- |
|
|
set -e
|
|
echo "Downloading kubectl v1.35.0..."
|
|
curl -fsSL -o /tools/kubectl \
|
|
https://dl.k8s.io/release/v1.35.0/bin/linux/amd64/kubectl
|
|
chmod +x /tools/kubectl
|
|
echo "Downloading helm v3.16.3..."
|
|
curl -fsSL https://get.helm.sh/helm-v3.16.3-linux-amd64.tar.gz \
|
|
| tar -xz -C /tools --strip-components=1 linux-amd64/helm
|
|
chmod +x /tools/helm
|
|
echo "Tools installed:"; ls -la /tools
|
|
volumeMounts:
|
|
- name: tools
|
|
mountPath: /tools
|
|
|
|
# Seed /opt/data with config.yaml + SOUL.md on first boot only.
|
|
# ArgoCD owns the manifests; the PVC is runtime state and is NOT reconciled.
|
|
- name: seed-data
|
|
image: busybox:1.36
|
|
command: ["sh", "-c"]
|
|
args:
|
|
- |
|
|
set -e
|
|
if [ ! -f /opt/data/config.yaml ]; then
|
|
echo "First boot: seeding /opt/data from ConfigMap..."
|
|
cp /seed/config.yaml /opt/data/config.yaml
|
|
cp /seed/SOUL.md /opt/data/SOUL.md
|
|
chmod 600 /opt/data/config.yaml
|
|
else
|
|
echo "/opt/data already initialized — leaving runtime state intact."
|
|
fi
|
|
mkdir -p /opt/data/home/.kube /opt/data/cron/output /opt/data/scripts /workspace
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /opt/data
|
|
- name: seed
|
|
mountPath: /seed
|
|
|
|
containers:
|
|
- name: hermes
|
|
image: nousresearch/hermes-agent:latest
|
|
imagePullPolicy: Always
|
|
# IMPORTANT: do NOT set `command:` — it would override the image's
|
|
# ENTRYPOINT (/init, s6-overlay), which sets up the hermes user, seeds
|
|
# config on first boot, and supervises the gateway. The image's CMD
|
|
# (main-wrapper.sh) already routes `gateway run` through s6.
|
|
args: ["gateway", "run"]
|
|
ports:
|
|
- name: gateway
|
|
containerPort: 8642
|
|
- name: dashboard
|
|
containerPort: 9119
|
|
envFrom:
|
|
- secretRef:
|
|
name: hermes-env
|
|
env:
|
|
# k3s injects KUBERNETES_SERVICE_HOST/PORT + the SA token automatically;
|
|
# kubectl inside the pod authenticates as the platform-engineer SA.
|
|
- name: HERMES_HOME
|
|
value: /opt/data
|
|
# Put the initContainer-installed kubectl/helm on PATH for the hermes user.
|
|
- name: PATH
|
|
value: /opt/hermes/bin:/opt/hermes/.venv/bin:/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /opt/data
|
|
- name: workspace
|
|
mountPath: /workspace
|
|
- name: tools
|
|
mountPath: /tools
|
|
readOnly: true
|
|
resources:
|
|
requests:
|
|
memory: "512Mi"
|
|
cpu: "250m"
|
|
limits:
|
|
memory: "2Gi"
|
|
cpu: "1000m"
|
|
livenessProbe:
|
|
httpGet:
|
|
path: /health
|
|
port: 8642
|
|
initialDelaySeconds: 60
|
|
periodSeconds: 30
|
|
failureThreshold: 3
|
|
securityContext:
|
|
allowPrivilegeEscalation: false
|
|
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: hermes-data
|
|
- name: workspace
|
|
emptyDir: {}
|
|
- name: tools
|
|
emptyDir: {}
|
|
- name: seed
|
|
configMap:
|
|
name: hermes-seed
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: hermes
|
|
namespace: platform-engineer
|
|
spec:
|
|
type: ClusterIP
|
|
selector:
|
|
app: hermes
|
|
ports:
|
|
- name: gateway
|
|
port: 80
|
|
targetPort: 8642
|
|
- name: dashboard
|
|
port: 9119
|
|
targetPort: 9119
|