skypilot-nightly 1.0.0.dev20250427__py3-none-any.whl → 1.0.0.dev20250429__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.
- sky/__init__.py +2 -2
- sky/adaptors/nebius.py +28 -40
- sky/backends/backend_utils.py +19 -2
- sky/backends/cloud_vm_ray_backend.py +33 -8
- sky/backends/local_docker_backend.py +1 -2
- sky/cli.py +91 -38
- sky/client/cli.py +91 -38
- sky/client/sdk.py +3 -2
- sky/clouds/aws.py +12 -6
- sky/clouds/azure.py +3 -0
- sky/clouds/cloud.py +8 -2
- sky/clouds/cudo.py +2 -0
- sky/clouds/do.py +3 -0
- sky/clouds/fluidstack.py +3 -0
- sky/clouds/gcp.py +7 -0
- sky/clouds/ibm.py +2 -0
- sky/clouds/kubernetes.py +42 -19
- sky/clouds/lambda_cloud.py +1 -0
- sky/clouds/nebius.py +18 -10
- sky/clouds/oci.py +6 -3
- sky/clouds/paperspace.py +2 -0
- sky/clouds/runpod.py +2 -0
- sky/clouds/scp.py +2 -0
- sky/clouds/service_catalog/constants.py +1 -1
- sky/clouds/service_catalog/kubernetes_catalog.py +7 -7
- sky/clouds/vast.py +2 -0
- sky/clouds/vsphere.py +2 -0
- sky/core.py +58 -29
- sky/dashboard/out/404.html +1 -1
- sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
- sky/dashboard/out/clusters/[cluster].html +1 -1
- sky/dashboard/out/clusters.html +1 -1
- sky/dashboard/out/favicon.ico +0 -0
- sky/dashboard/out/index.html +1 -1
- sky/dashboard/out/jobs/[job].html +1 -1
- sky/dashboard/out/jobs.html +1 -1
- sky/exceptions.py +6 -0
- sky/execution.py +19 -4
- sky/global_user_state.py +1 -0
- sky/optimizer.py +35 -11
- sky/provision/common.py +2 -5
- sky/provision/docker_utils.py +22 -16
- sky/provision/instance_setup.py +1 -1
- sky/provision/kubernetes/instance.py +276 -93
- sky/provision/kubernetes/network.py +1 -1
- sky/provision/kubernetes/utils.py +36 -24
- sky/provision/provisioner.py +6 -0
- sky/serve/replica_managers.py +51 -5
- sky/serve/serve_state.py +41 -0
- sky/serve/service.py +108 -63
- sky/server/common.py +6 -3
- sky/server/config.py +184 -0
- sky/server/requests/executor.py +17 -156
- sky/server/server.py +4 -4
- sky/setup_files/dependencies.py +0 -1
- sky/skylet/constants.py +7 -0
- sky/skypilot_config.py +27 -6
- sky/task.py +1 -1
- sky/templates/kubernetes-ray.yml.j2 +145 -15
- sky/templates/nebius-ray.yml.j2 +63 -0
- sky/utils/command_runner.py +17 -3
- sky/utils/command_runner.pyi +2 -0
- sky/utils/controller_utils.py +24 -0
- sky/utils/kubernetes/rsync_helper.sh +20 -4
- sky/utils/schemas.py +13 -0
- {skypilot_nightly-1.0.0.dev20250427.dist-info → skypilot_nightly-1.0.0.dev20250429.dist-info}/METADATA +2 -2
- {skypilot_nightly-1.0.0.dev20250427.dist-info → skypilot_nightly-1.0.0.dev20250429.dist-info}/RECORD +73 -72
- {skypilot_nightly-1.0.0.dev20250427.dist-info → skypilot_nightly-1.0.0.dev20250429.dist-info}/WHEEL +1 -1
- /sky/dashboard/out/_next/static/{kTfCjujxwqIQ4b7YvP7Uq → BMtJJ079_cyYmtW2-7nVS}/_buildManifest.js +0 -0
- /sky/dashboard/out/_next/static/{kTfCjujxwqIQ4b7YvP7Uq → BMtJJ079_cyYmtW2-7nVS}/_ssgManifest.js +0 -0
- {skypilot_nightly-1.0.0.dev20250427.dist-info → skypilot_nightly-1.0.0.dev20250429.dist-info}/entry_points.txt +0 -0
- {skypilot_nightly-1.0.0.dev20250427.dist-info → skypilot_nightly-1.0.0.dev20250429.dist-info}/licenses/LICENSE +0 -0
- {skypilot_nightly-1.0.0.dev20250427.dist-info → skypilot_nightly-1.0.0.dev20250429.dist-info}/top_level.txt +0 -0
sky/server/config.py
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
"""SkyPilot API Server configuration."""
|
2
|
+
|
3
|
+
import dataclasses
|
4
|
+
import enum
|
5
|
+
|
6
|
+
from sky import sky_logging
|
7
|
+
from sky.server import constants as server_constants
|
8
|
+
from sky.utils import common_utils
|
9
|
+
|
10
|
+
# Constants based on profiling the peak memory usage while serving various
|
11
|
+
# sky commands. These estimation are highly related to usage patterns
|
12
|
+
# (clouds enabled, type of requests, etc. see `tests/load_tests` for details.),
|
13
|
+
# the profiling covers major clouds and common usage patterns. For user has
|
14
|
+
# deviated usage pattern, they can override the default estimation by
|
15
|
+
# environment variables.
|
16
|
+
# NOTE(dev): update these constants for each release according to the load
|
17
|
+
# test results.
|
18
|
+
# TODO(aylei): maintaining these constants is error-prone, we may need to
|
19
|
+
# automatically tune parallelism at runtime according to system usage stats
|
20
|
+
# in the future.
|
21
|
+
_LONG_WORKER_MEM_GB = 0.4
|
22
|
+
_SHORT_WORKER_MEM_GB = 0.25
|
23
|
+
# To control the number of long workers.
|
24
|
+
_CPU_MULTIPLIER_FOR_LONG_WORKERS = 2
|
25
|
+
# Limit the number of long workers of local API server, since local server is
|
26
|
+
# typically:
|
27
|
+
# 1. launched automatically in an environment with high resource contention
|
28
|
+
# (e.g. Laptop)
|
29
|
+
# 2. used by a single user
|
30
|
+
_MAX_LONG_WORKERS_LOCAL = 4
|
31
|
+
# Percentage of memory for long requests
|
32
|
+
# from the memory reserved for SkyPilot.
|
33
|
+
# This is to reserve some memory for short requests.
|
34
|
+
_MAX_MEM_PERCENT_FOR_BLOCKING = 0.6
|
35
|
+
# Minimal number of long workers to ensure responsiveness.
|
36
|
+
_MIN_LONG_WORKERS = 1
|
37
|
+
# Minimal number of short workers, there is a daemon task running on short
|
38
|
+
# workers so at least 2 workers are needed to ensure responsiveness.
|
39
|
+
_MIN_SHORT_WORKERS = 2
|
40
|
+
|
41
|
+
# Default number of burstable workers for local API server. A heuristic number
|
42
|
+
# that is large enough for most local cases.
|
43
|
+
# TODO(aylei): the number of burstable workers should be auto-tuned based on the
|
44
|
+
# system usage stats.
|
45
|
+
_BURSTABLE_WORKERS_FOR_LOCAL = 1024
|
46
|
+
|
47
|
+
logger = sky_logging.init_logger(__name__)
|
48
|
+
|
49
|
+
|
50
|
+
class QueueBackend(enum.Enum):
|
51
|
+
# Local queue backend serves queues in each process locally, which has
|
52
|
+
# lower resource usage but the consumer must be in the same process, i.e.
|
53
|
+
# this only works in single-process mode.
|
54
|
+
LOCAL = 'local'
|
55
|
+
# Multi-process queue backend starts a dedicated process for serving queues.
|
56
|
+
MULTIPROCESSING = 'multiprocessing'
|
57
|
+
# TODO(zhwu): we can add redis backend in the future.
|
58
|
+
|
59
|
+
|
60
|
+
@dataclasses.dataclass
|
61
|
+
class WorkerConfig:
|
62
|
+
garanteed_parallelism: int
|
63
|
+
burstable_parallelism: int
|
64
|
+
|
65
|
+
|
66
|
+
@dataclasses.dataclass
|
67
|
+
class ServerConfig:
|
68
|
+
num_server_workers: int
|
69
|
+
long_worker_config: WorkerConfig
|
70
|
+
short_worker_config: WorkerConfig
|
71
|
+
queue_backend: QueueBackend
|
72
|
+
|
73
|
+
|
74
|
+
def compute_server_config(deploy: bool) -> ServerConfig:
|
75
|
+
"""Compute the server config based on environment.
|
76
|
+
|
77
|
+
We have different assumptions for the resources in different deployment
|
78
|
+
modes, which leads to different worker setups:
|
79
|
+
|
80
|
+
- Deployment mode (deploy=True), we assume the resources are dedicated to
|
81
|
+
the API server and the resources will be tuned for serious use cases, so:
|
82
|
+
- Use multiprocessing queue backend and dedicated workers processes to
|
83
|
+
avoid GIL contention.
|
84
|
+
- Parallelism (number of executor processes) is fixed and executor
|
85
|
+
processes have same lifecycle with the server, which ensures
|
86
|
+
best-effort cache reusing and stable resources consumption.
|
87
|
+
- Reject to start in low resource environments, to avoid flaky
|
88
|
+
deployments.
|
89
|
+
- Local mode (deploy=False), we assume the server is running in a shared
|
90
|
+
environment (e.g. laptop) and users typically do not pay attention to
|
91
|
+
the resource setup of the server. Moreover, existing users may expect
|
92
|
+
some consistent behaviors with old versions, i.e. before API server was
|
93
|
+
introduced, so:
|
94
|
+
- The max number of long-running executor processes are limited, to avoid
|
95
|
+
high memory consumption when the server is idle.
|
96
|
+
- Allow burstable workers to handle requests when all long-running
|
97
|
+
workers are busy, which mimics the behavior of local sky CLI before
|
98
|
+
API server was introduced.
|
99
|
+
- Works in low resources environments, and further reduce the memory
|
100
|
+
consumption in low resource environments.
|
101
|
+
|
102
|
+
Note that there is still significant overhead for SDK users when migrate to
|
103
|
+
local API server. Since the users are free to run sky operations in Threads
|
104
|
+
when using SDK but all client operations will occupy at least one worker
|
105
|
+
process after API server was introduced.
|
106
|
+
"""
|
107
|
+
cpu_count = common_utils.get_cpu_count()
|
108
|
+
mem_size_gb = common_utils.get_mem_size_gb()
|
109
|
+
max_parallel_for_long = _max_long_worker_parallism(cpu_count,
|
110
|
+
mem_size_gb,
|
111
|
+
local=not deploy)
|
112
|
+
max_parallel_for_short = _max_short_worker_parallism(
|
113
|
+
mem_size_gb, max_parallel_for_long)
|
114
|
+
queue_backend = QueueBackend.MULTIPROCESSING
|
115
|
+
burstable_parallel_for_long = 0
|
116
|
+
burstable_parallel_for_short = 0
|
117
|
+
num_server_workers = cpu_count
|
118
|
+
if not deploy:
|
119
|
+
# For local mode, use local queue backend since we only run 1 uvicorn
|
120
|
+
# worker in local mode and no multiprocessing is needed.
|
121
|
+
num_server_workers = 1
|
122
|
+
queue_backend = QueueBackend.LOCAL
|
123
|
+
# Enable burstable workers for local API server.
|
124
|
+
burstable_parallel_for_long = _BURSTABLE_WORKERS_FOR_LOCAL
|
125
|
+
burstable_parallel_for_short = _BURSTABLE_WORKERS_FOR_LOCAL
|
126
|
+
# Runs in low resource mode if the available memory is less than
|
127
|
+
# server_constants.MIN_AVAIL_MEM_GB.
|
128
|
+
if not deploy and mem_size_gb < server_constants.MIN_AVAIL_MEM_GB:
|
129
|
+
# Permanent worker process may have significant memory consumption
|
130
|
+
# (~350MB per worker) after running commands like `sky check`, so we
|
131
|
+
# don't start any permanent workers in low resource local mode. This
|
132
|
+
# mimics the behavior of local sky CLI before API server was
|
133
|
+
# introduced, where the CLI will start new process everytime and
|
134
|
+
# never reject to start due to resource constraints.
|
135
|
+
# Note that the refresh daemon will still occupy one worker
|
136
|
+
# permanently because it never exits.
|
137
|
+
max_parallel_for_long = 0
|
138
|
+
max_parallel_for_short = 0
|
139
|
+
logger.warning(
|
140
|
+
'SkyPilot API server will run in low resource mode because '
|
141
|
+
'the available memory is less than '
|
142
|
+
f'{server_constants.MIN_AVAIL_MEM_GB}GB.')
|
143
|
+
logger.info(
|
144
|
+
f'SkyPilot API server will start {num_server_workers} server processes '
|
145
|
+
f'with {max_parallel_for_long} background workers for long requests '
|
146
|
+
f'and will allow at max {max_parallel_for_short} short requests in '
|
147
|
+
f'parallel.')
|
148
|
+
return ServerConfig(
|
149
|
+
num_server_workers=num_server_workers,
|
150
|
+
queue_backend=queue_backend,
|
151
|
+
long_worker_config=WorkerConfig(
|
152
|
+
garanteed_parallelism=max_parallel_for_long,
|
153
|
+
burstable_parallelism=burstable_parallel_for_long),
|
154
|
+
short_worker_config=WorkerConfig(
|
155
|
+
garanteed_parallelism=max_parallel_for_short,
|
156
|
+
burstable_parallelism=burstable_parallel_for_short),
|
157
|
+
)
|
158
|
+
|
159
|
+
|
160
|
+
def _max_long_worker_parallism(cpu_count: int,
|
161
|
+
mem_size_gb: float,
|
162
|
+
local=False) -> int:
|
163
|
+
"""Max parallelism for long workers."""
|
164
|
+
# Reserve min available memory to avoid OOM.
|
165
|
+
available_mem = max(0, mem_size_gb - server_constants.MIN_AVAIL_MEM_GB)
|
166
|
+
cpu_based_max_parallel = cpu_count * _CPU_MULTIPLIER_FOR_LONG_WORKERS
|
167
|
+
mem_based_max_parallel = int(available_mem * _MAX_MEM_PERCENT_FOR_BLOCKING /
|
168
|
+
_LONG_WORKER_MEM_GB)
|
169
|
+
n = max(_MIN_LONG_WORKERS,
|
170
|
+
min(cpu_based_max_parallel, mem_based_max_parallel))
|
171
|
+
if local:
|
172
|
+
return min(n, _MAX_LONG_WORKERS_LOCAL)
|
173
|
+
return n
|
174
|
+
|
175
|
+
|
176
|
+
def _max_short_worker_parallism(mem_size_gb: float,
|
177
|
+
long_worker_parallism: int) -> int:
|
178
|
+
"""Max parallelism for short workers."""
|
179
|
+
# Reserve memory for long workers and min available memory.
|
180
|
+
reserved_mem = server_constants.MIN_AVAIL_MEM_GB + (long_worker_parallism *
|
181
|
+
_LONG_WORKER_MEM_GB)
|
182
|
+
available_mem = max(0, mem_size_gb - reserved_mem)
|
183
|
+
n = max(_MIN_SHORT_WORKERS, int(available_mem / _SHORT_WORKER_MEM_GB))
|
184
|
+
return n
|
sky/server/requests/executor.py
CHANGED
@@ -19,7 +19,6 @@ The number of the workers is determined by the system resources.
|
|
19
19
|
See the [README.md](../README.md) for detailed architecture of the executor.
|
20
20
|
"""
|
21
21
|
import contextlib
|
22
|
-
import enum
|
23
22
|
import multiprocessing
|
24
23
|
import os
|
25
24
|
import queue as queue_lib
|
@@ -37,6 +36,7 @@ from sky import models
|
|
37
36
|
from sky import sky_logging
|
38
37
|
from sky import skypilot_config
|
39
38
|
from sky.server import common as server_common
|
39
|
+
from sky.server import config as server_config
|
40
40
|
from sky.server import constants as server_constants
|
41
41
|
from sky.server.requests import payloads
|
42
42
|
from sky.server.requests import preconditions
|
@@ -70,53 +70,6 @@ logger = sky_logging.init_logger(__name__)
|
|
70
70
|
# platforms, including macOS.
|
71
71
|
multiprocessing.set_start_method('spawn', force=True)
|
72
72
|
|
73
|
-
# Constants based on profiling the peak memory usage while serving various
|
74
|
-
# sky commands. These estimation are highly related to usage patterns
|
75
|
-
# (clouds enabled, type of requests, etc. see `tests/load_tests` for details.),
|
76
|
-
# the profiling covers major clouds and common usage patterns. For user has
|
77
|
-
# deviated usage pattern, they can override the default estimation by
|
78
|
-
# environment variables.
|
79
|
-
# NOTE(dev): update these constants for each release according to the load
|
80
|
-
# test results.
|
81
|
-
# TODO(aylei): maintaining these constants is error-prone, we may need to
|
82
|
-
# automatically tune parallelism at runtime according to system usage stats
|
83
|
-
# in the future.
|
84
|
-
_LONG_WORKER_MEM_GB = 0.4
|
85
|
-
_SHORT_WORKER_MEM_GB = 0.25
|
86
|
-
# To control the number of long workers.
|
87
|
-
_CPU_MULTIPLIER_FOR_LONG_WORKERS = 2
|
88
|
-
# Limit the number of long workers of local API server, since local server is
|
89
|
-
# typically:
|
90
|
-
# 1. launched automatically in an environment with high resource contention
|
91
|
-
# (e.g. Laptop)
|
92
|
-
# 2. used by a single user
|
93
|
-
_MAX_LONG_WORKERS_LOCAL = 4
|
94
|
-
# Percentage of memory for long requests
|
95
|
-
# from the memory reserved for SkyPilot.
|
96
|
-
# This is to reserve some memory for short requests.
|
97
|
-
_MAX_MEM_PERCENT_FOR_BLOCKING = 0.6
|
98
|
-
# Minimal number of long workers to ensure responsiveness.
|
99
|
-
_MIN_LONG_WORKERS = 1
|
100
|
-
# Minimal number of short workers, there is a daemon task running on short
|
101
|
-
# workers so at least 2 workers are needed to ensure responsiveness.
|
102
|
-
_MIN_SHORT_WORKERS = 2
|
103
|
-
|
104
|
-
# Default number of burstable workers for local API server. A heuristic number
|
105
|
-
# that is large enough for most local cases.
|
106
|
-
# TODO(aylei): the number of burstable workers should be auto-tuned based on the
|
107
|
-
# system usage stats.
|
108
|
-
_BURSTABLE_WORKERS_FOR_LOCAL = 1024
|
109
|
-
|
110
|
-
|
111
|
-
class QueueBackend(enum.Enum):
|
112
|
-
# Local queue backend serves queues in each process locally, which has
|
113
|
-
# lower resource usage but the consumer must be in the same process, i.e.
|
114
|
-
# this only works in single-process mode.
|
115
|
-
LOCAL = 'local'
|
116
|
-
# Multi-process queue backend starts a dedicated process for serving queues.
|
117
|
-
MULTIPROCESSING = 'multiprocessing'
|
118
|
-
# TODO(zhwu): we can add redis backend in the future.
|
119
|
-
|
120
73
|
|
121
74
|
class RequestQueue:
|
122
75
|
"""The queue for the requests, either redis or multiprocessing.
|
@@ -126,12 +79,12 @@ class RequestQueue:
|
|
126
79
|
|
127
80
|
def __init__(self,
|
128
81
|
schedule_type: api_requests.ScheduleType,
|
129
|
-
backend: Optional[QueueBackend] = None) -> None:
|
82
|
+
backend: Optional[server_config.QueueBackend] = None) -> None:
|
130
83
|
self.name = schedule_type.value
|
131
84
|
self.backend = backend
|
132
|
-
if backend == QueueBackend.MULTIPROCESSING:
|
85
|
+
if backend == server_config.QueueBackend.MULTIPROCESSING:
|
133
86
|
self.queue = mp_queue.get_queue(self.name)
|
134
|
-
elif backend == QueueBackend.LOCAL:
|
87
|
+
elif backend == server_config.QueueBackend.LOCAL:
|
135
88
|
self.queue = local_queue.get_queue(self.name)
|
136
89
|
else:
|
137
90
|
raise RuntimeError(f'Invalid queue backend: {backend}')
|
@@ -162,7 +115,7 @@ class RequestQueue:
|
|
162
115
|
return self.queue.qsize()
|
163
116
|
|
164
117
|
|
165
|
-
queue_backend = QueueBackend.MULTIPROCESSING
|
118
|
+
queue_backend = server_config.QueueBackend.MULTIPROCESSING
|
166
119
|
|
167
120
|
|
168
121
|
def executor_initializer(proc_group: str):
|
@@ -186,13 +139,11 @@ class RequestWorker:
|
|
186
139
|
# if there are available CPU/memory resources.
|
187
140
|
burstable_parallelism: int = 0
|
188
141
|
|
189
|
-
def __init__(self,
|
190
|
-
|
191
|
-
garanteed_parallelism: int,
|
192
|
-
burstable_parallelism: int = 0) -> None:
|
142
|
+
def __init__(self, schedule_type: api_requests.ScheduleType,
|
143
|
+
config: server_config.WorkerConfig) -> None:
|
193
144
|
self.schedule_type = schedule_type
|
194
|
-
self.garanteed_parallelism = garanteed_parallelism
|
195
|
-
self.burstable_parallelism = burstable_parallelism
|
145
|
+
self.garanteed_parallelism = config.garanteed_parallelism
|
146
|
+
self.burstable_parallelism = config.burstable_parallelism
|
196
147
|
|
197
148
|
def __str__(self) -> str:
|
198
149
|
return f'Worker(schedule_type={self.schedule_type.value})'
|
@@ -455,80 +406,17 @@ def schedule_request(
|
|
455
406
|
enqueue()
|
456
407
|
|
457
408
|
|
458
|
-
def start(
|
409
|
+
def start(config: server_config.ServerConfig) -> List[multiprocessing.Process]:
|
459
410
|
"""Start the request workers.
|
460
411
|
|
461
412
|
Request workers run in background, schedule the requests and delegate the
|
462
|
-
request execution to executor processes.
|
463
|
-
the resources in different deployment modes, which leads to different
|
464
|
-
worker setups:
|
465
|
-
|
466
|
-
- Deployment mode (deploy=True), we assume the resources are dedicated to
|
467
|
-
the API server and the resources will be tuned for serious use cases, so:
|
468
|
-
- Use multiprocessing queue backend and dedicated workers processes to
|
469
|
-
avoid GIL contention.
|
470
|
-
- Parallelism (number of executor processes) is fixed and executor
|
471
|
-
processes have same lifecycle with the server, which ensures
|
472
|
-
best-effort cache reusing and stable resources consumption.
|
473
|
-
- Reject to start in low resource environments, to avoid flaky
|
474
|
-
deployments.
|
475
|
-
- Local mode (deploy=False), we assume the server is running in a shared
|
476
|
-
environment (e.g. laptop) and users typically do not pay attention to
|
477
|
-
the resource setup of the server. Moreover, existing users may expect
|
478
|
-
some consistent behaviors with old versions, i.e. before API server was
|
479
|
-
introduced, so:
|
480
|
-
- The max number of long-running executor processes are limited, to avoid
|
481
|
-
high memory consumption when the server is idle.
|
482
|
-
- Allow burstable workers to handle requests when all long-running
|
483
|
-
workers are busy, which mimics the behavior of local sky CLI before
|
484
|
-
API server was introduced.
|
485
|
-
- Works in low resources environments, and further reduce the memory
|
486
|
-
consumption in low resource environments.
|
487
|
-
|
488
|
-
Note that there is still significant overhead for SDK users when migrate to
|
489
|
-
local API server. Since the users are free to run sky operations in Threads
|
490
|
-
when using SDK but all client operations will occupy at least one worker
|
491
|
-
process after API server was introduced.
|
413
|
+
request execution to executor processes.
|
492
414
|
"""
|
493
|
-
|
494
|
-
|
495
|
-
mem_size_gb = common_utils.get_mem_size_gb()
|
496
|
-
mem_size_gb = max(0, mem_size_gb - server_constants.MIN_AVAIL_MEM_GB)
|
497
|
-
# Runs in low resource mode if the available memory is less than
|
498
|
-
# server_constants.MIN_AVAIL_MEM_GB.
|
499
|
-
max_parallel_for_long = _max_long_worker_parallism(cpu_count,
|
500
|
-
mem_size_gb,
|
501
|
-
local=not deploy)
|
502
|
-
max_parallel_for_short = _max_short_worker_parallism(
|
503
|
-
mem_size_gb, max_parallel_for_long)
|
504
|
-
if mem_size_gb < server_constants.MIN_AVAIL_MEM_GB:
|
505
|
-
# Permanent worker process may have significant memory consumption
|
506
|
-
# (~350MB per worker) after running commands like `sky check`, so we
|
507
|
-
# don't start any permanent workers in low resource local mode. This
|
508
|
-
# mimics the behavior of local sky CLI before API server was
|
509
|
-
# introduced, where the CLI will start new process everytime and
|
510
|
-
# never reject to start due to resource constraints.
|
511
|
-
# Note that the refresh daemon will still occupy one worker
|
512
|
-
# permanently because it never exits.
|
513
|
-
max_parallel_for_long = 0
|
514
|
-
max_parallel_for_short = 0
|
515
|
-
logger.warning(
|
516
|
-
'SkyPilot API server will run in low resource mode because '
|
517
|
-
'the available memory is less than '
|
518
|
-
f'{server_constants.MIN_AVAIL_MEM_GB}GB.')
|
519
|
-
else:
|
520
|
-
logger.info(
|
521
|
-
f'SkyPilot API server will start {max_parallel_for_long} workers '
|
522
|
-
f'for long requests and will allow at max '
|
523
|
-
f'{max_parallel_for_short} short requests in parallel.')
|
524
|
-
if not deploy:
|
525
|
-
# For local mode, use local queue backend since we only run 1 uvicorn
|
526
|
-
# worker in local mode.
|
527
|
-
global queue_backend
|
528
|
-
queue_backend = QueueBackend.LOCAL
|
415
|
+
global queue_backend
|
416
|
+
queue_backend = config.queue_backend
|
529
417
|
sub_procs = []
|
530
418
|
# Setup the queues.
|
531
|
-
if queue_backend == QueueBackend.MULTIPROCESSING:
|
419
|
+
if queue_backend == server_config.QueueBackend.MULTIPROCESSING:
|
532
420
|
logger.info('Creating shared request queues')
|
533
421
|
queue_names = [
|
534
422
|
schedule_type.value for schedule_type in api_requests.ScheduleType
|
@@ -547,7 +435,7 @@ def start(deploy: bool) -> List[multiprocessing.Process]:
|
|
547
435
|
mp_queue.wait_for_queues_to_be_ready(queue_names,
|
548
436
|
queue_server,
|
549
437
|
port=port)
|
550
|
-
elif queue_backend == QueueBackend.LOCAL:
|
438
|
+
elif queue_backend == server_config.QueueBackend.LOCAL:
|
551
439
|
# No setup is needed for local queue backend.
|
552
440
|
pass
|
553
441
|
else:
|
@@ -563,40 +451,13 @@ def start(deploy: bool) -> List[multiprocessing.Process]:
|
|
563
451
|
thread = threading.Thread(target=worker.run, daemon=True)
|
564
452
|
thread.start()
|
565
453
|
|
566
|
-
burstable_parallelism = _BURSTABLE_WORKERS_FOR_LOCAL if not deploy else 0
|
567
454
|
# Start a worker for long requests.
|
568
455
|
long_worker = RequestWorker(schedule_type=api_requests.ScheduleType.LONG,
|
569
|
-
|
570
|
-
burstable_parallelism=burstable_parallelism)
|
456
|
+
config=config.long_worker_config)
|
571
457
|
run_worker_in_background(long_worker)
|
572
458
|
|
573
459
|
# Start a worker for short requests.
|
574
460
|
short_worker = RequestWorker(schedule_type=api_requests.ScheduleType.SHORT,
|
575
|
-
|
576
|
-
burstable_parallelism=burstable_parallelism)
|
461
|
+
config=config.short_worker_config)
|
577
462
|
run_worker_in_background(short_worker)
|
578
463
|
return sub_procs
|
579
|
-
|
580
|
-
|
581
|
-
@annotations.lru_cache(scope='global', maxsize=1)
|
582
|
-
def _max_long_worker_parallism(cpu_count: int,
|
583
|
-
mem_size_gb: float,
|
584
|
-
local=False) -> int:
|
585
|
-
"""Max parallelism for long workers."""
|
586
|
-
cpu_based_max_parallel = cpu_count * _CPU_MULTIPLIER_FOR_LONG_WORKERS
|
587
|
-
mem_based_max_parallel = int(mem_size_gb * _MAX_MEM_PERCENT_FOR_BLOCKING /
|
588
|
-
_LONG_WORKER_MEM_GB)
|
589
|
-
n = max(_MIN_LONG_WORKERS,
|
590
|
-
min(cpu_based_max_parallel, mem_based_max_parallel))
|
591
|
-
if local:
|
592
|
-
return min(n, _MAX_LONG_WORKERS_LOCAL)
|
593
|
-
return n
|
594
|
-
|
595
|
-
|
596
|
-
@annotations.lru_cache(scope='global', maxsize=1)
|
597
|
-
def _max_short_worker_parallism(mem_size_gb: float,
|
598
|
-
long_worker_parallism: int) -> int:
|
599
|
-
"""Max parallelism for short workers."""
|
600
|
-
available_mem = mem_size_gb - (long_worker_parallism * _LONG_WORKER_MEM_GB)
|
601
|
-
n = max(_MIN_SHORT_WORKERS, int(available_mem / _SHORT_WORKER_MEM_GB))
|
602
|
-
return n
|
sky/server/server.py
CHANGED
@@ -35,6 +35,7 @@ from sky.jobs.server import server as jobs_rest
|
|
35
35
|
from sky.provision.kubernetes import utils as kubernetes_utils
|
36
36
|
from sky.serve.server import server as serve_rest
|
37
37
|
from sky.server import common
|
38
|
+
from sky.server import config as server_config
|
38
39
|
from sky.server import constants as server_constants
|
39
40
|
from sky.server import stream_utils
|
40
41
|
from sky.server.requests import executor
|
@@ -1166,13 +1167,12 @@ if __name__ == '__main__':
|
|
1166
1167
|
# that it is shown only when the API server is started.
|
1167
1168
|
usage_lib.maybe_show_privacy_policy()
|
1168
1169
|
|
1169
|
-
|
1170
|
-
|
1171
|
-
num_workers = common_utils.get_cpu_count()
|
1170
|
+
config = server_config.compute_server_config(cmd_args.deploy)
|
1171
|
+
num_workers = config.num_server_workers
|
1172
1172
|
|
1173
1173
|
sub_procs = []
|
1174
1174
|
try:
|
1175
|
-
sub_procs = executor.start(
|
1175
|
+
sub_procs = executor.start(config)
|
1176
1176
|
logger.info(f'Starting SkyPilot API server, workers={num_workers}')
|
1177
1177
|
# We don't support reload for now, since it may cause leakage of request
|
1178
1178
|
# workers or interrupt running requests.
|
sky/setup_files/dependencies.py
CHANGED
sky/skylet/constants.py
CHANGED
@@ -368,6 +368,13 @@ ROLE_ASSIGNMENT_FAILURE_ERROR_MSG = (
|
|
368
368
|
'Failed to assign Storage Blob Data Owner role to the '
|
369
369
|
'storage account {storage_account_name}.')
|
370
370
|
|
371
|
+
# Constants for path in K8S pod to store persistent setup and run scripts
|
372
|
+
# so that we can run them again after the pod restarts.
|
373
|
+
# Path within user home. For HA controller, assumes home directory is
|
374
|
+
# persistent through PVC. See kubernetes-ray.yml.j2.
|
375
|
+
PERSISTENT_SETUP_SCRIPT_PATH = '~/.sky/.controller_recovery_setup_commands.sh'
|
376
|
+
PERSISTENT_RUN_SCRIPT_DIR = '~/.sky/.controller_recovery_task_run'
|
377
|
+
|
371
378
|
# The placeholder for the local skypilot config path in file mounts for
|
372
379
|
# controllers.
|
373
380
|
LOCAL_SKYPILOT_CONFIG_PATH_PLACEHOLDER = 'skypilot:local_skypilot_config_path'
|
sky/skypilot_config.py
CHANGED
@@ -56,8 +56,6 @@ import threading
|
|
56
56
|
import typing
|
57
57
|
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
58
58
|
|
59
|
-
from omegaconf import OmegaConf
|
60
|
-
|
61
59
|
from sky import exceptions
|
62
60
|
from sky import sky_logging
|
63
61
|
from sky.adaptors import common as adaptors_common
|
@@ -321,6 +319,31 @@ def _parse_config_file(config_path: str) -> config_utils.Config:
|
|
321
319
|
return config
|
322
320
|
|
323
321
|
|
322
|
+
def _parse_dotlist(dotlist: List[str]) -> config_utils.Config:
|
323
|
+
"""Parse a comma-separated list of key-value pairs into a dictionary.
|
324
|
+
|
325
|
+
Args:
|
326
|
+
dotlist: A comma-separated list of key-value pairs.
|
327
|
+
|
328
|
+
Returns:
|
329
|
+
A config_utils.Config object with the parsed key-value pairs.
|
330
|
+
"""
|
331
|
+
config: config_utils.Config = config_utils.Config()
|
332
|
+
for arg in dotlist:
|
333
|
+
try:
|
334
|
+
key, value = arg.split('=', 1)
|
335
|
+
except ValueError as e:
|
336
|
+
raise ValueError(f'Invalid config override: {arg}. '
|
337
|
+
'Please use the format: key=value') from e
|
338
|
+
if len(key) == 0 or len(value) == 0:
|
339
|
+
raise ValueError(f'Invalid config override: {arg}. '
|
340
|
+
'Please use the format: key=value')
|
341
|
+
value = yaml.safe_load(value)
|
342
|
+
nested_keys = tuple(key.split('.'))
|
343
|
+
config.set_nested(nested_keys, value)
|
344
|
+
return config
|
345
|
+
|
346
|
+
|
324
347
|
def _reload_config_from_internal_file(internal_config_path: str) -> None:
|
325
348
|
global _dict, _loaded_config_path
|
326
349
|
# Reset the global variables, to avoid using stale values.
|
@@ -483,11 +506,9 @@ def _compose_cli_config(cli_config: Optional[List[str]]) -> config_utils.Config:
|
|
483
506
|
'Cannot use multiple --config flags with a config file.')
|
484
507
|
config_source = maybe_config_path
|
485
508
|
# cli_config is a path to a config file
|
486
|
-
parsed_config =
|
487
|
-
OmegaConf.load(maybe_config_path))
|
509
|
+
parsed_config = _parse_config_file(maybe_config_path)
|
488
510
|
else: # cli_config is a comma-separated list of key-value pairs
|
489
|
-
parsed_config =
|
490
|
-
OmegaConf.from_dotlist(cli_config))
|
511
|
+
parsed_config = _parse_dotlist(cli_config)
|
491
512
|
_validate_config(parsed_config, config_source)
|
492
513
|
except ValueError as e:
|
493
514
|
raise ValueError(f'Invalid config override: {cli_config}. '
|
sky/task.py
CHANGED
@@ -306,7 +306,7 @@ class Task:
|
|
306
306
|
self.service_name: Optional[str] = None
|
307
307
|
|
308
308
|
# Filled in by the optimizer. If None, this Task is not planned.
|
309
|
-
self.best_resources = None
|
309
|
+
self.best_resources: Optional[sky.Resources] = None
|
310
310
|
|
311
311
|
# For internal use only.
|
312
312
|
self.file_mounts_mapping = file_mounts_mapping
|