Tutorial: Hello, Workload!¶
The shortest path from zero to a running container on DataRobot. In about five minutes you'll deploy the containous/whoami image as a draft Workload, hit its endpoint, and tear it down.
A draft Workload is the hello-world equivalent for the Workload API: one POST /workloads call, no artifact registration ceremony, auto-cleanup after 8 hours.
Tutorial format¶
When appropriate, steps below show the call using curl, followed by a code cell that runs the same call via requests. The check(resp) helper raises on non-2xx and prints the API's response body so any validation detail is visible alongside the traceback.
Connect to DataRobot¶
To connect to DataRobot, you need the following:
- DataRobot API endpoint,
DATAROBOT_ENDPOINT. - DataRobot API token,
DATAROBOT_API_TOKEN. - For the cURL examples, a terminal with
curlandjq. - For the Python Notebook examples, Python
requestsanddatarobot.
The connection details are set automatically inside this DataRobot Notebook. To run the cURL commands directly, export DATAROBOT_ENDPOINT and DATAROBOT_API_TOKEN:
export DATAROBOT_ENDPOINT=https://app.datarobot.com/api/v2
export DATAROBOT_API_TOKEN=<your-api-token>
The cell below connects this notebook to DataRobot and sets up the check(resp) helper.
import json
import time
import datarobot as dr
import requests
client = dr.Client()
# client = dr.Client(endpoint="https://app.datarobot.com/api/v2", token="YOUR_API_TOKEN")
API_BASE = client.endpoint.rstrip("/")
HEADERS = {
"Authorization": f"Bearer {client.token}",
"Content-Type": "application/json",
}
def check(resp):
if not resp.ok:
print("Status:", resp.status_code, resp.reason)
try:
print("Body:", json.dumps(resp.json(), indent=2))
except ValueError:
print("Body:", resp.text)
resp.raise_for_status()
return resp
print("Connected:", API_BASE)
Deploy whoami¶
Create a Workload with an inline draft artifact. containous/whoami is a tiny HTTP server that echoes request information, perfect for confirming traffic flows end-to-end.
curl -s -X POST "${DATAROBOT_ENDPOINT}/workloads" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "hello-whoami",
"artifact": {
"name": "whoami-artifact",
"type": "service",
"spec": {
"containerGroups": [{
"name": "default",
"containers": [{
"name": "whoami",
"imageUri": "containous/whoami:latest",
"port": 8080,
"primary": true,
"entrypoint": ["/whoami", "--port", "8080"],
"readinessProbe": {"path": "/", "port": 8080, "initialDelaySeconds": 5}
}]
}]
}
},
"runtime": {
"containerGroups": [{
"name": "default",
"replicaCount": 1,
"containers": [{
"name": "whoami",
"resourceAllocation": {"cpu": 1, "memory": "512MB"}
}]
}]
}
}'
The response is a WorkloadFormatted object. workload_id is all you need for the rest of the tutorial.
The artifact describes the container topology: image, port, entrypoint, env vars, probes. Anything you'd want a different deployment of the same artifact to be able to override (replica count, CPU/memory, autoscaling, resource bundles) lives in runtime.containerGroups[]. Entries are looked up by group name (default group is "default"); per-container overrides are looked up by container name, which is why both objects need names when you're customizing resources.
payload = {
"name": f"hello-whoami-{int(time.time())}",
"artifact": {
"name": f"whoami-artifact-{int(time.time())}",
"type": "service",
"spec": {
"containerGroups": [{
"name": "default",
"containers": [{
"name": "whoami",
"imageUri": "containous/whoami:latest",
"port": 8080,
"primary": True,
"entrypoint": ["/whoami", "--port", "8080"],
"readinessProbe": {"path": "/", "port": 8080, "initialDelaySeconds": 5},
}]
}]
},
},
"runtime": {
"containerGroups": [{
"name": "default",
"replicaCount": 1,
"containers": [{
"name": "whoami",
"resourceAllocation": {"cpu": 1, "memory": "512MB"},
}],
}]
},
}
resp = requests.post(f"{API_BASE}/workloads", headers=HEADERS, json=payload, timeout=120)
check(resp)
workload_id = resp.json()["id"]
print("Workload ID:", workload_id)
Wait for running¶
Poll the Workload's status until it reaches running. Expected happy-path progression: submitted → provisioning → launching → running.
curl -s "${DATAROBOT_ENDPOINT}/workloads/${WORKLOAD_ID}" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" | jq -r '.status'
running requires the readiness probe to pass. The platform polls readinessProbe.path (here, /) on the container's port. If you typo the path or your container doesn't serve a 2xx on it, the Workload sits in launching even though the container itself is up.
If you see errored, the events endpoint tells you what container failed and why:
curl -s "${DATAROBOT_ENDPOINT}/workloads/${WORKLOAD_ID}/events" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" | jq '.'
deadline = time.time() + 600
while time.time() < deadline:
r = requests.get(f"{API_BASE}/workloads/{workload_id}", headers=HEADERS, timeout=60)
check(r)
status = r.json().get("status")
print("status:", status)
if status == "running":
break
if status == "errored":
ev = requests.get(f"{API_BASE}/workloads/{workload_id}/events", headers=HEADERS, timeout=60)
print("events:", ev.text[:4000])
raise RuntimeError("Workload errored; see events above")
time.sleep(5)
else:
raise TimeoutError("Timed out waiting for running")
Say hello¶
Read the invoke URL from the Workload's endpoint field and call it. whoami will echo request headers and connection info. That's your hello world.
ENDPOINT=$(curl -s "${DATAROBOT_ENDPOINT}/workloads/${WORKLOAD_ID}" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" | jq -r '.endpoint')
curl -H "Authorization: Bearer ${DATAROBOT_API_TOKEN}" "${ENDPOINT}"
401 error: If the call returns
401immediately afterrunning, the new route isn't active yet; wait a few seconds and retry.
r = requests.get(f"{API_BASE}/workloads/{workload_id}", headers=HEADERS, timeout=60)
check(r)
invoke_url = r.json()["endpoint"]
if invoke_url.startswith("http://"):
invoke_url = "https://" + invoke_url.removeprefix("http://")
print("Invoke URL:", invoke_url)
hello = requests.get(invoke_url, headers={"Authorization": f"Bearer {client.token}"}, timeout=60)
check(hello)
print(hello.text)
Clean up¶
POST /workloads/{id}/stop returns 202 Accepted and stops the underlying proton. Draft Workloads also auto-terminate after 8 hours, so cleanup is optional, but good hygiene.
curl -X POST "${DATAROBOT_ENDPOINT}/workloads/${WORKLOAD_ID}/stop" \
-H "Authorization: Bearer ${DATAROBOT_API_TOKEN}"
resp = requests.post(f"{API_BASE}/workloads/{workload_id}/stop", headers=HEADERS, timeout=120)
print("stop status:", resp.status_code)
if resp.text:
print(resp.text[:2000])
Summary¶
In this tutorial:
- You created a draft Workload from an inline draft artifact (type
service). - The platform built the container group, ran readiness probes, and assigned an invoke URL.
- Because the artifact is
draft, the Workload is short-lived: 8-hour TTL, one Workload per draft artifact, automatic cleanup.