Observability
Metrics, logging, and monitoring for site recovery operations
This page describes how to monitor Trilio Site Recovery using its built-in observability primitives: Custom Resource status fields, RPO and replication health CRDs, and structured controller logs. Because all DR operations are CRD-driven, the primary observability surface is Kubernetes itself—you can query replication health, RPO violations, and failover progress with standard kubectl commands, integrate them into GitOps pipelines, or scrape them for alerting. Understanding this surface lets you catch degraded replication before a disaster, verify RTO and RPO objectives are being met, and diagnose issues without requiring access to the underlying storage layer.
Before using the observability features described on this page:
- OpenShift ≥ 4.14 with
ocorkubectlCLI access to all three clusters (quorum, primary, DR) - Trilio Site Recovery deployed:
site-recovery-quorum-control-planechart running on the quorum cluster andsite-recovery-workload-control-planechart running on both primary and DR clusters - Kubeconfig contexts configured for all clusters; the examples below use
--contextflags to distinguish them - The
tsr-gatherdiagnostic tool available if you need to collect a full support bundle - Sufficient RBAC to read CRDs across namespaces:
get,list, andwatchonreplicationgroupstatuses,rpoevents,protectiongroups,failoverrequests,testfailovers, anddrbdresourcesin the DR namespace (dr-<name>) on the quorum cluster and on both workload clusters
Observability primitives are installed automatically as part of the standard Site Recovery deployment. No additional Helm charts or agents are required. Verify that the CRDs are present on the appropriate clusters after deploying the operator.
Step 1 – Confirm CRDs are registered on the quorum cluster
oc --context quorum get crd replicationgroupstatuses.siterecovery.trilio.io \
rpoevents.siterecovery.trilio.io
Expected output (both CRDs present):
NAME CREATED AT
replicationgroupstatuses.siterecovery.trilio.io 2024-06-01T10:00:00Z
rpoevents.siterecovery.trilio.io 2024-06-01T10:00:00Z
Step 2 – Confirm CRDs are registered on the primary and DR clusters
oc --context primary get crd protectiongroups.siterecovery.trilio.io \
drbdresources.siterecovery.trilio.io
oc --context dr get crd protectiongroups.siterecovery.trilio.io \
drbdresources.siterecovery.trilio.io
Step 3 – Verify the quorum control plane is running
oc --context quorum get pods -n dr-<name> -l app.kubernetes.io/name=site-recovery-quorum-control-plane
All pods must be in Running state before replication health data is written to CRs.
Step 4 – Verify the workload control plane is running on both workload clusters
oc --context primary get pods -n dr-<name> -l app.kubernetes.io/name=site-recovery-workload-control-plane
oc --context dr get pods -n dr-<name> -l app.kubernetes.io/name=site-recovery-workload-control-plane
Step 5 – (Optional) Configure the tsr-gather support bundle tool
tsr-gather is an oc adm must-gather compatible tool run against the quorum cluster. It collects logs, CRD state, and configuration from all clusters into a single diagnostic bundle.
oc adm must-gather --image=<tsr-gather-image> -- /usr/bin/tsr-gather
Replace <tsr-gather-image> with the registry path provided by your Trilio distribution.
Observability behavior is configured through spec fields on the ReplicationGroupStatus and RPOEvent CRDs, and through the ProtectionZone logging configuration. The fields below are the only configurable options—do not assume others exist.
ReplicationGroupStatus – polling and RPO objective
This CR is created in the DR namespace (dr-<name>) on the quorum cluster, one per Protection Group. It controls how often the replication monitor reconciler polls DRBD resource state and what lag threshold triggers a violation.
| Field | Type | Required | Effect |
|---|---|---|---|
spec.protectionGroupRef | object | Yes | Identifies the Protection Group to monitor. Must set name and namespace. |
spec.pollingIntervalSeconds | integer | No | How often (in seconds) the replication monitor checks DRBD volume sync state. Lower values increase API server load. |
spec.replicationProtocol | string | No | Expected DRBD protocol: A (asynchronous) or C (synchronous). Used to contextualize lag reporting. |
spec.rpoObjectiveSeconds | integer | No | The RPO threshold in seconds. When observed replication lag exceeds this value, the monitor records an RPOEvent with severity warning or critical. |
Example manifest:
apiVersion: siterecovery.trilio.io/v1alpha1
kind: ReplicationGroupStatus
metadata:
name: prod-replication-status
namespace: dr-production
spec:
protectionGroupRef:
name: prod-protection-group
namespace: dr-production
pollingIntervalSeconds: 30
replicationProtocol: "C"
rpoObjectiveSeconds: 0
With replicationProtocol: "C" and rpoObjectiveSeconds: 0, any observed lag records a critical RPOEvent, which is appropriate for synchronous (Protocol C) deployments where RPO=0 is required.
For asynchronous (Protocol A) deployments, set rpoObjectiveSeconds to your acceptable lag budget (for example, 30 for a 30-second RPO).
RPOEvent – event structure (read-only at runtime)
RPOEvent CRs are written by the replication-monitor reconciler; you do not create them manually. The fields below describe what the operator populates so you can filter and alert on them.
| Field | Type | Meaning |
|---|---|---|
spec.protectionGroupRef | object | Protection Group that triggered the event. |
spec.eventType | string | Type of RPO violation event. |
spec.timestamp | string | RFC 3339 timestamp when the violation was detected. |
spec.rpoAtEvent | integer | Observed replication lag in seconds at the time of the event. |
spec.newRPOStatus | string | RPO status after this event (for example, Violated). |
spec.previousRPOStatus | string | RPO status before this event. |
spec.violationReason | string | Human-readable explanation of why RPO was violated. |
spec.affectedVolumes | array | List of DRBD volumes that are out of sync. |
spec.outOfSyncBytesAtEvent | integer | Bytes of unreplicated data at event time. |
spec.message | string | Free-text summary for dashboards and alerts. |
ProtectionZone – logging configuration
The ProtectionZone CR includes a spec.logging object that controls log verbosity for the quorum control plane components. Adjust this if you need more detailed output during troubleshooting.
apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionZone
metadata:
name: zone-production
namespace: dr-production
spec:
displayName: "Production DR Zone"
storageBackendMode: "drbd-operator"
clusters:
- name: primary
- name: dr
logging:
# Set log level here; consult your operator version docs for valid values
level: info
The following patterns cover the most common day-to-day observability tasks.
Check aggregated replication health for a Protection Group
The ReplicationGroupStatus CR provides a single Healthy, Degraded, or Critical status for all volumes in a Protection Group. Query it on the quorum cluster.
oc --context quorum get replicationgroupstatus prod-replication-status \
-n dr-production \
-o jsonpath='{.status.health}: last sync {.status.lastSyncTimestamp}'
Watch for transitions in real time:
oc --context quorum get replicationgroupstatus prod-replication-status \
-n dr-production -w
List all RPO violations for a Protection Group
RPOEvent CRs accumulate as an audit trail. Filter by the Protection Group label or name to see the full violation history.
oc --context quorum get rpoevents -n dr-production \
-o custom-columns=\
'NAME:.metadata.name,TIMESTAMP:.spec.timestamp,LAG:.spec.rpoAtEvent,REASON:.spec.violationReason,STATUS:.spec.newRPOStatus'
To see the full detail of the most recent event:
oc --context quorum get rpoevents -n dr-production \
--sort-by='.spec.timestamp' \
-o yaml | tail -60
Inspect per-volume sync state on the DR cluster
DRBDResource CRs report per-volume sync progress and connection state for each VM. One CR exists per VM per cluster side.
oc --context dr get drbdresources -n dr-production
For detailed per-volume status:
oc --context dr get drbdresource prod-vm-1-drbd -n dr-production -o yaml
The status section of the CR shows sync percentage, connection state, and role (Primary/Secondary) for each volume.
Monitor an in-progress failover
FailoverRequest CRs track progress from Pending through InProgress to Completed or Failed. Watch the CR on the quorum cluster.
oc --context quorum get failoverrequest prod-failover -n dr-production -w
For full status including per-VM detail:
oc --context quorum get failoverrequest prod-failover -n dr-production -o yaml
Monitor a test failover
TestFailover CRs progress through CreatingSnapshots → VerifyingData → Succeeded (or Failed) → CleaningUp.
oc --context quorum get testfailover prod-test-failover -n dr-production -w
Read controller logs
All Site Recovery controllers run in the DR namespace on the quorum cluster. Use the following patterns to access their logs.
failover-controller logs:
oc --context quorum logs -n dr-production \
-l app.kubernetes.io/component=failover-controller --tail=200 -f
protection-controller logs:
oc --context quorum logs -n dr-production \
-l app.kubernetes.io/component=protection-controller --tail=200 -f
pg-sync-controller logs:
oc --context quorum logs -n dr-production \
-l app.kubernetes.io/component=pg-sync-controller --tail=200 -f
test-failover-controller logs:
oc --context quorum logs -n dr-production \
-l app.kubernetes.io/component=test-failover-controller --tail=200 -f
drbd-node-agent logs (on a specific worker node):
oc --context primary logs -n dr-production \
-l app.kubernetes.io/component=drbd-node-agent \
--field-selector spec.nodeName=worker-node-1 --tail=100 -f
Collect a full diagnostic bundle
Run tsr-gather against the quorum cluster to collect logs, CRD state, and configuration from all clusters:
oc adm must-gather --image=<tsr-gather-image> \
--dest-dir=./tsr-bundle \
-- /usr/bin/tsr-gather
The bundle is written to ./tsr-bundle and contains no secrets or sensitive credentials.
Example 1 – Confirm replication is healthy before a planned maintenance window
oc --context quorum get replicationgroupstatus prod-replication-status \
-n dr-production \
-o jsonpath='Health: {.status.health}\nLast sync: {.status.lastSyncTimestamp}\nPer-volume detail: {.status.volumes}\n'
Expected output when healthy:
Health: Healthy
Last sync: 2024-06-15T08:42:01Z
Per-volume detail: [{"name":"prod-vm-1-disk","syncState":"Consistent"},{"name":"prod-vm-2-disk","syncState":"Consistent"}]
If the health is Degraded or Critical, investigate RPOEvent CRs and DRBDResource status before proceeding with any planned failover.
Example 2 – List all RPO violations in the last 24 hours
oc --context quorum get rpoevents -n dr-production \
-o custom-columns=\
'TIMESTAMP:.spec.timestamp,LAG_S:.spec.rpoAtEvent,BYTES_OUT:.spec.outOfSyncBytesAtEvent,REASON:.spec.violationReason'
Expected output:
TIMESTAMP LAG_S BYTES_OUT REASON
2024-06-15T06:12:04Z 8 204800 Network congestion on replication link
2024-06-15T06:13:34Z 0 0 Replication recovered
A LAG_S of 0 and BYTES_OUT of 0 in the most recent event indicates replication has recovered. An accumulating sequence of events with non-zero lag suggests a persistent connectivity or bandwidth problem.
Example 3 – Inspect per-volume DRBD sync state for a specific VM
oc --context dr get drbdresource prod-vm-1-drbd \
-n dr-production \
-o yaml
Expected status section (fully synced, secondary role):
status:
role: Secondary
connectionState: Connected
volumes:
- name: prod-vm-1-disk
syncPercent: 100
diskState: UpToDate
replicationState: Established
Degraded example (sync in progress):
status:
role: Secondary
connectionState: Connected
volumes:
- name: prod-vm-1-disk
syncPercent: 67
diskState: Inconsistent
replicationState: SyncTarget
A volume with diskState: Inconsistent and syncPercent below 100 is still synchronizing. Do not trigger a failover until diskState reaches UpToDate on the DR side.
Example 4 – Watch a failover request from start to completion
Create the FailoverRequest and immediately start watching it:
oc --context quorum apply -f - <<EOF
apiVersion: siterecovery.trilio.io/v1alpha1
kind: FailoverRequest
metadata:
name: prod-planned-failover
namespace: dr-production
spec:
protectionGroupRef:
name: prod-protection-group
namespace: dr-production
targetCluster: dr
failoverType: planned
drainTimeoutSeconds: 120
batchBootTimeoutSeconds: 300
EOF
oc --context quorum get failoverrequest prod-planned-failover \
-n dr-production -w
Expected status progression:
NAME STATUS PHASE
prod-planned-failover Pending Initializing
prod-planned-failover InProgress StoppingVMs
prod-planned-failover InProgress PromotingVolumes
prod-planned-failover InProgress StartingVMs
prod-planned-failover Completed Done
Example 5 – Collect a diagnostic bundle after an incident
oc adm must-gather --image=<tsr-gather-image> \
--dest-dir=./tsr-incident-bundle \
-- /usr/bin/tsr-gather
ls -lh ./tsr-incident-bundle/
Expected output:
total 18M
drwxr-xr-x quorum-cluster/
drwxr-xr-x primary-cluster/
drwxr-xr-x dr-cluster/
-rw-r--r-- tsr-gather-summary.txt
The bundle contains controller logs, CRD snapshots, and configuration from all clusters in the DR estate. Attach this directory when opening a support case with TrilioData.
Use a consistent diagnostic sequence: check the relevant CRD status first, then controller logs, then node-agent logs, then collect a tsr-gather bundle if the issue is not resolved.
ReplicationGroupStatus shows Degraded or Critical
Symptom: oc get replicationgroupstatus returns health: Degraded or health: Critical.
Likely causes:
- DRBD replication link between primary and DR worker nodes is interrupted (TCP port 7000–7999 blocked or network partition)
- A worker node running the
drbd-node-agentDaemonSet has gone offline - A
DRBDResourcevolume is stuck inInconsistentdisk state and is not resynchronizing
Fix:
- Check
DRBDResourcestatus for the affected VMs:oc --context dr get drbdresources -n dr-production -o yaml - Look at
drbd-node-agentlogs on the affected nodes:oc --context primary logs -n dr-production \ -l app.kubernetes.io/component=drbd-node-agent --tail=100 - Verify network connectivity on TCP ports 7000–7999 between primary and DR worker nodes.
- If a node is offline, check node status:
oc --context primary get nodes - Once the link is restored, DRBD will begin resynchronizing automatically. Monitor sync progress:
oc --context dr get drbdresource <name> -n dr-production \ -o jsonpath='{.status.volumes[*].syncPercent}'
RPOEvent records accumulating with non-zero rpoAtEvent
Symptom: oc get rpoevents shows repeated events with increasing spec.rpoAtEvent values and non-zero spec.outOfSyncBytesAtEvent.
Likely causes:
- Replication protocol is
A(asynchronous) and network bandwidth between clusters is saturated - Write workload on the primary has exceeded replication throughput
- Intermittent packet loss on the replication link causing retransmissions
Fix:
- Check
spec.violationReasonon recent events for detail:oc --context quorum get rpoevents -n dr-production \ -o jsonpath='{range .items[-3:]}{.spec.timestamp}: {.spec.violationReason} ({.spec.rpoAtEvent}s lag)\n{end}' - Review the
DRBDReplicationPolicyto confirm the protocol and endpoint configuration:oc --context primary get drbdreplicationpolicy -n dr-production -o yaml - If lag is acceptable for Protocol A deployments but alerting is too aggressive, increase
spec.rpoObjectiveSecondsin theReplicationGroupStatusCR to match your actual RPO budget. - For sustained lag above your RPO objective, investigate network throughput between clusters and consider throttle settings in
spec.resyncConfigon theDRBDReplicationPolicy.
FailoverRequest stuck in InProgress
Symptom: A FailoverRequest remains in InProgress for longer than the configured batchBootTimeoutSeconds.
Likely causes:
- VMs on the source cluster did not stop within
drainTimeoutSeconds(unplanned failover scenario where source is partially reachable) - DRBD volumes on the DR cluster failed to promote to Primary role
- VMs on the DR cluster are not scheduling (node taints, resource constraints, or missing PVCs)
Fix:
- Inspect the full
FailoverRequeststatus for error messages:oc --context quorum get failoverrequest <name> -n dr-production -o yaml - Review the
failover-controllerlogs:oc --context quorum logs -n dr-production \ -l app.kubernetes.io/component=failover-controller --tail=300 - Check if VMs are in a
PendingorSchedulingstate on the DR cluster:oc --context dr get vmi -n <vm-namespace> - Check for node taints preventing VM scheduling on DR worker nodes:
oc --context dr get nodes -o custom-columns='NAME:.metadata.name,TAINTS:.spec.taints' - Verify that
DRBDResourcevolumes on the DR side have reachedrole: PrimaryanddiskState: UpToDate.
TestFailover stuck in CreatingSnapshots or CleaningUp
Symptom: A TestFailover CR does not advance past CreatingSnapshots or does not complete CleaningUp.
Likely causes:
- Volume snapshot infrastructure (VolumeSnapshotClass) is not configured on the DR cluster
- The test namespace already contains conflicting resources from a previous failed test
- The
test-failover-controllerpod has restarted and lost in-memory state
Fix:
- Inspect the
TestFailoverstatus:oc --context quorum get testfailover <name> -n dr-production -o yaml - Check
test-failover-controllerlogs:oc --context quorum logs -n dr-production \ -l app.kubernetes.io/component=test-failover-controller --tail=200 - Verify
VolumeSnapshotClassis available on the DR cluster:oc --context dr get volumesnapshotclass - If the
CleaningUpphase is stuck, check for orphaned resources in the test namespace:Manually delete the test namespace if it is safe to do so, then delete and recreate theoc --context dr get all -n <test-namespace>TestFailoverCR.
Controller logs show repeated reconciliation errors
Symptom: Controller logs emit repeated error messages for the same resource without making progress.
Likely causes:
- RBAC permissions missing for the controller's service account
- The quorum cluster cannot reach the primary or DR cluster API server on TCP port 6443
- A CRD schema validation error is rejecting status patches
Fix:
- Check the exact error in the logs:
oc --context quorum logs -n dr-production \ -l app.kubernetes.io/name=site-recovery-quorum-control-plane --tail=100 | grep -i error - Verify API server reachability from the quorum cluster:
oc --context quorum exec -n dr-production deploy/site-recovery-quorum-control-plane \ -- curl -sk https://<primary-api-server>:6443/healthz - Check service account RBAC:
oc --context quorum auth can-i get replicationgroupstatuses \ --as=system:serviceaccount:dr-production:<controller-sa> -n dr-production - If RBAC is the issue, re-apply the Helm chart to restore the expected ClusterRoles and bindings:
helm upgrade --install site-recovery-quorum-control-plane \ trilio/site-recovery-quorum-control-plane \ -n dr-production
tsr-gather bundle is empty or missing cluster data
Symptom: The collected bundle directory is missing data from one or more clusters.
Likely cause: The kubeconfig contexts for the missing clusters are not accessible from the quorum cluster, or the tsr-gather image does not have network access to those API servers.
Fix:
- Verify that the quorum cluster can reach each workload cluster API server on TCP port 6443 before running
tsr-gather. - Confirm the kubeconfig used by
tsr-gatherincludes valid contexts for all clusters. - Re-run with verbose output if the tool supports it:
oc adm must-gather --image=<tsr-gather-image> \ --dest-dir=./tsr-bundle \ -- /usr/bin/tsr-gather - If individual cluster data is still missing, collect logs manually from the affected cluster using the controller log commands in the Usage section and attach them alongside the bundle.