DEV Community

Cover image for Grafana OTEL App Observability Demo
Shakir
Shakir

Posted on • Updated on

Grafana OTEL App Observability Demo

Introduction

We are going to explore the built in app observability solution on Grafana cloud, which generates some built in dashboards for us. We would be using OTEL traces for this particular demo. You may check the video version here: https://youtu.be/jw2FXDwEFWg

We shall install a sample demo app that simulates a microservices environment. It would generate otel compatible traces which are sent to a local alloy instance. From the local alloy, the traces are sent to Grafana's OTLP gateway(alloy) on the cloud.

The setup would typically look as below.
Solution diagram

Note that certain metrics will be generated from the traces. So traces will be sent to tempo and the generated metrics from the traces will be sent to Mimir. Both Mimir and Tempo on the cloud are available as datasources in Grafana cloud.

OTLP endpoint

Go to grafana.com, sign in and access your account, the url of which would be like https://grafana.com/orgs/<org-name>. Click on the Stack at the top of the page. In my case, I am using a free account and the organization and stack name are both same. Note that I can only have one organization in the free account.

In the manage stack page, there should be an OTLP section, click on it and copy the gateway URL and instance id from there. Note that the instance id would be our username as well. On this page, you may generate a token with the predefined scope set:alloy-data-write. This has the sub scopes metrics:write, logs:write, traces:write, profiles:write, fleet-management:read. I have generated a new API token with the predefined options and given it a name app-observability-demo. Ensure you copy the token.

Alloy config

My alloy config would look as below. We need to put the username and password from what we collected in the previous step.



$ cat config.alloy 

otelcol.receiver.otlp "otlp_receiver" {
  grpc {
    endpoint = "0.0.0.0:4317"
  }

  output {
    traces = [otelcol.exporter.otlphttp.grafana_cloud.input,]
  }
}

otelcol.exporter.otlphttp "grafana_cloud" {
    client {
        endpoint = "https://otlp-gateway-prod-us-east-0.grafana.net/otlp"
        auth     = otelcol.auth.basic.grafana_cloud.handler
    }
}

otelcol.auth.basic "grafana_cloud" {
  username = "<username>"
  password = "<api-token>"
}


Enter fullscreen mode Exit fullscreen mode

Create a namespace and then a configmap with this config.



kubectl create ns app-o11y-demo

kubectl create configmap \
    alloy-config \
    --namespace app-o11y-demo \
    "--from-file=config.alloy=config.alloy"


Enter fullscreen mode Exit fullscreen mode

Alloy chart

We can define the helm values for alloy by referring to the configmap as follows.



$ cat alloy-values.yaml 
alloy:
  configMap:
    create: false
    name: alloy-config
    key: config.alloy
  extraPorts:
  - name: "grpc"
    port: 4317
    targetPort: 4317
    protocol: "TCP"


Enter fullscreen mode Exit fullscreen mode

And call this values file in the helmfile.



$ cat helmfile.yaml
repositories:
- name: grafana
  url: https://grafana.github.io/helm-charts

releases:
- name: alloy
  chart: grafana/alloy
  namespace: app-o11y-demo
  values:
  - alloy-values.yaml
  version: 0.5.0


Enter fullscreen mode Exit fullscreen mode

We can now install the helm chart with helmfile.



$ helmfile sync


Enter fullscreen mode Exit fullscreen mode

The alloy pod should be running.



$ kubectl get po -n app-o11y-demo
NAME                                  READY   STATUS    RESTARTS   AGE
alloy-cd4zp                           2/2     Running   0          6m10s


Enter fullscreen mode Exit fullscreen mode

And the service, endpoints can be validated.



$ kubectl get svc -n app-o11y-demo
NAME    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
alloy   ClusterIP   10.100.121.96   <none>        12345/TCP,4317/TCP   6m55s

$ kubectl get ep -n app-o11y-demo
NAME    ENDPOINTS                          AGE
alloy   10.1.4.159:12345,10.1.4.159:4317   7m11s

$ kubectl get po -n app-o11y-demo -o wide
NAME                                  READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
alloy-cd4zp                           2/2     Running   0          7m27s   10.1.4.159   docker-desktop   <none>           <none>


Enter fullscreen mode Exit fullscreen mode

Demo app

We can now deploy a demo app that would generate traces and send it alloy via grpc. The manifest for which is as follows.



$ cat xk6-client-tracing.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: xk6-client-tracing
  namespace: app-o11y-demo
spec:
  minReadySeconds: 10
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: xk6-client-tracing
      name: xk6-client-tracing
  template:
    metadata:
      labels:
        app: xk6-client-tracing
        name: xk6-client-tracing
    spec:
      containers:
      - env:
        - name: ENDPOINT
          value: alloy.app-o11y-demo.svc.cluster.local:4317
        image: ghcr.io/grafana/xk6-client-tracing:v0.0.2
        imagePullPolicy: IfNotPresent
        name: xk6-client-tracing


Enter fullscreen mode Exit fullscreen mode

Note the ENDPOINT variable above is pointing to the alloy service that we deployed earlier. We can now deploy it with kubectl.



$ kubectl create -f xk6-client-tracing.yaml
deployment.apps/xk6-client-tracing created


Enter fullscreen mode Exit fullscreen mode

The pod status can be checked.



$ kubectl get po -n app-o11y-demo | grep k6
xk6-client-tracing-6c49cdfbdb-cm25h   1/1     Running   0          5m44s


Enter fullscreen mode Exit fullscreen mode

If everything works as expected, the demo app should have sent traces to alloy and then to tempo backend in Grafana cloud. Likewise it should sent the trace metrics to alloy and then to Mimir on cloud.

Grafana cloud

We can now headover to Grafana cloud. The url of which would look like https://<stack-name>.grafana.net/. You may go to Home > Application and click on Enable Metrics and activate it. After that add a new a connection and select OTLP. Just scroll down and enter the service name as shop-backend as that's the root service of our demo app. Test the connection now, it should be successful.
OTLP connection successful

Once we click on Go to Application observability we should be able to see the pre built dashboards based on the traces and the trace metrics, predominantly based on the RED(Rate, Error, Duration method.
Shop backend dashboard

Note that in our case we are only sending traces from our application and the trace metrics were generated at Grafana cloud. We could also have sent direct metrics for ex. if it's a go application we could have sent go metrics, likewise we could also have send logs from our application, all of this to the same gateway(alloy on cloud). With the just the traces we have a handful of data to debug web app issues.

Also, though shop-backend is our root service, we also have other simulated microservices in this application.
Services page

There are a lot of other options in these built in dashboards to explore. Though we have these built in dashboards, we could also built any custom dashboards. For ex. let's explore what we get in the explore pane keeping tempo as the datasource and build a complete service map for all the services that are part of our demo app. Note that the service map from this is shown in the cover image of the blog.
Explore trace

Let's try one more for the trace metrics with mimir as the datasource.
Explore metrics

Summary

Alright so we have come to the end of this post, we saw how one of Grafana cloud's solutions around app observability could help us get built in dashboards quite quickly once the data starts flowing to the cloud backend. However note that instrumentation is still necessary on the app side, we were able to get a demo app that already had the OTEL tracing instrumentation for us. As a cleanup check you may run the following commands:



helm uninstall alloy -n app-o11y-demo
kubectl delete -f xk6-client-tracing.yaml

# if you also want to delete the ns
kubectl delete ns app-o11y-demo


Enter fullscreen mode Exit fullscreen mode

Thanks for reading!!!

Top comments (0)