OpenTelemetry#
Integration Details |
|
Ingester |
|
Kit |
OpenTelemetry Configuration#
OpenTelemetry uses Collectors to forward data from your application or infrastructure directly into Gravwell. While this guide demonstrates using Kubernetes, it could be adapted to route logs from any OpenTelemetry compatible service.
Example: Simple OpenTelemetry Example#
To send data to Gravwell, modify your OpenTelemetry Collector configuration file to include the Gravwell HTTP ingester into the exporters section.
exporters:
otlphttp:
endpoint: http://192.168.3.50:8080 # Change this line to point to your gravwell instance
encoding: json
tls:
insecure: true
Next, update your active data pipelines under the service section to route your data through the new otlphttp exporter:
service:
pipelines:
traces:
receivers: [otlp, jaeger, zipkin]
processors: [batch]
exporters: [otlp_http, debug]
metrics:
receivers: [otlp, prometheus]
processors: [batch]
exporters: [otlp_http, debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlp_http, debug]
Note
You can validate your Collector config with:
otelcol validate --config=customconfig.yaml
Example: Kubernetes Manifest File#
This example provides a native Kubernetes manifest file that sets up an OpenTelemetry Collector to automatically ingest logs and performance metrics from your Kubernetes cluster. To route this data to your environment, you must modify the following line in the manifest to point to your Gravwell HTTP Ingester:
endpoint: http://192.168.3.50:8080
This manifest utilizes Custom Resource Definitions from the OpenTelemetry Operator. The following commands provide an example that could be run on your master node to install the operator via Helm to deploy the collector.
# 1. Add the OpenTelemetry Helm repository
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
# 2. Install the OpenTelemetry Operator chart
helm install my-otel-operator open-telemetry/opentelemetry-operator --set admissionWebhooks.certManager.enabled=false
# 3. Deploy the custom Collector configuration
kubectl apply -f otel_gravwell_collector.yml
apiVersion: v1
kind: ServiceAccount
metadata:
name: otelcol
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: otelcol
rules:
- apiGroups: [""]
resources:
- nodes
- nodes/stats
- nodes/proxy
- pods
- namespaces
- services
- endpoints
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- deployments
- daemonsets
- replicasets
- statefulsets
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: otelcol
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: otelcol
subjects:
- kind: ServiceAccount
name: otelcol
namespace: default
---
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel
namespace: default
spec:
image: otel/opentelemetry-collector-contrib:0.112.0
mode: daemonset
serviceAccount: otelcol
# hostNetwork is required for the `from: connection` pod_association in
# k8sattributes to resolve the real client pod IP. Without it, all incoming
# OTLP traffic looks like it came from the CNI's masqueraded address.
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
securityContext:
runAsUser: 0
env:
- name: K8S_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumes:
- name: varlogpods
hostPath:
path: /var/log/pods
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
volumeMounts:
- name: varlogpods
mountPath: /var/log/pods
readOnly: true
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
kubeletstats:
collection_interval: 20s
auth_type: serviceAccount
# Explicit https:// scheme is required by recent versions
endpoint: https://${env:K8S_NODE_NAME}:10250
insecure_skip_verify: true
metric_groups: [node, pod, container]
filelog:
include: [/var/log/pods/*/*/*.log]
# Exclude this collector's own pods to avoid a feedback loop.
# The operator names DaemonSet pods `<cr-name>-collector-<hash>`,
# so for a CR named `otel` in namespace `default` the pod dirs are:
# /var/log/pods/default_otel-collector-<hash>_<uid>/...
exclude:
- /var/log/pods/default_otel-collector-*/*/*.log
include_file_path: true
start_at: end
operators:
- type: container
id: container-parser
processors:
batch: {}
k8sattributes:
auth_type: serviceAccount
passthrough: false
extract:
metadata:
- k8s.pod.name
- k8s.pod.uid
- k8s.namespace.name
- k8s.node.name
- k8s.deployment.name
pod_association:
- sources:
- from: resource_attribute
name: k8s.pod.uid
- sources:
- from: connection
exporters:
otlphttp:
endpoint: http://192.168.3.50:8080 # Change this line to point to your gravwell instance
encoding: json
tls:
insecure: true
debug:
verbosity: basic
service:
pipelines:
metrics:
receivers: [otlp, kubeletstats]
processors:
- k8sattributes
- batch
exporters: [otlphttp, debug]
logs:
receivers: [otlp, filelog]
processors: [k8sattributes, batch]
# NOTE: do NOT add `debug` here unless your filelog `exclude`
# pattern definitely matches this collector's pod path, or you
# will create a feedback loop.
exporters: [otlphttp]
Gravwell Configuration#
Gravwell Storage Well Configuration#
Setup the well configuration in your Gravwell indexers.
Sample well config:
Create or edit: /opt/gravwell/etc/gravwell.conf.d/opentelemetry-well.conf
[Storage-Well "opentelemetry"]
Location=/opt/gravwell/storage/opentelemetry
Tags=opentelemetry*
Gravwell Ingester Configuration: HTTP#
Sample OpenTelemetry config:
Create or edit: /opt/gravwell/etc/gravwell_http_ingester.conf.d/opentelemetry.conf
[OpenTelemetry-Logs-Listener "kubelogs"]
URL="/v1/logs"
Tag-Name=k8s-logs
[OpenTelemetry-Metrics-Listener "kubemetrics"]
URL="/v1/metrics"
Tag-Name=k8s-metricsQ
Note
Remember to restart the service to apply the new config:
sudo systemctl restart gravwell_http_ingester.service