Skip to content

Commit 7668445

Browse files
committed
Merge remote-tracking branch 'origin/master' into pr_docs_py310
2 parents 3891af1 + 91d59f6 commit 7668445

39 files changed

Lines changed: 2616 additions & 205 deletions

File tree

.github/workflows/build-artifacts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474
- name: Install dependencies
7575
run: uv sync --all-extras
7676
- name: Run pyright
77-
uses: jakebailey/pyright-action@v2
77+
uses: jakebailey/pyright-action@v3
7878
with:
7979
pylance-version: latest-release
8080
- name: Download frontend build

mkdocs/docs/concepts/backends.md

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,26 @@ projects:
918918

919919
</div>
920920

921+
### JarvisLabs
922+
923+
Log into your [JarvisLabs](https://cloud.jarvislabs.ai/) account and create an API key.
924+
925+
Then, go ahead and configure the backend:
926+
927+
<div editor-title="~/.dstack/server/config.yml">
928+
929+
```yaml
930+
projects:
931+
- name: main
932+
backends:
933+
- type: jarvislabs
934+
creds:
935+
type: api_key
936+
api_key: ...
937+
```
938+
939+
</div>
940+
921941
### CloudRift
922942

923943
Log into your [CloudRift](https://console.cloudrift.ai/) console, click `API Keys` in the sidebar and click the button to create a new API key.
@@ -1051,9 +1071,9 @@ Compared to [VM-based](#vm-based) backends, they offer less fine-grained control
10511071

10521072
### Kubernetes
10531073

1054-
Regardless of whether it’s on-prem Kubernetes or managed, `dstack` can orchestrate container-based runs across your clusters.
1074+
Regardless of whether it’s on-prem Kubernetes or managed, `dstack` can orchestrate container-based runs across your clusters. A single `kubernetes` backend can manage one or many clusters — each cluster is selected via a kubeconfig [context](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context).
10551075

1056-
To use the `kubernetes` backend with `dstack`, you need to configure it with the path to the kubeconfig file, the IP address of any node in the cluster, and the port that `dstack` will use for proxying SSH traffic.
1076+
The recommended way is to enable clusters explicitly via the `contexts` property:
10571077

10581078
<div editor-title="~/.dstack/server/config.yml">
10591079

@@ -1066,22 +1086,48 @@ projects:
10661086
kubeconfig:
10671087
filename: ~/.kube/config
10681088
1069-
proxy_jump:
1070-
hostname: 204.12.171.137
1071-
port: 32000
1089+
contexts:
1090+
- name: gpu-cluster-a
1091+
- name: gpu-cluster-b
10721092
```
10731093

10741094
</div>
10751095

10761096
!!! info "Proxy jump"
1077-
To allow the `dstack` server and CLI to access runs via SSH, `dstack` requires a node that acts as a jump host to proxy SSH traffic into containers.
1097+
To allow the `dstack` server and CLI to access runs via SSH, `dstack` uses a node in each cluster as a jump host to proxy SSH traffic into containers. No additional setup is required — `dstack` configures and manages the proxy automatically.
1098+
1099+
By default, `dstack` autodetects the jump host:
1100+
1101+
- `hostname` — picks the `ExternalIP` of the jump pod's node, or a random node `ExternalIP` from the cluster if the jump pod's node has none. If no node in the cluster has an `ExternalIP`, provisioning fails and you must set `hostname` explicitly.
1102+
- `port` — Kubernetes allocates a port from the cluster's NodePort range.
1103+
1104+
Set `proxy_jump.hostname` and `proxy_jump.port` per context to override autodetection — useful when nodes lack `ExternalIP`s, or when you want a stable, firewall-friendly port:
1105+
1106+
```yaml
1107+
contexts:
1108+
- name: gpu-cluster-a
1109+
proxy_jump:
1110+
hostname: 204.12.171.137
1111+
port: 32000
1112+
```
1113+
1114+
Both fields are independent — you can set just one.
10781115

1079-
To configure this node, specify `hostname` and `port` under the `proxy_jump` property:
1116+
The jump host can be a GPU node or a CPU-only node — it makes no difference. The only requirement is that both the `dstack` server and CLI can reach `hostname:port`.
10801117

1081-
- `hostname` — the IP address of any cluster node selected as the jump host. Both the `dstack` server and CLI must be able to reach it. This node can be either a GPU node or a CPU-only node — it makes no difference.
1082-
- `port` — any accessible port on that node, which `dstack` uses to forward SSH traffic.
1118+
!!! info "Region and namespace"
1119+
Each enabled context becomes its own `dstack` region, named after the context. When creating a `dstack` [volume](volumes.md) or [gateway](gateways.md), the `region` field selects which cluster the resource is provisioned in.
10831120

1084-
No additional setup is required — `dstack` configures and manages the proxy automatically.
1121+
The namespace `dstack` uses for managed resources is taken from each kubeconfig context's `namespace` property, defaulting to `default` if not set:
1122+
1123+
```yaml
1124+
contexts:
1125+
- name: gpu-cluster-a
1126+
context:
1127+
cluster: gpu-cluster-a
1128+
user: kubernetes-admin
1129+
namespace: dstack
1130+
```
10851131

10861132
??? info "User interface"
10871133
If you are configuring the `kubernetes` backend on the [project settings page](projects.md#backends),
@@ -1091,17 +1137,16 @@ projects:
10911137

10921138
```yaml
10931139
type: kubernetes
1094-
1140+
10951141
kubeconfig:
10961142
data: |
10971143
apiVersion: v1
10981144
kind: Config
1099-
current-context: kubernetes-admin@gpu-cluster
11001145
11011146
clusters:
1102-
- name: gpu-cluster
1147+
- name: gpu-cluster-a
11031148
cluster:
1104-
server: https://gpu-cluster.internal.example.com:6443
1149+
server: https://gpu-cluster-a.internal.example.com:6443
11051150
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...LS0tLQo=
11061151
11071152
users:
@@ -1111,17 +1156,50 @@ projects:
11111156
client-key-data: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...LS0tLQo=
11121157
11131158
contexts:
1114-
- name: kubernetes-admin@gpu-cluster
1159+
- name: gpu-cluster-a
11151160
context:
1116-
cluster: gpu-cluster
1161+
cluster: gpu-cluster-a
11171162
user: kubernetes-admin
1118-
1119-
proxy_jump:
1120-
hostname: 204.12.171.137
1121-
port: 32000
1163+
namespace: dstack
1164+
1165+
contexts:
1166+
- name: gpu-cluster-a
1167+
proxy_jump:
1168+
hostname: 204.12.171.137
1169+
port: 32000
1170+
```
1171+
1172+
</div>
1173+
1174+
??? warning "Legacy configuration (without `contexts`)"
1175+
If `contexts` is not set, `dstack` falls back to using the kubeconfig's `current-context` as the only cluster, and the top-level `proxy_jump` and `namespace` properties apply:
1176+
1177+
<div editor-title="~/.dstack/server/config.yml">
1178+
1179+
```yaml
1180+
projects:
1181+
- name: main
1182+
backends:
1183+
- type: kubernetes
1184+
1185+
kubeconfig:
1186+
filename: ~/.kube/config
1187+
1188+
namespace: dstack
1189+
1190+
proxy_jump:
1191+
hostname: 204.12.171.137
1192+
port: 32000
11221193
```
11231194

11241195
</div>
1196+
1197+
This mode is not recommended and may be deprecated and removed in the future. It also has a namespace-handling quirk: the top-level `namespace` property **overrides** the kubeconfig context's namespace (defaulting to `default` if not set in the config), unlike the `contexts` mode where the kubeconfig is authoritative. A warning is logged when the two disagree. To prepare for a possible future change, set the same value in both your kubeconfig context and the backend config.
1198+
1199+
With this configuration, the cluster's region is an empty string. When creating a `dstack` volume or gateway, set `region: ''` explicitly in the configuration.
1200+
1201+
!!! warning "Migrating from legacy to `contexts`"
1202+
Switching an existing backend from the legacy mode to `contexts` is not transparent for already-provisioned resources: their region changes from an empty string to the context name, so `dstack` can no longer terminate them. Terminate all jobs, gateways, and volumes managed by the backend before changing the configuration.
11251203

11261204
??? info "Required operators"
11271205
=== "NVIDIA"
@@ -1149,7 +1227,7 @@ projects:
11491227
--8<-- "snippets/kubernetes/dstack-backend-role.yaml"
11501228
```
11511229

1152-
Ensure you've created a ClusterRoleBinding to grant the role to the user or the service account you're using.
1230+
Ensure you've created a ClusterRoleBinding and RoleBinding to grant the roles to the user or the service account you're using.
11531231

11541232
??? info "Resources and offers"
11551233
If you use ranges with [`resources`](../concepts/tasks.md#resources) (e.g. `gpu: 1..8` or `memory: 64GB..`) in fleet or run configurations, other backends collect and try all offers that satisfy the range.

mkdocs/docs/examples/inference/dynamo.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Dynamo
3-
description: Deploying zai-org/GLM-4.5-Air-FP8 using NVIDIA Dynamo
2+
title: NVIDIA Dynamo
3+
description: Deploying zai-org/GLM-4.5-Air-FP8 using NVIDIA Dynamo with Prefill-Decode disaggregation.
44
---
55

66
# Dynamo

mkdocs/docs/reference/dstack.yml/volume.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,5 @@ The `volume` configuration type allows creating, registering, and updating [volu
6060
show_root_heading: false
6161
backend:
6262
required: true
63+
region:
64+
required: true

mkdocs/docs/reference/server/config.yml.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,18 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
278278
yq -o=json ~/.kube/config | jq -c | jq -R
279279
```
280280

281+
###### `projects[n].backends[type=kubernetes].contexts[n]` { #kubernetes-contexts data-toc-label="contexts" }
282+
283+
#SCHEMA# dstack._internal.core.backends.kubernetes.models.KubernetesContextConfig
284+
overrides:
285+
show_root_heading: false
286+
287+
###### `projects[n].backends[type=kubernetes].contexts[n].proxy_jump` { #kubernetes-contexts-proxy_jump data-toc-label="proxy_jump" }
288+
289+
#SCHEMA# dstack._internal.core.backends.kubernetes.models.KubernetesProxyJumpConfig
290+
overrides:
291+
show_root_heading: false
292+
281293
###### `projects[n].backends[type=kubernetes].proxy_jump` { #kubernetes-proxy_jump data-toc-label="proxy_jump" }
282294

283295
#SCHEMA# dstack._internal.core.backends.kubernetes.models.KubernetesProxyJumpConfig
@@ -369,6 +381,23 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
369381
type:
370382
required: true
371383

384+
##### `projects[n].backends[type=jarvislabs]` { #jarvislabs data-toc-label="jarvislabs" }
385+
386+
#SCHEMA# dstack._internal.core.backends.jarvislabs.models.JarvisLabsBackendFileConfigWithCreds
387+
overrides:
388+
show_root_heading: false
389+
type:
390+
required: true
391+
item_id_prefix: jarvislabs-
392+
393+
###### `projects[n].backends[type=jarvislabs].creds` { #jarvislabs-creds data-toc-label="creds" }
394+
395+
#SCHEMA# dstack._internal.core.backends.jarvislabs.models.JarvisLabsAPIKeyCreds
396+
overrides:
397+
show_root_heading: false
398+
type:
399+
required: true
400+
372401
##### `projects[n].backends[type=cloudrift]` { #cloudrift data-toc-label="cloudrift" }
373402

374403
#SCHEMA# dstack._internal.core.backends.cloudrift.models.CloudRiftBackendConfigWithCreds

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ dependencies = [
3232
"python-multipart>=0.0.16",
3333
"filelock",
3434
"psutil",
35-
"gpuhunt==0.1.21",
35+
"gpuhunt==0.1.22",
3636
"argcomplete>=3.5.0",
3737
"ignore-python>=0.2.0",
3838
"orjson",
@@ -152,7 +152,7 @@ dev = [
152152
"testcontainers>=4.9.2",
153153
"pytest-xdist>=3.6.1",
154154
"pyinstrument>=5.0.0",
155-
"kubernetes-stubs-elephant-fork",
155+
"kubernetes-stubs-elephant-fork>=35.0.0.post3",
156156
{include-group = "docs"},
157157
]
158158
docs = [

scripts/merge_kubeconfigs.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
if [ ${#} -lt 2 ]; then
5+
echo "usage: $(basename "${0}") PATH1 PATH2 [PATH3 ...]" >&2
6+
exit 1
7+
fi
8+
9+
# Windows is not supported; on Windows a path separator is ';', not ':'
10+
KUBECONFIG=$(IFS=':'; echo "${*}")
11+
export KUBECONFIG
12+
kubectl config view --raw --flatten | grep -Ev '^current-context: '

scripts/setup_kubernetes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ def generate_kubeconfig(
201201
service_account_token: str,
202202
) -> str:
203203
logging.info("generating kubeconfig")
204-
kubeconfig_content = kubectl.call("config", "view", "--minify", "--raw", capture_stdout=True)
204+
kubeconfig_content = kubectl.call(
205+
"config", "view", "--minify", "--raw", "--flatten", capture_stdout=True
206+
)
205207
with tempfile.NamedTemporaryFile("w+") as f:
206208
f.write(kubeconfig_content)
207209
f.flush()

src/dstack/_internal/core/backends/configurators.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,15 @@
8787
except ImportError:
8888
pass
8989

90+
try:
91+
from dstack._internal.core.backends.jarvislabs.configurator import (
92+
JarvisLabsConfigurator,
93+
)
94+
95+
_CONFIGURATOR_CLASSES.append(JarvisLabsConfigurator)
96+
except ImportError:
97+
pass
98+
9099
try:
91100
from dstack._internal.core.backends.kubernetes.configurator import (
92101
KubernetesConfigurator,

src/dstack/_internal/core/backends/jarvislabs/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)