From f5fc8874e0b9d922eb2882ec8c741956c0d7e3e7 Mon Sep 17 00:00:00 2001 From: Aleksandra Spilkowska Date: Fri, 19 Jun 2026 18:07:49 +0200 Subject: [PATCH 1/6] Add upstream OpenTelemetry Collector guide to documentation --- .../opentelemetry/use-cases/index.md | 1 + .../use-cases/upstream-collector/index.md | 352 ++++++++++++++++++ solutions/toc.yml | 1 + 3 files changed, 354 insertions(+) create mode 100644 solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md diff --git a/solutions/observability/get-started/opentelemetry/use-cases/index.md b/solutions/observability/get-started/opentelemetry/use-cases/index.md index 766d3b86ea..9718e51333 100644 --- a/solutions/observability/get-started/opentelemetry/use-cases/index.md +++ b/solutions/observability/get-started/opentelemetry/use-cases/index.md @@ -18,5 +18,6 @@ Explore specific use cases for the {{edot}}: - [Kubernetes observability](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) - [LLM observability](/solutions/observability/get-started/opentelemetry/use-cases/llms/index.md) +- [Upstream OpenTelemetry Collector (self-managed)](/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md) diff --git a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md new file mode 100644 index 0000000000..e7dee1ded5 --- /dev/null +++ b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md @@ -0,0 +1,352 @@ +--- +navigation_title: Contrib collector +description: Send data from a Contrib OpenTelemetry Collector to a self-managed Elastic Stack by routing it through an EDOT Collector gateway. +applies_to: + stack: ga 9.2 +products: + - id: observability + - id: edot-collector +--- + +# Send data from an contrib OpenTelemetry Collector [upstream-collector-self-managed] + +This guide shows how to forward telemetry data from an upstream contrib OpenTelemetry Collector to a self-managed {{stack}} using an EDOT Collector in gateway mode. + +## When to use this setup + +Use this pattern if you: + +* Already run a contrib OpenTelemetry Collector and want to add Elastic as a backend without replacing your existing setup +* Need to fan out telemetry to multiple observability backends from a single contrib Collector +* Evaluate Elastic alongside another backend before committing to a full migration +* Use a technology or language that Elastic doesn't provide an EDOT distribution for + +## Architecture + +Your services send telemetry to the contrib otelcol-contrib, which forwards it over OTLP/gRPC to the EDOT Collector gateway. The gateway applies Elastic-specific processing and writes directly to {{es}}. + +```mermaid +flowchart LR + S["Your services"] -->|OTLP| U["Upstream otelcol-contrib\n(one per host)"] + U -->|"OTLP/gRPC :4317"| G + + subgraph G["EDOT Collector (gateway)"] + P["elasticapm processor\nenriches spans"] + C["elasticapm connector\ngenerates aggregated metrics"] + end + + G -->|"elasticsearch exporter\nmapping.mode: otel"| E[("ES")] +``` + +The `elasticsearch` exporter with `mapping.mode: otel` is the recommended path for self-managed deployments. The Managed OTLP endpoint is not available for self-managed installations. + +## Prerequisites + +* A running self-managed {{es}} cluster +* The [EDOT Collector binary](elastic-agent://reference/edot-collector/index.md) installed on the gateway host +* The [contrib OpenTelemetry Collector](https://opentelemetry.io/docs/collector/installation/) installed on your agent hosts +* Network connectivity from the contrib Collector hosts to the EDOT gateway host on port 4317 + +::::{stepper} + +:::{step} Create an {{es}} API key + +The EDOT gateway authenticates to {{es}} using an API key. + +1. In {{kib}}, navigate to **{{stack-manage-app}}** → **API keys**. +2. Select **Create API key**. +3. Give the key a name (for example, `edot-gateway`) and assign it the necessary privileges for writing to {{es}} data streams. +4. Copy the encoded key. You will use it as `ELASTIC_API_KEY` in the gateway configuration. + +::: + +:::{step} Configure the EDOT gateway + +Create a configuration file for the EDOT Collector running in gateway mode. Save this as `gateway.yml` on the gateway host. + +Set the following environment variables on the gateway host before starting the Collector: + +```bash +export ELASTIC_ENDPOINT=https://your-elasticsearch:9200 +export ELASTIC_API_KEY=your-encoded-api-key +``` + +Then create `gateway.yml`: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + +connectors: + elasticapm: {} + +processors: + batch: + send_batch_size: 1000 + timeout: 1s + send_batch_max_size: 1500 + batch/metrics: + send_batch_max_size: 0 + timeout: 1s + elasticapm: {} + +exporters: + elasticsearch/otel: + endpoints: + - ${env:ELASTIC_ENDPOINT} + api_key: ${env:ELASTIC_API_KEY} + mapping: + mode: otel + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch, elasticapm] + exporters: [elasticapm, elasticsearch/otel] + metrics: + receivers: [otlp] + processors: [batch/metrics] + exporters: [elasticsearch/otel] + metrics/aggregated-otel-metrics: + receivers: [elasticapm] + processors: [] + exporters: [elasticsearch/otel] + logs: + receivers: [otlp] + processors: [batch] + exporters: [elasticsearch/otel] +``` + +Key components in this configuration: + +* **`elasticapm` processor** (under `processors`): Enriches spans with attributes required by the {{product.apm}} UI. +* **`elasticapm` connector** (under `connectors`): Generates pre-aggregated {{product.apm}} metrics from trace data. It appears as an exporter in the `traces` pipeline and as a receiver in the `metrics/aggregated-otel-metrics` pipeline. +* **`elasticsearch/otel` exporter**: Writes data directly to {{es}} using native OpenTelemetry data streams (`mapping.mode: otel`). + +:::{note} +The `elasticapm` connector and processor are required for full {{product.apm}} functionality (service maps, transaction histograms, service-level indicators). You only need them when exporting directly to {{es}}. If you send to the Managed OTLP endpoint or {{apm-server-or-mis}}, they are not required. + +Refer to [{{product.apm}} services missing due to misconfigured elasticapm connector](/troubleshoot/ingest/opentelemetry/edot-collector/misconfigured-elasticapm-connector.md) for more information. +::: + +Start the EDOT gateway: + +```bash +edot-collector --config gateway.yml +``` + +::: + +:::{step} Configure the contrib otelcol-contrib + +On each contrib Collector host, configure the OTLP exporter to point to the EDOT gateway. Add or update the `exporters` and `service` sections in your existing `config.yml`: + +```yaml +exporters: + otlp: + endpoint: "gateway-host:4317" + tls: + insecure: true # Set to false and configure ca_file for production + +service: + pipelines: + traces: + exporters: [otlp] + metrics: + exporters: [otlp] + logs: + exporters: [otlp] +``` + +Replace `gateway-host` with the hostname or IP of your EDOT gateway host. In production, configure TLS to secure communication between the contrib Collector and the gateway. + +:::{tip} +Set the `deployment.environment` resource attribute in your contrib Collector so that services appear under the correct environment in the {{kib}} {{product.apm}} Service Map. Without it, all services show as "unset" in the environment selector. + +```yaml +processors: + resource: + attributes: + - key: deployment.environment + action: insert + value: production +``` + +Refer to [Attributes and labels](/solutions/observability/apm/opentelemetry/attributes.md) for more details. +::: + +Restart the contrib Collector to apply the changes. + +::: + +:::{step} Verify data in {{kib}} + +After starting both Collectors, wait a few minutes for data to appear. Then verify in {{kib}}: + +1. Navigate to **Observability** → **{{product.apm}}** → **Services** to confirm your services appear. +2. Navigate to **Observability** → **{{product.apm}}** → **Service Map** to confirm environment-based filtering works. +3. Navigate to **Discover** and check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams for incoming data. + +If no data appears, refer to [No logs, metrics, or traces visible in {{kib}}](/troubleshoot/ingest/opentelemetry/no-data-in-kibana.md). + +::: + +:::: + +## Deploy on {{k8s}} [upstream-collector-k8s] + +If you're running in {{k8s}}, you can deploy the EDOT gateway as a {{k8s}} `Deployment` and expose it as a `Service` so contrib Collectors can reach it using the cluster DNS. + +The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. + +### Create a secret for credentials + +```bash +kubectl create secret generic elastic-secret-otel \ + --from-literal=elastic_endpoint='https://your-elasticsearch:9200' \ + --from-literal=elastic_api_key='your-encoded-api-key' +``` + +### Deploy the EDOT gateway + +Apply the following manifest. The `ConfigMap` holds the gateway configuration, the `Deployment` runs the EDOT Collector, and the `Service` exposes port 4317 for contrib Collectors inside the cluster. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: edot-gateway-config +data: + gateway.yaml: | + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + connectors: + elasticapm: {} + processors: + batch: + send_batch_size: 1000 + timeout: 1s + send_batch_max_size: 1500 + batch/metrics: + send_batch_max_size: 0 + timeout: 1s + elasticapm: {} + exporters: + elasticsearch/otel: + endpoints: + - ${env:ELASTIC_ENDPOINT} + api_key: ${env:ELASTIC_API_KEY} + mapping: + mode: otel + service: + pipelines: + traces: + receivers: [otlp] + processors: [batch, elasticapm] + exporters: [elasticapm, elasticsearch/otel] + metrics: + receivers: [otlp] + processors: [batch/metrics] + exporters: [elasticsearch/otel] + metrics/aggregated-otel-metrics: + receivers: [elasticapm] + processors: [] + exporters: [elasticsearch/otel] + logs: + receivers: [otlp] + processors: [batch] + exporters: [elasticsearch/otel] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: edot-gateway + labels: + app: edot-gateway +spec: + replicas: 2 + selector: + matchLabels: + app: edot-gateway + template: + metadata: + labels: + app: edot-gateway + spec: + containers: + - name: edot-gateway + image: docker.elastic.co/elastic-agent/elastic-otel-collector:{{version.edot_collector}} + args: ["--config", "/etc/edot/gateway.yaml"] + env: + - name: ELASTIC_AGENT_OTEL + value: "true" + - name: ELASTIC_ENDPOINT + valueFrom: + secretKeyRef: + name: elastic-secret-otel + key: elastic_endpoint + - name: ELASTIC_API_KEY + valueFrom: + secretKeyRef: + name: elastic-secret-otel + key: elastic_api_key + ports: + - containerPort: 4317 # gRPC + - containerPort: 4318 # HTTP + volumeMounts: + - name: config + mountPath: /etc/edot + volumes: + - name: config + configMap: + name: edot-gateway-config +--- +apiVersion: v1 +kind: Service +metadata: + name: edot-gateway +spec: + selector: + app: edot-gateway + ports: + - name: otlp-grpc + port: 4317 + targetPort: 4317 + - name: otlp-http + port: 4318 + targetPort: 4318 +``` + +### Configure the contrib Collector + +Point the contrib otelcol-contrib OTLP exporter at the gateway `Service`: + +```yaml +exporters: + otlp: + endpoint: "edot-gateway:4317" + tls: + insecure: true # Set to false and configure ca_file for production +``` + +:::{note} +For comprehensive {{k8s}} observability (including host metrics, pod logs, {{k8s}} events, and cluster metrics) use the `opentelemetry-kube-stack` Helm chart with the Elastic values instead. Refer to [{{k8s}} observability](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md). +::: + +## Next steps + +* [EDOT Collector gateway configuration reference](elastic-agent://reference/edot-collector/config/default-config-standalone.md#gateway-mode) +* [{{k8s}} observability with EDOT](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) +* [Attributes and labels](/solutions/observability/apm/opentelemetry/attributes.md) +* [EDOT compared to contrib OpenTelemetry](opentelemetry://reference/compatibility/edot-vs-upstream.md) diff --git a/solutions/toc.yml b/solutions/toc.yml index 5e63d02d0e..bc4774a243 100644 --- a/solutions/toc.yml +++ b/solutions/toc.yml @@ -129,6 +129,7 @@ toc: - file: observability/get-started/opentelemetry/use-cases/kubernetes/upgrade.md - file: observability/get-started/opentelemetry/use-cases/kubernetes/customization.md - folder: observability/get-started/opentelemetry/use-cases/llms + - file: observability/get-started/opentelemetry/use-cases/upstream-collector/index.md - file: observability/get-started/other-tutorials/index.md children: - file: observability/get-started/other-tutorials/tutorial-monitor-java-application.md From 919736d025d5d2e8dc138f76c2010f7ccff6f1e9 Mon Sep 17 00:00:00 2001 From: Aleksandra Spilkowska Date: Mon, 22 Jun 2026 14:38:12 +0200 Subject: [PATCH 2/6] update style and technical details --- .../use-cases/upstream-collector/index.md | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md index e7dee1ded5..486a62aa53 100644 --- a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md +++ b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md @@ -1,14 +1,14 @@ --- -navigation_title: Contrib collector -description: Send data from a Contrib OpenTelemetry Collector to a self-managed Elastic Stack by routing it through an EDOT Collector gateway. +navigation_title: Contrib Collector +description: Send data from a contrib OpenTelemetry Collector to a self-managed Elastic Stack by routing it through an EDOT Collector gateway. applies_to: - stack: ga 9.2 + stack: ga 9.2+ products: - id: observability - id: edot-collector --- -# Send data from an contrib OpenTelemetry Collector [upstream-collector-self-managed] +# Send data from a contrib OpenTelemetry Collector [upstream-collector-self-managed] This guide shows how to forward telemetry data from an upstream contrib OpenTelemetry Collector to a self-managed {{stack}} using an EDOT Collector in gateway mode. @@ -19,31 +19,31 @@ Use this pattern if you: * Already run a contrib OpenTelemetry Collector and want to add Elastic as a backend without replacing your existing setup * Need to fan out telemetry to multiple observability backends from a single contrib Collector * Evaluate Elastic alongside another backend before committing to a full migration -* Use a technology or language that Elastic doesn't provide an EDOT distribution for +* Use a technology or language for which Elastic doesn't provide an EDOT distribution ## Architecture -Your services send telemetry to the contrib otelcol-contrib, which forwards it over OTLP/gRPC to the EDOT Collector gateway. The gateway applies Elastic-specific processing and writes directly to {{es}}. +Your services send telemetry to the contrib Collector (`otelcol-contrib`), which forwards it over OTLP/gRPC to the EDOT Collector gateway. The gateway applies Elastic-specific processing and writes directly to {{es}}. ```mermaid flowchart LR - S["Your services"] -->|OTLP| U["Upstream otelcol-contrib\n(one per host)"] + S["Your services"] -->|OTLP| U["Upstream otelcol-contrib
(one per host)"] U -->|"OTLP/gRPC :4317"| G subgraph G["EDOT Collector (gateway)"] - P["elasticapm processor\nenriches spans"] - C["elasticapm connector\ngenerates aggregated metrics"] + P["elasticapm processor
enriches spans"] + C["elasticapm connector
generates aggregated metrics"] end - G -->|"elasticsearch exporter\nmapping.mode: otel"| E[("ES")] + G -->|"elasticsearch exporter
mapping.mode: otel"| E[("ES")] ``` -The `elasticsearch` exporter with `mapping.mode: otel` is the recommended path for self-managed deployments. The Managed OTLP endpoint is not available for self-managed installations. +The `elasticsearch` exporter with `mapping.mode: otel` is the recommended path for self-managed deployments. The Managed OTLP endpoint isn't available for self-managed installations. Sending directly to {{apm-server-or-mis}} through OTLP is also possible but not recommended. Use the EDOT gateway path when available. ## Prerequisites * A running self-managed {{es}} cluster -* The [EDOT Collector binary](elastic-agent://reference/edot-collector/index.md) installed on the gateway host +* The [EDOT Collector](elastic-agent://reference/edot-collector/index.md) installed on the gateway host. It ships as part of the {{agent}} package and runs as {{agent}} in `otel` mode. * The [contrib OpenTelemetry Collector](https://opentelemetry.io/docs/collector/installation/) installed on your agent hosts * Network connectivity from the contrib Collector hosts to the EDOT gateway host on port 4317 @@ -56,7 +56,7 @@ The EDOT gateway authenticates to {{es}} using an API key. 1. In {{kib}}, navigate to **{{stack-manage-app}}** → **API keys**. 2. Select **Create API key**. 3. Give the key a name (for example, `edot-gateway`) and assign it the necessary privileges for writing to {{es}} data streams. -4. Copy the encoded key. You will use it as `ELASTIC_API_KEY` in the gateway configuration. +4. Copy the encoded key to use as the `ELASTIC_API_KEY` value in the gateway configuration. ::: @@ -135,15 +135,15 @@ The `elasticapm` connector and processor are required for full {{product.apm}} f Refer to [{{product.apm}} services missing due to misconfigured elasticapm connector](/troubleshoot/ingest/opentelemetry/edot-collector/misconfigured-elasticapm-connector.md) for more information. ::: -Start the EDOT gateway: +Start the EDOT gateway. The EDOT Collector is the {{agent}} binary run in `otel` mode, so start it with the `otel` subcommand: ```bash -edot-collector --config gateway.yml +elastic-agent otel --config gateway.yml ``` ::: -:::{step} Configure the contrib otelcol-contrib +:::{step} Configure the contrib Collector On each contrib Collector host, configure the OTLP exporter to point to the EDOT gateway. Add or update the `exporters` and `service` sections in your existing `config.yml`: @@ -152,7 +152,7 @@ exporters: otlp: endpoint: "gateway-host:4317" tls: - insecure: true # Set to false and configure ca_file for production + insecure: true # Set to `false` and configure `ca_file` for production service: pipelines: @@ -189,9 +189,9 @@ Restart the contrib Collector to apply the changes. After starting both Collectors, wait a few minutes for data to appear. Then verify in {{kib}}: -1. Navigate to **Observability** → **{{product.apm}}** → **Services** to confirm your services appear. -2. Navigate to **Observability** → **{{product.apm}}** → **Service Map** to confirm environment-based filtering works. -3. Navigate to **Discover** and check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams for incoming data. +* Navigate to **{{observability}}** → **{{product.apm}}** → **Services** to confirm your services appear. +* Navigate to **{{observability}}** → **{{product.apm}}** → **Service Map** to confirm environment-based filtering works. +* Navigate to **Discover** and check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams for incoming data. If no data appears, refer to [No logs, metrics, or traces visible in {{kib}}](/troubleshoot/ingest/opentelemetry/no-data-in-kibana.md). @@ -203,9 +203,11 @@ If no data appears, refer to [No logs, metrics, or traces visible in {{kib}}](/t If you're running in {{k8s}}, you can deploy the EDOT gateway as a {{k8s}} `Deployment` and expose it as a `Service` so contrib Collectors can reach it using the cluster DNS. -The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. +The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. The image entrypoint starts {{agent}} in `otel` mode when the `ELASTIC_AGENT_OTEL` environment variable is set to `true`, so the container only needs the `--config` flag in its `args`. -### Create a secret for credentials +::::{stepper} + +:::{step} Create a secret for credentials ```bash kubectl create secret generic elastic-secret-otel \ @@ -213,7 +215,9 @@ kubectl create secret generic elastic-secret-otel \ --from-literal=elastic_api_key='your-encoded-api-key' ``` -### Deploy the EDOT gateway +::: + +:::{step} Deploy the EDOT gateway Apply the following manifest. The `ConfigMap` holds the gateway configuration, the `Deployment` runs the EDOT Collector, and the `Service` exposes port 4317 for contrib Collectors inside the cluster. @@ -328,25 +332,33 @@ spec: targetPort: 4318 ``` -### Configure the contrib Collector +::: + +:::{step} Configure the contrib Collector -Point the contrib otelcol-contrib OTLP exporter at the gateway `Service`: +Point the contrib Collector's OTLP exporter at the gateway `Service`: ```yaml exporters: otlp: endpoint: "edot-gateway:4317" tls: - insecure: true # Set to false and configure ca_file for production + insecure: true # Set to `false` and configure `ca_file` for production ``` -:::{note} -For comprehensive {{k8s}} observability (including host metrics, pod logs, {{k8s}} events, and cluster metrics) use the `opentelemetry-kube-stack` Helm chart with the Elastic values instead. Refer to [{{k8s}} observability](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md). ::: -## Next steps +:::{step} Verify data in {{kib}} + +After the gateway pods are running and your contrib Collectors point to the `edot-gateway` Service, confirm that data flows in: + +1. Check your services in **{{observability}}** → **{{product.apm}}**. +2. Check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams in **Discover**. + +::: + +:::: -* [EDOT Collector gateway configuration reference](elastic-agent://reference/edot-collector/config/default-config-standalone.md#gateway-mode) -* [{{k8s}} observability with EDOT](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) -* [Attributes and labels](/solutions/observability/apm/opentelemetry/attributes.md) -* [EDOT compared to contrib OpenTelemetry](opentelemetry://reference/compatibility/edot-vs-upstream.md) +:::{note} +For comprehensive {{k8s}} observability (including host metrics, pod logs, {{k8s}} events, and cluster metrics), use the `opentelemetry-kube-stack` Helm chart with the Elastic values instead. Refer to [{{k8s}} observability](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) for more information. +::: From b6b579e1ca201860ed68a9bcf15987d1211f8241 Mon Sep 17 00:00:00 2001 From: Aleksandra Spilkowska Date: Mon, 22 Jun 2026 14:47:37 +0200 Subject: [PATCH 3/6] Move K8s deployment section to dedicated page Removes the "Deploy on k8s" section from the bare-metal guide and replaces it with a Next steps link to the upcoming kubernetes.md page. Keeps the bare-metal guide self-contained so it can merge independently. Co-Authored-By: Claude Sonnet 4.6 --- .../use-cases/upstream-collector/index.md | 168 +----------------- 1 file changed, 6 insertions(+), 162 deletions(-) diff --git a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md index 486a62aa53..371d062070 100644 --- a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md +++ b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md @@ -199,166 +199,10 @@ If no data appears, refer to [No logs, metrics, or traces visible in {{kib}}](/t :::: -## Deploy on {{k8s}} [upstream-collector-k8s] +## Next steps -If you're running in {{k8s}}, you can deploy the EDOT gateway as a {{k8s}} `Deployment` and expose it as a `Service` so contrib Collectors can reach it using the cluster DNS. - -The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. The image entrypoint starts {{agent}} in `otel` mode when the `ELASTIC_AGENT_OTEL` environment variable is set to `true`, so the container only needs the `--config` flag in its `args`. - -::::{stepper} - -:::{step} Create a secret for credentials - -```bash -kubectl create secret generic elastic-secret-otel \ - --from-literal=elastic_endpoint='https://your-elasticsearch:9200' \ - --from-literal=elastic_api_key='your-encoded-api-key' -``` - -::: - -:::{step} Deploy the EDOT gateway - -Apply the following manifest. The `ConfigMap` holds the gateway configuration, the `Deployment` runs the EDOT Collector, and the `Service` exposes port 4317 for contrib Collectors inside the cluster. - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: edot-gateway-config -data: - gateway.yaml: | - receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - http: - endpoint: 0.0.0.0:4318 - connectors: - elasticapm: {} - processors: - batch: - send_batch_size: 1000 - timeout: 1s - send_batch_max_size: 1500 - batch/metrics: - send_batch_max_size: 0 - timeout: 1s - elasticapm: {} - exporters: - elasticsearch/otel: - endpoints: - - ${env:ELASTIC_ENDPOINT} - api_key: ${env:ELASTIC_API_KEY} - mapping: - mode: otel - service: - pipelines: - traces: - receivers: [otlp] - processors: [batch, elasticapm] - exporters: [elasticapm, elasticsearch/otel] - metrics: - receivers: [otlp] - processors: [batch/metrics] - exporters: [elasticsearch/otel] - metrics/aggregated-otel-metrics: - receivers: [elasticapm] - processors: [] - exporters: [elasticsearch/otel] - logs: - receivers: [otlp] - processors: [batch] - exporters: [elasticsearch/otel] ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: edot-gateway - labels: - app: edot-gateway -spec: - replicas: 2 - selector: - matchLabels: - app: edot-gateway - template: - metadata: - labels: - app: edot-gateway - spec: - containers: - - name: edot-gateway - image: docker.elastic.co/elastic-agent/elastic-otel-collector:{{version.edot_collector}} - args: ["--config", "/etc/edot/gateway.yaml"] - env: - - name: ELASTIC_AGENT_OTEL - value: "true" - - name: ELASTIC_ENDPOINT - valueFrom: - secretKeyRef: - name: elastic-secret-otel - key: elastic_endpoint - - name: ELASTIC_API_KEY - valueFrom: - secretKeyRef: - name: elastic-secret-otel - key: elastic_api_key - ports: - - containerPort: 4317 # gRPC - - containerPort: 4318 # HTTP - volumeMounts: - - name: config - mountPath: /etc/edot - volumes: - - name: config - configMap: - name: edot-gateway-config ---- -apiVersion: v1 -kind: Service -metadata: - name: edot-gateway -spec: - selector: - app: edot-gateway - ports: - - name: otlp-grpc - port: 4317 - targetPort: 4317 - - name: otlp-http - port: 4318 - targetPort: 4318 -``` - -::: - -:::{step} Configure the contrib Collector - -Point the contrib Collector's OTLP exporter at the gateway `Service`: - -```yaml -exporters: - otlp: - endpoint: "edot-gateway:4317" - tls: - insecure: true # Set to `false` and configure `ca_file` for production -``` - -::: - -:::{step} Verify data in {{kib}} - -After the gateway pods are running and your contrib Collectors point to the `edot-gateway` Service, confirm that data flows in: - -1. Check your services in **{{observability}}** → **{{product.apm}}**. -2. Check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams in **Discover**. - -::: - -:::: - -:::{note} -For comprehensive {{k8s}} observability (including host metrics, pod logs, {{k8s}} events, and cluster metrics), use the `opentelemetry-kube-stack` Helm chart with the Elastic values instead. Refer to [{{k8s}} observability](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) for more information. -::: +* [Deploy this setup on {{k8s}}](/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md) +* [EDOT Collector gateway configuration reference](elastic-agent://reference/edot-collector/config/default-config-standalone.md#gateway-mode) +* [{{k8s}} observability with EDOT](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) +* [Attributes and labels](/solutions/observability/apm/opentelemetry/attributes.md) +* [EDOT compared to contrib OpenTelemetry](opentelemetry://reference/compatibility/edot-vs-upstream.md) From 413546d7fe6e961048092e68aba365abe934fe03 Mon Sep 17 00:00:00 2001 From: Aleksandra Spilkowska Date: Mon, 22 Jun 2026 14:48:36 +0200 Subject: [PATCH 4/6] Add draft K8s deployment guide for contrib collector to EDOT gateway --- .../upstream-collector/kubernetes.md | 183 ++++++++++++++++++ solutions/toc.yml | 2 + 2 files changed, 185 insertions(+) create mode 100644 solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md diff --git a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md new file mode 100644 index 0000000000..550719aaad --- /dev/null +++ b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md @@ -0,0 +1,183 @@ +--- +navigation_title: On Kubernetes +description: Deploy an EDOT Collector gateway on Kubernetes to receive telemetry from a contrib OpenTelemetry Collector and forward it to Elasticsearch. +applies_to: + stack: ga 9.2+ +products: + - id: observability + - id: edot-collector +--- + +# Deploy the EDOT gateway on {{k8s}} [upstream-collector-k8s] + +This page extends [Send data from a contrib OpenTelemetry Collector](/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md) with {{k8s}}-specific deployment steps. If you haven't read that page, start there for context on the architecture and components. + +In {{k8s}}, you deploy the EDOT gateway as a `Deployment` and expose it as a `Service` so contrib Collectors can reach it using cluster DNS. + +The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. The image entrypoint starts {{agent}} in `otel` mode when the `ELASTIC_AGENT_OTEL` environment variable is set to `true`, so the container only needs the `--config` flag in its `args`. + +## Prerequisites + +* A running {{k8s}} cluster +* `kubectl` configured to access the cluster +* A running self-managed {{es}} cluster reachable from the {{k8s}} cluster + +::::{stepper} + +:::{step} Create a secret for credentials + +```bash +kubectl create secret generic elastic-secret-otel \ + --from-literal=elastic_endpoint='https://your-elasticsearch:9200' \ + --from-literal=elastic_api_key='your-encoded-api-key' +``` + +::: + +:::{step} Deploy the EDOT gateway + +Apply the following manifest. The `ConfigMap` holds the gateway configuration, the `Deployment` runs the EDOT Collector, and the `Service` exposes port 4317 for contrib Collectors inside the cluster. + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: edot-gateway-config +data: + gateway.yaml: | + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + connectors: + elasticapm: {} + processors: + batch: + send_batch_size: 1000 + timeout: 1s + send_batch_max_size: 1500 + batch/metrics: + send_batch_max_size: 0 + timeout: 1s + elasticapm: {} + exporters: + elasticsearch/otel: + endpoints: + - ${env:ELASTIC_ENDPOINT} + api_key: ${env:ELASTIC_API_KEY} + mapping: + mode: otel + service: + pipelines: + traces: + receivers: [otlp] + processors: [batch, elasticapm] + exporters: [elasticapm, elasticsearch/otel] + metrics: + receivers: [otlp] + processors: [batch/metrics] + exporters: [elasticsearch/otel] + metrics/aggregated-otel-metrics: + receivers: [elasticapm] + processors: [] + exporters: [elasticsearch/otel] + logs: + receivers: [otlp] + processors: [batch] + exporters: [elasticsearch/otel] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: edot-gateway + labels: + app: edot-gateway +spec: + replicas: 2 + selector: + matchLabels: + app: edot-gateway + template: + metadata: + labels: + app: edot-gateway + spec: + containers: + - name: edot-gateway + image: docker.elastic.co/elastic-agent/elastic-otel-collector:{{version.edot_collector}} + args: ["--config", "/etc/edot/gateway.yaml"] + env: + - name: ELASTIC_AGENT_OTEL + value: "true" + - name: ELASTIC_ENDPOINT + valueFrom: + secretKeyRef: + name: elastic-secret-otel + key: elastic_endpoint + - name: ELASTIC_API_KEY + valueFrom: + secretKeyRef: + name: elastic-secret-otel + key: elastic_api_key + ports: + - containerPort: 4317 # gRPC + - containerPort: 4318 # HTTP + volumeMounts: + - name: config + mountPath: /etc/edot + volumes: + - name: config + configMap: + name: edot-gateway-config +--- +apiVersion: v1 +kind: Service +metadata: + name: edot-gateway +spec: + selector: + app: edot-gateway + ports: + - name: otlp-grpc + port: 4317 + targetPort: 4317 + - name: otlp-http + port: 4318 + targetPort: 4318 +``` + +::: + +:::{step} Configure the contrib Collector + +Point the contrib Collector's OTLP exporter at the gateway `Service`: + +```yaml +exporters: + otlp: + endpoint: "edot-gateway:4317" + tls: + insecure: true # Set to `false` and configure `ca_file` for production +``` + +::: + +:::{step} Verify data in {{kib}} + +After the gateway pods are running and your contrib Collectors point to the `edot-gateway` Service, confirm that data flows in: + +1. Check your services in **{{observability}}** → **{{product.apm}}**. +2. Check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams in **Discover**. + +If no data appears, refer to [No logs, metrics, or traces visible in {{kib}}](/troubleshoot/ingest/opentelemetry/no-data-in-kibana.md). + +::: + +:::: + +:::{note} +For comprehensive {{k8s}} observability (including host metrics, pod logs, {{k8s}} events, and cluster metrics), use the `opentelemetry-kube-stack` Helm chart with the Elastic values instead. Refer to [{{k8s}} observability](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) for more information. +::: diff --git a/solutions/toc.yml b/solutions/toc.yml index 9acad0f230..400285deee 100644 --- a/solutions/toc.yml +++ b/solutions/toc.yml @@ -129,6 +129,8 @@ toc: - file: observability/get-started/opentelemetry/use-cases/kubernetes/customization.md - folder: observability/get-started/opentelemetry/use-cases/llms - file: observability/get-started/opentelemetry/use-cases/upstream-collector/index.md + children: + - file: observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md - file: observability/get-started/other-tutorials/index.md children: - file: observability/get-started/other-tutorials/tutorial-monitor-java-application.md From 9e014e02a21890f8ab521e98ce160bf071f0f471 Mon Sep 17 00:00:00 2001 From: Aleksandra Spilkowska Date: Mon, 22 Jun 2026 16:36:05 +0200 Subject: [PATCH 5/6] Fix ELASTIC_AGENT_OTEL env var in K8s manifest --- .../opentelemetry/use-cases/upstream-collector/kubernetes.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md index 550719aaad..cb10a17216 100644 --- a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md +++ b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md @@ -14,7 +14,7 @@ This page extends [Send data from a contrib OpenTelemetry Collector](/solutions/ In {{k8s}}, you deploy the EDOT gateway as a `Deployment` and expose it as a `Service` so contrib Collectors can reach it using cluster DNS. -The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. The image entrypoint starts {{agent}} in `otel` mode when the `ELASTIC_AGENT_OTEL` environment variable is set to `true`, so the container only needs the `--config` flag in its `args`. +The EDOT Collector image for standalone use is `docker.elastic.co/elastic-agent/elastic-otel-collector`. Unlike the full {{agent}} image, this image's entrypoint unconditionally starts in `otel` mode — no extra environment variables are required. ## Prerequisites @@ -110,8 +110,6 @@ spec: image: docker.elastic.co/elastic-agent/elastic-otel-collector:{{version.edot_collector}} args: ["--config", "/etc/edot/gateway.yaml"] env: - - name: ELASTIC_AGENT_OTEL - value: "true" - name: ELASTIC_ENDPOINT valueFrom: secretKeyRef: From f62cebb7ed4ecb97d1a6f82250c4f56853e21e93 Mon Sep 17 00:00:00 2001 From: Aleksandra Spilkowska Date: Mon, 22 Jun 2026 18:40:35 +0200 Subject: [PATCH 6/6] Remove outdated content --- .../use-cases/upstream-collector/index.md | 197 +----------------- 1 file changed, 1 insertion(+), 196 deletions(-) diff --git a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md index 371d062070..f9f0bcb3d6 100644 --- a/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md +++ b/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/index.md @@ -10,199 +10,4 @@ products: # Send data from a contrib OpenTelemetry Collector [upstream-collector-self-managed] -This guide shows how to forward telemetry data from an upstream contrib OpenTelemetry Collector to a self-managed {{stack}} using an EDOT Collector in gateway mode. - -## When to use this setup - -Use this pattern if you: - -* Already run a contrib OpenTelemetry Collector and want to add Elastic as a backend without replacing your existing setup -* Need to fan out telemetry to multiple observability backends from a single contrib Collector -* Evaluate Elastic alongside another backend before committing to a full migration -* Use a technology or language for which Elastic doesn't provide an EDOT distribution - -## Architecture - -Your services send telemetry to the contrib Collector (`otelcol-contrib`), which forwards it over OTLP/gRPC to the EDOT Collector gateway. The gateway applies Elastic-specific processing and writes directly to {{es}}. - -```mermaid -flowchart LR - S["Your services"] -->|OTLP| U["Upstream otelcol-contrib
(one per host)"] - U -->|"OTLP/gRPC :4317"| G - - subgraph G["EDOT Collector (gateway)"] - P["elasticapm processor
enriches spans"] - C["elasticapm connector
generates aggregated metrics"] - end - - G -->|"elasticsearch exporter
mapping.mode: otel"| E[("ES")] -``` - -The `elasticsearch` exporter with `mapping.mode: otel` is the recommended path for self-managed deployments. The Managed OTLP endpoint isn't available for self-managed installations. Sending directly to {{apm-server-or-mis}} through OTLP is also possible but not recommended. Use the EDOT gateway path when available. - -## Prerequisites - -* A running self-managed {{es}} cluster -* The [EDOT Collector](elastic-agent://reference/edot-collector/index.md) installed on the gateway host. It ships as part of the {{agent}} package and runs as {{agent}} in `otel` mode. -* The [contrib OpenTelemetry Collector](https://opentelemetry.io/docs/collector/installation/) installed on your agent hosts -* Network connectivity from the contrib Collector hosts to the EDOT gateway host on port 4317 - -::::{stepper} - -:::{step} Create an {{es}} API key - -The EDOT gateway authenticates to {{es}} using an API key. - -1. In {{kib}}, navigate to **{{stack-manage-app}}** → **API keys**. -2. Select **Create API key**. -3. Give the key a name (for example, `edot-gateway`) and assign it the necessary privileges for writing to {{es}} data streams. -4. Copy the encoded key to use as the `ELASTIC_API_KEY` value in the gateway configuration. - -::: - -:::{step} Configure the EDOT gateway - -Create a configuration file for the EDOT Collector running in gateway mode. Save this as `gateway.yml` on the gateway host. - -Set the following environment variables on the gateway host before starting the Collector: - -```bash -export ELASTIC_ENDPOINT=https://your-elasticsearch:9200 -export ELASTIC_API_KEY=your-encoded-api-key -``` - -Then create `gateway.yml`: - -```yaml -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - http: - endpoint: 0.0.0.0:4318 - -connectors: - elasticapm: {} - -processors: - batch: - send_batch_size: 1000 - timeout: 1s - send_batch_max_size: 1500 - batch/metrics: - send_batch_max_size: 0 - timeout: 1s - elasticapm: {} - -exporters: - elasticsearch/otel: - endpoints: - - ${env:ELASTIC_ENDPOINT} - api_key: ${env:ELASTIC_API_KEY} - mapping: - mode: otel - -service: - pipelines: - traces: - receivers: [otlp] - processors: [batch, elasticapm] - exporters: [elasticapm, elasticsearch/otel] - metrics: - receivers: [otlp] - processors: [batch/metrics] - exporters: [elasticsearch/otel] - metrics/aggregated-otel-metrics: - receivers: [elasticapm] - processors: [] - exporters: [elasticsearch/otel] - logs: - receivers: [otlp] - processors: [batch] - exporters: [elasticsearch/otel] -``` - -Key components in this configuration: - -* **`elasticapm` processor** (under `processors`): Enriches spans with attributes required by the {{product.apm}} UI. -* **`elasticapm` connector** (under `connectors`): Generates pre-aggregated {{product.apm}} metrics from trace data. It appears as an exporter in the `traces` pipeline and as a receiver in the `metrics/aggregated-otel-metrics` pipeline. -* **`elasticsearch/otel` exporter**: Writes data directly to {{es}} using native OpenTelemetry data streams (`mapping.mode: otel`). - -:::{note} -The `elasticapm` connector and processor are required for full {{product.apm}} functionality (service maps, transaction histograms, service-level indicators). You only need them when exporting directly to {{es}}. If you send to the Managed OTLP endpoint or {{apm-server-or-mis}}, they are not required. - -Refer to [{{product.apm}} services missing due to misconfigured elasticapm connector](/troubleshoot/ingest/opentelemetry/edot-collector/misconfigured-elasticapm-connector.md) for more information. -::: - -Start the EDOT gateway. The EDOT Collector is the {{agent}} binary run in `otel` mode, so start it with the `otel` subcommand: - -```bash -elastic-agent otel --config gateway.yml -``` - -::: - -:::{step} Configure the contrib Collector - -On each contrib Collector host, configure the OTLP exporter to point to the EDOT gateway. Add or update the `exporters` and `service` sections in your existing `config.yml`: - -```yaml -exporters: - otlp: - endpoint: "gateway-host:4317" - tls: - insecure: true # Set to `false` and configure `ca_file` for production - -service: - pipelines: - traces: - exporters: [otlp] - metrics: - exporters: [otlp] - logs: - exporters: [otlp] -``` - -Replace `gateway-host` with the hostname or IP of your EDOT gateway host. In production, configure TLS to secure communication between the contrib Collector and the gateway. - -:::{tip} -Set the `deployment.environment` resource attribute in your contrib Collector so that services appear under the correct environment in the {{kib}} {{product.apm}} Service Map. Without it, all services show as "unset" in the environment selector. - -```yaml -processors: - resource: - attributes: - - key: deployment.environment - action: insert - value: production -``` - -Refer to [Attributes and labels](/solutions/observability/apm/opentelemetry/attributes.md) for more details. -::: - -Restart the contrib Collector to apply the changes. - -::: - -:::{step} Verify data in {{kib}} - -After starting both Collectors, wait a few minutes for data to appear. Then verify in {{kib}}: - -* Navigate to **{{observability}}** → **{{product.apm}}** → **Services** to confirm your services appear. -* Navigate to **{{observability}}** → **{{product.apm}}** → **Service Map** to confirm environment-based filtering works. -* Navigate to **Discover** and check the `traces-generic.otel-default`, `logs-generic.otel-default`, and `metrics-generic.otel-default` data streams for incoming data. - -If no data appears, refer to [No logs, metrics, or traces visible in {{kib}}](/troubleshoot/ingest/opentelemetry/no-data-in-kibana.md). - -::: - -:::: - -## Next steps - -* [Deploy this setup on {{k8s}}](/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector/kubernetes.md) -* [EDOT Collector gateway configuration reference](elastic-agent://reference/edot-collector/config/default-config-standalone.md#gateway-mode) -* [{{k8s}} observability with EDOT](/solutions/observability/get-started/opentelemetry/use-cases/kubernetes/index.md) -* [Attributes and labels](/solutions/observability/apm/opentelemetry/attributes.md) -* [EDOT compared to contrib OpenTelemetry](opentelemetry://reference/compatibility/edot-vs-upstream.md) + \ No newline at end of file