Enable tracing for Container Observability
This guide explains how to enable distributed tracing for Container Observability (CO) in the Virtana Platform. Tracing allows you to track requests as they travel across your microservices, making it easier to identify performance bottlenecks and diagnose errors.
Virtana's tracing architecture consists of three components that work together:
Component | Role |
|---|---|
OTEL Collector | Deployed in each South cluster as part of the CO South deployment. Collects trace spans from instrumented applications and forwards them to the Trace Router. |
Trace Router | A central hub is deployed once in the virtana-trace-router namespace. It receives traces from all South clusters by OTEL collectors and routes them to the correct TraceGW based on a cluster tag. |
TraceGW (Trace Gateway) | Deployed per-cluster in the North Backend namespace. Receives trace data and forwards it to your Tempo backend. Also calculates SLO (Service Level Objective) metrics from trace spans. |
Tempo (external method only) | Your external distributed tracing backend (not deployed by Virtana). TraceGW forwards processed traces to it for storage and querying. |
Virtana supports two methods for enabling tracing. Both methods share the same core pipeline. They differ in what happens after TraceGW processes the traces.
Standard method: TraceGW stores SLO metrics in-cluster and surfaces them in the Virtana CO UI. No external backend is required. Use this method for most on-premises customer deployments.
Data flow: OTEL Collector > Trace Router > TraceGW > Virtana CO UI
External method: TraceGW forwards processed traces to an external Tempo backend in addition to computing SLO metrics. Use this method when your organization already operates a centralized Tempo instance.
Data flow: OTEL Collector > Trace Router > TraceGW > Tempo
Prerequisites
Complete all prerequisites before you start any deployment steps.
CO South deployment
The OTEL Collector is deployed as part of the CO South deployment. Before enabling tracing, confirm that CO South is already running in every South cluster where you want to collect traces. If it is not, complete the Deploy CO South procedure first. See the CO South deployment for more details.
Namespace and quota requirements
Create the Trace Router namespace in Fuze before deploying. The table below lists the minimum required resource quotas for each namespace involved in the tracing pipeline.
Namespace | PVCs | NodePorts | CPU | Memory |
|---|---|---|---|---|
North Backend namespace | 3 | 2 | 2 cores | 10 GB |
virtana-trace-router | 3 | 2 | 2 cores | 10 GB |
To create the Trace Router namespace, run the following command in Fuze:
kubectl create namespace virtana-trace-router
Secret synchronization
Sync the required secrets from Fuze to the following namespaces before deploying. These secrets supply image pull credentials and Keycloak authentication settings that TraceGW and the Trace Router need at startup.
Target namespace | Required secrets | Purpose |
|---|---|---|
North Backend namespace (TraceGW) |
| Image pull credentials and Keycloak authentication for TraceGW. |
virtana-trace-router |
| Image pull credentials for the Trace Router. |
Tempo backend details (external method only)
If you are using the external method, obtain the following values from your Tempo instance before you begin. If Virtana manages your Tempo instance, contact your Virtana administrator. You do not need these values for the standard method.
Parameter | Description |
|---|---|
Trace_external_url | The URL of your external Tempo backend. TraceGW uses this address to forward processed trace data. |
Trace_headers | Optional HTTP headers are included in every request to Tempo. Used for multi-tenant Tempo setups. Leave empty if your Tempo instance does not require custom headers. |
Trace_auth_token | A non-expiring Bearer token for authenticating requests to Tempo. Ensure the token does not have an expiry date. An expired token will silently break trace ingestion. |
Error tag list
Obtain the list of span tags that your application uses to mark errors. TraceGW uses this list to classify spans when calculating SLO metrics. Confirm these values with your development team, as they are application-specific.
Standard method
Use this method for most on-premises customer environments. It enables full tracing and SLO computation within the Virtana platform without requiring an external backend.
Update the global section of your North Backend Helm values file to activate tracing. In the standard method, tracing is handled entirely within the cluster, so trace_external is set to false and no external URL is needed.
global: backend: trace_enabled: true trace_client: "Jaeger" trace_external: false trace_headers: ""
The following table describes each field in the configuration file.
Field | Description | Default value |
|---|---|---|
| Activates the tracing feature in the CO backend. Set to |
|
| Specifies the tracing client protocol. |
|
| Set to false for the standard method. Traces are processed and stored in-cluster by TraceGW. |
|
| Not required for the standard method. |
|
Add the TraceGW application configuration to your ArgoCD-managed values.yaml file. This deploys one TraceGW instance per cluster. Replace the xxxx placeholder values with your actual cluster names and tenant IDs.
namespaces:
argocd: argocd
apps:
virtana_tracing_onprem:
enabled: true
version: 2024.12.4
clusters:
- name: xxxx
tenantID: xxxx
names:
app: "virtana-tracegw"
values:
global:
secret_source: "none"
gatewayCreds:
environment:
OPSCRUISE_ENDPOINT: "kafka-service.{{ .clusterName }}.svc.cluster.local:9092"
KEYCLOAK_ENABLED: "true"
KEYCLOAK_URL: "https://wind-keycloak.oc.dev.cloud.virtana.com:443"
KEYCLOAK_REALM: "{{ .tenantID }}"
OPSCRUISE_ACCOUNT_ID: "{{ .clusterName }}"
k8sgw:
enabled: false
promgw:
enabled: false
loggw-loki:
enabled: false
loki-stack:
enabled: false
prometheus:
enabled: false
opscruise-node-exporter-new:
enabled: false
kube-state-metrics:
enabled: false
tracegw:
enabled: true
tracegw:
storageClassName: ""
slo_storage_size: 20Gi
config:
tracegw:
tracegwconf.trace-source: jaeger
tracegwconf.slo-enabled: "true"
tracegwconf.slo-training-minutes: "30"
tracegwconf.write-slo-slow-traces-to-alogq: "true"
tracegwconf.meters-exclude-all-tags: "true"
tracegwconf.mode: listen
tracegwconf.enable-trace-forwarding: "false"
tracegwconf.filter-tag-key: "cluster"
tracegwconf.find-cluster-tag-keys: "cluster"Cluster top-level fields
Field | Description | Default value |
|---|---|---|
| Activates this ArgoCD application. Set to |
|
| The Helm chart version to deploy. Check the Virtana Helm chart repository for the current version. |
|
| The cluster name for this TraceGW instance. Also used as the Kafka topic identifier and the cluster tag key for routing. |
|
| Your Virtana tenant ID is used as the Keycloak realm identifier. Contact your Virtana administrator if you are unsure of this value. |
|
Gateway Credentials fields
Field | Description | Default value |
|---|---|---|
| Kafka broker endpoint for publishing processed trace metrics. |
|
| Enables Keycloak authentication for TraceGW. Set to |
|
| URL of your Keycloak instance. Replace with your actual endpoint. |
|
| Keycloak realm for TraceGW authentication. Automatically populated from tenantID. |
|
| Account identifier used when publishing data to Virtana. Automatically populated from the cluster name. |
|
Tracegw configuration fields
Field | Description | Default value |
|---|---|---|
| Kubernetes storage class for TraceGW's PVCs. Leave empty to use the cluster default. |
|
| Size of the persistent volume for SLO training data. Increase for high-traffic environments. |
|
| Trace backend type. |
|
| Enables SLO calculation from trace spans. Set to |
|
| Minutes of trace data TraceGW observes before its SLO baseline model is trained. |
|
| When |
|
| When |
|
| Operating mode for TraceGW. |
|
| Set to |
|
| Span tag key TraceGW uses to identify the originating cluster. |
|
| Comma-separated list of tag keys TraceGW searches to identify the cluster. Typically matches filter-tag-key. |
|
Apply the following ArgoCD Application manifest to deploy both TraceGW (one per cluster) and the Trace Router (one shared instance). Save the file as virtana-tracing.yaml and apply it to your ArgoCD namespace.
{{- $app := .Values.apps.virtana_tracing_onprem }}
{{- $namespace := .Values.namespaces.argocd }}
{{- if $app.enabled }}
{{- range $cluster := $app.clusters }}
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: {{ $app.names.app }}-{{ $cluster.name }}
namespace: {{ $namespace }}
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
destination:
server: https://kubernetes.default.svc
namespace: {{ $cluster.name }}
source:
chart: virtana-co
repoURL: https://virtana.gitlab.io/helm-charts
targetRevision: {{ $app.version }}
helm:
releaseName: virtana-tracegw
valueFiles:
- values.yaml
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
{{- end }}
{{- end }}
# --- Trace Router (single shared instance) ---
{{- if $app.enabled }}
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: virtana-trace-router
namespace: {{ $namespace }}
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
destination:
server: https://kubernetes.default.svc
namespace: virtana-trace-router
source:
chart: virtana-co
repoURL: https://virtana.gitlab.io/helm-charts
targetRevision: {{ $app.version }}
helm:
releaseName: virtana-trace-router
valueFiles:
- values.yaml
values: |
global:
secret_source: "none"
k8sgw:
enabled: false
promgw:
enabled: false
loggw-loki:
enabled: false
loki-stack:
enabled: false
prometheus:
enabled: false
opscruise-node-exporter-new:
enabled: false
kube-state-metrics:
enabled: false
tracegw:
enabled: false
trace-router:
enabled: true
trace-router:
machine_type: large
service:
type: NodePort
configMap:
tracerouter:
trace-gateway-endpoints:
{{- range $cluster := $app.clusters }}
- key: cluster
value: {{ $cluster.name }}
endpoint: tracegw-service.{{ $cluster.name }}.svc.cluster.local:9000
{{- end }}
envConfig:
tracerouter.traces-in-queue: "0"
tracerouter.initial-delay-secs: "30"
tracerouter.scale-tracegw: false
tracerouter.trace-completion-wait-time-secs: "120"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
{{- end }}Trace Router parameters
Field | Description | Default value |
|---|---|---|
| Resource profile for the Trace Router pod. large is correct for production. |
|
| Exposes the Trace Router on a node port so OTEL collectors in South clusters can reach it without an ingress controller. |
|
| Span tag key the Trace Router checks to identify the destination cluster. |
|
| The cluster name value the tag must equal. The Trace Router forwards the trace to the TraceGW endpoint whose value matches. |
|
| Internal DNS address of the TraceGW service for this cluster. |
|
| Traces to buffer before forwarding. 0 means immediate forwarding without buffering. |
|
| Seconds the Trace Router waits after startup before forwarding. Allows TraceGW to finish initializing first. |
|
| Reserved for future use. Leave set to false. |
|
| Seconds the Trace Router waits after the first span arrives before forwarding the complete trace. |
|
Update the OTEL collector configuration in each South cluster to export traces to the Trace Router. The collector is part of the CO South deployment. Locate its Helm values or configMap and update the exporters section.
exporters:
otlp:
endpoint: trace-router-service.virtana-trace-router.svc.cluster.local:4317
timeout: 60s
retry_on_failure:
enabled: trueField | Description | Default value |
|---|---|---|
| In-cluster DNS address and port of the Trace Router service. Update the namespace if you used a different name. |
|
| The maximum time the collector waits for the Trace Router to acknowledge a batch before retrying. |
|
| When true, the collector retries failed export attempts automatically. |
|
Note
Choose a port based on your OTEL collector's trace format.
GRPC Jaeger Port: 9001GRPC OTLP Port: 4317
External method
Use this method when your organization operates a centralized Tempo backend, and you want TraceGW to forward processed traces to it for long-term storage and querying. This method extends the standard pipeline with additional Tempo connection settings.
Update the global section of your North Backend Helm values file to activate tracing and configure the Tempo connection. The key difference from the standard method is that trace_external is set to true, and the Tempo URL and headers are provided.
global: backend: trace_enabled: true trace_client: "TEMPO" trace_external: true trace_external_url: "http://example.tempo.com" ## Optional. Format: "X-Scope-OrgID=my-org, Bearer=<token>" trace_headers: ""
The following table describes each field in the configuration file.
Field | Description | Default value |
|---|---|---|
| Activates the tracing feature in the CO backend. |
|
| Specifies the tracing client protocol. |
|
| Set to true for the external Tempo method. TraceGW uses |
|
| Full URL of your external Tempo backend. Replace the example value with your actual Tempo endpoint. |
|
| Optional HTTP headers are included in every request to Tempo. Use for multi-tenant Tempo setups that require an org ID header (for example, X-Scope-OrgID=my-org). |
|
The ArgoCD values configuration for the external Tempo method is the same as the standard method, with one important difference: set tracegwconf.enable-trace-forwarding to true to activate forwarding to the Tempo backend.
namespaces:
argocd: argocd
apps:
virtana_tracing_onprem:
enabled: true
version: 2024.12.4
clusters:
- name: xxxx
tenantID: xxxx
names:
app: "virtana-tracegw"
values:
global:
secret_source: "none"
gatewayCreds:
environment:
OPSCRUISE_ENDPOINT: "kafka-service.{{ .clusterName }}.svc.cluster.local:9092"
KEYCLOAK_ENABLED: "true"
KEYCLOAK_URL: "https://wind-keycloak.oc.dev.cloud.virtana.com:443"
KEYCLOAK_REALM: "{{ .tenantID }}"
OPSCRUISE_ACCOUNT_ID: "{{ .clusterName }}"
k8sgw:
enabled: false
promgw:
enabled: false
loggw-loki:
enabled: false
loki-stack:
enabled: false
prometheus:
enabled: false
opscruise-node-exporter-new:
enabled: false
kube-state-metrics:
enabled: false
tracegw:
enabled: true
tracegw:
storageClassName: ""
slo_storage_size: 20Gi
config:
tracegw:
tracegwconf.trace-source: tempo
tracegwconf.slo-enabled: "true"
tracegwconf.slo-training-minutes: "30"
tracegwconf.write-slo-slow-traces-to-alogq: "true"
tracegwconf.meters-exclude-all-tags: "true"
tracegwconf.mode: listen
tracegwconf.enable-trace-forwarding: "true"
tracegwconf.filter-tag-key: "cluster"
tracegwconf.find-cluster-tag-keys: "cluster"Refer to Step 2 of the Standard method for a detailed description of all fields.
Verify the deployment
After completing all deployment steps for either method, verify that the tracing components are running and data is flowing correctly.
Perform the following steps:
Run the command to check that TraceGW pods are running in the North Backend namespace:
kubectl get pods -n <your-backend-namespace> | grep tracegw
Check that the Trace Router pod is running:
kubectl get pods -n virtana-trace-router
Check that the OTEL Collector pods are running in each South cluster:
kubectl get pods -n <your-south-namespace> | grep otel
Send a test trace from a South cluster OTEL Collector and confirm it reaches TraceGW.
For the external method, also verify that the trace appears in your Tempo backend.
In the Virtana CO UI, navigate to the service map and confirm that trace-based latency and error metrics are visible for your instrumented services.