pathwaysutils 0.1.11__tar.gz → 0.1.12__tar.gz
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.
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/PKG-INFO +17 -2
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/__init__.py +1 -1
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/_initialize.py +9 -5
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/collect_profile.py +4 -1
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/elastic/elastic.py +7 -2
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/elastic/manager.py +18 -16
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/gke/jobset.py +100 -102
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/README.md +43 -14
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/deploy_pathways_service.py +79 -9
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/gke_utils.py +376 -56
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/isc_pathways.py +269 -97
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/metrics_collector.py +9 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/run_connect_example.py +35 -7
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/run_workload.py +28 -7
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/validators.py +0 -16
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/persistence/helper.py +0 -13
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/profiling.py +131 -30
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/proxy_backend.py +14 -4
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/experimental/gke/jobset_test.py +77 -204
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/experimental/gke/testdata/model_lite_tpuv5e_4x8.yaml +0 -4
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/deploy_pathways_service_test.py +313 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/gke_utils_test.py +1599 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/isc_pathways_test.py +1507 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/metrics_collector_test.py +247 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/run_workload_test.py +240 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/start_vscode_on_cpu_np_test.py +324 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/tpu_specs_test.py +77 -0
- pathwaysutils-0.1.12/pathwaysutils/test/experimental/shared_pathways_service/validators_test.py +314 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/initialize_test.py +15 -0
- pathwaysutils-0.1.12/pathwaysutils/test/mock_fault_injection.py +293 -0
- pathwaysutils-0.1.12/pathwaysutils/test/mock_fault_injection_test.py +201 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/profiling_test.py +334 -18
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/proxy_backend_test.py +41 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pyproject.toml +20 -1
- pathwaysutils-0.1.11/pathwaysutils/persistence/orbax_handler.py +0 -292
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/LICENSE +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/README.md +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/debug/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/debug/timing.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/debug/watchdog.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/elastic/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/concatenate_by_mesh_axis.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/gke/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/profiling.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/reshard.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/dockerfiles/sample_sidecar.dockerfile +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/start_vscode_on_cpu_np.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/tpu_specs.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/yamls/code-server.yaml +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/shared_pathways_service/yamls/pw-proxy.yaml +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/experimental/split_by_mesh_axis.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/jax/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/lru_cache.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/persistence/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/plugin_executable.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/reshard.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/debug/__init__.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/debug/timing_test.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/debug/watchdog_test.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/experimental/reshard_test.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/lru_cache_test.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/persistence_test.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/plugin_executable_test.py +0 -0
- {pathwaysutils-0.1.11 → pathwaysutils-0.1.12}/pathwaysutils/test/reshard_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pathwaysutils
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: Package of Pathways-on-Cloud utilities.
|
|
5
5
|
Keywords:
|
|
6
6
|
Author-email: Pathways-on-Cloud Utilities Developers <pathwaysutils-dev@google.com>
|
|
@@ -11,16 +11,31 @@ License-File: LICENSE
|
|
|
11
11
|
Requires-Dist: absl-py
|
|
12
12
|
Requires-Dist: fastapi
|
|
13
13
|
Requires-Dist: jax>=0.8.3
|
|
14
|
-
Requires-Dist: orbax-checkpoint
|
|
14
|
+
Requires-Dist: orbax-checkpoint>=0.12.3
|
|
15
15
|
Requires-Dist: uvicorn
|
|
16
16
|
Requires-Dist: requests
|
|
17
17
|
Requires-Dist: packaging
|
|
18
18
|
Requires-Dist: pylint>=2.6.0 ; extra == "dev"
|
|
19
19
|
Requires-Dist: pyink ; extra == "dev"
|
|
20
|
+
Requires-Dist: kubernetes ; extra == "gke"
|
|
21
|
+
Requires-Dist: pyyaml ; extra == "gke"
|
|
22
|
+
Requires-Dist: portpicker ; extra == "sps"
|
|
23
|
+
Requires-Dist: google-cloud-monitoring ; extra == "sps"
|
|
24
|
+
Requires-Dist: portpicker ; extra == "sps-without-monitoring"
|
|
25
|
+
Requires-Dist: google-cloud-monitoring ; extra == "test"
|
|
26
|
+
Requires-Dist: httpx ; extra == "test"
|
|
27
|
+
Requires-Dist: kubernetes ; extra == "test"
|
|
28
|
+
Requires-Dist: portpicker ; extra == "test"
|
|
29
|
+
Requires-Dist: pyyaml ; extra == "test"
|
|
30
|
+
Requires-Dist: pytest ; extra == "test"
|
|
20
31
|
Project-URL: changelog, https://github.com/AI-Hypercomputer/pathways-utils/blob/main/CHANGELOG.md
|
|
21
32
|
Project-URL: homepage, https://github.com/AI-Hypercomputer/pathways-utils
|
|
22
33
|
Project-URL: repository, https://github.com/AI-Hypercomputer/pathways-utils
|
|
23
34
|
Provides-Extra: dev
|
|
35
|
+
Provides-Extra: gke
|
|
36
|
+
Provides-Extra: sps
|
|
37
|
+
Provides-Extra: sps-without-monitoring
|
|
38
|
+
Provides-Extra: test
|
|
24
39
|
|
|
25
40
|
# Package of Pathways-on-Cloud utilities
|
|
26
41
|
|
|
@@ -18,10 +18,11 @@ import logging
|
|
|
18
18
|
import os
|
|
19
19
|
|
|
20
20
|
import jax
|
|
21
|
+
from orbax.checkpoint import type_handlers
|
|
21
22
|
from orbax.checkpoint._src.metadata import array_metadata_store as array_metadata_store_lib
|
|
23
|
+
from orbax.checkpoint._src.serialization import cloud_pathways_array_handler
|
|
22
24
|
from pathwaysutils import profiling
|
|
23
25
|
from pathwaysutils import proxy_backend
|
|
24
|
-
from pathwaysutils.persistence import orbax_handler
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
_logger = logging.getLogger(__name__)
|
|
@@ -91,11 +92,14 @@ def initialize() -> None:
|
|
|
91
92
|
_logger.debug("Detected Pathways-on-Cloud backend. Applying changes.")
|
|
92
93
|
proxy_backend.register_backend_factory()
|
|
93
94
|
profiling.monkey_patch_jax()
|
|
94
|
-
# TODO: b/365549911 - Remove when OCDBT-compatible
|
|
95
95
|
if _is_persistence_enabled():
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
type_handlers.register_type_handler(
|
|
97
|
+
jax.Array,
|
|
98
|
+
cloud_pathways_array_handler.CloudPathwaysArrayHandler(
|
|
99
|
+
timeout=datetime.timedelta(hours=1),
|
|
100
|
+
array_metadata_store=array_metadata_store_lib.Store(),
|
|
101
|
+
),
|
|
102
|
+
override=True,
|
|
99
103
|
)
|
|
100
104
|
|
|
101
105
|
# Turn off JAX compilation cache because Pathways handles its own
|
|
@@ -67,7 +67,10 @@ def main() -> None:
|
|
|
67
67
|
args = parser.parse_args()
|
|
68
68
|
|
|
69
69
|
if profiling.collect_profile(
|
|
70
|
-
args.port,
|
|
70
|
+
args.port,
|
|
71
|
+
args.duration_ms,
|
|
72
|
+
args.host,
|
|
73
|
+
args.log_dir,
|
|
71
74
|
):
|
|
72
75
|
_logger.info("Dumped profiling information in: %s", args.log_dir)
|
|
73
76
|
else:
|
|
@@ -171,7 +171,7 @@ def get_active_slice_indices(
|
|
|
171
171
|
|
|
172
172
|
|
|
173
173
|
def wait_for_slices(
|
|
174
|
-
slice_count: int,
|
|
174
|
+
slice_count: int | None = None,
|
|
175
175
|
poll_interval: float | int = 10,
|
|
176
176
|
timeout: float | int | None = None,
|
|
177
177
|
slice_to_devices: Mapping[int, Sequence[jax.Device]] | None = None,
|
|
@@ -180,7 +180,8 @@ def wait_for_slices(
|
|
|
180
180
|
"""Waits until after at least `slice_count` slices become active.
|
|
181
181
|
|
|
182
182
|
Args:
|
|
183
|
-
slice_count: The number of slices to wait for.
|
|
183
|
+
slice_count: The number of slices to wait for. If None, defaults to the
|
|
184
|
+
total number of slices.
|
|
184
185
|
poll_interval: The minimum number of seconds to wait between availability
|
|
185
186
|
checks. If the check takes longer than this, the next check will start
|
|
186
187
|
immediately after the current check completes. Defaults to 10 seconds.
|
|
@@ -201,6 +202,10 @@ def wait_for_slices(
|
|
|
201
202
|
_logger.debug("slice_to_devices is None. Getting from jax.devices().")
|
|
202
203
|
slice_to_devices = get_slice_to_devices(jax.devices())
|
|
203
204
|
|
|
205
|
+
if slice_count is None:
|
|
206
|
+
_logger.debug("slice_count is None. Using len(slice_to_devices).")
|
|
207
|
+
slice_count = len(slice_to_devices)
|
|
208
|
+
|
|
204
209
|
_logger.info(
|
|
205
210
|
"Waiting for %s slices. Poll interval: %s, Timeout: %s",
|
|
206
211
|
slice_count,
|
|
@@ -116,11 +116,12 @@ class Manager:
|
|
|
116
116
|
|
|
117
117
|
self.all_slice_indices = frozenset(self.slice_to_devices.keys())
|
|
118
118
|
|
|
119
|
+
self._active_slice_indices = frozenset()
|
|
120
|
+
self.available_inactive_slices = frozenset()
|
|
121
|
+
|
|
119
122
|
self.active_slice_indices = elastic.get_active_slice_indices(
|
|
120
123
|
slice_to_devices=self.slice_to_devices
|
|
121
124
|
)
|
|
122
|
-
self.inactive_slice_indices = self.all_slice_indices - self.active_slice_indices
|
|
123
|
-
self.available_inactive_slices = frozenset()
|
|
124
125
|
|
|
125
126
|
self._stop_event = None
|
|
126
127
|
self._monitor_thread = None
|
|
@@ -180,22 +181,26 @@ class Manager:
|
|
|
180
181
|
except StopIteration as error:
|
|
181
182
|
raise ValueError("No active slices") from error
|
|
182
183
|
|
|
184
|
+
@property
|
|
185
|
+
def active_slice_indices(self) -> frozenset[int]:
|
|
186
|
+
"""The indices of active slices."""
|
|
187
|
+
return self._active_slice_indices
|
|
188
|
+
|
|
189
|
+
@active_slice_indices.setter
|
|
190
|
+
def active_slice_indices(self, value: Set[int]) -> None:
|
|
191
|
+
self._active_slice_indices = frozenset(value)
|
|
192
|
+
self.inactive_slice_indices = (
|
|
193
|
+
self.all_slice_indices - self._active_slice_indices
|
|
194
|
+
)
|
|
195
|
+
self.available_inactive_slices = frozenset(
|
|
196
|
+
self.available_inactive_slices - self._active_slice_indices
|
|
197
|
+
)
|
|
198
|
+
|
|
183
199
|
@property
|
|
184
200
|
def active_slice_count(self) -> int:
|
|
185
201
|
"""The number of active slices."""
|
|
186
202
|
return len(self.active_slice_indices)
|
|
187
203
|
|
|
188
|
-
@property
|
|
189
|
-
def new_slice_event(self) -> threading.Event:
|
|
190
|
-
"""Deprecated compatibility property for un-updated MaxText code.
|
|
191
|
-
|
|
192
|
-
TODO: b/527183831 - Remove this property once MaxText CL 2 is submitted.
|
|
193
|
-
"""
|
|
194
|
-
event = threading.Event()
|
|
195
|
-
if self.available_inactive_slices:
|
|
196
|
-
event.set()
|
|
197
|
-
return event
|
|
198
|
-
|
|
199
204
|
def scale_by_active_slices(self, x: int | float) -> int | float:
|
|
200
205
|
"""Scale x by the number of active slices."""
|
|
201
206
|
if isinstance(x, int):
|
|
@@ -372,9 +377,6 @@ class Manager:
|
|
|
372
377
|
poll_interval=poll_interval,
|
|
373
378
|
timeout=timeout,
|
|
374
379
|
)
|
|
375
|
-
self.inactive_slice_indices = (
|
|
376
|
-
self.all_slice_indices - self.active_slice_indices
|
|
377
|
-
)
|
|
378
380
|
# Reset available_inactive_slices at attempt start since
|
|
379
381
|
# active_slice_indices has just been updated by wait_for_slices.
|
|
380
382
|
self.available_inactive_slices = frozenset()
|
|
@@ -11,17 +11,27 @@
|
|
|
11
11
|
# limitations under the License.
|
|
12
12
|
"""Pathways JobSet generator and builder (with Worker Job Config)."""
|
|
13
13
|
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
14
16
|
import hashlib
|
|
15
17
|
import json
|
|
16
18
|
import logging
|
|
17
19
|
import math
|
|
18
20
|
import time
|
|
19
|
-
from typing import Any, Mapping, Sequence
|
|
20
|
-
from kubernetes import client
|
|
21
|
-
from kubernetes import config as k8s_config
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Mapping, Sequence
|
|
22
22
|
import yaml
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
try:
|
|
25
|
+
import kubernetes
|
|
26
|
+
except ImportError as e:
|
|
27
|
+
raise ImportError(
|
|
28
|
+
"GKE utilities require `kubernetes`. "
|
|
29
|
+
"Please install pathwaysutils with GKE support:\n\n"
|
|
30
|
+
" pip install 'pathwaysutils[gke]'\n"
|
|
31
|
+
) from e
|
|
32
|
+
|
|
33
|
+
from kubernetes import client
|
|
34
|
+
from kubernetes import config as k8s_config
|
|
25
35
|
|
|
26
36
|
_logger = logging.getLogger(__name__)
|
|
27
37
|
|
|
@@ -71,8 +81,18 @@ MACHINE_TYPE_TO_GKE_ACCELERATOR_TYPE_MAP = {
|
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
|
|
84
|
+
def _format_image(image: str, default_tag: str) -> str:
|
|
85
|
+
"""Appends default_tag if image does not already specify a tag or digest."""
|
|
86
|
+
if "@" in image:
|
|
87
|
+
return image
|
|
88
|
+
last_slash = image.rfind("/")
|
|
89
|
+
if ":" in image[last_slash + 1:]:
|
|
90
|
+
return image
|
|
91
|
+
return f"{image}:{default_tag}"
|
|
92
|
+
|
|
93
|
+
|
|
74
94
|
def _deserialize_dict(
|
|
75
|
-
api_client:
|
|
95
|
+
api_client: Any, data_dict: Mapping[str, Any], klass: Any
|
|
76
96
|
) -> Any:
|
|
77
97
|
class FakeResponse:
|
|
78
98
|
|
|
@@ -93,10 +113,8 @@ class PathwaysJobSet:
|
|
|
93
113
|
tpu_type: str,
|
|
94
114
|
topology: str,
|
|
95
115
|
num_slices: int,
|
|
96
|
-
user_pod_template: Mapping[str, Any] | None = None,
|
|
97
|
-
main_container_name: str = "main",
|
|
98
116
|
max_restarts: int = 0,
|
|
99
|
-
max_slice_restarts: int =
|
|
117
|
+
max_slice_restarts: int = 1_000_000,
|
|
100
118
|
termination_grace_period_seconds: int | None = None,
|
|
101
119
|
pathways_version: str = "latest",
|
|
102
120
|
jobset_api_version: str = "v1alpha2",
|
|
@@ -104,6 +122,8 @@ class PathwaysJobSet:
|
|
|
104
122
|
labels: Mapping[str, str] | None = None,
|
|
105
123
|
annotations: Mapping[str, str] | None = None,
|
|
106
124
|
shared_pathways_service: bool = False,
|
|
125
|
+
pathways_rm_and_worker_image: str = DEFAULT_PATHWAYS_RM_AND_WORKER_IMAGE,
|
|
126
|
+
pathways_proxy_image: str = DEFAULT_PATHWAYS_PROXY_IMAGE,
|
|
107
127
|
):
|
|
108
128
|
"""Initializes the instance.
|
|
109
129
|
|
|
@@ -114,23 +134,22 @@ class PathwaysJobSet:
|
|
|
114
134
|
tpu_type: TPU type (e.g., "v5e").
|
|
115
135
|
topology: TPU topology (e.g., "2x2").
|
|
116
136
|
num_slices: Number of slices.
|
|
117
|
-
user_pod_template: Optional user pod template for the head job.
|
|
118
|
-
main_container_name: Name of the main container in user_pod_template.
|
|
119
137
|
max_restarts: Maximum number of restarts for the JobSet.
|
|
120
|
-
max_slice_restarts: Maximum number of slice restarts.
|
|
138
|
+
max_slice_restarts: Maximum number of slice restarts (defaults to 1_000_000 in headless and SPS mode).
|
|
121
139
|
termination_grace_period_seconds: Optional termination grace period.
|
|
122
140
|
pathways_version: Version tag for Pathways images.
|
|
123
141
|
jobset_api_version: API version of JobSet.
|
|
124
142
|
elastic_slices: Number of elastic slices.
|
|
125
143
|
labels: Optional labels for the JobSet.
|
|
126
144
|
annotations: Optional annotations for the JobSet.
|
|
145
|
+
shared_pathways_service: Whether to run only RM for Shared Pathways Service.
|
|
146
|
+
pathways_rm_and_worker_image: Base Docker image for Resource Manager and
|
|
147
|
+
Worker containers.
|
|
148
|
+
pathways_proxy_image: Base Docker image for Proxy container.
|
|
127
149
|
"""
|
|
128
|
-
if shared_pathways_service and user_pod_template:
|
|
129
|
-
raise ValueError(
|
|
130
|
-
"Cannot enable shared_pathways_service when user_pod_template is"
|
|
131
|
-
" provided."
|
|
132
|
-
)
|
|
133
150
|
self._shared_pathways_service = shared_pathways_service
|
|
151
|
+
self._pathways_rm_and_worker_image = pathways_rm_and_worker_image
|
|
152
|
+
self._pathways_proxy_image = pathways_proxy_image
|
|
134
153
|
|
|
135
154
|
self._name = name
|
|
136
155
|
self._namespace = namespace
|
|
@@ -140,6 +159,11 @@ class PathwaysJobSet:
|
|
|
140
159
|
self._labels = dict(labels) if labels else {}
|
|
141
160
|
self._annotations = dict(annotations) if annotations else {}
|
|
142
161
|
|
|
162
|
+
if elastic_slices > num_slices:
|
|
163
|
+
raise ValueError(
|
|
164
|
+
f"elastic_slices ({elastic_slices}) cannot be greater than "
|
|
165
|
+
f"num_slices ({num_slices})"
|
|
166
|
+
)
|
|
143
167
|
tpu_version = MACHINE_TYPE_TO_TPU_VERSION_MAP.get(tpu_type.lower())
|
|
144
168
|
if not tpu_version:
|
|
145
169
|
raise ValueError(f"Unsupported TPU type: {tpu_type}")
|
|
@@ -166,10 +190,10 @@ class PathwaysJobSet:
|
|
|
166
190
|
num_slices=num_slices,
|
|
167
191
|
instance_type=instance_type,
|
|
168
192
|
image_tag=image_tag,
|
|
169
|
-
user_pod_template=user_pod_template,
|
|
170
|
-
main_container_name=main_container_name,
|
|
171
193
|
elastic_slices=elastic_slices,
|
|
172
194
|
shared_pathways_service=shared_pathways_service,
|
|
195
|
+
pathways_rm_and_worker_image=pathways_rm_and_worker_image,
|
|
196
|
+
pathways_proxy_image=pathways_proxy_image,
|
|
173
197
|
)
|
|
174
198
|
|
|
175
199
|
# Build worker template.
|
|
@@ -182,10 +206,11 @@ class PathwaysJobSet:
|
|
|
182
206
|
image_tag=image_tag,
|
|
183
207
|
max_slice_restarts=max_slice_restarts,
|
|
184
208
|
termination_grace_period_seconds=termination_grace_period_seconds,
|
|
209
|
+
pathways_rm_and_worker_image=pathways_rm_and_worker_image,
|
|
185
210
|
)
|
|
186
211
|
|
|
187
212
|
self._success_policy = None
|
|
188
|
-
if
|
|
213
|
+
if shared_pathways_service:
|
|
189
214
|
self._success_policy = {
|
|
190
215
|
"operator": "All",
|
|
191
216
|
"targetReplicatedJobs": [PATHWAYS_HEAD_JOB_NAME],
|
|
@@ -199,16 +224,24 @@ class PathwaysJobSet:
|
|
|
199
224
|
def worker_job_template(self) -> client.V1JobTemplateSpec:
|
|
200
225
|
return self._worker_job_template
|
|
201
226
|
|
|
227
|
+
@property
|
|
228
|
+
def pathways_rm_and_worker_image(self) -> str:
|
|
229
|
+
return self._pathways_rm_and_worker_image
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def pathways_proxy_image(self) -> str:
|
|
233
|
+
return self._pathways_proxy_image
|
|
234
|
+
|
|
202
235
|
def _build_head_job_template(
|
|
203
236
|
self,
|
|
204
237
|
pathways_dir: str,
|
|
205
238
|
num_slices: int,
|
|
206
239
|
instance_type: str,
|
|
207
240
|
image_tag: str,
|
|
208
|
-
user_pod_template: Mapping[str, Any] | None,
|
|
209
|
-
main_container_name: str,
|
|
210
241
|
elastic_slices: int,
|
|
211
242
|
shared_pathways_service: bool,
|
|
243
|
+
pathways_rm_and_worker_image: str,
|
|
244
|
+
pathways_proxy_image: str,
|
|
212
245
|
) -> client.V1JobTemplateSpec:
|
|
213
246
|
"""Builds the head job template for the JobSet.
|
|
214
247
|
|
|
@@ -217,15 +250,16 @@ class PathwaysJobSet:
|
|
|
217
250
|
num_slices: Number of slices.
|
|
218
251
|
instance_type: TPU instance type (e.g., "tpuv5:2x2").
|
|
219
252
|
image_tag: Version tag for Pathways images.
|
|
220
|
-
user_pod_template: Optional user pod template for the head job.
|
|
221
|
-
main_container_name: Name of the main container in user_pod_template.
|
|
222
253
|
elastic_slices: Number of elastic slices.
|
|
254
|
+
shared_pathways_service: Whether to run only RM for Shared Pathways Service.
|
|
255
|
+
pathways_rm_and_worker_image: Base Docker image for Resource Manager.
|
|
256
|
+
pathways_proxy_image: Base Docker image for Proxy container.
|
|
223
257
|
|
|
224
258
|
Returns:
|
|
225
259
|
The head job template.
|
|
226
260
|
"""
|
|
227
|
-
rm_image =
|
|
228
|
-
proxy_image =
|
|
261
|
+
rm_image = _format_image(pathways_rm_and_worker_image, image_tag)
|
|
262
|
+
proxy_image = _format_image(pathways_proxy_image, image_tag)
|
|
229
263
|
|
|
230
264
|
rm_args = [
|
|
231
265
|
f"--server_port={PATHWAYS_RM_PORT}",
|
|
@@ -318,74 +352,18 @@ class PathwaysJobSet:
|
|
|
318
352
|
),
|
|
319
353
|
)
|
|
320
354
|
|
|
321
|
-
|
|
355
|
+
containers = [rm_container]
|
|
356
|
+
if not shared_pathways_service:
|
|
357
|
+
containers.append(proxy_container)
|
|
322
358
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
head_pod_spec = user_template_obj.spec
|
|
328
|
-
head_pod_spec.host_network = True
|
|
329
|
-
head_pod_spec.dns_policy = "ClusterFirstWithHostNet"
|
|
330
|
-
|
|
331
|
-
rm_container.restart_policy = "Always" # pyrefly: ignore[missing-attribute]
|
|
332
|
-
proxy_container.restart_policy = "Always" # pyrefly: ignore[missing-attribute]
|
|
333
|
-
|
|
334
|
-
init_containers = head_pod_spec.init_containers or []
|
|
335
|
-
init_containers.extend([rm_container, proxy_container])
|
|
336
|
-
head_pod_spec.init_containers = init_containers
|
|
337
|
-
|
|
338
|
-
# Inject JAX env vars into main container.
|
|
339
|
-
jax_env = [
|
|
340
|
-
client.V1EnvVar(
|
|
341
|
-
name="PATHWAYS_HEAD",
|
|
342
|
-
value_from=client.V1EnvVarSource(
|
|
343
|
-
field_ref=client.V1ObjectFieldSelector(
|
|
344
|
-
field_path=(
|
|
345
|
-
"metadata.labels['jobset.sigs.k8s.io/coordinator']"
|
|
346
|
-
)
|
|
347
|
-
)
|
|
348
|
-
),
|
|
349
|
-
),
|
|
350
|
-
client.V1EnvVar(name="JAX_PLATFORMS", value="proxy"),
|
|
351
|
-
client.V1EnvVar(name="XCLOUD_ENVIRONMENT", value="GCP"),
|
|
352
|
-
client.V1EnvVar(
|
|
353
|
-
name="JAX_BACKEND_TARGET",
|
|
354
|
-
value=f"grpc://$(PATHWAYS_HEAD):{PATHWAYS_PROXY_PORT}",
|
|
355
|
-
),
|
|
356
|
-
]
|
|
357
|
-
containers = head_pod_spec.containers or []
|
|
358
|
-
for c in containers:
|
|
359
|
-
if c.name == main_container_name:
|
|
360
|
-
env = c.env or []
|
|
361
|
-
env.extend(jax_env)
|
|
362
|
-
c.env = env
|
|
363
|
-
break
|
|
364
|
-
head_pod_spec.containers = containers
|
|
365
|
-
|
|
366
|
-
annotations = user_pod_template.get("metadata", {}).get("annotations", {})
|
|
367
|
-
labels = user_pod_template.get("metadata", {}).get("labels", {})
|
|
368
|
-
else:
|
|
369
|
-
# Headless mode.
|
|
370
|
-
containers = [rm_container]
|
|
371
|
-
if not shared_pathways_service:
|
|
372
|
-
containers.append(proxy_container)
|
|
373
|
-
head_pod_spec = client.V1PodSpec(
|
|
374
|
-
host_network=True,
|
|
375
|
-
dns_policy="ClusterFirstWithHostNet",
|
|
376
|
-
containers=containers,
|
|
377
|
-
)
|
|
378
|
-
annotations = {}
|
|
379
|
-
labels = {}
|
|
380
|
-
|
|
381
|
-
if not head_pod_spec.restart_policy:
|
|
382
|
-
head_pod_spec.restart_policy = "Never"
|
|
359
|
+
head_pod_spec = client.V1PodSpec(
|
|
360
|
+
containers=containers,
|
|
361
|
+
restart_policy="Never",
|
|
362
|
+
)
|
|
383
363
|
|
|
384
|
-
# Default annotations
|
|
385
364
|
job_annotations = {
|
|
386
365
|
"alpha.jobset.sigs.k8s.io/exclusive-topology": "kubernetes.io/hostname"
|
|
387
366
|
}
|
|
388
|
-
job_annotations.update(annotations)
|
|
389
367
|
|
|
390
368
|
head_job_template = client.V1JobTemplateSpec(
|
|
391
369
|
metadata=client.V1ObjectMeta(annotations=job_annotations),
|
|
@@ -396,7 +374,7 @@ class PathwaysJobSet:
|
|
|
396
374
|
parallelism=1,
|
|
397
375
|
template=client.V1PodTemplateSpec(
|
|
398
376
|
metadata=client.V1ObjectMeta(
|
|
399
|
-
annotations=job_annotations, labels=
|
|
377
|
+
annotations=job_annotations, labels={}
|
|
400
378
|
),
|
|
401
379
|
spec=head_pod_spec,
|
|
402
380
|
),
|
|
@@ -414,8 +392,10 @@ class PathwaysJobSet:
|
|
|
414
392
|
image_tag: str,
|
|
415
393
|
max_slice_restarts: int,
|
|
416
394
|
termination_grace_period_seconds: int | None,
|
|
395
|
+
pathways_rm_and_worker_image: str,
|
|
417
396
|
) -> client.V1JobTemplateSpec:
|
|
418
|
-
|
|
397
|
+
"""Builds the worker job template for the JobSet."""
|
|
398
|
+
worker_image = _format_image(pathways_rm_and_worker_image, image_tag)
|
|
419
399
|
|
|
420
400
|
args = [
|
|
421
401
|
f"--resource_manager_address=$(PATHWAYS_HEAD):{PATHWAYS_RM_PORT}",
|
|
@@ -510,9 +490,7 @@ class PathwaysJobSet:
|
|
|
510
490
|
"cloud.google.com/gke-tpu-topology": topology,
|
|
511
491
|
}
|
|
512
492
|
|
|
513
|
-
backoff_limit = num_vms *
|
|
514
|
-
if max_slice_restarts > 0:
|
|
515
|
-
backoff_limit = num_vms * max_slice_restarts
|
|
493
|
+
backoff_limit = num_vms * max_slice_restarts
|
|
516
494
|
|
|
517
495
|
worker_pod_spec = client.V1PodSpec(
|
|
518
496
|
containers=[worker_container],
|
|
@@ -525,8 +503,6 @@ class PathwaysJobSet:
|
|
|
525
503
|
),
|
|
526
504
|
)
|
|
527
505
|
],
|
|
528
|
-
host_network=True,
|
|
529
|
-
dns_policy="ClusterFirstWithHostNet",
|
|
530
506
|
restart_policy="OnFailure",
|
|
531
507
|
)
|
|
532
508
|
if termination_grace_period_seconds is not None:
|
|
@@ -637,14 +613,15 @@ class PathwaysJobSet:
|
|
|
637
613
|
ports=[client.V1ContainerPort(container_port=50051)],
|
|
638
614
|
volume_mounts=[
|
|
639
615
|
client.V1VolumeMount(name="shared-tmp", mount_path="/tmp"),
|
|
640
|
-
client.V1VolumeMount(
|
|
616
|
+
client.V1VolumeMount(
|
|
617
|
+
name=shm_volume_name, mount_path=shm_mount_path
|
|
618
|
+
),
|
|
641
619
|
],
|
|
642
620
|
)
|
|
643
|
-
colocated_container.restart_policy = "Always" # pyrefly: ignore[missing-attribute]
|
|
644
621
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
pod_spec.
|
|
622
|
+
containers = pod_spec.containers or []
|
|
623
|
+
containers.append(colocated_container)
|
|
624
|
+
pod_spec.containers = containers
|
|
648
625
|
|
|
649
626
|
# Add volume mount to pathways-worker.
|
|
650
627
|
for container in pod_spec.containers:
|
|
@@ -660,7 +637,7 @@ class PathwaysJobSet:
|
|
|
660
637
|
env = container.env or []
|
|
661
638
|
env.append(
|
|
662
639
|
client.V1EnvVar(
|
|
663
|
-
name="
|
|
640
|
+
name="CLOUD_PATHWAYS_SIDECAR_SHM_DIRECTORY",
|
|
664
641
|
value=shm_mount_path,
|
|
665
642
|
)
|
|
666
643
|
)
|
|
@@ -694,7 +671,9 @@ class PathwaysJobSet:
|
|
|
694
671
|
|
|
695
672
|
for job_template in (self._head_job_template, self._worker_job_template):
|
|
696
673
|
pod_spec = job_template.spec.template.spec
|
|
697
|
-
all_containers = (pod_spec.containers or []) + (
|
|
674
|
+
all_containers = (pod_spec.containers or []) + (
|
|
675
|
+
pod_spec.init_containers or []
|
|
676
|
+
)
|
|
698
677
|
|
|
699
678
|
matching = self._filter_matching_containers(containers, all_containers)
|
|
700
679
|
if not matching:
|
|
@@ -824,6 +803,25 @@ class PathwaysJobSet:
|
|
|
824
803
|
instance._head_job_template = head_job_template
|
|
825
804
|
instance._worker_job_template = worker_job_template
|
|
826
805
|
|
|
806
|
+
rm_image = DEFAULT_PATHWAYS_RM_AND_WORKER_IMAGE
|
|
807
|
+
proxy_image = DEFAULT_PATHWAYS_PROXY_IMAGE
|
|
808
|
+
if (
|
|
809
|
+
head_job_template.spec
|
|
810
|
+
and head_job_template.spec.template
|
|
811
|
+
and head_job_template.spec.template.spec
|
|
812
|
+
):
|
|
813
|
+
pod_spec = head_job_template.spec.template.spec
|
|
814
|
+
all_containers = (pod_spec.containers or []) + (
|
|
815
|
+
pod_spec.init_containers or []
|
|
816
|
+
)
|
|
817
|
+
for c in all_containers:
|
|
818
|
+
if c.name == "pathways-rm" and c.image:
|
|
819
|
+
rm_image = c.image
|
|
820
|
+
elif c.name == "pathways-proxy" and c.image:
|
|
821
|
+
proxy_image = c.image
|
|
822
|
+
instance._pathways_rm_and_worker_image = rm_image
|
|
823
|
+
instance._pathways_proxy_image = proxy_image
|
|
824
|
+
|
|
827
825
|
instance._success_policy = config["spec"].get("successPolicy")
|
|
828
826
|
return instance
|
|
829
827
|
|
|
@@ -16,13 +16,19 @@ You have a GKE cluster with at least 1 TPU slice (v5e, v5p or v6e).
|
|
|
16
16
|
|
|
17
17
|
### 2. Deploy the Pathways head pod
|
|
18
18
|
|
|
19
|
-
Start the Shared Pathways Service by
|
|
20
|
-
Make sure to modify the following values to deploy the Pathways pods:
|
|
19
|
+
Start the Shared Pathways Service by running `deploy_pathways_service.py`.
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
```shell
|
|
22
|
+
python3 -m pathwaysutils.experimental.shared_pathways_service.deploy_pathways_service \
|
|
23
|
+
--jobset_name ${SERVICE_JOBSET_NAME} \
|
|
24
|
+
--gcs_bucket ${GCS_BUCKET} \
|
|
25
|
+
--server_image ${SERVER_IMAGE} \
|
|
26
|
+
--tpu_type ${TPU_TYPE} \
|
|
27
|
+
--topology ${TOPOLOGY} \
|
|
28
|
+
--num_slices ${NUM_SLICES} [--sidecar_image ${SIDECAR_IMAGE}] [--dry_run]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Note, `server_image` is the Pathways server image, e.g, `us-docker.pkg.dev/cloud-tpu-v2-images/pathways/server:<tag>`.
|
|
26
32
|
|
|
27
33
|
### 3. Verify that the pods created in [Step#2](#2-deploy-the-pathways-head-pod) are running
|
|
28
34
|
|
|
@@ -55,13 +61,13 @@ pathways-cluster-worker-1-0-km2rf 1/1 Running 0 3m36s
|
|
|
55
61
|
|
|
56
62
|
```shell
|
|
57
63
|
# e.g., pathways-cluster
|
|
58
|
-
$
|
|
64
|
+
$ SERVICE_JOBSET_NAME=<your-jobset-name> # same as you used in [pw-service-example.yaml](#pw-service-yaml)
|
|
59
65
|
|
|
60
66
|
# e.g., pathways-cluster-pathways-head-0-0-zzmn2
|
|
61
|
-
$ HEAD_POD_NAME=$(kubectl get pods --selector=jobset.sigs.k8s.io/jobset-name=${
|
|
67
|
+
$ HEAD_POD_NAME=$(kubectl get pods --selector=jobset.sigs.k8s.io/jobset-name=${SERVICE_JOBSET_NAME} -o jsonpath='{.items[?(@.status.phase=="Running")].metadata.name}' | sed 's/ /\n/g' | grep head)
|
|
62
68
|
|
|
63
69
|
# e.g., pathways-cluster-worker-0-0-bdzq4
|
|
64
|
-
$ WORKER0_POD_NAME=$(kubectl get pods --selector=jobset.sigs.k8s.io/jobset-name=${
|
|
70
|
+
$ WORKER0_POD_NAME=$(kubectl get pods --selector=jobset.sigs.k8s.io/jobset-name=${SERVICE_JOBSET_NAME} -o jsonpath='{.items[?(@.status.phase=="Running")].metadata.name}' | sed 's/ /\n/g' | grep 'worker-0-0-')
|
|
65
71
|
```
|
|
66
72
|
|
|
67
73
|
#### Option 3: Check project logs
|
|
@@ -80,15 +86,33 @@ I1208 20:10:18.148825 ...] argv[2]: '--resource_manager_address=pathways-c
|
|
|
80
86
|
|
|
81
87
|
## Instructions
|
|
82
88
|
|
|
83
|
-
### 1.
|
|
89
|
+
### 1. Install `pathwaysutils` and SPS dependencies.
|
|
84
90
|
|
|
85
91
|
```shell
|
|
86
|
-
|
|
92
|
+
pip install --upgrade "pathwaysutils[sps] @ git+https://github.com/AI-Hypercomputer/pathways-utils.git"
|
|
93
|
+
|
|
94
|
+
# Note: google-cloud-monitoring is optional. If you don't wish to collect SPS usage metrics, install with pathways-utils[sps_without_monitoring].
|
|
87
95
|
```
|
|
88
96
|
|
|
89
|
-
### 2.
|
|
97
|
+
### 2. Either use `run_workload.py` or move your workload under the `isc_pathways` Context Manager
|
|
98
|
+
|
|
99
|
+
Make sure to use the proxy image compatible with the server image used while deploying SPS.
|
|
100
|
+
|
|
101
|
+
```shell
|
|
102
|
+
python3 -m pathwaysutils.experimental.shared_pathways_service.run_workload \
|
|
103
|
+
--cluster=$CLUSTER_NAME \
|
|
104
|
+
--project=$PROJECT \
|
|
105
|
+
--region=$REGION \
|
|
106
|
+
--gcs_bucket=$GCS_BUCKET \
|
|
107
|
+
--pathways_service="$SERVICE_JOBSET_NAME-pathways-head-0-0.$SERVICE_JOBSET_NAME:29001" \
|
|
108
|
+
--tpu_type="$TPU_TYPE:$TOPOLOGY" \
|
|
109
|
+
--tpu_count=$NUM_SLICES \
|
|
110
|
+
--proxy_server_image=$PROXY_IMAGE \
|
|
111
|
+
--collect_service_metrics \
|
|
112
|
+
--command="python3 run_my_sample_workload.py" [--proxy_options=sidecar:true]
|
|
113
|
+
```
|
|
90
114
|
|
|
91
|
-
|
|
115
|
+
Alternatively, in your script,
|
|
92
116
|
|
|
93
117
|
1. Import `isc_pathways`
|
|
94
118
|
2. Add `with isc_pathways.connect(...)` statement. The function takes the below values:
|
|
@@ -97,6 +121,10 @@ In your script,
|
|
|
97
121
|
- Region
|
|
98
122
|
- GCS bucket name
|
|
99
123
|
- Pathways Service (See instructions to find the RM address [here](#4-find-the-pathways-service-address))
|
|
124
|
+
- Expected TPU instances
|
|
125
|
+
- Proxy server image
|
|
126
|
+
- Proxy options
|
|
127
|
+
- Whether to collect SPS usage metrics
|
|
100
128
|
<a name="ml-code"></a>
|
|
101
129
|
3. Write your ML code under this context manager (the `with` block) to run your JAX code on the underlying TPUs.
|
|
102
130
|
|
|
@@ -111,7 +139,8 @@ python3 pathwaysutils/experimental/shared_pathways_service/run_connect_example.p
|
|
|
111
139
|
--gcs_bucket="gs://user-bucket" \
|
|
112
140
|
--pathways_service="pathways-cluster-pathways-head-0-0.pathways-cluster:29001" \
|
|
113
141
|
--tpu_type="tpuv6e:2x2" \
|
|
114
|
-
--tpu_count=1
|
|
142
|
+
--tpu_count=1 \
|
|
143
|
+
--proxy_server_image="proxy_image"
|
|
115
144
|
```
|
|
116
145
|
|
|
117
146
|
The connect block will deploy a proxy pod dedicated to your client and connect
|