Skip to content

Commit 8328412

Browse files
authored
feat(vm): export driver traces over OTLP (#2564)
Continue distributed traces across the gateway-to-driver process boundary and export VM driver spans to the same OTLP/gRPC collector. The driver reports as the distinct openshell-driver-vm service. Updated the gateway architecture and configuration reference with a generic external-driver forwarding contract. Instrumented: - Every RemoteComputeDriver RPC injects the active W3C trace context into tonic metadata. Managed VM readiness and runtime initialization give startup capability probes stable parent operations rather than isolated root spans. - A tonic service layer creates fixed, low-cardinality server spans for every ComputeDriver RPC. New handlers inherit tracing automatically; failures record OpenTelemetry error status and the gRPC status code. - Background provisioning remains attached to CreateSandbox after the RPC returns without extending the RPC span lifetime. - Provisioning records image preparation, bootstrap image resolution, overlay preparation, lifecycle configuration, pre-launch hooks, guest preparation, and launcher spawn as child spans. - VM startup reconciliation roots one trace for the persisted-sandbox scan, with per-sandbox restore and provision operations beneath it. The root remains open until all spawned restore tasks finish. - Delete cleanup records its own child operation. Design notes: - The gateway forwards its configured OTLP endpoint to managed external drivers. SDK `OTEL_*` variables continue to own sampling, batching, limits, headers, and transport tuning. - The VM driver has its own tracer provider and service resource so trace backends preserve the service boundary. - RPC operation names come from an explicit method mapping, keeping cardinality bounded without parsing the protobuf descriptor set at runtime. - Propagation uses a remote SpanContext for spawned provisioning. This keeps one trace while allowing the CreateSandbox server span to finish when the RPC response is sent. - Startup restoration is independent of gateway requests. It begins at the VM driver reconciliation span rather than attaching to an unrelated RPC. - Existing tracing events remain on the logging path. The OpenTelemetry layer exports spans only and excludes the SDK exporter callsites to avoid recursive traces. - Export configuration failures do not prevent the driver from serving, and buffered spans are drained during graceful shutdown. - Trace fields identify drivers, sandboxes, images, lifecycle phases, and gRPC outcomes without recording credentials, sandbox tokens, or request query parameters. Refs #2507 Signed-off-by: Kris Hicks <khicks@nvidia.com>
1 parent 0a3ec7a commit 8328412

24 files changed

Lines changed: 1798 additions & 132 deletions

File tree

‎Cargo.lock‎

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎architecture/gateway.md‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -637,17 +637,21 @@ export: the table's presence is the on-switch, and `OTEL_EXPORTER_OTLP_ENDPOINT`
637637
is ignored so enablement has a single source. TOML decides whether and where
638638
to export; the SDK's `OTEL_*` variables tune how. Transport is OTLP over gRPC
639639
only. Shared provider, resource, and tracing-layer construction lives in
640-
`openshell-otel`.
641-
642-
Span emission requires no per-handler instrumentation. The `tower_http`
643-
`TraceLayer` in `multiplex.rs` opens a span per inbound request, and that span
644-
continues incoming W3C trace context when present or starts a new trace
645-
otherwise. It is named for the RPC and carries the request ID that also appears
646-
in the gateway's logs — the identifier that lets an operator pivot between a
647-
trace and its log lines. Store and compute-driver spans become children of the
648-
request span. Reconciliation, provider refresh, and driver-watch loops create
649-
their own operation spans because they have no inbound request to provide a
650-
parent. gRPC status is recorded when response trailers arrive.
640+
`openshell-otel`, along with shared HTTP/tonic trace-context propagation and
641+
gRPC failure recording.
642+
643+
The `tower_http` `TraceLayer` in `multiplex.rs` opens a span per inbound request,
644+
and that span continues incoming W3C trace context when present or starts a new
645+
trace otherwise. It is named for the RPC and carries the request ID that also
646+
appears in the gateway's logs — the identifier that lets an operator pivot
647+
between a trace and its log lines. Store and compute-driver spans become
648+
children of the request span. Reconciliation, provider refresh, and
649+
driver-watch loops create their own operation spans because they have no
650+
inbound request to provide a parent. gRPC status is recorded when response
651+
trailers arrive.
652+
653+
The gateway forwards OTLP configuration and W3C trace context to managed
654+
external drivers. Each driver exports under its own service name.
651655

652656
Two invariants shape the failure behavior. Telemetry is diagnostic, so no OTLP
653657
failure stops the gateway from serving: a malformed endpoint is logged at

‎crates/openshell-driver-vm/Cargo.toml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ path = "src/main.rs"
2020

2121
[dependencies]
2222
openshell-core = { path = "../openshell-core", default-features = false }
23+
openshell-otel = { path = "../openshell-otel" }
2324
openshell-policy = { path = "../openshell-policy" }
2425
openshell-vfio = { path = "../openshell-vfio" }
2526

2627
bollard = { version = "0.20", features = ["ssh"] }
2728
tokio = { workspace = true }
2829
tonic = { workspace = true, features = ["transport"] }
30+
tower-http = { workspace = true }
31+
http = { workspace = true }
2932
prost = { workspace = true }
3033
prost-types = { workspace = true }
3134
futures = { workspace = true }
@@ -34,6 +37,9 @@ nix = { workspace = true }
3437
clap = { workspace = true }
3538
tracing = { workspace = true }
3639
tracing-subscriber = { workspace = true }
40+
opentelemetry = { workspace = true }
41+
opentelemetry_sdk = { workspace = true }
42+
tracing-opentelemetry = { workspace = true }
3743
miette = { workspace = true }
3844
url = { workspace = true }
3945
serde = { workspace = true }
@@ -56,6 +62,9 @@ telemetry = ["openshell-core/telemetry"]
5662

5763
[dev-dependencies]
5864
temp-env = "0.3"
65+
tempfile = "3"
66+
opentelemetry_sdk = { workspace = true, features = ["testing"] }
67+
opentelemetry-proto = { version = "0.32", default-features = false, features = ["gen-tonic", "trace"] }
5968

6069
# smol-rs/polling drives the BSD/macOS parent-death detection in
6170
# procguard via kqueue's EVFILT_PROC / NOTE_EXIT filter. We could use

‎crates/openshell-driver-vm/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ RUST_LOG=openshell_server=debug,openshell_driver_vm=debug \
208208
mise run gateway:vm
209209
```
210210
211-
The VM guest's serial console is appended to `<state-dir>/<sandbox-id>/console.log`. Sandbox IDs must match `[A-Za-z0-9._-]{1,128}` before the driver uses them in host paths. The gateway-owned compute-driver socket lives at `<state-dir>/run/compute-driver.sock`; OpenShell creates `run/` with owner-only permissions, removes same-owner stale sockets, and the gateway removes the socket on clean shutdown via `ManagedDriverProcess::drop`. UDS clients must match the driver UID and provide the expected gateway process PID by default. Standalone same-UID UDS mode requires the explicit `--allow-same-uid-peer` development flag. TCP mode is disabled by default because it is unauthenticated; use `--allow-unauthenticated-tcp --bind-address 127.0.0.1:50061` only for local development.
211+
The VM guest's serial console is appended to `<state-dir>/<sandbox-id>/console.log`. Sandbox IDs must match `[A-Za-z0-9._-]{1,128}` before the driver uses them in host paths. The gateway-owned compute-driver socket lives at `<state-dir>/run/compute-driver.sock`; OpenShell creates `run/` with owner-only permissions and removes same-owner stale sockets. On clean shutdown, the gateway sends the managed driver `SIGTERM`, waits up to five seconds for it to flush telemetry and exit, then force-kills it if necessary and removes the socket. UDS clients must match the driver UID and provide the expected gateway process PID by default. Standalone same-UID UDS mode requires the explicit `--allow-same-uid-peer` development flag. TCP mode is disabled by default because it is unauthenticated; use `--allow-unauthenticated-tcp --bind-address 127.0.0.1:50061` only for local development.
212212
213213
## Host-side nftables rules
214214

0 commit comments

Comments
 (0)