dstack 0.19.31__py3-none-any.whl → 0.19.33__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/offer.py +1 -1
- dstack/_internal/cli/services/configurators/run.py +1 -5
- dstack/_internal/core/backends/aws/compute.py +8 -5
- dstack/_internal/core/backends/azure/compute.py +9 -6
- dstack/_internal/core/backends/base/compute.py +40 -17
- dstack/_internal/core/backends/base/offers.py +5 -1
- dstack/_internal/core/backends/datacrunch/compute.py +9 -6
- dstack/_internal/core/backends/gcp/compute.py +137 -7
- dstack/_internal/core/backends/gcp/models.py +7 -0
- dstack/_internal/core/backends/gcp/resources.py +87 -5
- dstack/_internal/core/backends/hotaisle/compute.py +30 -0
- dstack/_internal/core/backends/kubernetes/compute.py +218 -77
- dstack/_internal/core/backends/kubernetes/models.py +4 -2
- dstack/_internal/core/backends/nebius/compute.py +24 -6
- dstack/_internal/core/backends/nebius/configurator.py +15 -0
- dstack/_internal/core/backends/nebius/models.py +57 -5
- dstack/_internal/core/backends/nebius/resources.py +45 -2
- dstack/_internal/core/backends/oci/compute.py +9 -6
- dstack/_internal/core/backends/runpod/compute.py +10 -6
- dstack/_internal/core/backends/vastai/compute.py +3 -1
- dstack/_internal/core/backends/vastai/configurator.py +0 -1
- dstack/_internal/core/compatibility/runs.py +8 -0
- dstack/_internal/core/models/fleets.py +1 -1
- dstack/_internal/core/models/profiles.py +12 -5
- dstack/_internal/core/models/runs.py +3 -2
- dstack/_internal/core/models/users.py +10 -0
- dstack/_internal/core/services/configs/__init__.py +1 -0
- dstack/_internal/server/background/tasks/process_fleets.py +75 -17
- dstack/_internal/server/background/tasks/process_instances.py +6 -4
- dstack/_internal/server/background/tasks/process_running_jobs.py +1 -0
- dstack/_internal/server/background/tasks/process_runs.py +27 -23
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +63 -20
- dstack/_internal/server/migrations/versions/ff1d94f65b08_user_ssh_key.py +34 -0
- dstack/_internal/server/models.py +3 -0
- dstack/_internal/server/routers/runs.py +5 -1
- dstack/_internal/server/routers/users.py +14 -2
- dstack/_internal/server/services/runs.py +9 -4
- dstack/_internal/server/services/users.py +35 -2
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/main-720ce3a11140daa480cc.css +3 -0
- dstack/_internal/server/statics/{main-c51afa7f243e24d3e446.js → main-97c7e184573ca23f9fe4.js} +12218 -7625
- dstack/_internal/server/statics/{main-c51afa7f243e24d3e446.js.map → main-97c7e184573ca23f9fe4.js.map} +1 -1
- dstack/api/_public/__init__.py +9 -12
- dstack/api/_public/repos.py +0 -21
- dstack/api/_public/runs.py +64 -9
- dstack/api/server/_users.py +17 -2
- dstack/version.py +2 -2
- {dstack-0.19.31.dist-info → dstack-0.19.33.dist-info}/METADATA +12 -14
- {dstack-0.19.31.dist-info → dstack-0.19.33.dist-info}/RECORD +52 -51
- dstack/_internal/server/statics/main-56191fbfe77f49b251de.css +0 -3
- {dstack-0.19.31.dist-info → dstack-0.19.33.dist-info}/WHEEL +0 -0
- {dstack-0.19.31.dist-info → dstack-0.19.33.dist-info}/entry_points.txt +0 -0
- {dstack-0.19.31.dist-info → dstack-0.19.33.dist-info}/licenses/LICENSE.md +0 -0
dstack/api/_public/__init__.py
CHANGED
|
@@ -2,12 +2,11 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import dstack._internal.core.services.api_client as api_client_service
|
|
4
4
|
from dstack._internal.core.errors import ConfigurationError
|
|
5
|
-
from dstack._internal.core.services.configs import ConfigManager
|
|
6
5
|
from dstack._internal.utils.logging import get_logger
|
|
7
|
-
from dstack._internal.utils.path import PathLike
|
|
6
|
+
from dstack._internal.utils.path import PathLike as PathLike
|
|
8
7
|
from dstack.api._public.backends import BackendCollection
|
|
9
|
-
from dstack.api._public.repos import RepoCollection
|
|
10
|
-
from dstack.api._public.runs import RunCollection
|
|
8
|
+
from dstack.api._public.repos import RepoCollection
|
|
9
|
+
from dstack.api._public.runs import RunCollection, warn
|
|
11
10
|
from dstack.api.server import APIClient
|
|
12
11
|
|
|
13
12
|
logger = get_logger(__name__)
|
|
@@ -35,24 +34,24 @@ class Client:
|
|
|
35
34
|
# Args:
|
|
36
35
|
# api_client: low-level server API client
|
|
37
36
|
# project_name: project name used for runs
|
|
38
|
-
# ssh_identity_file:
|
|
37
|
+
# ssh_identity_file: deprecated and will be removed in 0.19.40
|
|
39
38
|
# """
|
|
40
39
|
self._client = api_client
|
|
41
40
|
self._project = project_name
|
|
42
41
|
self._repos = RepoCollection(api_client, project_name)
|
|
43
42
|
self._backends = BackendCollection(api_client, project_name)
|
|
44
43
|
self._runs = RunCollection(api_client, project_name, self)
|
|
45
|
-
if ssh_identity_file:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
if ssh_identity_file is not None:
|
|
45
|
+
warn(
|
|
46
|
+
"[code]ssh_identity_file[/code] in [code]Client[/code] is deprecated and ignored; will be removed"
|
|
47
|
+
" since 0.19.40"
|
|
48
|
+
)
|
|
49
49
|
|
|
50
50
|
@staticmethod
|
|
51
51
|
def from_config(
|
|
52
52
|
project_name: Optional[str] = None,
|
|
53
53
|
server_url: Optional[str] = None,
|
|
54
54
|
user_token: Optional[str] = None,
|
|
55
|
-
ssh_identity_file: Optional[PathLike] = None,
|
|
56
55
|
) -> "Client":
|
|
57
56
|
"""
|
|
58
57
|
Creates a Client using the default configuration from `~/.dstack/config.yml` if it exists.
|
|
@@ -61,7 +60,6 @@ class Client:
|
|
|
61
60
|
project_name: The name of the project. required if `server_url` and `user_token` are specified.
|
|
62
61
|
server_url: The dstack server URL (e.g. `http://localhost:3000/` or `https://sky.dstack.ai`).
|
|
63
62
|
user_token: The dstack user token.
|
|
64
|
-
ssh_identity_file: The private SSH key path for SSH tunneling.
|
|
65
63
|
|
|
66
64
|
Returns:
|
|
67
65
|
A client instance.
|
|
@@ -75,7 +73,6 @@ class Client:
|
|
|
75
73
|
return Client(
|
|
76
74
|
api_client=api_client,
|
|
77
75
|
project_name=project_name,
|
|
78
|
-
ssh_identity_file=ssh_identity_file,
|
|
79
76
|
)
|
|
80
77
|
|
|
81
78
|
@property
|
dstack/api/_public/repos.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
1
|
from typing import Literal, Optional, Union, overload
|
|
3
2
|
|
|
4
3
|
from git import InvalidGitRepositoryError
|
|
@@ -18,7 +17,6 @@ from dstack._internal.core.services.repos import (
|
|
|
18
17
|
get_repo_creds_and_default_branch,
|
|
19
18
|
load_repo,
|
|
20
19
|
)
|
|
21
|
-
from dstack._internal.utils.crypto import generate_rsa_key_pair
|
|
22
20
|
from dstack._internal.utils.logging import get_logger
|
|
23
21
|
from dstack._internal.utils.path import PathLike
|
|
24
22
|
from dstack.api.server import APIClient
|
|
@@ -209,22 +207,3 @@ class RepoCollection:
|
|
|
209
207
|
return method(self._project, repo_id)
|
|
210
208
|
except ResourceNotExistsError:
|
|
211
209
|
return None
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def get_ssh_keypair(key_path: Optional[PathLike], dstack_key_path: Path) -> str:
|
|
215
|
-
"""Returns a path to the private key"""
|
|
216
|
-
if key_path is not None:
|
|
217
|
-
key_path = Path(key_path).expanduser().resolve()
|
|
218
|
-
pub_key = (
|
|
219
|
-
key_path
|
|
220
|
-
if key_path.suffix == ".pub"
|
|
221
|
-
else key_path.with_suffix(key_path.suffix + ".pub")
|
|
222
|
-
)
|
|
223
|
-
private_key = pub_key.with_suffix("")
|
|
224
|
-
if pub_key.exists() and private_key.exists():
|
|
225
|
-
return str(private_key)
|
|
226
|
-
raise ConfigurationError(f"Make sure valid keypair exists: {private_key}(.pub)")
|
|
227
|
-
|
|
228
|
-
if not dstack_key_path.exists():
|
|
229
|
-
generate_rsa_key_pair(private_key_path=dstack_key_path)
|
|
230
|
-
return str(dstack_key_path)
|
dstack/api/_public/runs.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import base64
|
|
2
|
+
import hashlib
|
|
3
|
+
import os
|
|
2
4
|
import queue
|
|
3
5
|
import tempfile
|
|
4
6
|
import threading
|
|
@@ -15,6 +17,7 @@ from urllib.parse import urlparse
|
|
|
15
17
|
from websocket import WebSocketApp
|
|
16
18
|
|
|
17
19
|
import dstack.api as api
|
|
20
|
+
from dstack._internal.cli.utils.common import warn
|
|
18
21
|
from dstack._internal.core.consts import DSTACK_RUNNER_HTTP_PORT, DSTACK_RUNNER_SSH_PORT
|
|
19
22
|
from dstack._internal.core.errors import ClientError, ConfigurationError, ResourceNotExistsError
|
|
20
23
|
from dstack._internal.core.models.backends.base import BackendType
|
|
@@ -45,11 +48,14 @@ from dstack._internal.core.models.runs import (
|
|
|
45
48
|
get_service_port,
|
|
46
49
|
)
|
|
47
50
|
from dstack._internal.core.models.runs import Run as RunModel
|
|
51
|
+
from dstack._internal.core.models.users import UserWithCreds
|
|
52
|
+
from dstack._internal.core.services.configs import ConfigManager
|
|
48
53
|
from dstack._internal.core.services.logs import URLReplacer
|
|
49
54
|
from dstack._internal.core.services.ssh.attach import SSHAttach
|
|
50
55
|
from dstack._internal.core.services.ssh.ports import PortsLock
|
|
51
56
|
from dstack._internal.server.schemas.logs import PollLogsRequest
|
|
52
57
|
from dstack._internal.utils.common import get_or_error, make_proxy_url
|
|
58
|
+
from dstack._internal.utils.crypto import generate_rsa_key_pair
|
|
53
59
|
from dstack._internal.utils.files import create_file_archive
|
|
54
60
|
from dstack._internal.utils.logging import get_logger
|
|
55
61
|
from dstack._internal.utils.path import PathLike, path_in_dir
|
|
@@ -72,16 +78,20 @@ class Run(ABC):
|
|
|
72
78
|
self,
|
|
73
79
|
api_client: APIClient,
|
|
74
80
|
project: str,
|
|
75
|
-
ssh_identity_file: Optional[PathLike],
|
|
76
81
|
run: RunModel,
|
|
77
82
|
ports_lock: Optional[PortsLock] = None,
|
|
83
|
+
ssh_identity_file: Optional[PathLike] = None,
|
|
78
84
|
):
|
|
79
85
|
self._api_client = api_client
|
|
80
86
|
self._project = project
|
|
81
|
-
self._ssh_identity_file = ssh_identity_file
|
|
82
87
|
self._run = run
|
|
83
88
|
self._ports_lock: Optional[PortsLock] = ports_lock
|
|
84
89
|
self._ssh_attach: Optional[SSHAttach] = None
|
|
90
|
+
if ssh_identity_file is not None:
|
|
91
|
+
warn(
|
|
92
|
+
"[code]ssh_identity_file[/code] in [code]Run[/code] is deprecated and ignored; will be removed"
|
|
93
|
+
" since 0.19.40"
|
|
94
|
+
)
|
|
85
95
|
|
|
86
96
|
@property
|
|
87
97
|
def name(self) -> str:
|
|
@@ -270,9 +280,33 @@ class Run(ABC):
|
|
|
270
280
|
Raises:
|
|
271
281
|
dstack.api.PortUsedError: If ports are in use or the run is attached by another process.
|
|
272
282
|
"""
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
283
|
+
if not ssh_identity_file:
|
|
284
|
+
user = self._api_client.users.get_my_user()
|
|
285
|
+
run_ssh_key_pub = self._run.run_spec.ssh_key_pub
|
|
286
|
+
config_manager = ConfigManager()
|
|
287
|
+
if isinstance(user, UserWithCreds) and user.ssh_public_key == run_ssh_key_pub:
|
|
288
|
+
token_hash = hashlib.sha1(user.creds.token.encode()).hexdigest()[:8]
|
|
289
|
+
config_manager.dstack_ssh_dir.mkdir(parents=True, exist_ok=True)
|
|
290
|
+
ssh_identity_file = config_manager.dstack_ssh_dir / token_hash
|
|
291
|
+
|
|
292
|
+
def key_opener(path, flags):
|
|
293
|
+
return os.open(path, flags, 0o600)
|
|
294
|
+
|
|
295
|
+
with open(ssh_identity_file, "wb", opener=key_opener) as f:
|
|
296
|
+
assert user.ssh_private_key
|
|
297
|
+
f.write(user.ssh_private_key.encode())
|
|
298
|
+
else:
|
|
299
|
+
if config_manager.dstack_key_path.exists():
|
|
300
|
+
# TODO: Remove since 0.19.40
|
|
301
|
+
warn(
|
|
302
|
+
f"Using legacy [code]{config_manager.dstack_key_path}[/code]."
|
|
303
|
+
" Future versions will use the user SSH key from the server.",
|
|
304
|
+
)
|
|
305
|
+
ssh_identity_file = config_manager.dstack_key_path
|
|
306
|
+
else:
|
|
307
|
+
raise ConfigurationError(
|
|
308
|
+
f"User SSH key doen't match; default SSH key ({config_manager.dstack_key_path}) doesn't exist"
|
|
309
|
+
)
|
|
276
310
|
ssh_identity_file = str(ssh_identity_file)
|
|
277
311
|
|
|
278
312
|
job = self._find_job(replica_num=replica_num, job_num=job_num)
|
|
@@ -434,6 +468,7 @@ class RunCollection:
|
|
|
434
468
|
profile: Optional[Profile] = None,
|
|
435
469
|
configuration_path: Optional[str] = None,
|
|
436
470
|
repo_dir: Optional[str] = None,
|
|
471
|
+
ssh_identity_file: Optional[PathLike] = None,
|
|
437
472
|
) -> RunPlan:
|
|
438
473
|
"""
|
|
439
474
|
Get a run plan.
|
|
@@ -465,6 +500,19 @@ class RunCollection:
|
|
|
465
500
|
if repo_dir is None and configuration.repos:
|
|
466
501
|
repo_dir = configuration.repos[0].path
|
|
467
502
|
|
|
503
|
+
if ssh_identity_file:
|
|
504
|
+
ssh_key_pub = Path(ssh_identity_file).with_suffix(".pub").read_text()
|
|
505
|
+
else:
|
|
506
|
+
config_manager = ConfigManager()
|
|
507
|
+
if not config_manager.dstack_key_path.exists():
|
|
508
|
+
generate_rsa_key_pair(private_key_path=config_manager.dstack_key_path)
|
|
509
|
+
warn(
|
|
510
|
+
f"Using legacy [code]{config_manager.dstack_key_path.with_suffix('.pub')}[/code]."
|
|
511
|
+
" Future versions will use the user SSH key from the server.",
|
|
512
|
+
)
|
|
513
|
+
ssh_key_pub = config_manager.dstack_key_path.with_suffix(".pub").read_text()
|
|
514
|
+
# TODO: Uncomment after 0.19.40
|
|
515
|
+
# ssh_key_pub = None
|
|
468
516
|
run_spec = RunSpec(
|
|
469
517
|
run_name=configuration.name,
|
|
470
518
|
repo_id=repo.repo_id,
|
|
@@ -477,7 +525,7 @@ class RunCollection:
|
|
|
477
525
|
configuration_path=configuration_path,
|
|
478
526
|
configuration=configuration,
|
|
479
527
|
profile=profile,
|
|
480
|
-
ssh_key_pub=
|
|
528
|
+
ssh_key_pub=ssh_key_pub,
|
|
481
529
|
)
|
|
482
530
|
logger.debug("Getting run plan")
|
|
483
531
|
run_plan = self._api_client.runs.get_plan(self._project, run_spec)
|
|
@@ -546,6 +594,7 @@ class RunCollection:
|
|
|
546
594
|
profile: Optional[Profile] = None,
|
|
547
595
|
configuration_path: Optional[str] = None,
|
|
548
596
|
reserve_ports: bool = True,
|
|
597
|
+
ssh_identity_file: Optional[PathLike] = None,
|
|
549
598
|
) -> Run:
|
|
550
599
|
"""
|
|
551
600
|
Apply the run configuration.
|
|
@@ -567,6 +616,7 @@ class RunCollection:
|
|
|
567
616
|
repo=repo,
|
|
568
617
|
profile=profile,
|
|
569
618
|
configuration_path=configuration_path,
|
|
619
|
+
ssh_identity_file=ssh_identity_file,
|
|
570
620
|
)
|
|
571
621
|
run = self.apply_plan(
|
|
572
622
|
run_plan=run_plan,
|
|
@@ -709,6 +759,13 @@ class RunCollection:
|
|
|
709
759
|
creation_policy=creation_policy,
|
|
710
760
|
idle_duration=idle_duration, # type: ignore[assignment]
|
|
711
761
|
)
|
|
762
|
+
config_manager = ConfigManager()
|
|
763
|
+
if not config_manager.dstack_key_path.exists():
|
|
764
|
+
generate_rsa_key_pair(private_key_path=config_manager.dstack_key_path)
|
|
765
|
+
warn(
|
|
766
|
+
f"Using legacy [code]{config_manager.dstack_key_path.with_suffix('.pub')}[/code]."
|
|
767
|
+
" Future versions will use the user SSH key from the server.",
|
|
768
|
+
)
|
|
712
769
|
run_spec = RunSpec(
|
|
713
770
|
run_name=run_name,
|
|
714
771
|
repo_id=repo.repo_id,
|
|
@@ -718,7 +775,7 @@ class RunCollection:
|
|
|
718
775
|
configuration_path=configuration_path,
|
|
719
776
|
configuration=configuration,
|
|
720
777
|
profile=profile,
|
|
721
|
-
ssh_key_pub=
|
|
778
|
+
ssh_key_pub=config_manager.dstack_key_path.with_suffix(".pub").read_text(),
|
|
722
779
|
)
|
|
723
780
|
logger.debug("Getting run plan")
|
|
724
781
|
run_plan = self._api_client.runs.get_plan(self._project, run_spec)
|
|
@@ -800,7 +857,6 @@ class RunCollection:
|
|
|
800
857
|
return Run(
|
|
801
858
|
self._api_client,
|
|
802
859
|
self._project,
|
|
803
|
-
self._client.ssh_identity_file,
|
|
804
860
|
run,
|
|
805
861
|
)
|
|
806
862
|
|
|
@@ -808,7 +864,6 @@ class RunCollection:
|
|
|
808
864
|
return Run(
|
|
809
865
|
self._api_client,
|
|
810
866
|
self._project,
|
|
811
|
-
self._client.ssh_identity_file,
|
|
812
867
|
run,
|
|
813
868
|
ports_lock,
|
|
814
869
|
)
|
dstack/api/server/_users.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
|
-
from pydantic import parse_obj_as
|
|
3
|
+
from pydantic import ValidationError, parse_obj_as
|
|
4
4
|
|
|
5
5
|
from dstack._internal.core.models.users import GlobalRole, User, UserWithCreds
|
|
6
6
|
from dstack._internal.server.schemas.users import (
|
|
@@ -18,8 +18,23 @@ class UsersAPIClient(APIClientGroup):
|
|
|
18
18
|
return parse_obj_as(List[User.__response__], resp.json())
|
|
19
19
|
|
|
20
20
|
def get_my_user(self) -> User:
|
|
21
|
+
"""
|
|
22
|
+
Returns `User` with pre-0.19.33 servers, or `UserWithCreds` with newer servers.
|
|
23
|
+
"""
|
|
24
|
+
|
|
21
25
|
resp = self._request("/api/users/get_my_user")
|
|
22
|
-
|
|
26
|
+
try:
|
|
27
|
+
return parse_obj_as(UserWithCreds.__response__, resp.json())
|
|
28
|
+
except ValidationError as e:
|
|
29
|
+
# Compatibility with pre-0.19.33 server
|
|
30
|
+
if (
|
|
31
|
+
len(e.errors()) == 1
|
|
32
|
+
and e.errors()[0]["loc"] == ("__root__", "creds")
|
|
33
|
+
and e.errors()[0]["type"] == "value_error.missing"
|
|
34
|
+
):
|
|
35
|
+
return parse_obj_as(User.__response__, resp.json())
|
|
36
|
+
else:
|
|
37
|
+
raise
|
|
23
38
|
|
|
24
39
|
def get_user(self, username: str) -> User:
|
|
25
40
|
body = GetUserRequest(username=username)
|
dstack/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dstack
|
|
3
|
-
Version: 0.19.
|
|
3
|
+
Version: 0.19.33
|
|
4
4
|
Summary: dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises.
|
|
5
5
|
Project-URL: Homepage, https://dstack.ai
|
|
6
6
|
Project-URL: Source, https://github.com/dstackai/dstack
|
|
@@ -22,7 +22,7 @@ Requires-Dist: cryptography
|
|
|
22
22
|
Requires-Dist: cursor
|
|
23
23
|
Requires-Dist: filelock
|
|
24
24
|
Requires-Dist: gitpython
|
|
25
|
-
Requires-Dist: gpuhunt==0.1.
|
|
25
|
+
Requires-Dist: gpuhunt==0.1.10
|
|
26
26
|
Requires-Dist: ignore-python>=0.2.0
|
|
27
27
|
Requires-Dist: jsonschema
|
|
28
28
|
Requires-Dist: orjson
|
|
@@ -73,7 +73,7 @@ Requires-Dist: grpcio>=1.50; extra == 'all'
|
|
|
73
73
|
Requires-Dist: httpx; extra == 'all'
|
|
74
74
|
Requires-Dist: jinja2; extra == 'all'
|
|
75
75
|
Requires-Dist: kubernetes; extra == 'all'
|
|
76
|
-
Requires-Dist: nebius
|
|
76
|
+
Requires-Dist: nebius<=0.2.72,>=0.2.40; (python_version >= '3.10') and extra == 'all'
|
|
77
77
|
Requires-Dist: oci>=2.150.0; extra == 'all'
|
|
78
78
|
Requires-Dist: prometheus-client; extra == 'all'
|
|
79
79
|
Requires-Dist: pyopenssl>=23.2.0; extra == 'all'
|
|
@@ -259,7 +259,7 @@ Requires-Dist: fastapi; extra == 'nebius'
|
|
|
259
259
|
Requires-Dist: grpcio>=1.50; extra == 'nebius'
|
|
260
260
|
Requires-Dist: httpx; extra == 'nebius'
|
|
261
261
|
Requires-Dist: jinja2; extra == 'nebius'
|
|
262
|
-
Requires-Dist: nebius
|
|
262
|
+
Requires-Dist: nebius<=0.2.72,>=0.2.40; (python_version >= '3.10') and extra == 'nebius'
|
|
263
263
|
Requires-Dist: prometheus-client; extra == 'nebius'
|
|
264
264
|
Requires-Dist: python-dxf==12.1.0; extra == 'nebius'
|
|
265
265
|
Requires-Dist: python-json-logger>=3.1.0; extra == 'nebius'
|
|
@@ -340,15 +340,13 @@ It streamlines development, training, and inference, and is compatible with any
|
|
|
340
340
|
`dstack` supports `NVIDIA`, `AMD`, `Google TPU`, `Intel Gaudi`, and `Tenstorrent` accelerators out of the box.
|
|
341
341
|
|
|
342
342
|
## Latest news ✨
|
|
343
|
-
- [2025/
|
|
344
|
-
- [2025/08] [dstack 0.19.26: Repos
|
|
345
|
-
- [2025/08] [dstack 0.19.
|
|
346
|
-
- [2025/08] [dstack 0.19.22: Service probes, GPU health-checks, Tenstorrent Galaxy, Secrets UI](https://github.com/dstackai/dstack/releases/tag/0.19.22)
|
|
343
|
+
- [2025/10] [dstack 0.19.31: Kubernetes, GCP A4 spot](https://github.com/dstackai/dstack/releases/tag/0.19.31)
|
|
344
|
+
- [2025/08] [dstack 0.19.26: Repos](https://github.com/dstackai/dstack/releases/tag/0.19.26)
|
|
345
|
+
- [2025/08] [dstack 0.19.22: Service probes, GPU health-checks, Tenstorrent Galaxy](https://github.com/dstackai/dstack/releases/tag/0.19.22)
|
|
347
346
|
- [2025/07] [dstack 0.19.21: Scheduled tasks](https://github.com/dstackai/dstack/releases/tag/0.19.21)
|
|
348
347
|
- [2025/07] [dstack 0.19.17: Secrets, Files, Rolling deployment](https://github.com/dstackai/dstack/releases/tag/0.19.17)
|
|
349
|
-
- [2025/06] [dstack 0.19.16: Docker in Docker
|
|
350
|
-
- [2025/06] [dstack 0.19.13:
|
|
351
|
-
- [2025/06] [dstack 0.19.12: Simplified use of MPI](https://github.com/dstackai/dstack/releases/tag/0.19.12)
|
|
348
|
+
- [2025/06] [dstack 0.19.16: Docker in Docker](https://github.com/dstackai/dstack/releases/tag/0.19.16)
|
|
349
|
+
- [2025/06] [dstack 0.19.13: Default images with InfiniBand support](https://github.com/dstackai/dstack/releases/tag/0.19.13)
|
|
352
350
|
|
|
353
351
|
## How does it work?
|
|
354
352
|
|
|
@@ -364,11 +362,11 @@ It streamlines development, training, and inference, and is compatible with any
|
|
|
364
362
|
|
|
365
363
|
To orchestrate compute across cloud providers or existing Kubernetes clusters, you need to configure backends.
|
|
366
364
|
|
|
367
|
-
Backends can be set up in `~/.dstack/server/config.yml` or through the [project settings page](
|
|
365
|
+
Backends can be set up in `~/.dstack/server/config.yml` or through the [project settings page](https://dstack.ai/docs/concepts/projects#backends) in the UI.
|
|
368
366
|
|
|
369
|
-
For more details, see [Backends](
|
|
367
|
+
For more details, see [Backends](https://dstack.ai/docs/concepts/backends).
|
|
370
368
|
|
|
371
|
-
> When using `dstack` with on-prem servers, backend configuration isn’t required. Simply create [SSH fleets](
|
|
369
|
+
> When using `dstack` with on-prem servers, backend configuration isn’t required. Simply create [SSH fleets](https://dstack.ai/docs/concepts/fleets#ssh) once the server is up.
|
|
372
370
|
|
|
373
371
|
##### Start the server
|
|
374
372
|
|