Files
zeekayandHanzo Dev e4d0e884a8 zoo: schedule each workload where its image exists
Every image in zoo-mainnet except consul and the ingress is published for
linux/amd64 alone, and none of these files said so. Unpinned, the scheduler is
free to place them on spark, where the kubelet refuses the pull with `no match
for platform in manifest` -- which reads as a broken image and is a misplaced
pod. zoo-ui, zoo-gym, zoo-bot and zoo-build had been sitting in
ImagePullBackOff there.

The pin follows the artifact: an image that exists for one architecture may
only be scheduled where that architecture is. consul and the ingress are
multi-arch and stay free to land anywhere.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-09 00:19:27 -07:00

86 lines
2.8 KiB
YAML

# Generated from the live zoo-mainnet/zoo-build Deployment on do-sfo3-zoo-k8s
# by hack/live2values.py. Live is the source: a repo of hand-applied
# manifests has unknown currency, the running fleet does not.
replicas: 1
selectorLabels:
app: zoo-build
containerName: build
image:
repository: ghcr.io/luxfi/build-zoo
tag: 0.1.9
digest: sha256:946f21b4481d81169a8e104c5669c1fd0f1911c7d8cb6732451442e25c39c03e
pullPolicy: IfNotPresent
labels:
app.kubernetes.io/cluster: zoo-k8s
app.kubernetes.io/env: mainnet
env:
- name: NODE_ENV
value: production
- name: NEXT_TELEMETRY_DISABLED
value: '1'
# V8 sizes its own heap and caps at ~258 MB inside this 512Mi container, so HALF the
# granted memory is unreachable and it dies there:
# FATAL ERROR: Ineffective mark-compacts near heap limit
# Mark-Compact 251.9 (258.3) -> 251.1 (258.8) MB
# exit 139
# The container is never out of memory — only the JS heap is, so raising limits.memory
# would not have moved a V8-internal ceiling. 384 MB leaves ~128Mi for RSS outside the
# heap. The lux-k8s copy of this same app crashed ~13-hourly until it got this; this
# copy has the identical shape and simply has not tripped yet.
- name: NODE_OPTIONS
value: --max-old-space-size=384
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
livenessProbe:
failureThreshold: 3
httpGet:
path: /
port: 3000
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 1
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: 3000
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
ports:
- containerPort: 3000
# ghcr-zooai, because that is the name this namespace has. `ghcr-luxfi` does
# not exist in zoo-mainnet -- a pod naming it gets no credential at all and the
# kubelet falls back to anonymous, which GHCR answers 403/NotFound for a private
# package. That reads as a missing image and is a missing secret. Verified
# 2026-09-09: ghcr-zooai pulls every repo this namespace references, zooai/* and
# the luxfi/* ones alike.
imagePullSecrets:
- name: ghcr-zooai
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
topologySpread:
enabled: false
lifecycle: null
terminationGracePeriodSeconds: 30
# The image for this workload is published for linux/amd64 only, and this
# cluster has an arm64 node. Unpinned, the scheduler is free to place it on
# spark, where the kubelet refuses the pull with `no match for platform in
# manifest` -- which reads as a broken image and is a misplaced pod. Measured
# 2026-09-09: zoo-ui, zoo-gym, zoo-bot and zoo-build had all been sitting in
# ImagePullBackOff on spark. Pin follows the artifact: an image that exists for
# one architecture may only be scheduled where that architecture is.
nodeSelector:
kubernetes.io/arch: amd64