|
5 | 5 | import logging |
6 | 6 | import math |
7 | 7 | import os |
8 | | -import string |
9 | 8 | from typing import Any |
10 | 9 | from absl import app |
11 | 10 | from absl import flags |
12 | 11 | from kubernetes import client |
13 | 12 | from kubernetes import config |
| 13 | +from pathwaysutils.experimental.gke import jobset |
14 | 14 | import yaml |
15 | 15 |
|
16 | 16 | _logger = logging.getLogger(__name__) |
|
45 | 45 | "gs://pathways-test-bucket", |
46 | 46 | "GCS bucket name for scratch space", |
47 | 47 | ) |
48 | | -_TEMPLATE_FILE = flags.DEFINE_string( |
49 | | - "template_file", |
50 | | - os.path.join( |
51 | | - os.path.dirname(__file__), "yamls/pw-service.yaml", |
52 | | - ), |
53 | | - "Path to the JobSet YAML template file", |
54 | | -) |
55 | 48 | _DRY_RUN = flags.DEFINE_boolean( |
56 | 49 | "dry_run", |
57 | 50 | False, |
@@ -149,25 +142,6 @@ def calculate_vms_per_slice(topology: str, chips_per_vm: int) -> int: |
149 | 142 | ) from e |
150 | 143 |
|
151 | 144 |
|
152 | | -def load_and_substitute_template( |
153 | | - template_path: str, context: dict[str, Any] |
154 | | -) -> dict[str, Any]: |
155 | | - """Loads and substitutes the string.Template from the given path.""" |
156 | | - try: |
157 | | - with open(template_path, "r") as f: |
158 | | - template_str = f.read() |
159 | | - except OSError as err: |
160 | | - raise ValueError( |
161 | | - f"Could not read template file: {template_path}: {err}" |
162 | | - ) from err |
163 | | - |
164 | | - _logger.info("Template file: %s", template_path) |
165 | | - _logger.info("Context: %s", context) |
166 | | - template = string.Template(template_str) |
167 | | - _logger.info("Template: %s", template) |
168 | | - substituted_yaml = template.substitute(context) |
169 | | - return yaml.safe_load(substituted_yaml) |
170 | | - |
171 | 145 |
|
172 | 146 | def deploy_jobset(jobset_yaml: dict[str, Any]) -> None: |
173 | 147 | """Deploys the JobSet to the current Kubernetes cluster.""" |
@@ -198,29 +172,61 @@ def run_deployment( |
198 | 172 | gcs_bucket, |
199 | 173 | server_image, |
200 | 174 | sidecar_image, |
201 | | - template_file, |
202 | 175 | dry_run, |
203 | 176 | deploy_func: Callable[[dict[str, Any]], None] = deploy_jobset, |
204 | 177 | ) -> None: |
205 | 178 | """Executes the deployment logic.""" |
206 | | - tpu_config = get_tpu_config(tpu_type) |
207 | | - vms_per_slice = calculate_vms_per_slice(topology, tpu_config.chips_per_vm) |
208 | | - |
209 | | - context = { |
210 | | - "JOBSET_NAME": jobset_name, |
211 | | - "SERVER_IMAGE": server_image, |
212 | | - "SIDECAR_IMAGE": sidecar_image, |
213 | | - "SIDECAR_SHM_DIR": _SIDECAR_SHM_DIR, |
214 | | - "GCS_SCRATCH_LOCATION": gcs_bucket, |
215 | | - "NUM_SLICES": num_slices, |
216 | | - "INSTANCE_TYPE": f"{tpu_config.instance_prefix}:{topology}", |
217 | | - "VMS_PER_SLICE": vms_per_slice, |
218 | | - "CHIPS_PER_VM": tpu_config.chips_per_vm, |
219 | | - "ACCELERATOR_LABEL": tpu_config.accelerator_label, |
220 | | - "TOPOLOGY": topology, |
221 | | - } |
| 179 | + # Use PathwaysJobSet builder instead of YAML template. |
| 180 | + pw_jobset = jobset.PathwaysJobSet( |
| 181 | + name=jobset_name, |
| 182 | + namespace="default", |
| 183 | + pathways_dir=gcs_bucket, |
| 184 | + tpu_type=tpu_type, |
| 185 | + topology=topology, |
| 186 | + num_slices=num_slices, |
| 187 | + shared_pathways_service=True, |
| 188 | + ) |
222 | 189 |
|
223 | | - jobset_config = load_and_substitute_template(template_file, context) |
| 190 | + # If custom server_image is provided, mutate the templates to use it. |
| 191 | + if server_image: |
| 192 | + # Mutate head job. |
| 193 | + for container in pw_jobset.head_job_template.spec.template.spec.containers: |
| 194 | + if container.name == "pathways-rm": |
| 195 | + container.image = server_image |
| 196 | + # Mutate worker job. |
| 197 | + for container in pw_jobset.worker_job_template.spec.template.spec.containers: |
| 198 | + if container.name == "pathways-worker": |
| 199 | + container.image = server_image |
| 200 | + |
| 201 | + # Add colocated python sidecar. |
| 202 | + pw_jobset.add_colocated_python(image=sidecar_image, shm_mount_path=_SIDECAR_SHM_DIR) |
| 203 | + |
| 204 | + # Mutate the sidecar configuration to match what HEAD expects. |
| 205 | + worker_spec = pw_jobset.worker_job_template.spec.template.spec |
| 206 | + |
| 207 | + # 1. Add extra logging env vars to sidecar. |
| 208 | + for container in worker_spec.init_containers: |
| 209 | + if container.name == "colocated-python-sidecar": |
| 210 | + container.env.extend([ |
| 211 | + client.V1EnvVar(name="PYTHONUNBUFFERED", value="1"), |
| 212 | + client.V1EnvVar(name="LOGLEVEL", value="DEBUG"), |
| 213 | + client.V1EnvVar(name="GLOG_minloglevel", value="0"), |
| 214 | + client.V1EnvVar(name="GLOG_v", value="5"), |
| 215 | + client.V1EnvVar(name="TF_CPP_MIN_LOG_LEVEL", value="0"), |
| 216 | + client.V1EnvVar(name="TF_CPP_MIN_VLOG_LEVEL", value="5"), |
| 217 | + client.V1EnvVar(name="TPU_MIN_LOG_LEVEL", value="0"), |
| 218 | + client.V1EnvVar(name="GLOG_vmodule", value="jax_array_handlers=5,type_handlers=5,tensorstore_utils=5"), |
| 219 | + ]) |
| 220 | + |
| 221 | + # 2. Add arg to pathways-worker container (in addition to env var set by builder). |
| 222 | + for container in worker_spec.containers: |
| 223 | + if container.name == "pathways-worker": |
| 224 | + args = container.args or [] |
| 225 | + if not any(a.startswith("--cloud_pathways_sidecar_shm_directory=") for a in args): |
| 226 | + args.append(f"--cloud_pathways_sidecar_shm_directory={_SIDECAR_SHM_DIR}") |
| 227 | + container.args = args |
| 228 | + |
| 229 | + jobset_config = pw_jobset.to_dict() |
224 | 230 |
|
225 | 231 | _logger.info("--- Generated JobSet YAML ---") |
226 | 232 | _logger.info("\n%s", yaml.dump(jobset_config)) |
@@ -256,15 +262,10 @@ def main(argv: Sequence[str]) -> None: |
256 | 262 | gcs_bucket=_GCS_BUCKET.value, |
257 | 263 | server_image=server_image, |
258 | 264 | sidecar_image=_SIDECAR_IMAGE.value, |
259 | | - template_file=_TEMPLATE_FILE.value, |
260 | 265 | dry_run=_DRY_RUN.value, |
261 | 266 | ) |
262 | 267 | except ValueError as e: |
263 | 268 | _logger.exception("Error: %s", e) |
264 | | - except FileNotFoundError: |
265 | | - _logger.exception( |
266 | | - "Error: Template file not found at %s", _TEMPLATE_FILE.value |
267 | | - ) |
268 | 269 |
|
269 | 270 |
|
270 | 271 | if __name__ == "__main__": |
|
0 commit comments