tpu-inference/k8s: the TPU workload launcher - #467
Draft
theminghuang wants to merge 1 commit into
Draft
Conversation
Collaborator
Author
|
This change is part of the following stack: Change managed by git-spice. |
This was referenced Aug 11, 2026
theminghuang
changed the base branch from
kube-ci-infra
to
feature/tpu-multikueue-k8s-infra
August 11, 2026 08:16
theminghuang
force-pushed
the
kube-launcher-final
branch
4 times, most recently
from
August 11, 2026 17:04
2e58524 to
414a29e
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 11, 2026 19:50
d41056b to
9f72f1f
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
10 times, most recently
from
August 11, 2026 21:37
9b841ee to
dff405d
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 11, 2026 21:43
4de0fe7 to
0fc31af
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
2 times, most recently
from
August 11, 2026 22:17
0ad44a9 to
f2573ae
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 11, 2026 22:42
0fc31af to
81ecf54
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
from
August 11, 2026 22:42
f2573ae to
04cf491
Compare
theminghuang
marked this pull request as draft
August 11, 2026 22:53
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 11, 2026 22:57
81ecf54 to
a6ea640
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
8 times, most recently
from
August 14, 2026 04:57
0b94958 to
1c92d1b
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 14, 2026 05:01
6cd16c1 to
3255eb4
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
8 times, most recently
from
August 18, 2026 05:55
689d5dc to
57235d3
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 18, 2026 05:55
9cef2c8 to
c4f616a
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 18, 2026 07:01
c4f616a to
cf308ce
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
from
August 18, 2026 07:01
57235d3 to
2ca8857
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 19, 2026 00:27
0e0bfb6 to
3d1f0cd
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
from
August 19, 2026 00:27
2ca8857 to
cbaadb4
Compare
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 19, 2026 00:35
3d1f0cd to
128ca12
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
2 times, most recently
from
August 19, 2026 03:44
8f10cc6 to
d7afa9c
Compare
Every TPU step runs /opt/launcher/launch --profile <name> -- <command> in a
CPU-only agent pod. The launcher renders the workload manifest that lives in
the repo under test - a Job, or a JobSet for a slice across hosts - fills in
chip count, topology, node labels and queue from a profile registry
generated here, forwards the step's BUILDKITE_* environment plus a named
list, submits it to the profile's Kueue queue, waits for admission (as long
as the total budget allows less one full run), polls the pod logs back over
Connect Gateway, mirrors the container's exit code, and files the log as a
Buildkite artifact. Cancellation is a SIGTERM that uploads and deletes the
workload; an ownerReference from the launcher pod covers the cases that
never deliver one.
It exists because agent-stack-k8s can only create a plain Job, and because
the agent must stay outside the Kueue workload: a preempted or disrupted run
pauses the step log and resumes rather than failing the build, and an
admission wait holds a CPU pod, not a TPU.
The manifest is untrusted input and is validated: the Kueue queue label must
be the profile's, serviceAccountName must be one the cluster publishes for
workloads, the image must come from allowed_image_repos (the CI registry),
and there must be a container named workload. PodSecurity baseline on the
namespace covers the rest.
A manifest's ${NAME} placeholders are resolved in two passes, because the two
sides of the contract fail differently. The profile's nine names are required
and applied with substitute(), which raises if the manifest still holds an
unresolved name; the step's environment is optional and applied first with
safe_substitute(), so a manifest may reference ${BUILDKITE_COMMIT} or
${GANG_SIZE} without the launcher knowing about them. safe_substitute() alone
would leave a name nothing provided as the literal text "${NAME}", which
Kubernetes rejects nearly everywhere it can land - it is not a valid
quantity, integer or label value - but takes happily in a plain string like
an env value:, and the test then reads "${CHIPS}" as its chip count. One
consequence worth knowing: substitute() also rejects a lone `$` anywhere in
the file, comments included. Write `$$` for a literal one.
${ACCELERATOR_LABEL}, ${TOPOLOGY} and ${CHIPS} must additionally appear in
the manifest at all. The split above catches a name nothing provides, but not
a manifest that never mentions one, and placement is where that matters: a
manifest with a hardcoded or missing nodeSelector still requests TPU chips,
so Kueue admits it against the profile's quota and the pod takes any pool
with room - possibly a different topology than the profile promised. Nothing
rejects it and the run looks normal.
Cluster-side: 06-launcher.yaml (ServiceAccount, Role, the launcher script
and profile registry as ConfigMaps, the PodTemplate agent-stack-k8s uses)
and a per-worker ClusterRole for reading pod logs (07-launcher-rbac.yaml),
Connect Gateway reader and scoped Test Engine secret access for the launcher
identity, and the agent deadline derived from tpu_total_max_seconds. The
generator gains the launcher outputs, the profile registry (with the gcsfuse
file-cache size per machine type and hosts per slice), and a check that a
multi-host lane's quota is whole slices.
theminghuang
force-pushed
the
feature/tpu-multikueue-k8s-infra
branch
from
August 26, 2026 23:09
128ca12 to
ac00311
Compare
theminghuang
force-pushed
the
kube-launcher-final
branch
from
August 26, 2026 23:09
d7afa9c to
1d209e8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The TPU workload launcher, on top of #438. Every TPU step in tpu-inference#3377 runs it:
What it does
In a CPU-only agent pod, the launcher renders the workload manifest that lives in the repo under test - a
Job, or aJobSetfor a slice across hosts - fills in chip count, topology, node labels and queue from the profile registry generated here, forwards the step'sBUILDKITE_*environment plus a named list, submits it to the profile's Kueue queue, waits for admission for as long as the total budget allows less one full run, polls the pod logs back over Connect Gateway (short requests, not a stream; keyed by owning Job so a preempt-and-resume reads as one log), mirrors the container's exit code, and files the log as a Buildkite artifact. Cancellation is a SIGTERM that uploads and deletes the workload; an ownerReference from the launcher pod covers the cases that never deliver one.Why
agent-stack-k8s can only create a plain
batch/v1Job, and the agent must stay outside the Kueue workload: a preempted or disrupted run then pauses the step log and resumes rather than failing the build, and an admission wait holds a CPU pod rather than a TPU. Routing single-host work through the same path keeps one code path.The split of responsibility is the point: the profile registry is generated from the same tfvars that build the node pools, so placement cannot drift from the queues; the manifest lives in the repo under test, so the shape of a job is a PR rather than an infrastructure change.
The manifest is untrusted input
Validated before submission: the Kueue queue label must be the profile's (quota cannot be side-stepped),
serviceAccountNamemust be one the cluster publishes for workloads (tpu-workload; a workload may not run as the launcher and submit further work), the image must come fromallowed_image_repos(the CI registry, set in tfvars), and there must be a container namedworkload. PodSecuritybaselineon the namespace covers privileged, hostPath and host networking.Files
kueue/launcher/launch.py- the launcher.kueue/templates/launcher.yaml.tpl->generated/manager/06-launcher.yaml: ServiceAccount, Role, the launcher and the profile registry as ConfigMaps, and the PodTemplate agent-stack-k8s runs it in.launcher_rbac_worker.yaml.tpl->07-launcher-rbac.yaml: a per-worker ClusterRole for pod logs.scripts/generate_manifests.py- the launcher outputs; the profile registry (queue, chips, hosts, topology, accelerator label, max runtime, gcsfuse file-cache size per machine type); a check that a multi-host lane's quota is whole slices.TPU_WORKER_HOSTNAMESinjected, one 16-device mesh, vLLM TP=16 served) as well as the single-host suite; the multi-host profile itself is not declared in this stack yet.iam.tf- Connect Gateway reader for the launcher identity on manager and workers; secret-scoped access to the Test Engine token.buildkite.tf,variables.tf- the agent deadline derived fromtpu_total_max_seconds; the analytics-token secret ids.prod.auto.tfvars-allowed_image_repos.