Skip to Content

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 default init(), 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 in RIUS_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 stale after 30 seconds and gone after 60 seconds without a ping, so keep RIUS_HEARTBEAT_INTERVAL at 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 stopped ping and the instance reports gone, which is how Rius shows a crash. A pod that stops during a rollout should therefore exit cleanly through client.shutdown().

Set it up

  1. 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.

  2. 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 of init() 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
  1. 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.

  1. 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 call flush() inside a request handler, since it blocks until the export completes.

  2. Check the settings that matter here.

    • Leave RIUS_SESSION_ID unset in a server that handles many users, because it stamps one session.id on every span in the process.
    • Keep RIUS_HEARTBEAT on for long-running pods. Set it to false only for runtimes that freeze the process between calls.
    • Raise OTEL_BSP_MAX_QUEUE_SIZE before you lower RIUS_SAMPLE_RATE if a busy agent drops spans.

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() shows stopped, and one that is killed abruptly shows gone, 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=false and call client.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=true so your model calls keep their parents.

Still stuck? Get help

Last updated on