Skip to content

Run ate-api-server in HA mode#538

Open
Julian Gutierrez Oschmann (juli4n) wants to merge 5 commits into
agent-substrate:mainfrom
juli4n:replicas
Open

Run ate-api-server in HA mode#538
Julian Gutierrez Oschmann (juli4n) wants to merge 5 commits into
agent-substrate:mainfrom
juli4n:replicas

Conversation

@juli4n

@juli4n Julian Gutierrez Oschmann (juli4n) commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

This PR updates ate-apiserver deployment to run with 2 replicas.

Some changes were needed to enable this, and are separate in a series of independent commits.

  • Refactor port-forwarding logic: A few components were doing this, in different ways, but most importantly, the kubectl-ate binary was not checking for Pod readiness. With multiple replicas, there is a risk of picking a not ready Pod and failing the request.
  • Delete agent-secret demo: This demo is broken now that ate-apiserver requires authn. I don't see an easy way to fix it, and the demo was not really adding much value.
  • Change mock volume plugin so that it doesn't require global in-memory state: This just doesn't work when running multiple replicas. Luckily, we don't need to keep any state, so change the mock to be a no-op.
  • Enable round-robin load balancing across replicas: Switch gRPC clients to keep a channel with a sub-channel per replica, and then round robin traffic across them. The current approach of connecting to the Service ClusterIP VIP doesn't really load balance as gRPC is session based and keeps connection open for long time. The fix is to switch to a headless service and let the gRPC client create the connections by discovering the DNS A records.
  • Set ate-apiserver deployment to replicas: 2: Additionally, configure the deployment rolling update so that we update one instance at a time (make before break) and PDB so that we never lose more than one replica during eviction events.

Part of #181

We have a few places where we need to do port-forwarding
to Pods behind a service. Consolidate this logic under `internal/portforward`.
This demo is broken now that we require authn to ate-apiserver. The
fix is not trivial as it requires the actor to be able to authenticate, plus
it doesn't really adds much.
Holding state in memory means that we cannot scale to more than one replica.
Change the mock implementation to be stateless. It doesn't really need to
track state anyways.
A ClusterIP Service load-balances per TCP connection, not per RPC,
but gRPC multiplexes many RPCs over one long-lived HTTP/2 connection,
so a client that dials once (e.g. ate-controller, atenet) gets pinned to
whichever single pod that connection was routed to, for its entire
lifetime.

Make the "api" Service headless so DNS returns one A record per ready
pod instead of a VIP, and switch clients to a "dns:///" dial target
with the round_robin load balancing policy so RPCs actually spread
across every replica.
This is the minimum HA setup we can run. Configure rolling update
policy to recreate replicas one by one, make before break.

Also configure PDBs so that we never lose more than one replica during
pod eviction events.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant