Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
065cb2a
feat: update k8s deploy yamls to use binary/python3
hhzhang16 Jul 11, 2025
aee478c
config part working
tedzhouhk Jul 11, 2025
9455ad1
feat: add component type worker and bump image
hhzhang16 Jul 12, 2025
f3dd01a
fix: merge conflicts
mohammedabdulwahhab Jul 14, 2025
7de97ef
fix: using health checks exposed by dynamo-run
mohammedabdulwahhab Jul 14, 2025
16fd7f2
Merge branch 'main' of github.com:ai-dynamo/dynamo into hannahz/dep-2…
hhzhang16 Jul 14, 2025
3a29913
Merge branch 'hannahz/dep-216-create-deploy-crds-for-vllm_v1-example'…
hhzhang16 Jul 14, 2025
51835db
fix: check for message in logs
mohammedabdulwahhab Jul 14, 2025
39b377f
Merge branch 'hannahz/dep-216-create-deploy-crds-for-vllm_v1-example'…
mohammedabdulwahhab Jul 14, 2025
dddb45f
Merge branch 'hannahz/dep-216-create-deploy-crds-for-vllm_v1-example'…
tedzhouhk Jul 14, 2025
34bc79c
define apis
tedzhouhk Jul 14, 2025
8c22d14
update script
tedzhouhk Jul 14, 2025
9856dde
fix: add dynamodeployment lib
mohammedabdulwahhab Jul 14, 2025
61a215b
fix: working client lib
mohammedabdulwahhab Jul 14, 2025
5141334
fix: working client lib
mohammedabdulwahhab Jul 14, 2025
8e25a29
integrate with utils.dynamo_deployment
tedzhouhk Jul 15, 2025
1d87164
fix: port forward works
mohammedabdulwahhab Jul 15, 2025
aaf4544
Merge branch 'hzhou/profile_vllmv1_k8s' of https://github.com/ai-dyna…
mohammedabdulwahhab Jul 15, 2025
65dec07
pc
tedzhouhk Jul 15, 2025
0af209b
add dep; bug fix
tedzhouhk Jul 15, 2025
918733a
Merge branch 'main' of https://github.com/ai-dynamo/dynamo into hzhou…
tedzhouhk Jul 15, 2025
3f900ef
staging, port forward not working
tedzhouhk Jul 15, 2025
bd12d40
stage
tedzhouhk Jul 15, 2025
7ac43a9
Merge branch 'main' of https://github.com/ai-dynamo/dynamo into hzhou…
mohammedabdulwahhab Jul 15, 2025
9971acf
fix: running script
mohammedabdulwahhab Jul 16, 2025
a5d8aca
fix: fix
mohammedabdulwahhab Jul 16, 2025
7b1d99a
Merge branch 'main' of https://github.com/ai-dynamo/dynamo into hzhou…
tedzhouhk Jul 16, 2025
f8f9363
add logic to find a free port
tedzhouhk Jul 16, 2025
8e292f6
feat: add Kubernetes service account configuration for SLA profiling …
hhzhang16 Jul 17, 2025
d62731f
feat: use service DNS for interfacing with deployments when profiling…
hhzhang16 Jul 17, 2025
a1aea5a
Revert "feat: use service DNS for interfacing with deployments when p…
hhzhang16 Jul 17, 2025
06bfe3b
feat: use service DNS instead of port forwarding for K8s-deployed SLA…
hhzhang16 Jul 18, 2025
ff96b9e
add try-catch waiting for deployment
tedzhouhk Jul 18, 2025
5419885
Merge branch 'main' of https://github.com/ai-dynamo/dynamo into hzhou…
tedzhouhk Jul 21, 2025
d2b6b00
feat: clean up outlying DGDs upon SLA profiling failure (#2016)
hhzhang16 Jul 21, 2025
450d371
add debug info
tedzhouhk Jul 22, 2025
d8ffe1a
Merge branch 'hzhou/profile_vllmv1_k8s' of https://github.com/ai-dyna…
tedzhouhk Jul 22, 2025
769c98e
sla planner
tedzhouhk Jul 22, 2025
e726d43
add choices
tedzhouhk Jul 22, 2025
3663c5c
Merge branch 'main' of github.com:ai-dynamo/dynamo into hzhou/sla-pla…
hhzhang16 Jul 22, 2025
ff6c491
feat: vllm_v1 -> vllm and remove vllm_v0 from planner
hhzhang16 Jul 22, 2025
6ebfe73
feat: remove local connector from init
hhzhang16 Jul 22, 2025
fb89fc2
feat: remove LocalConnector from core
hhzhang16 Jul 22, 2025
047cecb
feat: rework prometheus file for planner deployment
hhzhang16 Jul 22, 2025
894f2e7
Merge branch 'main' of github.com:ai-dynamo/dynamo into hannahz/dep-2…
hhzhang16 Jul 23, 2025
cd268ca
Merge branch 'main' of github.com:ai-dynamo/dynamo into hzhou/sla-pla…
hhzhang16 Jul 23, 2025
0f5082c
deprecate old docs
tedzhouhk Jul 23, 2025
9751e65
Merge branch 'main' of https://github.com/ai-dynamo/dynamo into hzhou…
tedzhouhk Jul 23, 2025
c33713f
Merge branch 'hzhou/sla-planner-ux-refac' of github.com:ai-dynamo/dyn…
hhzhang16 Jul 24, 2025
33371db
feat: update prometheus to work
hhzhang16 Jul 24, 2025
60dd89d
feat: k8s connector scaling P/D in one call (#2103)
tedzhouhk Jul 25, 2025
61a5e9a
Merge branch 'main' of github.com:ai-dynamo/dynamo into hannahz/dep-2…
hhzhang16 Jul 25, 2025
41f1ca0
fix: vllm_v1 -> vllm
hhzhang16 Jul 25, 2025
1584cd0
feat: remove unneeded files
hhzhang16 Jul 25, 2025
dd3f161
docs: update docs
hhzhang16 Jul 25, 2025
97f3f88
fix: vllm config in profiler
hhzhang16 Jul 25, 2025
9b34ee9
feat: wip but tentatively working planner, with documentation
hhzhang16 Jul 25, 2025
eb56dbb
fi: use provided namespace for decode
hhzhang16 Jul 25, 2025
b68779d
feat: use k8s deployment info instead of hardcoding prometheus endpoint
hhzhang16 Jul 25, 2025
c8e394d
fix: if no requests have been made yet, don't try to access list
hhzhang16 Jul 25, 2025
1bbfd8d
feat: use SLAPLannerDefaults port
hhzhang16 Jul 25, 2025
ba6b5c1
docs: clean up sla planner deployment docs
hhzhang16 Jul 25, 2025
e533dda
feat: use DYNAMO_NAMESPACE env var instead of --namespace arg
hhzhang16 Jul 26, 2025
a548d74
feat: fixes for working planner
hhzhang16 Jul 26, 2025
f6af0d5
feat: skip adjustments if no traffic
hhzhang16 Jul 26, 2025
445fe74
docs: doc updates for planner deployment
hhzhang16 Jul 26, 2025
f0999da
Merge branch 'main' of github.com:ai-dynamo/dynamo into hannahz/dep-2…
hhzhang16 Jul 26, 2025
bab714c
feat: delete k8s.sh
hhzhang16 Jul 26, 2025
a29c397
docs: slight doc modification
hhzhang16 Jul 26, 2025
daa3c4e
update resources
tedzhouhk Jul 26, 2025
539ff3e
update readme
tedzhouhk Jul 26, 2025
f994128
feat: address coderabbit MR comments
hhzhang16 Jul 28, 2025
d44b042
Merge branch 'main' of github.com:ai-dynamo/dynamo into hannahz/dep-2…
hhzhang16 Jul 28, 2025
f601f88
fix pytest
tedzhouhk Jul 28, 2025
69d64dc
mypy
tedzhouhk Jul 28, 2025
1fdf3e9
feat: addressing MR comments
hhzhang16 Jul 28, 2025
bf58bd0
feat: addressing MR comments
hhzhang16 Jul 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: add dynamodeployment lib
  • Loading branch information
mohammedabdulwahhab committed Jul 14, 2025
commit 9856dde5673f764d0a7d1096c4dc673e352b188b
252 changes: 252 additions & 0 deletions benchmarks/profiler/utils/dynamo_deployment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
#!/usr/bin/env -S uv run --script
#
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "PyYAML",
# "aiofiles",
# "kubernetes-asyncio",
# ]
# ///

import asyncio
import json
import os
import random
import yaml
from pathlib import Path
from typing import Dict, List, Optional, Union
import aiofiles
import kubernetes_asyncio as kubernetes
from kubernetes_asyncio import client, config
from contextlib import asynccontextmanager

# Example chat completion request for testing deployments
EXAMPLE_CHAT_REQUEST = {
"model": "Qwen/Qwen3-0.6B",
"messages": [
{
"role": "user",
"content": "In the heart of Eldoria, an ancient land of boundless magic and mysterious creatures, lies the long-forgotten city of Aeloria. Once a beacon of knowledge and power, Aeloria was buried beneath the shifting sands of time, lost to the world for centuries. You are an intrepid explorer, known for your unparalleled curiosity and courage, who has stumbled upon an ancient map hinting at ests that Aeloria holds a secret so profound that it has the potential to reshape the very fabric of reality. Your journey will take you through treacherous deserts, enchanted forests, and across perilous mountain ranges. Your Task: Character Background: Develop a detailed background for your character. Describe their motivations for seeking out Aeloria, their skills and weaknesses, and any personal connections to the ancient city or its legends. Are they driven by a quest for knowledge, a search for lost familt clue is hidden."
}
],
"stream": False,
"max_tokens": 30
}

class DynamoDeploymentClient:
def __init__(self, namespace: str, deployment_name: str = "vllm-v1-agg", base_log_dir: Optional[str] = None):
"""
Initialize the client with the namespace and deployment name.

Args:
namespace: The Kubernetes namespace
deployment_name: Name of the deployment, defaults to vllm-v1-agg
base_log_dir: Base directory for storing logs, defaults to ./logs if not specified
"""
self.namespace = namespace
self.deployment_name = deployment_name
self.components = [] # Will store component names from CR
self.deployment_spec = None # Will store the full deployment spec
self.base_log_dir = Path(base_log_dir) if base_log_dir else Path("logs")

async def _init_kubernetes(self):
"""Initialize kubernetes client"""
await config.load_kube_config()
self.k8s_client = client.ApiClient()
self.custom_api = client.CustomObjectsApi(self.k8s_client)
self.core_api = client.CoreV1Api(self.k8s_client)

async def create_deployment(self, deployment: Union[dict, str]):
"""
Create a DynamoGraphDeployment from either a dict or yaml file path.

Args:
deployment: Either a dict containing the deployment spec or a path to a yaml file
"""
await self._init_kubernetes()

if isinstance(deployment, str):
# Load from yaml file
async with aiofiles.open(deployment, 'r') as f:
content = await f.read()
self.deployment_spec = yaml.safe_load(content)
else:
self.deployment_spec = deployment

# Extract component names
self.components = [svc.lower() for svc in self.deployment_spec['spec']['services'].keys()]

# Ensure name and namespace are set correctly
self.deployment_spec['metadata']['name'] = self.deployment_name
self.deployment_spec['metadata']['namespace'] = self.namespace

try:
await self.custom_api.create_namespaced_custom_object(
group="nvidia.com",
version="v1alpha1",
namespace=self.namespace,
plural="dynamographdeployments",
body=self.deployment_spec
)
except kubernetes.client.rest.ApiException as e:
if e.status == 409: # Already exists
print(f"Deployment {self.deployment_name} already exists")
else:
raise

async def wait_for_deployment_ready(self, timeout: int = 300):
"""
Wait for the custom resource to be ready.

Args:
timeout: Maximum time to wait in seconds
"""
start_time = asyncio.get_event_loop().time()
while (asyncio.get_event_loop().time() - start_time) < timeout:
try:
status = await self.custom_api.get_namespaced_custom_object_status(
group="nvidia.com",
version="v1alpha1",
namespace=self.namespace,
plural="dynamographdeployments",
name=self.deployment_name
)
if status.get('status', {}).get('ready', False):
return True
except kubernetes.client.rest.ApiException:
pass
await asyncio.sleep(5)
raise TimeoutError("Deployment failed to become ready within timeout")

@asynccontextmanager
async def port_forward(self, port: Optional[int] = None):
"""
Port forward the frontend service to local machine.

Args:
port: Local port to use. If None, uses a random port.

Yields:
The local port number being used
"""
if port is None:
port = random.randint(49152, 65535)

service_name = f"{self.deployment_name}-frontend"
cmd = f"kubectl port-forward service/{service_name} {port}:8000 -n {self.namespace}"

process = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)

try:
# Wait briefly to ensure port-forward is established
await asyncio.sleep(2)
yield port
finally:
process.terminate()
await process.wait()

async def check_chat_completion(self):
"""
Test the deployment with a chat completion request.
"""
async with self.port_forward() as port:
url = f"http://localhost:{port}/v1/chat/completions"

cmd = f"""curl -X POST {url} \\
-H "Content-Type: application/json" \\
-d '{json.dumps(EXAMPLE_CHAT_REQUEST)}'"""

process = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await process.communicate()
return stdout.decode()

async def get_deployment_logs(self):
"""
Get logs from all pods in the deployment, organized by component.
"""
# Create logs directory
base_dir = self.base_log_dir / self.deployment_name
base_dir.mkdir(parents=True, exist_ok=True)

for component in self.components:
component_dir = base_dir / component
component_dir.mkdir(exist_ok=True)

# List pods for this component
label_selector = f"app={self.deployment_name}-{component}"
pods = await self.core_api.list_namespaced_pod(
namespace=self.namespace,
label_selector=label_selector
)

# Get logs for each pod
for i, pod in enumerate(pods.items):
try:
logs = await self.core_api.read_namespaced_pod_log(
name=pod.metadata.name,
namespace=self.namespace
)
async with aiofiles.open(component_dir / f"replica_{i}.log", 'w') as f:
await f.write(logs)
except kubernetes.client.rest.ApiException as e:
print(f"Error getting logs for pod {pod.metadata.name}: {e}")

async def delete_deployment(self):
"""
Delete the DynamoGraphDeployment CR.
"""
try:
await self.custom_api.delete_namespaced_custom_object(
group="nvidia.com",
version="v1alpha1",
namespace=self.namespace,
plural="dynamographdeployments",
name=self.deployment_name
)
except kubernetes.client.rest.ApiException as e:
if e.status != 404: # Ignore if already deleted
raise

async def main():
# Example usage with custom log directory
client = DynamoDeploymentClient(
namespace="default",
base_log_dir="/tmp/dynamo_logs" # Example custom log directory
)

try:
# Create deployment from yaml file
await client.create_deployment("examples/vllm/deploy/agg.yaml")

# Wait for deployment to be ready
print("Waiting for deployment to be ready...")
await client.wait_for_deployment_ready()
print("Deployment is ready!")

# Test chat completion
print("Testing chat completion...")
response = await client.check_chat_completion()
print(f"Chat completion response: {response}")

# Get logs
print("Getting deployment logs...")
await client.get_deployment_logs()
print(f"Logs have been saved to {client.base_log_dir / client.deployment_name}!")

finally:
# Cleanup
print("Cleaning up deployment...")
await client.delete_deployment()
print("Deployment deleted!")

if __name__ == "__main__":
asyncio.run(main())