Files
Hanzo AI 320f061bda k8s: collapse zoo-operator to zoo-system, migrate to CR-driven bring-up
Operator topology fix:
- base/operator.yaml: pin ghcr.io/zooai/operator:v0.1.1 (was :main floating).
  Add OPERATOR_NAMESPACE env via fieldRef: metadata.namespace so the
  leader-election Lease lock is namespace-scoped, not racing in a single
  hardcoded 'zoo-system' literal. imagePullPolicy: Always → IfNotPresent
  (semver tag is immutable).
- zoo-system/patches.yaml: zoo-operator replicas → 2 (HA, leader-election
  picks one). Drop the stale zoo-explorer raw-Deployment patch (operator
  emits it now via ZooExplorer CR).
- zoo-{mainnet,testnet,devnet}/network-patch.yaml: scale per-env
  zoo-operator → 0. Single zoo-system operator watches every namespace
  (--namespace '' default); per-env operators were thrashing the same
  Lease and never becoming leader.

CR migration (away from raw manifests):
- base/network.yaml: full rewrite as ZooNetwork + ZooChain + ZooExplorer
  + ZooGateway base CRs (CRDs already installed: zoonetworks /
  zoochains / zooexplorers / zoogateways).
- base/explorer.yaml: emptied — ZooExplorer CR replaces the raw
  Deployment; operator renders zoo-explorer-explorer Deployment + Svc.
- base/gateway.yaml: Service-only — ZooGateway moved to network.yaml.
- base/ingress.yaml: kind: Ingress → 4 kind: IngressRoute
  (hanzo.ai/v1alpha1). Traefik proxy Deployment kept (it IS the
  hanzoai/ingress L7 proxy on this cluster).
- zoo-{mainnet,testnet}/network-patch.yaml: env-specific patches —
  validators (mainnet=5, testnet=3), bootstrap IPs+IDs from live Lux
  mainnet+testnet svcs, TRACK_CHAINS = Zoo blockchainID per env.
- zoo-{testnet,devnet}/ingressroute.yaml: new — api.test.zoo.network,
  explore.test.zoo.network, api-explore.test.zoo.network (and dev.*
  variants).
- zoo-{system,testnet,devnet}/kustomization.yaml: include new
  ingressroute.yaml resource + strip mainnet-only IngressRoutes via
  $patch: delete pattern.
- zoo-mainnet/zoo-industries.yaml: kind: Ingress → IngressRoute.

Blockers surfaced (separate followups):
1. Operator does NOT translate `spec.config` env keys to lqd CLI
   flags. Rendered StatefulSet sets ZOO_BOOTSTRAP_IPS etc., but
   luxd reads LUXD_*-prefixed env. Operator controller.rs needs to
   render as CLI args. BLOCKS validators joining Lux primary.
2. hanzo.ai/v1alpha1 IngressRoute CRDs not registered on
   do-sfo3-zoo-k8s — current ingress is traefik:v3.3 with
   traefik.io/v1alpha1 native. Either install hanzoai/ingress CRDs
   OR migrate IngressRoutes to traefik.io group.
3. ZooNetwork operator doesn't emit per-pod LoadBalancer Services
   needed for validators to be reachable as external bootstrap peers.
4. Validator staking keys: operator only takes secret_prefix; doesn't
   derive from a KMS mnemonic. Need 5 Secrets per env OR operator
   extension.
2026-06-10 09:13:57 -07:00

283 lines
7.5 KiB
YAML

# Ingress — hanzoai/ingress (Traefik) for Zoo TLS termination
# cert-manager handles ACME via letsencrypt-prod ClusterIssuer
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: ingress
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: zoo-ingress
labels:
app: zoo-ingress
app.kubernetes.io/name: zoo-ingress
app.kubernetes.io/part-of: zoo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: zoo-ingress
rules:
- apiGroups: [""]
resources: [services, endpoints, secrets, namespaces, configmaps]
verbs: [get, list, watch]
- apiGroups: [discovery.k8s.io]
resources: [endpointslices]
verbs: [get, list, watch]
- apiGroups: [networking.k8s.io]
resources: [ingresses, ingressclasses]
verbs: [get, list, watch]
- apiGroups: [networking.k8s.io]
resources: [ingresses/status]
verbs: [update]
- apiGroups: [traefik.io]
resources: ["*"]
verbs: [get, list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: zoo-ingress
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: zoo-ingress
subjects:
- kind: ServiceAccount
name: zoo-ingress
namespace: zoo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: zoo-ingress
labels:
app: zoo-ingress
app.kubernetes.io/name: zoo-ingress
app.kubernetes.io/part-of: zoo
spec:
replicas: 2
selector:
matchLabels:
app: zoo-ingress
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: zoo-ingress
spec:
serviceAccountName: zoo-ingress
imagePullSecrets:
- name: ghcr-secret
- name: hanzo
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
terminationGracePeriodSeconds: 30
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- zoo-ingress
topologyKey: kubernetes.io/hostname
containers:
- name: ingress
image: traefik:v3.3
imagePullPolicy: Always
args:
- --providers.kubernetesingress=true
- --providers.kubernetesingress.ingressclass=ingress
- --providers.kubernetesingress.allowemptyservices=true
- --providers.kubernetescrd=true
- --providers.kubernetescrd.allowcrossnamespace=true
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=:443
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
- --entrypoints.websecure.address=:443
- --entrypoints.websecure.http.tls=true
- --entrypoints.websecure.http.tls.certresolver=letsencrypt
- --[email protected]
- --certificatesresolvers.letsencrypt.acme.storage=/data/acme.json
- --certificatesresolvers.letsencrypt.acme.dnschallenge=true
- --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
- --certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
- --ping=true
- --ping.entryPoint=web
- --log.level=INFO
- --accesslog=true
env:
- name: CF_API_EMAIL
valueFrom:
secretKeyRef:
name: cloudflare-api-credentials
key: EMAIL
- name: CF_API_KEY
valueFrom:
secretKeyRef:
name: cloudflare-api-credentials
key: APIKEY
ports:
- name: web
containerPort: 80
hostPort: 80
protocol: TCP
- name: websecure
containerPort: 443
hostPort: 443
protocol: TCP
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "1"
memory: 512Mi
securityContext:
runAsUser: 0
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
livenessProbe:
httpGet:
path: /ping
port: 80
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ping
port: 80
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
hostPath:
path: /var/lib/zoo-ingress
type: DirectoryOrCreate
---
# IngressRoutes for Zoo services — hanzoai/ingress native CRD shape
# (kind: IngressRoute, apiVersion: hanzo.ai/v1alpha1). NO `kind: Ingress`
# with annotation-driven middleware — that was the Traefik compat path.
#
# Mainnet/testnet host names come from each overlay's ZooGateway CR;
# the routes themselves are static here. Per-env overlays patch the
# hosts in their network-patch.yaml ZooGateway and add explore-specific
# IngressRoutes if needed.
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: zoo-rpc
labels:
app.kubernetes.io/part-of: zoo
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.zoo.network`)
kind: Rule
services:
- name: zoo-chain-rpc
port: 9630
passHostHeader: false
tls:
certResolver: letsencrypt
domains:
- main: api.zoo.network
---
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: zoo-gateway
labels:
app.kubernetes.io/part-of: zoo
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.zoo.ngo`)
kind: Rule
services:
- name: zoo-gateway
port: 9630
passHostHeader: false
tls:
certResolver: letsencrypt
domains:
- main: api.zoo.ngo
---
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: zoo-explorer
labels:
app.kubernetes.io/part-of: zoo
spec:
entryPoints:
- websecure
routes:
- match: Host(`explore.zoo.network`)
kind: Rule
services:
- name: zoo-explorer-explorer
port: 8090
passHostHeader: false
- match: Host(`api-explore.zoo.network`)
kind: Rule
services:
- name: zoo-explorer-explorer
port: 8090
passHostHeader: false
tls:
certResolver: letsencrypt
domains:
- main: explore.zoo.network
- main: api-explore.zoo.network
---
apiVersion: hanzo.ai/v1alpha1
kind: IngressRoute
metadata:
name: zoo-bridge
labels:
app.kubernetes.io/part-of: zoo
spec:
entryPoints:
- websecure
routes:
- match: Host(`bridge.zoo.network`)
kind: Rule
services:
- name: zoo-bridge
port: 3000
passHostHeader: false
tls:
certResolver: letsencrypt
domains:
- main: bridge.zoo.network