Migrating from Alauda Container Platform Tracing
This document describes how to migrate an existing tracing deployment based on the legacy Alauda Container Platform (ACP) Tracing stack — Alauda Build of Jaeger (Jaeger 1.60.0) plus Alauda Build of OpenTelemetry — to Alauda Distributed Tracing based on Jaeger v2 (2.20.0) plus Alauda Build of OpenTelemetry v2.
The migration is performed in two stages:
-
OpenTelemetry stack migration. Replace the legacy
Alauda Build of OpenTelemetryOperator and Collector withAlauda Build of OpenTelemetry v2. Application pods are rolled out so that the v2 Java agent is injected. After this stage, telemetry is collected by the v2 Collector but still written to the legacy Jaeger backend. This stage is performed by following Migrating from Alauda Build of OpenTelemetry to Alauda Build of OpenTelemetry v2. -
Jaeger backend migration. Deploy a new
Jaeger v2instance alongside the legacy Jaeger, attach the legacy Elasticsearch indices to the read aliases of the new one, switch the v2 Collector's trace exporter to the new backend, and uninstall the legacy Jaeger once the new pipeline has been validated.
The legacy trace data is not copied or reindexed. Its indices are attached to the read aliases of the new Jaeger, so after the cutover the new Jaeger UI serves pre-cutover and post-cutover traces together, and the legacy stack can be removed as soon as you trust the new pipeline instead of after a full retention period.
TOC
OverviewWhat changes between ACP Tracing and Alauda Distributed TracingMigration outage windowsMigration flow at a glanceTrace data continuity strategyPrerequisitesPre-migration tasksMigrate Alauda Build of OpenTelemetry to v2Inventory the legacy Jaeger deploymentBack up the legacy Jaeger resourcesVerify Elasticsearch capacityMigration procedureValidation periodCleanupRollbackFAQOverview
What changes between ACP Tracing and Alauda Distributed Tracing
For changes to the OpenTelemetry Operator, Collector, and Instrumentation resources (including the now-required spec.java.image field, Service Mesh v1 incompatibility, and Collector configuration schema migration), see What changes between v1 and v2 in the OpenTelemetry v2 migration guide.
The legacy Alauda Build of Jaeger Operator owns a separate CRD (jaegertracing.io/v1.Jaeger) and does not conflict with the v2 OpenTelemetry Operator. It is therefore kept running during the migration, so that the legacy Jaeger stays available as a rollback target until the new pipeline has been validated.
Migration outage windows
Trace ingestion is interrupted in two places:
-
During the OpenTelemetry v1 → v2 migration, between the time the legacy
OpenTelemetryCollectoris deleted and the time the v2OpenTelemetryCollectorbecomes ready. See Migration outage window in the OpenTelemetry v2 migration guide. -
During the Jaeger cutover, when the v2 Collector is patched to redirect its trace exporter from the legacy Jaeger to the new Jaeger v2 backend. The Collector deployment is rolled, so a short ingestion gap may occur during the rollout.
Application pods continue to run normally throughout both windows, but telemetry generated during the gaps may be temporarily buffered and can be dropped if it cannot be exported in time. Plan each stage during a low-traffic window and notify telemetry consumers (developers, SRE, Kiali users) in advance.
The legacy Jaeger query path remains available throughout the migration, so previously stored traces can still be searched in the legacy Jaeger UI while the new pipeline is being brought up.
Migration flow at a glance
Trace data continuity strategy
By following the Recreate the OpenTelemetryCollector resources guidance during the OpenTelemetry v2 migration, the v2 OpenTelemetry Collector is deployed in the same namespace and with the same Service name (otel-collector in cpaas-system) as the legacy Collector. Applications that export OTLP to otel-collector.cpaas-system keep working without any configuration change.
Trace data is neither copied nor reindexed. The legacy date-stamped indices stay where they are and are attached to the read aliases of the new Jaeger in Attach the legacy indices to the new read aliases. Jaeger v2 resolves every read through those aliases, so after the attachment:
- Traces ingested before the cutover stay in the legacy indices (
acp-tracing-<cluster>-jaeger-*-YYYY-MM-DD) and are served by the new Jaeger UI at<platform-url>/clusters/<cluster>/jaegeralongside new traces, until their own retention policy deletes them. They also remain visible in the legacy Jaeger UI for as long as the legacy instance is kept. - Traces ingested after the cutover are written to the new index family (
acp-<cluster>-jaeger-*-000001,-000002, …) through the*-writealiases. The legacy indices are never written to again.
This works because Jaeger 2.20.0 reads documents written by Jaeger 1.60.0 as they are: both versions use the same Elasticsearch document model, and the mapping differences between them — the legacy templates declare tags.tagType where the v2 templates declare tags.type, and v2 adds scopeTags and references.traceState — do not touch any field that Jaeger queries on.
Because the new Jaeger UI is complete from the moment the aliases are attached, users do not have to know which UI holds which time range, and the legacy stack no longer has to be kept alive for a full retention period just to answer historical queries.
Prerequisites
- An active ACP CLI (
kubectl) session by a cluster administrator with thecluster-adminrole. - The legacy ACP Tracing stack (
Alauda Build of JaegerOperator with aJaegerinstance, plusAlauda Build of OpenTelemetrywith anOpenTelemetryCollectorand one or moreInstrumentationresources) is currently installed. - Elasticsearch 8.x is reachable from the cluster, and you have an Elasticsearch user with permission to create ILM policies, index templates, and index aliases, and to update index settings.
- The
jqandenvsubstcommand-line tools are installed on the workstation that runs the migration commands. - Telemetry consumers (developers, Kiali users, SRE dashboards) and application owners are notified of the planned outage windows.
Pre-migration tasks
Migrate Alauda Build of OpenTelemetry to v2
Before migrating the Jaeger backend, complete the migration from Alauda Build of OpenTelemetry to Alauda Build of OpenTelemetry v2 by following Migrating from Alauda Build of OpenTelemetry to Alauda Build of OpenTelemetry v2. That guide covers:
- Backing up and uninstalling the legacy
Alauda Build of OpenTelemetryOperator and itsOpenTelemetryCollectorandInstrumentationresources. - Installing the v2 Operator.
- Preparing a Java auto-instrumentation image and recreating
OpenTelemetryCollectorandInstrumentationresources, including setting the now-requiredspec.java.imagefield. - Rolling out application pods so that the new Java agent is injected.
At the end of this stage:
- The v2
Alauda Build of OpenTelemetryOperator is installed and the legacy Operator is uninstalled. - The v2
OpenTelemetryCollectorincpaas-systemis running with its trace exporter still pointing at the legacy Jaeger (the natural outcome of recreating the Collector from the v1 backup). - All
Instrumentationresources havespec.java.imageset, and application pods have been rolled out with the v2 Java agent.
Trace ingestion continues to flow into the legacy Jaeger until Switch the v2 OpenTelemetry Collector to the new Jaeger in this guide.
Inventory the legacy Jaeger deployment
Capture the current state of the legacy Jaeger so that you understand the migration scope and can produce backups for rollback.
-
List the legacy
Alauda Build of JaegerOperator andJaegerinstances: -
Record the legacy Elasticsearch endpoint, credentials, and index prefix referenced by the legacy
Jaegerresource. They will also be reused by the new Jaeger v2 instance.
Back up the legacy Jaeger resources
Export the legacy Jaeger resources so that you can rebuild them (and roll back if needed):
The backup files are only used as configuration references and rollback artifacts. When you rebuild Jaeger on v2, follow the v2 conventions described in Installing Alauda Distributed Tracing.
Verify Elasticsearch capacity
The new Jaeger writes to a separate index family (acp-<cluster>-jaeger-*) while the legacy indices (acp-tracing-<cluster>-jaeger-*) age out over the legacy retention period. Plan for one extra full retention's worth of trace storage in Elasticsearch.
Migration procedure
Deploy the new Jaeger v2 instance
Follow Deploying the Alauda Build of Jaeger v2 in the Alauda Distributed Tracing installation guide. The new Jaeger v2 instance is deployed in a dedicated namespace (jaeger-system by default) so that it does not collide with the legacy jaeger-prod instance in cpaas-system.
Only follow the Deploying the Alauda Build of Jaeger v2 section linked above. Do not execute the Deploying the OpenTelemetry Collector section of the same installation guide — the application-facing v2 OpenTelemetry Collector was already deployed in cpaas-system during Stage 1 (OpenTelemetry v2 migration). Running that section would create a duplicate otel Collector in jaeger-system that no application talks to.
When you reach the variable-setup step, keep the default index prefix so that the new Jaeger writes to indices that are clearly separated from the legacy ones:
After completing the installation steps, verify that:
- The Jaeger Pod in
jaeger-systemisReady. - The Jaeger UI is reachable at
<platform-url>/clusters/<cluster>/jaeger. It is empty at this point, since no exporter is yet writing to it and the legacy indices have not been attached yet. - The Service
jaeger-collector.jaeger-system.svc.cluster.localaccepts OTLP gRPC on port4317— this is the endpoint that the v2 OpenTelemetry Collector will export to in Switch the v2 OpenTelemetry Collector to the new Jaeger.
Attach the legacy indices to the new read aliases
The jaeger-es-rollover init Job run in the previous step created the <prefix>-jaeger-<type>-read and <prefix>-jaeger-<type>-write aliases for the new index family. Jaeger v2 resolves every read through the read alias, so adding the legacy date-stamped indices to that alias is all it takes to make pre-cutover traces queryable in the new Jaeger UI. Nothing is copied, renamed, or reindexed, and the legacy Jaeger keeps serving the same indices.
Perform this step before the cutover, so that the new Jaeger UI is already complete when it starts receiving traces.
Procedure
-
Set the legacy index prefix. It is the
es.index-prefixvalue recorded in Inventory the legacy Jaeger deployment: -
Add the legacy indices to the read aliases of the new index family:
The
-2*pattern matches the date-stamped legacy indices, such as-2026-08-27, and never the numbered rollover indices of the new family. A legacy Jaeger that ran without dependency analysis and without adaptive sampling has nodependenciesand nosamplingindex, so those two iterations returnindex_not_found_exception. That is expected and does not affect the other two.To attach only part of the history, narrow the pattern to the date range you need, for example
${LEGACY_ES_INDEX_PREFIX}-jaeger-${TYPE}-2026-08-1*. Every attached index is scanned on every query, so attach only what you still want to be searchable. -
Confirm that both index families are resolved by the read aliases, and that the write aliases still point only at the new numbered indices:
Example output
Only the
-000001indices carryis_write_index: true. The legacy indices are attached for reading only, so the new Jaeger cannot write into them. -
Open the new Jaeger UI at
<platform-url>/clusters/<cluster>/jaegerand confirm that services which reported before the cutover are listed and that one of their traces can be opened.NOTEPick a time range in the UI that covers the legacy data. The Jaeger UI always sends an explicit range, so its time-range picker behaves as expected.
Switch the v2 OpenTelemetry Collector to the new Jaeger
After the OpenTelemetry v1 → v2 migration, the recreated otel OpenTelemetryCollector in cpaas-system still writes traces to the legacy Jaeger because its trace exporter was inherited from the v1 backup. The trace-related portion of its spec.config typically looks like this (other fields are unrelated to this step and are omitted):
- The legacy-Jaeger exporter — inherited from the v1 backup — is named
otlpand targets the legacy Jaeger collector's headless Service incpaas-system.balancer_name: round_robindistributes spans across the headless Service endpoints.otlpis a deprecated alias of theotlp_grpcexporter type, which the switch step below adopts. - The trace pipeline sends spans to
debug(logs) andotlp(legacy Jaeger).
Patch the Collector to (1) add a new otlp_grpc/jaeger-v2 exporter pointing at the new Jaeger v2 collector Service in jaeger-system, (2) remove the legacy otlp exporter by setting it to null, and (3) replace the trace pipeline's exporter list with [debug, otlp_grpc/jaeger-v2]:
otlp_grpc
The exporter type name is otlp_grpc, not otlp. The former name still works as a deprecated alias, but the Collector logs a deprecation warning at startup for each affected component instance, and upstream intends to remove the alias in a future release. This migration is a convenient point to adopt the current name, since the inherited exporter is replaced anyway.
The rename applies to newly created exporters. Keep otlp: null as it is: it removes the exporter that already exists on the Collector under that name.
service.pipelines.traces.exporters is an array, and a merge patch replaces arrays in their entirety rather than appending. The patch above lists every exporter that must remain in the trace pipeline (debug, otlp_grpc/jaeger-v2). If your trace pipeline contains additional custom exporters, add them to this list before applying the patch.
If the inherited legacy-Jaeger exporter on your Collector is not named otlp (for example, your v1 backup used jaeger or a different OTLP variant), substitute that name in the null removal step accordingly.
The patch above replaces the legacy exporter; it does not add a second one. Do not leave both in the pipeline. With the legacy indices attached to the read aliases, a span written to both backends is indexed in both index families and the new Jaeger returns it twice — a five-span trace renders as ten. See Trace data continuity strategy.
Trace ingestion is restored at this point. New traces are written to the new Jaeger v2 backend and become searchable in the new Jaeger UI.
Verify the migration
-
Confirm that both
OpenTelemetryCollectorresources are healthy:Example output:
Both
cpaas-system/otel(the application-facing Collector, redeployed by the OpenTelemetry v1 → v2 migration) andjaeger-system/jaeger(the new Jaeger v2 backend, deployed in Deploy the new Jaeger v2 instance) must reportREADYas<ready>/<desired>with<ready>equal to<desired>(typically1/1) andMANAGEMENTasmanaged. If theREADYcolumn shows0/1or is empty, inspect the Collector pod logs in the corresponding namespace before continuing. -
Generate sample traces with
telemetrygenand verify they appear in the new Jaeger UI:The new Jaeger UI at
<platform-url>/clusters/<cluster>/jaegershould list thejaeger-migration-checkservice and its traces. -
Confirm that the new index family is being created in Elasticsearch and that the legacy index family is intact:
You should see legacy indices matching
acp-tracing-<cluster>-jaeger-*(date-stamped, no longer growing) and new indices matchingacp-<cluster>-jaeger-*-000001(rollover, growing). -
Spot-check that a real business request produces a trace in the new Jaeger UI. Pick one or two already-instrumented applications, trigger a representative request, and look up its traceID in the new Jaeger UI.
-
Confirm that pre-cutover traces are still reachable and that the new Jaeger did not overwrite the index templates:
The read alias must list both the legacy date-stamped indices and the new
-000001index, and a service that last reported before the cutover must still appear in the new Jaeger UI.The second command must print the read alias and
rollover_alias. An empty result means the Jaeger instance recreated its index templates on startup becausecreate_mappingsis not set tofalse. The legacy indices already attached are unaffected, but future rollover indices would no longer be added to the read alias and ILM could no longer roll over. Setcreate_mappings: falseon the Jaeger instance and re-run thejaeger-es-rollover initJob from the installation guide.
Validation period
The legacy Jaeger does not have to be kept for a full retention period. Pre-cutover traces are served by the new Jaeger through the read aliases, so the legacy instance is only a rollback target. Keep it for as long as you want that fallback available — a few hours to a few days is typical — and use the window to:
- Validate dashboards, alerts, and Kiali integrations against the new Jaeger UI and the v2 Java agent metric names.
- Confirm that both pre-cutover and post-cutover traces are searchable in the new Jaeger UI.
- Tell users that the new Jaeger UI is now the single entry point for traces and that the legacy UI at
<platform-url>/clusters/<cluster>/acp/jaegerwill be retired.
When the new pipeline is trusted, continue with Cleanup.
Deleting the legacy Jaeger instance does not delete its Elasticsearch indices, so pre-cutover traces stay queryable in the new Jaeger UI afterwards. What it does delete is the legacy jaeger-es-index-cleaner CronJob, which is owned by the Jaeger resource. Nothing would then expire those indices, which is why Take over retention of the legacy indices comes first.
Cleanup
Take over retention of the legacy indices
Up to this point the legacy jaeger-es-index-cleaner CronJob has been expiring the legacy indices. That CronJob is owned by the legacy Jaeger resource and is garbage-collected together with it in the next step, so retention has to be handed over first. Otherwise the legacy indices stay in Elasticsearch indefinitely — and, because they are attached to the read aliases, they also keep being scanned by every query.
Give them an Index Lifecycle Management policy of their own, containing only a delete phase.
jaeger-ilm-policy cannot manage the legacy indices
jaeger-ilm-policy also contains a rollover action, and Elasticsearch can only roll over an index that is the write index of a rollover alias. Applied to a date-stamped legacy index it fails with setting [index.lifecycle.rollover_alias] for index [...] is empty or not defined, the index never leaves the hot phase, and it is never deleted. A separate delete-only policy is required.
Run this step after the cutover, once the legacy Jaeger has stopped creating new daily indices.
Procedure
-
Create the policy. Set
min_ageto the retention the legacy cleaner applied — theesIndexCleaner.numberOfDaysvalue recorded in Inventory the legacy Jaeger deployment,7by default: -
Apply it to the whole legacy index family:
-
Confirm that every legacy index is managed and that none of them reports an error. Elasticsearch polls ILM every 10 minutes by default, so the phase stays
newfor a while before it advances:Every index must report
"managed" : truewith policyjaeger-legacy-ilm-policy, and no"reason"line must appear.
When an index reaches the end of its retention, Elasticsearch deletes it and Elasticsearch removes it from the read alias automatically — the alias needs no maintenance.
Uninstall the legacy Jaeger instance
Delete the legacy Jaeger instance and its supporting resources. The Elasticsearch indices are not touched, so pre-cutover traces stay queryable in the new Jaeger UI:
Uninstall the Alauda Build of Jaeger Operator:
The jaegers.jaegertracing.io CRD can also be deleted if no other Jaeger resources remain in the cluster:
Disable the legacy feature switch and retire the legacy index family
-
In the ACP web console, open Feature Switch and disable
acp-tracing-ui. The platform-customized Observability → Tracing view is no longer functional after the legacy Alauda Build of OpenTelemetry v1 Operator is uninstalled. Update internal documentation and runbooks to point to the Jaeger UI URL<platform-url>/clusters/<cluster>/jaeger. -
Let the legacy indices expire. The policy applied in Take over retention of the legacy indices deletes each index once it reaches its retention and removes it from the read alias, so this step needs no action — only a check of what is left:
To retire the pre-cutover traces earlier than their retention, delete the indices directly instead of waiting:
Deleting an index removes it from the read alias as well, so the new Jaeger simply stops returning those traces.
-
Once the listing in the previous step comes back empty, remove what ILM does not clean up by itself — the legacy index templates and the delete-only policy:
Rollback
For rollback of the OpenTelemetry v1 → v2 stage, see Rollback in the OpenTelemetry v2 migration guide. For the Jaeger migration stage performed in this document, choose the rollback path that matches the phase you are in:
To detach the legacy indices, reverse the alias action from Attach the legacy indices to the new read aliases:
If the delete-only ILM policy from Take over retention of the legacy indices was already applied, remove it as well, so that the legacy indices are not expired while the legacy stack is being restored:
FAQ
Do applications need to update their OTLP endpoints?
No. By following the Recreate the OpenTelemetryCollector resources guidance in the OpenTelemetry v2 migration guide, the v2 OpenTelemetry Collector is deployed in the same namespace (cpaas-system), with the same Service name (otel-collector) and the same ports (4317/4318). Workloads that exported to otel-collector.cpaas-system:4317 keep working without any change.
Will the new Jaeger UI show traces from before the cutover?
Yes, once Attach the legacy indices to the new read aliases has been performed. The legacy indices are added to the read aliases of the new index family, so the new Jaeger UI serves pre-cutover and post-cutover traces together, until the legacy indices reach their retention. Nothing is copied or reindexed, and the legacy indices remain read-only.
If you skip that step, the behavior is the pre-2.1 one: pre-cutover traces stay reachable only through the legacy Jaeger UI, and are lost when the legacy stack is removed.
Do I need to reindex the legacy data, or convert it to the Jaeger v2 format?
No. Jaeger 1.60.0 and Jaeger 2.20.0 write the same Elasticsearch document model, so Jaeger v2 reads the legacy documents as they are. The index templates differ — the legacy one declares tags.tagType where the v2 one declares tags.type, and v2 adds scopeTags and references.traceState — but Jaeger resolves tag searches through tags.key and tags.value, which both templates index identically. Reindexing the legacy data into v2 mappings is possible but brings no functional benefit.
Can I write to both Jaeger backends at the same time?
No. With the legacy indices attached to the read aliases, a span exported to both backends is indexed once by the legacy Jaeger into the legacy index family and once by the new Jaeger into the new one. A single read alias resolves both, and Jaeger does not deduplicate spans, so the new Jaeger UI renders every span twice. Keep exactly one Jaeger exporter in the Collector trace pipeline. For rollback, switch the single exporter back to the legacy Jaeger rather than adding a second one; see Rollback.
Can the legacy and the new Jaeger share the same Elasticsearch index?
No — they share a read alias, not an index. The legacy Jaeger writes date-stamped daily indices (acp-tracing-<cluster>-jaeger-span-YYYY-MM-DD), while Jaeger v2 writes through rollover aliases (acp-<cluster>-jaeger-span-write backed by *-000001, *-000002, …). Their lifecycles are managed separately, and only the new numbered indices are ever written to. Keep the default prefixes shown in Deploy the new Jaeger v2 instance so that the two families cannot collide.
How much extra Elasticsearch storage is required during the migration?
The new index family grows from zero while the legacy index family ages out over the legacy retention; plan for one extra full retention's worth of trace storage as a steady-state buffer. Attaching the legacy indices to the read aliases adds no storage at all — it only changes which indices a query resolves to.
Can Service Performance Monitoring (SPM) be enabled as part of the migration?
SPM is optional and can be enabled at any time after Verify the migration. Follow (Optional) Enabling Service Performance Monitoring (SPM) to add the span_metrics connector and configure a metrics backend in the new Jaeger.