Skip to content

atenet: cap Envoy worker concurrency - #1199

Open
eliranw wants to merge 2 commits into
agent-substrate:mainfrom
eliranw:eliranw/atenet-envoy-concurrency-configurable
Open

atenet: cap Envoy worker concurrency#1199
eliranw wants to merge 2 commits into
agent-substrate:mainfrom
eliranw:eliranw/atenet-envoy-concurrency-configurable

Conversation

@eliranw

@eliranw eliranw commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1193

With --concurrency unset, Envoy sizes its worker pool from the host CPU count, which cgroup limits do not constrain. On a many-core node it exhausts the default nofile during startup and both atenet pods crashloop, with the Go container beside each one healthy:

[warn] evutil_make_internal_pipe_: pipe: Too many open files
[err]  evsig_init_: socketpair: Too many open files

Each Envoy container now takes its worker count from a variable defaulting to 4, so a cluster can be resized without editing manifests:

- --concurrency
- "$(ENVOY_CONCURRENCY)"
env:
- name: ENVOY_CONCURRENCY
  value: "4"

hack/install-ate.sh gains --atenet-router-concurrency and --atenet-egress-concurrency, following the shape of --podcert-workers-per-signer. They are separate flags because Envoy pins a connection to a worker for its lifetime, and egress holds CONNECT tunnels open far longer than the router's request/response connections. atenet-egress-with-sdsmint.yaml carries a third Envoy that no overlay reaches, so it is changed directly.

No CPU requests or limits are added. #1193 lists deriving the count from a cpu request as the other option; the measurements against it are in a comment there.

Tested on kind: full install, both proxies healthy, and the flags verified against Envoy's own server.concurrency. Whether 4 is the right number is still open, and #665 would answer it.

Known gap

--atenet-router=agentgateway runs no Envoy, so this does nothing there. agentgateway sizes its tokio runtime from num_cpus and needs its own change.

  • Tests pass
  • Appropriate changes to documentation are included in the PR

With --concurrency unset Envoy sizes its worker pool from the host cpu
count. Cgroup limits do not constrain that count, so on a many-core node
Envoy opens more sockets than the default nofile allows and dies during
startup with "Too many open files", leaving atenet-router and atenet-egress
in a crashloop while the Go container beside each one stays healthy.

Four workers suits both proxies today and, unlike the default, does not
change with the machine underneath.

Each proxy carries its own ENVOY_CONCURRENCY variable rather than sharing
one value, because the two have different connection shapes. The router
accepts short request/response connections. The egress gateway holds
CONNECT tunnels open for the life of an actor's connection, and Envoy pins
a connection to a worker for that lifetime, so a low count concentrates
long tunnels on few workers. They start equal and can diverge later.

Reading the count from a variable rather than a literal argument lets a
deployment be resized without editing manifests. Kubernetes expands $(VAR)
in command and args alike, so nothing wraps the entrypoint.

The flag and its value are separate argv entries. Envoy's parser rejects
--concurrency=N with "Couldn't find match for argument" and exits on a
usage dump, so the joined spelling used elsewhere in these manifests for
Go binaries does not carry over.

atenet-egress-with-sdsmint.yaml carries a third Envoy, selected by
--experimental-use-sdsmint rather than by an overlay, so it inherits
nothing from the other two and takes the same treatment directly.

Signed-off-by: Eliran Wolff <eliranw@nvidia.com>
Four worker threads suit the clusters we run today, but the right number
follows the machine and the traffic, and changing it meant editing
manifests.

The router and the egress gateway take separate flags because their
connection shapes differ: the router accepts short request/response
connections, while the egress gateway holds CONNECT tunnels open for the
life of an actor's connection. Both default to the manifest value.

The override edits the live Deployments rather than the rendered YAML
because the egress gateway is applied as a whole file rather than through
an overlay, so there is no single render path to parameterize. Both
Deployments run a Go container alongside Envoy, so the update is scoped
with --containers=envoy rather than defaulting to every container. Under
the agentgateway data plane there is no Envoy container and no variable to
read, and the override skips.

A value that differs from the manifest costs an extra rollout: the deploy
applies the manifest, resetting the variable, and the override then sets
it again. Requesting the manifest's own value, or passing no flag at all,
patches nothing. This matches how --podcert-workers-per-signer behaves.

Values are validated during argument parsing as well as at use, so a typo
fails before any cluster work rather than after the install has run.

Signed-off-by: Eliran Wolff <eliranw@nvidia.com>
@eliranw
eliranw force-pushed the eliranw/atenet-envoy-concurrency-configurable branch from a1de917 to 65f4e81 Compare August 25, 2026 18:41
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.

atenet: Envoy sizes its worker pool from the host CPU count

1 participant