19 KiB
19 KiB
Trade Migration Plan: k3s on mevnode alongside agave
Date: 2026-04-12
Migration Revision Baseline
- Accepted migration baseline revision:
R001 R001means the currently working trade application running onk3sbefore migration cutover.- The current source runtime for
R001is the livetrade-stagingstack onmevnode_bot. - The first migration target is not a redesign. It is a faithful
reconstruction of
R001on the targetk3senvironment. - Functional changes, refactors, topology changes, and cleanup should
be treated as later revisions after
R001is reproducible. - During reconstruction, every manifest, secret mapping, image
reference, and host dependency should be mapped against
R001.
Goal
- Prepare migration of the trade system to
k3son hostmevnode(sol) in parallel withagave-validator. - Do not degrade validator stability, memory headroom, or disk/I/O for ledger and accounts.
Current Findings
mevnode_botis reachable again after restart.- Previous reset on
mevnode_botlooks likeACPI power state transition occurred. - I did not find a clear
OOM,kernel panic,watchdog, orI/O errortrail explaining the previous host reset. k3sonmevnode_botis up and the node isReady.- The main runtime failure is not the host itself but the DLOB
publishers:
dlob-publisher-hotis0/1dlob-publisher-allis0/1- both have very high restart counts
- both fail startup/liveness probes
- Publisher logs show Redis connectivity problems:
ClusterAllFailedError: Failed to refresh slots cacheNone of startup nodes is available
dlob-rediscurrently exposes a singleClusterIPendpoint on6379, while the publishers behave like cluster-aware Redis clients.Hasura,trade-api,trade-frontend,trade-ingestor,postgres, and the writer pods are currently up.trade-ingestorhad transient fetch failures after reboot, then resumed inserts.- There is a persistent warning in
k3sabout missing image pull secretgitea-registry.
Resource Picture: mevnode
- CPU:
32 vCPU - RAM total: about
186 GiB - RAM available: about
111 GiBat check time - Swap total: about
31 GiB - Swap used: about
13 GiB - Root filesystem
/:btrfs, about1.8 TiBtotal, about1.6 TiBfree /data/ledgerlives on a separate1.8 TiBNVMe device./data/statelives on another separate1.8 TiBNVMe device.- The current candidate place for trade persistent storage on
solis the root NVMe filesystem/, not the validator ledger or accounts devices. agave-validatorRSS: about129 GiBagave-validatoris already a heavy resident workload and must remain the priority workload on this host.
Resource Picture: mevnode_bot
- Root filesystem
/:ext4onmd RAID, about3.6 TiBtotal, about2.8 TiBfree - Current memory use after reboot is low, but this is not the main constraint.
- The main storage warning is current
k3sdata growth:/var/lib/rancher/k3s: about593 GiB/var/lib/rancher/k3s/storage/...data-postgres-0: about585 GiB- the related PVC is declared as only
20Gi
- This means the current
local-pathstorage setup does not give a meaningful hard size limit in practice. - The target Gitea organization
trade-nextnow exists. - The target repository set now exists in
trade-next:trade-apitrade-frontendtrade-ingestortrade-bottrade-dlobtrade-gitopstrade-host-iactrade-docs
- The current live component-to-repository boundary was documented in
trade-next/trade-docs/doc/k3s-component-repo-map.md. - We have not yet reconstructed the current live
trade-stagingmanifests and host drift intotrade-gitopsandtrade-host-iac.
Migration Risks
- Risk 1: moving the current storage model unchanged to
mevnodecan silently consume hundreds of GiB on the host root filesystem. - Risk 2:
agave-validatoralready uses most of the memory footprint onmevnode; uncontrolledk3sworkloads can force more swap or destabilize validator performance. - Risk 3: if trade workloads share disks or I/O paths with ledger/accounts, validator performance may degrade.
- Risk 4: current publisher instability must be fixed before migration, otherwise we would move a broken topology.
- Risk 5: missing
gitea-registrysecret can break future image pulls and cold-start behavior.
Plan
1.
Reconstruct the current live mevnode_bot state into
canonical Git/IaC
- Map each live
trade-stagingobject to a target repository and file path. - Split application code, Kubernetes manifests, and host-level infrastructure into separate Git sources of truth.
- Capture the current live deltas that are not present in local repos
or in
~/trade-k8s. - Prefer a new Gitea organization for the migration target instead of long-lived migration branches in existing repos.
- Treat live
k3sas input for reconstruction, not as a YAML dump to mirror blindly.
2. Stabilize
the current trade stack on mevnode_bot
- Fix
dlob-publisher-hot. - Fix
dlob-publisher-all. - Verify Redis mode and publisher Redis client mode are aligned.
- Confirm all
trade-stagingpods become healthy after a full restart.
3. Audit storage before any migration
- Measure actual PostgreSQL data size inside the current PVC.
- Determine how much of the
585 GiBis real database payload versus local-path overhead. - Identify retention candidates for historical data, derived tables, logs, and caches.
- Decide what data must move and what can be rebuilt.
4. Define target
storage layout on mevnode
- Do not place trade persistent data on the same critical paths as agave ledger/accounts.
- Do not rely on the current local-path behavior without hard operational limits.
- Choose a dedicated storage path or dedicated device for trade state.
- Set explicit quotas/limits and monitor actual bytes used on disk.
5. Define resource
isolation on mevnode
- Reserve headroom for
agave-validator. - Add CPU and memory requests/limits for trade workloads.
- Keep low-priority or bursty trade jobs away from validator-critical resources.
- Treat publisher and DB workloads as separate scheduling/priority classes.
6. Recreate the missing operational prerequisites
- Restore or recreate
gitea-registryimage pull secret. - Verify registry pull works after node reboot and after cache eviction.
- Verify secrets for RPC, gRPC, Hasura, Postgres, and frontend auth are reproducible.
7. Prepare a staged migration
- Start with a minimal canary namespace on
mevnode. - Bring up infrastructure first: Redis, Postgres, Hasura.
- Then bring up API and frontend.
- Migrate publishers and ingest only after storage and Redis behavior are validated.
- Keep
mevnode_botas rollback target until the new stack is stable.
8. Add migration validation checks
- Host-level: memory, swap, disk, inode, and load checks.
- Agave-level: slot lag, RPC health, and validator memory trend.
- Trade-level: pod readiness, Redis health, Hasura health, API health, publisher health, ingest success.
- End-to-end: UI, API, DLOB data freshness, and bot-related paths.
Immediate Next Actions
- Map the current live
trade-stagingstate into target Git repositories and IaC paths. - Start the first canonical inventory pass into
trade-gitopsandtrade-host-iac. - Inspect publisher deployment/env and confirm whether Redis should be single-instance or cluster mode.
- Inspect Redis server configuration and topology in
trade-staging. - Measure real Postgres database size inside the trade PVC.
- Draft target disk placement for trade data on
mevnode. - Decide minimum safe RAM headroom to keep free for
agave-validator.
Open Questions
- Which repositories belong in the new Gitea organization, and which existing repos should stay only as upstream history?
- Do we need a clean cutover into a new organization, or only a temporary compatibility layer with the current repos?
- Is the intended Redis topology for trade a single Redis instance or Redis Cluster?
- Which trade datasets are required to migrate, and which can be rebuilt from source?
- Where exactly on
mevnodeshould persistent trade storage live? - What is the acceptable validator memory floor and swap ceiling during trade rollout?
Chapter 2
01. Define hard database and cache constraints
- In progress.
- Storage limits for
PostgresandRedismust be enforced by the real storage backend, not only declared in Kubernetes manifests. - In core
Postgres, the hard stop for total on-disk growth should still be treated as the filesystem quota; PostgreSQL itself should add earlier guardrails before that limit is reached. Postgresmust not be able to grow beyond the actually allocated disk budget.Postgresmust have explicit CPU and memory requests/limits consistent with host headroom.Postgresshould usetemp_file_limitso large sorts and hashes fail before they can consume uncontrolled temporary disk space.Postgresmust have operating thresholds for free disk, WAL growth, autovacuum, and table/index bloat.Postgresshould use boundedmax_wal_sizeand, if replication slots are present, boundedmax_slot_wal_keep_size, while treating WAL limits as early control signals rather than the final hard stop.Postgresshould have retention and pruning policies for time-series data so historical chunks are dropped before storage pressure reaches the OS quota.Postgresshould have an operational read-only switch prepared, so new write traffic can be stopped before the filesystem quota is actually hit.Redismust have explicit memory limits and an intentional eviction/persistence policy.Redismust use explicitmaxmemorybelow the container memory limit and must set an intentionalmaxmemory-policyinstead of relying on the default unlimited dataset behavior.Redisshould setmaxmemory-clientsso client buffers cannot grow without bound under connection pressure.Redismust not be allowed to consume unbounded RAM or disk on the target host.- Database and cache capacity alerts must fire before any hard limit is reached, not after the filesystem is already effectively full.
- Full-disk and out-of-memory behavior for
PostgresandRedismust be tested before application cutover. - Done on
2026-04-12: installedPostgreSQL 16andRedis 7onsoland started both services. - Done on
2026-04-12: movedPostgresdata directory to/srv/trade/postgres/16/main. - Done on
2026-04-12: appliedPostgresguardrails onsol:shared_buffers=2GB,effective_cache_size=8GB,work_mem=16MB,maintenance_work_mem=512MB,temp_file_limit=8GB,max_wal_size=4GB,min_wal_size=512MB,max_slot_wal_keep_size=2GB,idle_in_transaction_session_timeout=10min. - Done on
2026-04-12: prepared and tested operationalPostgresread-only switch at/usr/local/bin/trade-postgres-readonly. - Done on
2026-04-12: appliedRedisguardrails onsol:maxmemory=384MB,maxmemory-clients=64MB,maxmemory-policy=noeviction,save \"\",appendonly no. - Done on
2026-04-12: initializedRedisas a single-node cluster with all16384slots assigned andcluster_state: ok.
02.
Reconstruct the current live mevnode_bot and
trade-staging state into canonical Git/IaC in a new Gitea
organization
- Live
k3sis the input for reconstruction, not the final source of truth. - Split application repositories, Kubernetes manifests, and host IaC into separate repositories.
- Prefer a new Gitea organization over long-lived migration branches in existing repositories.
- Recommended target organization name:
trade-next. - Target repository set in
trade-next:trade-api,trade-frontend,trade-ingestor,trade-bot,trade-dlob,trade-gitops,trade-host-iac,trade-docs. - Keep the current repository names where possible and change only the organization to reduce migration risk.
- Use
trade-gitopsas the GitOps source fork3smanifests andtrade-host-iacfor host-level IaC, SSH, storage, and operations. - Record every current live drift before moving workloads.
- Done on
2026-04-12:trade-nextwas created and the target repositories now exist. - Done on
2026-04-12: the live component boundary map was written totrade-next/trade-docs/doc/k3s-component-repo-map.md. - Next for item
02: reconstruct the livetrade-stagingobjects, overlays, secrets wiring, and host-specific drift intotrade-gitopsandtrade-host-iac.
03.
Fix dlob-publisher-hot startup, readiness, and
liveness
- Pending.
04.
Fix dlob-publisher-all startup, readiness, and
liveness
- Pending.
05. Confirm whether trade Redis is meant to run as a single instance or as Redis Cluster
- Pending.
06. Align publisher Redis client mode with the actual deployed Redis topology
- Pending.
07.
Restore or recreate the missing gitea-registry image pull
secret
- Pending.
08. Verify that image pulls work after node reboot and on a cold start
- Pending.
09. Measure the real PostgreSQL data size inside the current trade PVC
- Pending.
10.
Explain why the declared 20Gi PVC currently consumes about
585 GiB on disk
- Pending.
11. Decide which trade datasets must be migrated and which can be rebuilt
- Pending.
12.
Define a dedicated storage path or device for trade data on
mevnode
- Done on
2026-04-12. - Current preferred target layout on
solis to place trade persistent storage on the root NVMe filesystem/, because validator ledger and validator state already have their own dedicated devices. - Do not place
PostgresorRedison/data/ledgeror/data/state. - Reserve a dedicated
1 TiBstorage budget forPostgresonsolas the first target sizing forR001. - Reserve a separate small storage budget for
Redisonsol, initially2 GiBunless persistence requirements change. - Prefer a dedicated path such as
/srv/trade/postgresand/srv/trade/redisover implicit storage under/var/lib/rancher/k3s/storage. - On the current
btrfsroot filesystem, the preferred implementation path is a dedicated subvolume plus enforcedqgroupquota for each persistent component. - Done on
2026-04-12: createdbtrfssubvolumes onsolat/srv/trade,/srv/trade/postgres, and/srv/trade/redis.
13.
Define hard storage quotas and monitoring for trade data on
mevnode
- In progress.
- Use enforceable limits, not only declarative PVC sizes without backend enforcement.
- Add warning and critical thresholds for disk usage, inode usage, and PVC growth.
- Target hard quota for
Postgresonsol:1 TiB. - Target hard quota for
Redisonsol:2 GiBunless later requirements justify persistence growth. - Keep enough unallocated free space on the root NVMe for the OS,
k3s, container images, logs, and rollback operations after trade quotas are reserved. - Done on
2026-04-12: enabledbtrfsqgroups on/atsoland applied hard quotas of1 TiBto/srv/trade/postgresand2 GiBto/srv/trade/redis.
14.
Define CPU and memory reservations that protect
agave-validator
- Pending.
15.
Define the minimum safe free RAM headroom on mevnode during
rollout
- Pending.
16.
Define the maximum acceptable swap usage on mevnode during
rollout
- Pending.
17. Install and
bootstrap k3s on mevnode
- Pending.
- Define the target
k3sinstallation model onmevnode. - Define the target storage class, host paths, and bootstrap dependencies needed before application rollout.
- Verify that the base cluster, registry access, namespaces, and secret distribution work before any application components are installed.
18. Install
Postgres and Redis on
mevnode
- Done on
2026-04-12for host-level bootstrap. - Bring up the data plane first:
PostgresandRedis. - Validate persistence, storage placement, service discovery, restart behavior, and cold start.
- Validate that
Postgreslands on the dedicated1 TiBtrade storage area and thatRedislands on its separate small quota before application rollout. - Do not install application components until
PostgresandRedisare healthy. - Done on
2026-04-12:PostgresandRedisare running onsolas host services, both bound to127.0.0.1at this stage. - Done on
2026-04-12: validatedPostgreson/srv/trade/postgres/16/main, validatedRedison/srv/trade/redis, and confirmedbtrfsquotas are active for both paths.
19.
Prepare a minimal canary namespace for trade on
mevnode
- Pending.
20. Bring up
Hasura, API, and frontend on mevnode
- Pending.
- Bring up
Hasuraonly afterPostgresis healthy. - Bring up API and frontend only after
Hasura,Postgres, andRedisare healthy.
21. Migrate publishers and ingest only after storage and Redis validation pass
- Pending.
22. Validate host health, agave health, trade health, and end-to-end flow after rollout
- Pending.
23.
Keep mevnode_bot as a rollback target until the new stack
is stable
- Pending.
Chapter 3
- ☐
- Define hard database and cache constraints.
- ☐
- Reconstruct the current live
mevnode_botandtrade-stagingstate into canonical Git/IaC in a new Gitea organization.
- Reconstruct the current live
- ☐
- Fix
dlob-publisher-hotstartup, readiness, and liveness.
- Fix
- ☐
- Fix
dlob-publisher-allstartup, readiness, and liveness.
- Fix
- ☐
- Confirm whether trade Redis is meant to run as a single instance or as Redis Cluster.
- ☐
- Align publisher Redis client mode with the actual deployed Redis topology.
- ☐
- Restore or recreate the missing
gitea-registryimage pull secret.
- Restore or recreate the missing
- ☐
- Verify that image pulls work after node reboot and on a cold start.
- ☐
- Measure the real PostgreSQL data size inside the current trade PVC.
- ☐
- Explain why the declared
20GiPVC currently consumes about585 GiBon disk.
- Explain why the declared
- ☐
- Decide which trade datasets must be migrated and which can be rebuilt.
- ☒
- Define a dedicated storage path or device for trade data on
mevnode.
- Define a dedicated storage path or device for trade data on
- ☐
- Define hard storage quotas and monitoring for trade data on
mevnode.
- Define hard storage quotas and monitoring for trade data on
- ☐
- Define CPU and memory reservations that protect
agave-validator.
- Define CPU and memory reservations that protect
- ☐
- Define the minimum safe free RAM headroom on
mevnodeduring rollout.
- Define the minimum safe free RAM headroom on
- ☐
- Define the maximum acceptable swap usage on
mevnodeduring rollout.
- Define the maximum acceptable swap usage on
- ☐
- Install and bootstrap
k3sonmevnode.
- Install and bootstrap
- ☒
- Install
PostgresandRedisonmevnode.
- Install
- ☐
- Prepare a minimal canary namespace for trade on
mevnode.
- Prepare a minimal canary namespace for trade on
- ☐
- Bring up
Hasura, API, and frontend onmevnode.
- Bring up
- ☐
- Migrate publishers and ingest only after storage and Redis validation pass.
- ☐
- Validate host health, agave health, trade health, and end-to-end flow after rollout.
- ☐
- Keep
mevnode_botas a rollback target until the new stack is stable.
- Keep