How do I trace a Python AI agent running in a Kubernetes deployment?
Rius traces a Python agent in Kubernetes when each pod calls rius.init() once at startup, reads its RIUS_* settings from the pod environment, and shuts the client down when the pod receives SIGTERM. Spans are exported in the background, so your request path never waits on the network. Every replica reports under the same service name, so three replicas show as three instances of one agent.
How it works
- One
init()per process. Call it as early as possible. A second global call logs a warning and returns the existing client, so a repeated call is harmless but does nothing new. With a defaultinit(), every installed integration is turned on automatically, so your model calls and framework steps are traced without extra code. The Integrations page lists them. - Background export. Spans queue in memory and leave in batches from a background thread. The queue holds 2048 spans by default (
OTEL_BSP_MAX_QUEUE_SIZE), and a batch is flushed every 5000 ms (OTEL_BSP_SCHEDULE_DELAY). - One identity per pod. Each process start is a new instance under the same
service.name. Keep the name constant across replicas and rollouts, and put the release inRIUS_SERVICE_VERSION, for example your image tag. - Forked workers. With gunicorn or multiprocessing, the exporter re-creates its background thread in each forked child. The heartbeat re-arms with a new instance ID, so you see one instance per worker.
- Heartbeat. The SDK pings Rius every 15 seconds by default, which powers the Agents view. It needs an API key. An instance is marked
staleafter 30 seconds andgoneafter 60 seconds without a ping, so keepRIUS_HEARTBEAT_INTERVALat 15 or below. - Shutdown. The Python SDK drains pending spans at a clean interpreter exit. It installs no signal handlers, so an unhandled SIGTERM or a SIGKILL sends no final
stoppedping and the instance reportsgone, which is how Rius shows a crash. A pod that stops during a rollout should therefore exit cleanly throughclient.shutdown().
Set it up
-
Add the SDK to your image. Add
pip install "glassflow-rius[openai]"to your build, swapping the extra for the library you call. An extra installs only the instrumentation, so your own library must already be in the image. The installation page covers the extras and every option. -
Put the settings in the Deployment. Store the key in a Secret and set the names in the container’s
env. Explicit arguments in code win over environment variables, so leave them out ofinit()and keep one image for every environment.
env:
- name: RIUS_API_KEY
valueFrom:
secretKeyRef:
name: rius-credentials # your own Secret
key: api-key
- name: RIUS_SERVICE_NAME
value: my-agent
- name: RIUS_SERVICE_VERSION
value: "2026.10.26" # for example your image tag- Initialize once and shut down on SIGTERM. Put this in the module your process starts from, before your first model call.
import signal
import sys
import rius
client = rius.init() # reads the RIUS_* variables from the pod environment
def stop(signum, frame):
client.shutdown() # drains pending spans and sends the final stopped ping
sys.exit(0)
signal.signal(signal.SIGTERM, stop)If your server already handles SIGTERM, call client.shutdown() from its shutdown hook instead.
-
Handle short-lived pods. For a Job or CronJob container, call
client.flush(timeout_millis=30_000)before the script ends, because a short process can finish before the first scheduled export. Never callflush()inside a request handler, since it blocks until the export completes. -
Check the settings that matter here.
- Leave
RIUS_SESSION_IDunset in a server that handles many users, because it stamps onesession.idon every span in the process. - Keep
RIUS_HEARTBEATon for long-running pods. Set it tofalseonly for runtimes that freeze the process between calls. - Raise
OTEL_BSP_MAX_QUEUE_SIZEbefore you lowerRIUS_SAMPLE_RATEif a busy agent drops spans.
- Leave
The Reliability page covers retries, flushing and the heartbeat in full.
What you get
- Every model call, tool call and agent step from every replica, with cost per call, in one agent under one name.
- One instance per pod or worker in the Agents view. A pod that calls
shutdown()showsstopped, and one that is killed abruptly showsgone, so you can tell a rollout from a crash. - Traces tagged with the release in
RIUS_SERVICE_VERSION, so you can tell which build produced a run. - Spans that leave the pod in the background and are drained when the pod exits cleanly, with failures logged and never raised into your code.
When something else fits
- For Python on Lambda-style runtimes that freeze the process between calls, set
RIUS_HEARTBEAT=falseand callclient.flush()at the end of every invocation instead. - If the pod already runs another OpenTelemetry SDK that owns the global tracer provider, set
RIUS_BRIDGE_FOREIGN_PROVIDER=trueso your model calls keep their parents.