Kuma Mesh with Datadog

Published: Aug 18, 2021 by Isaac Johnson

Kuma,which is a Cloud Native Computing Foundation (CNCF) sponsored service mesh, is the OSS backend of Kong Mesh. From the docs for mTLS we also see it is one of the implementers of the Spiffe universalIdP.

Kuma, meaning “bear” in Japanese, is the only Envoy-based service mesh sponsored thus far by the foundation.  It was Open Sourced and given to the CNCF by Kong back in September 2019.

Kuma is a multi-zone mesh that can have different control planes come together in a multi-zoned global control plane:

source: https://www.cncf.io/blog/2020/12/15/multi-cluster-multi-cloud-service-meshes-with-kuma-and-envoy/

You can get more of a high level overview from the docs.  As there is quite a lot to cover with Kuma, let’s get started and show a demo app and explore metrics, logging and tracing (and see how we can integrate with other systems like Datadog Logging and APM).

Getting Started

There are a few ways you can install Kuma.

Docker version

You can just run the docker container:

docker run \
    -p 5681:5681 \
    docker.io/kumahq/kuma-cp:1.2.3 run

where 1.2.3 was(is) the latest image from here: https://hub.docker.com/r/kumahq/kuma-cp/tags?page=1&ordering=last_updated

builder@DESKTOP-QADGF36:~$ docker run -p 5681:5681 docker.io/kumahq/kuma-cp:1.2.3 run
Unable to find image 'kumahq/kuma-cp:1.2.3' locally
1.2.3: Pulling from kumahq/kuma-cp
5843afab3874: Pull complete
877216791a32: Pull complete
c9950e6539e5: Pull complete
9577f971b34d: Pull complete
a336c81dba13: Pull complete
827f56217717: Pull complete
e6101ec954c6: Pull complete
Digest: sha256:0b79d03966c181d1e2871e0a40b71a376dc954e15f6611eea819b6a8b7e19d11
Status: Downloaded newer image for kumahq/kuma-cp:1.2.3
2021-08-17T11:49:19.036Z INFO Skipping reading config from file
2021-08-17T11:49:19.036Z INFO bootstrap.auto-configure directory /home/kuma-cp/.kuma will be used as a working directory, it could be changed using KUMA_GENERAL_WORK_DIR environment variable
2021-08-17T11:49:19.133Z INFO bootstrap.auto-configure TLS certificate autogenerated. Autogenerated certificates are not synchronized between CP instances. It is only valid if the data plane proxy connects to the CP by one of the following address 3543ad70ba7f, localhost, 127.0.0.1, 172.17.0.2. It is recommended to generate your own certificate based on yours trusted CA. You can also generate your own self-signed certificates using 'kumactl generate tls-certificate --type=server --cp-hostname=<hostname>' and configure them using KUMA_GENERAL_TLS_CERT_FILE and KUMA_GENERAL_TLS_KEY_FILE {"crtFile": "/home/kuma-cp/.kuma/kuma-cp.crt", "keyFile": "/home/kuma-cp/.kuma/kuma-cp.key"}
2021-08-17T11:49:19.135Z INFO kuma-cp.run Current config {"apiServer":{"auth":{"allowFromLocalhost":true,"clientCertsDir":""},"corsAllowedDomains":[".*"],"http":{"enabled":true,"interface":"0.0.0.0","port":5681},"https":{"enabled":true,"interface":"0.0.0.0","port":5682,"tlsCertFile":"/home/kuma-cp/.kuma/kuma-cp.crt","tlsKeyFile":"/home/kuma-cp/.kuma/kuma-cp.key"},"readOnly":false},"bootstrapServer":{"apiVersion":"v3","params":{"adminAccessLogPath":"/dev/null","adminAddress":"127.0.0.1","adminPort":0,"xdsConnectTimeout":"1s","xdsHost":"","xdsPort":5678}},"defaults":{"skipMeshCreation":false},"diagnostics":{"debugEndpoints":false,"serverPort":5680},"dnsServer":{"CIDR":"240.0.0.0/4","domain":"mesh","port":5653},"dpServer":{"auth":{"type":"dpToken"},"hds":{"checkDefaults":{"healthyThreshold":1,"interval":"1s","noTrafficInterval":"1s","timeout":"2s","unhealthyThreshold":1},"enabled":true,"interval":"5s","refreshInterval":"10s"},"port":5678,"tlsCertFile":"/home/kuma-cp/.kuma/kuma-cp.crt","tlsKeyFile":"/home/kuma-cp/.kuma/kuma-cp.key"},"environment":"universal","general":{"dnsCacheTTL":"10s","tlsCertFile":"/home/kuma-cp/.kuma/kuma-cp.crt","tlsKeyFile":"/home/kuma-cp/.kuma/kuma-cp.key","workDir":"/home/kuma-cp/.kuma"},"guiServer":{"apiServerUrl":""},"metrics":{"dataplane":{"enabled":true,"subscriptionLimit":10},"mesh":{"maxResyncTimeout":"20s","minResyncTimeout":"1s"},"zone":{"enabled":true,"subscriptionLimit":10}},"mode":"standalone","monitoringAssignmentServer":{"apiVersions":["v1alpha1","v1"],"assignmentRefreshInterval":"1s","defaultFetchTimeout":"30s","grpcPort":0,"port":5676},"multizone":{"global":{"kds":{"grpcPort":5685,"maxMsgSize":10485760,"refreshInterval":"1s","tlsCertFile":"/home/kuma-cp/.kuma/kuma-cp.crt","tlsKeyFile":"/home/kuma-cp/.kuma/kuma-cp.key","zoneInsightFlushInterval":"10s"}},"zone":{"kds":{"maxMsgSize":10485760,"refreshInterval":"1s","rootCaFile":""}}},"reports":{"enabled":true},"runtime":{"kubernetes":{"admissionServer":{"address":"","certDir":"","port":5443},"controlPlaneServiceName":"kuma-control-plane","injector":{"builtinDNS":{"enabled":true,"port":15053},"caCertFile":"","cniEnabled":false,"exceptions":{"labels":{"openshift.io/build.name":"*","openshift.io/deployer-pod-for.name":"*"}},"initContainer":{"image":"kuma/kuma-init:latest"},"sidecarContainer":{"adminPort":9901,"drainTime":"30s","envVars":{},"gid":5678,"image":"kuma/kuma-dp:latest","livenessProbe":{"failureThreshold":12,"initialDelaySeconds":60,"periodSeconds":5,"timeoutSeconds":3},"readinessProbe":{"failureThreshold":12,"initialDelaySeconds":1,"periodSeconds":5,"successThreshold":1,"timeoutSeconds":3},"redirectPortInbound":15006,"redirectPortInboundV6":15010,"redirectPortOutbound":15001,"resources":{"limits":{"cpu":"1000m","memory":"512Mi"},"requests":{"cpu":"50m","memory":"64Mi"}},"uid":5678},"sidecarTraffic":{"excludeInboundPorts":[],"excludeOutboundPorts":[]},"virtualProbesEnabled":true,"virtualProbesPort":9000},"marshalingCacheExpirationTime":"5m0s"},"universal":{"dataplaneCleanupAge":"72h0m0s"}},"sdsServer":{"dataplaneConfigurationRefreshInterval":"1s"},"store":{"cache":{"enabled":true,"expirationTime":"1s"},"kubernetes":{"systemNamespace":"kuma-system"},"postgres":{"connectionTimeout":5,"dbName":"kuma","host":"127.0.0.1","maxIdleConnections":0,"maxOpenConnections":0,"maxReconnectInterval":"1m0s","minReconnectInterval":"10s","password":" *****","port":15432,"tls":{"caPath":"","certPath":"","keyPath":"","mode":"disable"},"user":"kuma"},"type":"memory","upsert":{"conflictRetryBaseBackoff":"100ms","conflictRetryMaxTimes":5}},"xdsServer":{"dataplaneConfigurationRefreshInterval":"1s","dataplaneStatusFlushInterval":"10s","nackBackoff":"5s"}}
2021-08-17T11:49:19.135Z INFO kuma-cp.run Running in mode `standalone`
2021-08-17T11:49:19.135Z INFO mads-server MADS v1alpha1 is enabled
2021-08-17T11:49:19.135Z INFO mads-server MADS v1 is enabled
2021-08-17T11:49:19.198Z INFO xds-server registering Aggregated Discovery Service V3 in Dataplane Server
2021-08-17T11:49:19.198Z INFO bootstrap registering Bootstrap in Dataplane Server
2021-08-17T11:49:19.198Z INFO sds-server registering Secret Discovery Service V3 in Dataplane Server
2021-08-17T11:49:19.198Z INFO hds-server registering Health Discovery Service in Dataplane Server
2021-08-17T11:49:19.202Z INFO kuma-cp.run starting Control Plane {"version": "1.2.3"}
2021-08-17T11:49:19.202Z INFO dp-server starting {"interface": "0.0.0.0", "port": 5678, "tls": true}
2021-08-17T11:49:19.202Z INFO dns-vips-synchronizer starting the DNS VIPs Synchronizer
2021-08-17T11:49:19.202Z INFO bootstrap leader acquired
2021-08-17T11:49:19.202Z INFO metrics.store-counter starting the resource counter
2021-08-17T11:49:19.202Z INFO dns-server starting {"address": "0.0.0.0:5653"}
2021-08-17T11:49:19.202Z INFO xds-server.diagnostics starting {"interface": "0.0.0.0", "port": 5680}
2021-08-17T11:49:19.202Z INFO mesh-insight-resyncer starting resilient component ...
2021-08-17T11:49:19.202Z INFO dns-vips-allocator starting the DNS VIPs allocator
2021-08-17T11:49:19.202Z INFO garbage-collector started
2021-08-17T11:49:19.203Z INFO clusterID creating cluster ID {"clusterID": "1fa50603-9d7c-4b1e-b82c-9e75a45f4323"}
2021-08-17T11:49:19.203Z INFO vip-outbounds-reconciler starting the VIP outbounds reconciler
2021-08-17T11:49:19.203Z INFO defaults trying to create default Mesh
2021-08-17T11:49:19.203Z INFO defaults.mesh ensuring default resources for Mesh exist {"mesh": "default"}
2021-08-17T11:49:19.203Z INFO defaults.mesh default TrafficPermission created {"mesh": "default", "name": "allow-all-default"}
2021-08-17T11:49:19.203Z INFO api-server starting {"interface": "0.0.0.0", "port": 5681}
2021-08-17T11:49:19.203Z INFO api-server starting {"interface": "0.0.0.0", "port": 5682, "tls": true}
2021-08-17T11:49:19.203Z INFO defaults.mesh default TrafficRoute created {"mesh": "default", "name": "route-all-default"}
2021-08-17T11:49:19.203Z INFO mads-server starting {"interface": "0.0.0.0", "port": 5676}
2021-08-17T11:49:19.204Z INFO defaults.mesh default Timeout created {"mesh": "default", "name": "timeout-all-default"}
2021-08-17T11:49:19.204Z INFO defaults.mesh default CircuitBreaker created {"mesh": "default", "name": "circuit-breaker-all-default"}
2021-08-17T11:49:19.204Z INFO defaults.mesh default Retry created {"mesh": "default", "name": "retry-all-default"}
2021-08-17T11:49:19.334Z INFO defaults.mesh default Signing Key created {"mesh": "default", "name": "dataplane-token-signing-key-default"}
2021-08-17T11:49:19.518Z INFO defaults.mesh default Signing Key created {"mesh": "default", "name": "envoy-admin-client-token-signing-key-default"}
2021-08-17T11:49:19.518Z INFO defaults default Mesh created
2021-08-17T11:49:19.603Z INFO defaults trying to create a Zone Ingress signing key
2021-08-17T11:49:19.603Z INFO defaults Zone Ingress signing key created
2021-08-17T11:49:20.202Z INFO clusterID setting cluster ID {"clusterID": "1fa50603-9d7c-4b1e-b82c-9e75a45f4323"}

Then we can navigate to http://localhost:5681/gui

This will bring us to a dashboard where we can see our meshes as well as set a new one up:

We wouldn’t really use a standalone docker container for this.  So let us switch to using Helm with Kubernetes.

Helm install to Kubernetes

We will follow the instructions here.  First we need to add the helm repo.

Since I just started with a fresh WSL, i need to add Helm 3.

builder@DESKTOP-QADGF36:~$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
builder@DESKTOP-QADGF36:~$ chmod 700 get_helm.sh
builder@DESKTOP-QADGF36:~$ ./get_helm.sh
Downloading https://get.helm.sh/helm-v3.6.3-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
[sudo] password for builder:
helm installed into /usr/local/bin/helm

Now add the helm repo

builder@DESKTOP-QADGF36:~$ helm repo add kuma https://kumahq.github.io/charts
"kuma" has been added to your repositories

We will just double check we are on the right k8s cluster (home)

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
anna-macbookair Ready <none> 121d v1.19.5+k3s2
isaac-macbookpro Ready <none> 233d v1.19.5+k3s2
isaac-macbookair Ready master 233d v1.19.5+k3s2

The helm chart will create a new namespace and install kuma in one step

$ helm install --create-namespace --namespace kuma-system kuma kuma/kuma
NAME: kuma
LAST DEPLOYED: Tue Aug 17 06:56:23 2021
NAMESPACE: kuma-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The Kuma Control Plane has been installed!

You can access the control-plane via either the GUI, kubectl, the HTTP API, or the kumactl CLI

We can now see the Kuma Control Plane service is up and running:

$ kubectl get svc -n kuma-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kuma-control-plane ClusterIP 10.43.145.6 <none> 5681/TCP,5682/TCP,443/TCP,5676/TCP,5678/TCP,5653/UDP 11m

and we can port-forward to get started

$ kubectl port-forward svc/kuma-control-plane -n kuma-system 5681:5681
Forwarding from 127.0.0.1:5681 -> 5681
Forwarding from [::1]:5681 -> 5681

For now we will use the default mesh:

By default, it wants to use the ‘default’ namespace.  However, I will limit it this time to the “kuma-demo” namespace.

Then it would like us to add the namespace annotation:

echo "apiVersion: v1
kind: Namespace
metadata: 
  name: kuma-demo
  namespace: kuma-demo
  annotations: 
    kuma.io/sidecar-injection: enabled
    kuma.io/mesh: default" | kubectl apply -f - && kubectl delete pod --all -n kuma-demo

Here we can pause and first install the Demo Kuma app:

builder@DESKTOP-QADGF36:~$ kubectl apply -f https://bit.ly/demokuma
namespace/kuma-demo created
deployment.apps/postgres-master created
service/postgres created
deployment.apps/redis-master created
service/redis created
service/backend created
deployment.apps/kuma-demo-backend-v0 created
deployment.apps/kuma-demo-backend-v1 created
deployment.apps/kuma-demo-backend-v2 created
service/frontend created
deployment.apps/kuma-demo-app created

builder@DESKTOP-QADGF36:~$ kubectl get pods -n kuma-demo
NAME READY STATUS RESTARTS AGE
redis-master-55fd8f6f54-qgn4s 0/2 Init:0/1 0 7s
kuma-demo-app-6787b4f7f5-7f9b4 0/2 Init:0/1 0 7s
kuma-demo-backend-v0-56db47c579-q4gjh 0/2 Init:0/1 0 7s
postgres-master-645bc44fd-wrnlm 0/2 Init:0/1 0 7s

Then apply it.  Here the “default” mesh refers to the mesh name, not namespace

builder@DESKTOP-QADGF36:~$ echo "apiVersion: v1
> kind: Namespace
> metadata:
> name: kuma-demo
> namespace: kuma-demo
> annotations:
> kuma.io/sidecar-injection: enabled
> kuma.io/mesh: default" | kubectl apply -f - && kubectl delete pod --all -n kuma-demo
namespace/kuma-demo configured
pod "kuma-demo-backend-v0-56db47c579-q4gjh" deleted
pod "postgres-master-645bc44fd-wrnlm" deleted
pod "kuma-demo-app-6787b4f7f5-7f9b4" deleted
pod "redis-master-55fd8f6f54-qgn4s" deleted

The reason we apply the annotation then bounce the pods is to force the sidecar injection.

builder@DESKTOP-QADGF36:~$ kubectl get pods -n kuma-demo
NAME READY STATUS RESTARTS AGE
redis-master-55fd8f6f54-kx5tp 2/2 Running 0 74s
postgres-master-645bc44fd-fdkx4 2/2 Running 0 74s
kuma-demo-app-6787b4f7f5-f75r4 0/2 PodInitializing 0 74s
kuma-demo-backend-v0-56db47c579-qcdn9 0/2 PodInitializing 0 74s

It’s subtle, but you’ll notice the difference between the first get pods and this is the former was loading 0/1 and these are 2/2 or 0/2.. that is, the pod has 2 containers now (the app and the sidecar).

builder@DESKTOP-QADGF36:~$ kubectl get pods -n kuma-demo
NAME READY STATUS RESTARTS AGE
redis-master-55fd8f6f54-kx5tp 2/2 Running 0 2m17s
postgres-master-645bc44fd-fdkx4 2/2 Running 0 2m17s
kuma-demo-backend-v0-56db47c579-qcdn9 2/2 Running 0 2m17s
kuma-demo-app-6787b4f7f5-f75r4 2/2 Running 0 2m17s

Issues with Wizard

I tried many things at this point to get it to find my mesh in the wizard, but it always hung here:

builder@DESKTOP-QADGF36:~$ cat kuma-dp1.yaml
apiVersion: 'kuma.io/v1alpha1'
kind: Dataplane
mesh: default
metadata:
  name: dp-echo-1
  annotations:
    kuma.io/sidecar-injection: enabled
    kuma.io/mesh: default
networking:
  address: 10.0.0.1
  inbound:
  - port: 10000
    servicePort: 9000
    tags:
      kuma.io/service: echo

then applying

builder@DESKTOP-QADGF36:~$ kubectl apply -f kuma-dp1.yaml -n kuma-demo --validate=false
Error from server (InternalError): error when creating "kuma-dp1.yaml": Internal error occurred: failed calling webhook "validator.kuma-admission.kuma.io": Post "https://kuma-control-plane.kuma-system.svc:443/validate-kuma-io-v1alpha1?timeout=10s": EOF
builder@DESKTOP-QADGF36:~$ kubectl apply -f kuma-dp1.yaml -n kuma-system --validate=false
Error from server (InternalError): error when creating "kuma-dp1.yaml": Internal error occurred: failed calling webhook "validator.kuma-admission.kuma.io": Post "https://kuma-control-plane.kuma-system.svc:443/validate-kuma-io-v1alpha1?timeout=10s": EOF
builder@DESKTOP-QADGF36:~$ kubectl apply -f kuma-dp1.yaml --validate=false
Error from server (InternalError): error when creating "kuma-dp1.yaml": Internal error occurred: failed calling webhook "validator.kuma-admission.kuma.io": Post "https://kuma-control-plane.kuma-system.svc:443/validate-kuma-io-v1alpha1?timeout=10s": EOF

That said, i _do_ see Dataplane’s were created in kuma-demo:

builder@DESKTOP-QADGF36:~$ kubectl get dataplane --all-namespaces
NAMESPACE NAME AGE
kuma-demo redis-master-55fd8f6f54-kx5tp 13m
kuma-demo postgres-master-645bc44fd-fdkx4 13m
kuma-demo kuma-demo-backend-v0-56db47c579-qcdn9 13m
kuma-demo kuma-demo-app-6787b4f7f5-f75r4 13m

Moving on to Kuma Dashboard

Deciding the wizard was just a bit hung, i moved on to the GUI dashboard and found my mesh was just fine

http://localhost:5681/gui/#/mesh/all/internal-services

One thing we notice right away is that by default mTLS is not enabled:

We can use the built in CA (ca-1)

builder@DESKTOP-QADGF36:~$ cat enableMtls.yaml
apiVersion: kuma.io/v1alpha1
kind: Mesh
metadata:
  name: default
spec:
  mtls:
    enabledBackend: ca-1
    backends:
    - name: ca-1
      type: builtin
builder@DESKTOP-QADGF36:~$ kubectl apply -f enableMtls.yaml
Warning: resource meshes/default is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
mesh.kuma.io/default configured

Now when we refresh the Kuma dashboard we see mTLS is enabled:

With mTLS enabled, we now need to enable Traffic Permission to reach the demo app

By default, that was enabled (which is why we can see the above):

We can see the same from the command line with kubectl:

builder@DESKTOP-QADGF36:~$ kubectl get trafficpermission
NAME AGE
allow-all-default 44m
builder@DESKTOP-QADGF36:~$ kubectl get trafficpermission allow-all-default -o yaml
apiVersion: kuma.io/v1alpha1
kind: TrafficPermission
mesh: default
metadata:
  creationTimestamp: "2021-08-17T12:02:50Z"
  generation: 1
  name: allow-all-default
  ownerReferences:
  - apiVersion: kuma.io/v1alpha1
    kind: Mesh
    name: default
    uid: 9273129b-08e9-4b01-a84c-6980de9bfc37
  resourceVersion: "67307822"
  selfLink: /apis/kuma.io/v1alpha1/trafficpermissions/allow-all-default
  uid: 4a45b64c-9c6a-4c06-afe3-d61a8e9c954b
spec:
  destinations:
  - match:
      kuma.io/service: '*'
  sources:
  - match:
      kuma.io/service: '*'

let’s save it and then delete it:

builder@DESKTOP-QADGF36:~$ kubectl get trafficpermission allow-all-default -o yaml > all_traffic.yaml
builder@DESKTOP-QADGF36:~$ kubectl delete trafficpermission allow-all-default
trafficpermission.kuma.io "allow-all-default" deleted

Refreshing the page shows its now gone:

And now we see evidence that the traffic manager is blocking interpod communication:

We can just put back the one we deleted:

builder@DESKTOP-QADGF36:~$ cat all_traffic.yaml
apiVersion: kuma.io/v1alpha1
kind: TrafficPermission
mesh: default
metadata:
  creationTimestamp: "2021-08-17T12:02:50Z"
  generation: 1
  name: allow-all-default
  ownerReferences:
  - apiVersion: kuma.io/v1alpha1
    kind: Mesh
    name: default
    uid: 9273129b-08e9-4b01-a84c-6980de9bfc37
  resourceVersion: "67307822"
  selfLink: /apis/kuma.io/v1alpha1/trafficpermissions/allow-all-default
  uid: 4a45b64c-9c6a-4c06-afe3-d61a8e9c954b
spec:
  destinations:
  - match:
      kuma.io/service: '*'
  sources:
  - match:
      kuma.io/service: '*'
builder@DESKTOP-QADGF36:~$ kubectl apply -f ./all_traffic.yaml
trafficpermission.kuma.io/allow-all-default created

and see the app is back online

If you desire, you canfollow the guide to see traffic metrics.  However, it’s just installing prometheus and grafana.  We will cover this more later.

Features of Kuma

One of the nice features of Kuma is the automatic injection of livenessProbes added as a non-mTLS listener.  It injects this into the pod definition automatically.

For instance, we can see the Kuma Demo app that is NOT managed by Kuma has no defined livenessProbe (just the default):

$ kubectl get pods kuma-demo2-app-6cb59848bf-q6hpk -n kuma-demo2 -o yaml
apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: "2021-08-17T13:35:32Z"
  generateName: kuma-demo2-app-6cb59848bf-
  labels:
    app: kuma-demo2-frontend
    env: prod
    pod-template-hash: 6cb59848bf
    version: v8
  name: kuma-demo2-app-6cb59848bf-q6hpk
  namespace: kuma-demo2
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: kuma-demo2-app-6cb59848bf
    uid: e277a7da-a524-48d5-9a7e-9996a0c22280
  resourceVersion: "67335217"
  selfLink: /api/v1/namespaces/kuma-demo2/pods/kuma-demo2-app-6cb59848bf-q6hpk
  uid: b9f73eed-4a4d-4bc5-844c-da6168bf08df
spec:
  containers:
  - args:
    - -P
    - http://backend:3001
    image: kvn0218/kuma-demo-fe:latest
    imagePullPolicy: IfNotPresent
    name: kuma-fe
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-jppfz
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: anna-macbookair
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: default-token-jppfz
    secret:
      defaultMode: 420
      secretName: default-token-jppfz
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T13:35:32Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T13:35:34Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T13:35:34Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T13:35:32Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://0c4df86fdb188bf28e2a05debc16cb08348fa205fff28d711e850851d7b5ac3d
    image: docker.io/kvn0218/kuma-demo-fe:latest
    imageID: docker.io/kvn0218/kuma-demo-fe@sha256:e72c87263cc19bc7ea9d04978f6601d9adab01e9fc5a642219c206f154f9eb48
    lastState: {}
    name: kuma-fe
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2021-08-17T13:35:33Z"
  hostIP: 192.168.1.12
  phase: Running
  podIP: 10.42.2.195
  podIPs:
  - ip: 10.42.2.195
  qosClass: BestEffort
  startTime: "2021-08-17T13:35:32Z"

But if i look to the same app as managed by kuma in kuma-demo:

$ kubectl get pods kuma-demo-app-6787b4f7f5-f75r4 -n kuma-demo -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kuma.io/builtindns: enabled
    kuma.io/builtindnsport: "15053"
    kuma.io/mesh: default
    kuma.io/sidecar-injected: "true"
    kuma.io/sidecar-uid: "5678"
    kuma.io/transparent-proxying: enabled
    kuma.io/transparent-proxying-inbound-port: "15006"
    kuma.io/transparent-proxying-inbound-v6-port: "15010"
    kuma.io/transparent-proxying-outbound-port: "15001"
    kuma.io/virtual-probes: enabled
    kuma.io/virtual-probes-port: "9000"
  creationTimestamp: "2021-08-17T12:19:07Z"
  generateName: kuma-demo-app-6787b4f7f5-
  labels:
    app: kuma-demo-frontend
    env: prod
    pod-template-hash: 6787b4f7f5
    version: v8
  name: kuma-demo-app-6787b4f7f5-f75r4
  namespace: kuma-demo
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: kuma-demo-app-6787b4f7f5
    uid: 7699f429-f36b-4aa8-a1fe-e3295cee8256
  resourceVersion: "67313681"
  selfLink: /api/v1/namespaces/kuma-demo/pods/kuma-demo-app-6787b4f7f5-f75r4
  uid: ff77c875-3960-4b7b-bd34-76b09281e833
spec:
  containers:
  - args:
    - -P
    - http://backend:3001
    image: kvn0218/kuma-demo-fe:latest
    imagePullPolicy: IfNotPresent
    name: kuma-fe
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-84s7w
      readOnly: true
  - args:
    - run
    - --log-level=info
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: metadata.namespace
    - name: INSTANCE_IP
      valueFrom:
        fieldRef:
          apiVersion: v1
          fieldPath: status.podIP
    - name: KUMA_CONTROL_PLANE_CA_CERT
      value: |
        -----BEGIN CERTIFICATE-----
        MIIDDzCCAfeasdfasdfasdfasdfasdfasdfsFADAS
        MRAwDgYDVQQasdfasdfsadfasdfasdfgxNTExNTYy
        M1owEjEQMA4GA1UEAxMHa3VtYS1jYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
        AQoCggEBANCsl8PjpUPZoAeokwqlQEZdsRcSy3PHE7SVH6Uy2bo0lGa+rxA9w8Z2
        1K0mBOCznUnfoPmj6nt4gG89FH+j+ToXMN6R86ttt4fAxOVlS1dwPgn6UtWnbkM6
        FH+yJlSLNGO6wVhMT0emS5CPdxohodyHmpcgR7vtY2HPgfV99erwFJFADA0LjmaD
        6oOytbJwNXLag4KrPJuxVIVHWLzWabIH22Wulb6w9SWNo7WBSIFBqtzt6ke5nsFW
        7hfd6V0E7MuROzEOvVDyePVVIV+0QTuTPECjNEBZXI9jH2rfkTGVT1uadhE+ViGZ
        Yk2eHGBOa/AkcX0Q4QoazxXGnlOFWvkCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKk
        MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/
        MB0GA1UdDgQWBBTVBqGSnAIl6j9mdJQYJxz7R7GzUDANBgkqhkiG9w0BAQsFAAOC
        AQEASvTywVe6UmR6coNHCCEasgAGqgIN3wHkKa/qBUiIBKXM3ZGMcG6v9GgcvjFk
        D1Bt4rUlYrFZjHU+wXbhp6ddo3E9+Mk9bbn6tTfg/Yui1w+y6L4+S2GdGd6d1Ra2
        6qFKFOGqQF3whc17/zDZPYCoSiYrLe2uMiJ0bllHLVeTBkng7YkoANQRiMm4c7po
        Z7R18FKpuebKVlIgLv7QHBtHfvFuhhJvyZTq6qhzQGdsGCpF8l/URWmg4hV0zCbb
        vKa8we4hi3kKJosZqrzxSly62Ed9spV31mq/kPvy29+SfaR+ahU3JYhF6BkFQPOx
        xCgMvUPc1RU4BWdWivdooUJOZA==
        -----END CERTIFICATE-----
    - name: KUMA_CONTROL_PLANE_URL
      value: https://kuma-control-plane.kuma-system:5678
    - name: KUMA_DATAPLANE_ADMIN_PORT
      value: "9901"
    - name: KUMA_DATAPLANE_DRAIN_TIME
      value: 30s
    - name: KUMA_DATAPLANE_MESH
      value: default
    - name: KUMA_DATAPLANE_NAME
      value: $(POD_NAME).$(POD_NAMESPACE)
    - name: KUMA_DATAPLANE_RUNTIME_TOKEN_PATH
      value: /var/run/secrets/kubernetes.io/serviceaccount/token
    - name: KUMA_DNS_CORE_DNS_BINARY_PATH
      value: coredns
    - name: KUMA_DNS_CORE_DNS_EMPTY_PORT
      value: "15054"
    - name: KUMA_DNS_CORE_DNS_PORT
      value: "15053"
    - name: KUMA_DNS_ENABLED
      value: "true"
    - name: KUMA_DNS_ENVOY_DNS_PORT
      value: "15055"
    image: docker.io/kumahq/kuma-dp:1.2.3
    imagePullPolicy: IfNotPresent
    livenessProbe:
      failureThreshold: 12
      httpGet:
        path: /ready
        port: 9901
        scheme: HTTP
      initialDelaySeconds: 60
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 3
    name: kuma-sidecar
    readinessProbe:
      failureThreshold: 12
      httpGet:
        path: /ready
        port: 9901
        scheme: HTTP
      initialDelaySeconds: 1
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 3
    resources:
      limits:
        cpu: "1"
        memory: 512Mi
      requests:
        cpu: 50m
        memory: 64Mi
    securityContext:
      runAsGroup: 5678
      runAsUser: 5678
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-84s7w
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  initContainers:
  - args:
    - --redirect-outbound-port
    - "15001"
    - --redirect-inbound=true
    - --redirect-inbound-port
    - "15006"
    - --redirect-inbound-port-v6
    - "15010"
    - --kuma-dp-uid
    - "5678"
    - --exclude-inbound-ports
    - ""
    - --exclude-outbound-ports
    - ""
    - --verbose
    - --skip-resolv-conf
    - --redirect-all-dns-traffic
    - --redirect-dns-port
    - "15053"
    command:
    - /usr/bin/kumactl
    - install
    - transparent-proxy
    image: docker.io/kumahq/kuma-init:1.2.3
    imagePullPolicy: IfNotPresent
    name: kuma-init
    resources:
      limits:
        cpu: 100m
        memory: 50M
      requests:
        cpu: 10m
        memory: 10M
    securityContext:
      capabilities:
        add:
        - NET_ADMIN
      runAsGroup: 0
      runAsUser: 0
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-84s7w
      readOnly: true
  nodeName: isaac-macbookpro
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: default-token-84s7w
    secret:
      defaultMode: 420
      secretName: default-token-84s7w
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T12:19:31Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T12:21:16Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T12:21:16Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2021-08-17T12:19:07Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://dc78658979cee5fb71a2c61492c94c42dbce7f24301c61ca30844e122627a9b3
    image: docker.io/kvn0218/kuma-demo-fe:latest
    imageID: docker.io/kvn0218/kuma-demo-fe@sha256:e72c87263cc19bc7ea9d04978f6601d9adab01e9fc5a642219c206f154f9eb48
    lastState: {}
    name: kuma-fe
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2021-08-17T12:20:29Z"
  - containerID: containerd://1143f43b62f790199e16afa0a07660481fb7d14b423f891d5698bd463b9ca0e0
    image: docker.io/kumahq/kuma-dp:1.2.3
    imageID: docker.io/kumahq/kuma-dp@sha256:2dcf3feaeaa87db3e6e614057ce92b34e804658abd23d44c6d102e59a7f3f088
    lastState: {}
    name: kuma-sidecar
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2021-08-17T12:21:13Z"
  hostIP: 192.168.1.205
  initContainerStatuses:
  - containerID: containerd://027452e7ee6e91000381715ab24635bdf14fe97f816408fc1bdc57c6e771b933
    image: docker.io/kumahq/kuma-init:1.2.3
    imageID: docker.io/kumahq/kuma-init@sha256:d62dac9fd095c0c4675816d9a60b88d1cd74405627be7d5baae0d6dbd87b369a
    lastState: {}
    name: kuma-init
    ready: true
    restartCount: 0
    state:
      terminated:
        containerID: containerd://027452e7ee6e91000381715ab24635bdf14fe97f816408fc1bdc57c6e771b933
        exitCode: 0
        finishedAt: "2021-08-17T12:19:29Z"
        reason: Completed
        startedAt: "2021-08-17T12:19:28Z"
  phase: Running
  podIP: 10.42.1.182
  podIPs:
  - ip: 10.42.1.182
  qosClass: Burstable
  startTime: "2021-08-17T12:19:09Z"

We can see that block that allows a managed livenessProbe handled this time by Kuma, not the app itself:

    livenessProbe:
      failureThreshold: 12
      httpGet:
        path: /ready
        port: 9901
        scheme: HTTP
      initialDelaySeconds: 60
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 3
    name: kuma-sidecar
    readinessProbe:
      failureThreshold: 12
      httpGet:
        path: /ready
        port: 9901
        scheme: HTTP
      initialDelaySeconds: 1
      periodSeconds: 5
      successThreshold: 1
      timeoutSeconds: 3

we can see more on that in the Kuma documentation

Datadog

While prometheus and grafana are fine for some.  I generally prefer the metrics and log aggregation of Datadog.  

Since I rotated the key in the last 5 months, it’s time to fix that and send data in for this cluster:

builder@DESKTOP-QADGF36:~$ helm list | grep datadog
datadogrelease default 2 2021-03-01 07:16:57.3327545 -0600 CST deployed datadog-2.9.5                   

$ helm get values datadogrelease
USER-SUPPLIED VALUES:
USER-SUPPLIED VALUES: null
clusterAgent:
  enabled: true
  metricsProvider:
    enabled: true
datadog:
  apiKeyExistingSecret: dd-secret
  appKey: 60a70omyappkeymyappkeymyappkeyf9984
  logs:
    containerCollectAll: true
    enabled: true

The apikey is in dd-secret

We can see our values either in the integration wizard or under API Keys

There are a few ways to edit secrets (including kubectl edit secrets dd-secret) but i’ll just use base64 directly and edit/apply the file (note the tr -d to remove newline)

builder@DESKTOP-QADGF36:~$ kubectl get secret dd-secret -o yaml > dd-secret.yaml
builder@DESKTOP-QADGF36:~$ echo ******putrealkeyhere******** | tr -d '\n' | base64
asdfasdfasdfasdfasdfsadfasdfasdfasdf=
builder@DESKTOP-QADGF36:~$ vi dd-secret.yaml
builder@DESKTOP-QADGF36:~$ kubectl apply -f dd-secret.yaml
Warning: resource secrets/dd-secret is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
secret/dd-secret configured

The pods are spread over 3 labels:

builder@DESKTOP-QADGF36:~$ kubectl get pods -l app=datadogrelease
NAME READY STATUS RESTARTS AGE
datadogrelease-jhw4z 1/2 Running 7 168d
datadogrelease-wg7ks 1/2 Running 4 121d
datadogrelease-ws2jf 1/2 Running 5 168d
builder@DESKTOP-QADGF36:~$ kubectl get pods -l app=datadogrelease-cluster-agent
NAME READY STATUS RESTARTS AGE
datadogrelease-cluster-agent-86b57cc4c7-wvdbl 1/1 Running 0 22d
builder@DESKTOP-QADGF36:~$ kubectl get pods -l app.kubernetes.io/instance=datadogrelease
NAME READY STATUS RESTARTS AGE
datadogrelease-kube-state-metrics-5c6f76f766-n5jd6 1/1 Running 0 28d

We can rotate the bunch of them to get the fresh key:

$ kubectl delete pods -l app=datadogrelease-cluster-agent && kubectl delete pods -l app.kubernetes.io/instance=datadogrelease && kubectl delete pods -l app=datadogrelease
pod "datadogrelease-cluster-agent-86b57cc4c7-wvdbl" deleted
pod "datadogrelease-kube-state-metrics-5c6f76f766-n5jd6" deleted
pod "datadogrelease-jhw4z" deleted
pod "datadogrelease-wg7ks" deleted
pod "datadogrelease-ws2jf" deleted

builder@DESKTOP-QADGF36:~$ kubectl get pods | grep datadog
datadogrelease-kube-state-metrics-5c6f76f766-kztrt 1/1 Running 0 52s
datadogrelease-cluster-agent-86b57cc4c7-w7fms 1/1 Running 0 65s
datadogrelease-nk8zl 2/2 Running 0 39s
datadogrelease-hthkf 1/2 Running 0 27s
datadogrelease-gw9tq 2/2 Running 0 39s

We can see details from the cluster agent itself:

$ kubectl exec -it datadogrelease-cluster-agent-86b57cc4c7-w7fms -- datadog-cluster-agent status
Getting the status from the agent.
2021-08-17 13:24:34 UTC | CLUSTER | WARN | (pkg/util/log/log.go:541 in func1) | Agent configuration relax permissions constraint on the secret backend cmd, Group can read and exec
===============================
Datadog Cluster Agent (v1.10.0)
===============================

  Status date: 2021-08-17 13:24:34.861176 UTC
  Agent start: 2021-08-17 13:09:54.946022 UTC
  Pid: 1
  Go Version: go1.14.12
  Build arch: amd64
  Agent flavor: cluster_agent
  Check Runners: 4
  Log Level: INFO

  Paths
  =====
    Config File: /etc/datadog-agent/datadog-cluster.yaml
    conf.d: /etc/datadog-agent/conf.d

  Clocks
  ======
    System UTC time: 2021-08-17 13:24:34.861176 UTC

And we can see our Kubernetes dashboard is back up:

Scoping to Kuma-demo we can se we are able to detect pods by node:

This is hard to see in a blog post, but the overall dashboard

Essentially we just use the power of Datadog labels to scope our K8s dashboard to just the kuma-demo namespace:

In the upper right i set the timeframe to 5 minutes since i just corrected the keys.

I immediately checked where the workloads were running (since the MB Pro is quite old and my least performant node):

My second check was to see the CPU and Memory intensive pods.  Every now and then I see a real outlier that requires attention:

Lastly, the bottom left panes are all about the deployments.  Is anything down? did any not start. Here we will immediately see unavailable and unscheduled.

From our dashboard we can check out the pods themselves

However, this namespace is not giving us pods or logs presently:

This is because of the service mesh.

Let’s verify that hypothesis by creating a “demo2” without the service mesh:

$ wget https://bit.ly/demokuma
--2021-08-17 08:27:39-- https://bit.ly/demokuma
Resolving bit.ly (bit.ly)... 67.199.248.10, 67.199.248.11
Connecting to bit.ly (bit.ly)|67.199.248.10|:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://raw.githubusercontent.com/Kong/kuma-demo/master/kubernetes/kuma-demo-aio.yaml [following]
--2021-08-17 08:27:39-- https://raw.githubusercontent.com/Kong/kuma-demo/master/kubernetes/kuma-demo-aio.yaml
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.110.133, 185.199.111.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4905 (4.8K) [text/plain]
Saving to: ‘demokuma’

demokuma 100%[================================================================================================================================================================================================================================================>] 4.79K --.-KB/s in 0s

2021-08-17 08:27:39 (104 MB/s) - ‘demokuma’ saved [4905/4905]

builder@DESKTOP-QADGF36:~$ sed -i 's/name: kuma-demo/name: kuma-demo2/g' demokuma
builder@DESKTOP-QADGF36:~$ sed -i 's/namespace: kuma-demo/namespace: kuma-demo2/g' demokuma
builder@DESKTOP-QADGF36:~$ sed -i 's/app: kuma-demo/app: kuma-demo2/g' demokuma

$ kubectl apply -f demokuma
namespace/kuma-demo2 created
deployment.apps/postgres-master created
service/postgres created
deployment.apps/redis-master created
service/redis created
service/backend created
deployment.apps/kuma-demo2-backend-v0 created
deployment.apps/kuma-demo2-backend-v1 created
deployment.apps/kuma-demo2-backend-v2 created
service/frontend created
deployment.apps/kuma-demo2-app created

Verify its running

$ kubectl get pods -n kuma-demo2
NAME READY STATUS RESTARTS AGE
kuma-demo2-app-6cb59848bf-m7mcz 2/2 Running 0 64s
postgres-master-645bc44fd-cgkvf 2/2 Running 0 67s
kuma-demo2-backend-v0-5c6fc995c-62g7c 2/2 Running 0 64s
redis-master-55fd8f6f54-b9n2c 2/2 Running 0 67s

We cannot view pods

Now disable the Kuma injection by just removing the annotation at the top:

builder@DESKTOP-QADGF36:~$ diff demokuma demokuma.bak
5a6,7
> annotations:
> kuma.io/sidecar-injection: enabled

$ kubectl apply -f demokuma
namespace/kuma-demo2 created
deployment.apps/postgres-master created
service/postgres created
deployment.apps/redis-master created
service/redis created
service/backend created
deployment.apps/kuma-demo2-backend-v0 created
deployment.apps/kuma-demo2-backend-v1 created
deployment.apps/kuma-demo2-backend-v2 created
service/frontend created
deployment.apps/kuma-demo2-app created

Verification

$ kubectl get pods -n kuma-demo2
NAME READY STATUS RESTARTS AGE
redis-master-55fd8f6f54-tgft9 1/1 Running 0 30s
kuma-demo2-app-6cb59848bf-q6hpk 1/1 Running 0 30s
kuma-demo2-backend-v0-5c6fc995c-9ljqd 1/1 Running 0 30s
postgres-master-645bc44fd-qcv9r 1/1 Running 0 30s

And in fact I was in err.  It was just a timing issue for log ingestion.
Circling back, i found the logs for the secured container

Recall, the deployment with Kuma enabled:

$ kubectl get pods -n kuma-demo2
NAME READY STATUS RESTARTS AGE
kuma-demo2-app-6cb59848bf-m7mcz 2/2 Running 0 64s
postgres-master-645bc44fd-cgkvf 2/2 Running 0 67s
kuma-demo2-backend-v0-5c6fc995c-62g7c 2/2 Running 0 64s
redis-master-55fd8f6f54-b9n2c 2/2 Running 0 67s

Which i found by checking Pods in bad states in the Kubernetes Pods Dashboard in DD

From active pods, we can see we had sidecars but they were removed:

We can also view logs for pods that have logs to show:

And while that is for the kuma-demo2 not managed by kuma, we can see logs for the one that is:

Adding Ingress

I’m going to show how to do this since having to use kubectl port-forward forever isn’t really ideal.

Let’s first create a new entry in our Azure DNS for Kuma.

Usually we create a recordset in the portal:

But let’s use the CLI this time:

builder@DESKTOP-QADGF36:~$ az account set --subscription "Pay-As-You-Go"
builder@DESKTOP-QADGF36:~$ az network dns record-set a add-record --resource-group idjdnsrg --zone-name tpk.pw --record-set-name kuma --ipv4-address 73.242.50.46
{
  "aRecords": [
    {
      "ipv4Address": "73.242.50.46"
    }
  ],
  "etag": "94ef2117-3c5a-4960-994c-ec4763fe37e5",
  "fqdn": "kuma.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/kuma",
  "metadata": null,
  "name": "kuma",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {
    "id": null
  },
  "ttl": 3600,
  "type": "Microsoft.Network/dnszones/A"
}

Next we need a valid cert, so create a cert request:

$ cat kuma.cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: kuma-tpk-pw
  namespace: default
spec:
  commonName: kuma.tpk.pw
  dnsNames:
  - kuma.tpk.pw
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-prod
  secretName: kuma.tpk.pw-cert


builder@DESKTOP-QADGF36:~$ kubectl apply -f kuma.cert.yaml
certificate.cert-manager.io/kuma-tpk-pw created
builder@DESKTOP-QADGF36:~$ kubectl get cert kuma-tpk-pw
NAME READY SECRET AGE
kuma-tpk-pw False kuma.tpk.pw-cert 10s
builder@DESKTOP-QADGF36:~$ kubectl get cert kuma-tpk-pw
NAME READY SECRET AGE
kuma-tpk-pw True kuma.tpk.pw-cert 35s

which creates our valid TLS cert:

$ kubectl get secret | grep kuma
kuma.tpk.pw-cert kubernetes.io/tls 2 30s

Now we can easily create an ingress in the namespace:

$ cat kuma.ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: kuma-tpkpw-ingress
  namespace: kuma-system
spec:
  rules:
  - host: kuma.tpk.pw
    http:
      paths:
      - backend:
          serviceName: kuma-control-plane
          servicePort: 5681
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - kuma.tpk.pw
    secretName: kuma.tpk.pw-cert

And apply it:

$ kubectl apply -f kuma.ingress.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/kuma-tpkpw-ingress created

However the secret is in default, not kuma-system namespace, thus we see a Cert error:

Let’s copy that secret over:

$ kubectl get secret kuma.tpk.pw-cert -o yaml > kuma.crt.yaml

$ kubectl apply -f kuma.crt.yaml
secret/kuma.tpk.pw-cert created

$ sed -i 's/namespace: default/namespace: kuma-system/g' kuma.crt.yaml

$ kubectl get secrets -n kuma-system | grep cert
kuma-tls-cert kubernetes.io/tls 3 3h19m
default.ca-builtin-cert-ca-1 system.kuma.io/secret 1 156m
kuma.tpk.pw-cert kubernetes.io/tls 2 13s

I find it best to just delete and re-add the ingress to get nginx to find the cert:

$ kubectl delete -f kuma.ingress.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions "kuma-tpkpw-ingress" deleted
$ kubectl apply -f kuma.ingress.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/kuma-tpkpw-ingress created

And if I would like anyone in the world to view and update my mesh, I suppose i could leave it wide open.

Since Kuma does not integrate with any federated IdP for AuthN/AuthZ out of the box, let’s at the very least setup basic auth

I’ll create a quick “foo” user with a password locally and set a k8s secret in the kuma-system namespace with it:

builder@DESKTOP-QADGF36:~$ sudo apt install apache2-utils
[sudo] password for builder:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  libapr1 libaprutil1
The following NEW packages will be installed:
  apache2-utils libapr1 libaprutil1
0 upgraded, 3 newly installed, 0 to remove and 33 not upgraded.
Need to get 260 kB of archives.
After this operation, 969 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://archive.ubuntu.com/ubuntu focal/main amd64 libapr1 amd64 1.6.5-1ubuntu1 [91.4 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal/main amd64 libaprutil1 amd64 1.6.1-4ubuntu2 [84.7 kB]
Get:3 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 apache2-utils amd64 2.4.41-4ubuntu3.4 [84.0 kB]
Fetched 260 kB in 1s (279 kB/s)
Selecting previously unselected package libapr1:amd64.
(Reading database ... 97707 files and directories currently installed.)
Preparing to unpack .../libapr1_1.6.5-1ubuntu1_amd64.deb ...
Unpacking libapr1:amd64 (1.6.5-1ubuntu1) ...
Selecting previously unselected package libaprutil1:amd64.
Preparing to unpack .../libaprutil1_1.6.1-4ubuntu2_amd64.deb ...
Unpacking libaprutil1:amd64 (1.6.1-4ubuntu2) ...
Selecting previously unselected package apache2-utils.
Preparing to unpack .../apache2-utils_2.4.41-4ubuntu3.4_amd64.deb ...
Unpacking apache2-utils (2.4.41-4ubuntu3.4) ...
Setting up libapr1:amd64 (1.6.5-1ubuntu1) ...
Setting up libaprutil1:amd64 (1.6.1-4ubuntu2) ...
Setting up apache2-utils (2.4.41-4ubuntu3.4) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9.2) ...
builder@DESKTOP-QADGF36:~$ sudo htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo
builder@DESKTOP-QADGF36:~$ kubectl create secret generic basic-auth --from-file auth -n kuma-system
secret/basic-auth created

We will add the auth block to the ingress annotation to use it:

builder@DESKTOP-QADGF36:~$ kubectl get ingress kuma-tpkpw-ingress -n kuma-system -o yaml > kuma.ingress.yaml.2.old
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
builder@DESKTOP-QADGF36:~$ kubectl get ingress kuma-tpkpw-ingress -n kuma-system -o yaml > kuma.ingress.yaml.2
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
builder@DESKTOP-QADGF36:~$ vi kuma.ingress.yaml.2

Now update for basic auth

$ diff -c kuma.ingress.yaml.2 kuma.ingress.yaml.2.old
*** kuma.ingress.yaml.2 2021-08-17 10:26:00.395545999 -0500
--- kuma.ingress.yaml.2.old 2021-08-17 10:25:01.765545196 -0500
***************
***5,13****
      kubectl.kubernetes.io/last-applied-configuration: |
        {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"kuma-tpkpw-ingress","namespace":"kuma-system"},"spec":{"rules":[{"host":"kuma.tpk.pw","http":{"paths":[{"backend":{"serviceName":"kuma-control-plane","servicePort":5681},"path":"/","pathType":"ImplementationSpecific"}]}}],"tls":[{"hosts":["kuma.tpk.pw"],"secretName":"kuma.tpk.pw-cert"}]}}
      kubernetes.io/ingress.class: nginx
- nginx.ingress.kubernetes.io/auth-type: basic
- nginx.ingress.kubernetes.io/auth-secret: basic-auth
- nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
    creationTimestamp: "2021-08-17T15:20:05Z"
    generation: 1
    name: kuma-tpkpw-ingress
--- 5,10 ----

$ kubectl apply -f kuma.ingress.yaml.2
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/kuma-tpkpw-ingress configured

Now a reload will at least require basic auth over https:

and bad logins:

and on success

Metrics

For this, we will need kumactlto install

$ wget https://download.konghq.com/mesh-alpine/kuma-1.2.3-ubuntu-amd64.tar.gz
--2021-08-17 10:46:50-- https://download.konghq.com/mesh-alpine/kuma-1.2.3-ubuntu-amd64.tar.gz
Resolving download.konghq.com (download.konghq.com)... 3.20.242.208, 3.19.0.58, 18.220.94.64
Connecting to download.konghq.com (download.konghq.com)|3.20.242.208|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://kong-cloud-01-prod-us-east-2-kong-packages-origin.s3.amazonaws.com/pulp3-media/artifact/b8/db75682439f60399661963f6cc3f77df0ed5ceaf4b1d27efbdec86fdaa3acc?response-content-disposition=attachment%3Bx-pulp-artifact-path%3Dmesh-alpine__kuma-1.2.3-ubuntu-amd64.tar.gz%3Bfilename%3Dkuma-1.2.3-ubuntu-amd64.tar.gz&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQUIYLSLOCZOEMY4R%2F20210817%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210817T154656Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Security-Token=FwoGZXIvYXdzEGEaDCEW3u%2B%2FXK%2FdUOsghCKKBHEqwA3VC1HNHaLrqcJOJRAKqXdyt84rfTDu8DaPDl3wSwP7CgabyiZaUMwclJMklUsyO1PWlU%2BtMA4bBt%2FLVSahhjXMMYrPb8wlx61%2FLhcEW3PSw64zDEVkm9rKKuEAFU6GDmAOlstRtC%2FmFzVM50gauTempZy9%2BOL4X6I3TPXXUlvMyVaqJ4k5vGGXQFT9VlxB7dlXm9TRotUFThFYzIl0lRtlnfAnAiSfDYfvFlx1fMS21m3ymYYy00srfYC5hmXO5SDRXQEeYziyFIFo7AY6MloZDQRPdpa83%2FKiavRILsak55B4EBh%2FTmcqQybzgWbXw00NExQL87x%2FDzM6qGzczvpPA3e%2Bn%2FSHupwzZnlg3%2B0k0gl2Hi44QSnp2au88uSa1iaL08T4EImgBE7IxRAU0mCw%2BkpYbqGLNptOA6qFb5WpuKlDbXQNdQcHfT7nQSQpZEBO1amE4%2BFhJkFzGgpakoujKiG5j7YssUVdEnrl8iWd5MYEFxKpuM3DOm6C4TOHyr211xwDFtcImOZHNEPTjdQlBGjo9yQMyG7NvjeMj8Opi8MVd5A5eyStO4LFrPKNiMDAGhSK%2BFd%2FpsY7rw0%2FLxslu%2FlPdvsssF0UNf5vWxBxHiz3YpZQtujDe9DzCfYhZe%2BInwHAEvFfjPDtY6oOfQlaZYrZ90mnipC0%2FkM2WZKsd4BniMFZ4CjdpO%2BIBjIq5aJL%2BammL6YZwG5VeYwfQkf%2BWaH1ijKZgJPAwm11Q%2BP%2B0MoxMmUPZbn1&X-Amz-Signature=b9d3098074915edf4c4cb9fcead98bd4ef7379bad4ef3220eeee1e6645a90e89 [following]
--2021-08-17 10:46:50-- https://kong-cloud-01-prod-us-east-2-kong-packages-origin.s3.amazonaws.com/pulp3-media/artifact/b8/db75682439f60399661963f6cc3f77df0ed5ceaf4b1d27efbdec86fdaa3acc?response-content-disposition=attachment%3Bx-pulp-artifact-path%3Dmesh-alpine__kuma-1.2.3-ubuntu-amd64.tar.gz%3Bfilename%3Dkuma-1.2.3-ubuntu-amd64.tar.gz&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQUIYLSLOCZOEMY4R%2F20210817%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210817T154656Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Security-Token=FwoGZXIvYXdzEGEaDCEW3u%2B%2FXK%2FdUOsghCKKBHEqwA3VC1HNHaLrqcJOJRAKqXdyt84rfTDu8DaPDl3wSwP7CgabyiZaUMwclJMklUsyO1PWlU%2BtMA4bBt%2FLVSahhjXMMYrPb8wlx61%2FLhcEW3PSw64zDEVkm9rKKuEAFU6GDmAOlstRtC%2FmFzVM50gauTempZy9%2BOL4X6I3TPXXUlvMyVaqJ4k5vGGXQFT9VlxB7dlXm9TRotUFThFYzIl0lRtlnfAnAiSfDYfvFlx1fMS21m3ymYYy00srfYC5hmXO5SDRXQEeYziyFIFo7AY6MloZDQRPdpa83%2FKiavRILsak55B4EBh%2FTmcqQybzgWbXw00NExQL87x%2FDzM6qGzczvpPA3e%2Bn%2FSHupwzZnlg3%2B0k0gl2Hi44QSnp2au88uSa1iaL08T4EImgBE7IxRAU0mCw%2BkpYbqGLNptOA6qFb5WpuKlDbXQNdQcHfT7nQSQpZEBO1amE4%2BFhJkFzGgpakoujKiG5j7YssUVdEnrl8iWd5MYEFxKpuM3DOm6C4TOHyr211xwDFtcImOZHNEPTjdQlBGjo9yQMyG7NvjeMj8Opi8MVd5A5eyStO4LFrPKNiMDAGhSK%2BFd%2FpsY7rw0%2FLxslu%2FlPdvsssF0UNf5vWxBxHiz3YpZQtujDe9DzCfYhZe%2BInwHAEvFfjPDtY6oOfQlaZYrZ90mnipC0%2FkM2WZKsd4BniMFZ4CjdpO%2BIBjIq5aJL%2BammL6YZwG5VeYwfQkf%2BWaH1ijKZgJPAwm11Q%2BP%2B0MoxMmUPZbn1&X-Amz-Signature=b9d3098074915edf4c4cb9fcead98bd4ef7379bad4ef3220eeee1e6645a90e89
Resolving kong-cloud-01-prod-us-east-2-kong-packages-origin.s3.amazonaws.com (kong-cloud-01-prod-us-east-2-kong-packages-origin.s3.amazonaws.com)... 52.219.106.228
Connecting to kong-cloud-01-prod-us-east-2-kong-packages-origin.s3.amazonaws.com (kong-cloud-01-prod-us-east-2-kong-packages-origin.s3.amazonaws.com)|52.219.106.228|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 78202846 (75M) [application/x-tar]
Saving to: ‘kuma-1.2.3-ubuntu-amd64.tar.gz’

kuma-1.2.3-ubuntu-amd64.tar.gz 100%[====================================================================================================================================================================================================================================================>] 74.58M 80.6MB/s in 0.9s

2021-08-17 10:46:51 (80.6 MB/s) - ‘kuma-1.2.3-ubuntu-amd64.tar.gz’ saved [78202846/78202846]

builder@DESKTOP-QADGF36:~$ tar -xzvf kuma-1.2.3-ubuntu-amd64.tar.gz
./
./kuma-1.2.3/
./kuma-1.2.3/README
./kuma-1.2.3/NOTICE
./kuma-1.2.3/install_missing_crds.sh
./kuma-1.2.3/NOTICE-kumactl
./kuma-1.2.3/LICENSE
./kuma-1.2.3/conf/
./kuma-1.2.3/conf/kuma-cp.conf.yml
./kuma-1.2.3/bin/
./kuma-1.2.3/bin/kuma-dp
./kuma-1.2.3/bin/kuma-prometheus-sd
./kuma-1.2.3/bin/kuma-cp
./kuma-1.2.3/bin/envoy
./kuma-1.2.3/bin/kumactl
./kuma-1.2.3/bin/coredns

Then install metrics:

$ ./kuma-1.2.3/bin/kumactl install metrics | kubectl apply -f -
namespace/kuma-metrics created
podsecuritypolicy.policy/grafana created
serviceaccount/prometheus-alertmanager created
serviceaccount/prometheus-kube-state-metrics created
serviceaccount/prometheus-node-exporter created
serviceaccount/prometheus-pushgateway created
serviceaccount/prometheus-server created
serviceaccount/grafana created
configmap/grafana created
configmap/prometheus-alertmanager created
configmap/provisioning-datasource created
configmap/provisioning-dashboards created
configmap/prometheus-server created
configmap/provisioning-dashboards-0 created
configmap/provisioning-dashboards-1 created
configmap/provisioning-dashboards-2 created
configmap/provisioning-dashboards-3 created
configmap/provisioning-dashboards-4 created
persistentvolumeclaim/prometheus-alertmanager created
persistentvolumeclaim/prometheus-server created
clusterrole.rbac.authorization.k8s.io/grafana-clusterrole created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
clusterrole.rbac.authorization.k8s.io/prometheus-alertmanager created
clusterrole.rbac.authorization.k8s.io/prometheus-kube-state-metrics created
clusterrole.rbac.authorization.k8s.io/prometheus-pushgateway created
clusterrole.rbac.authorization.k8s.io/prometheus-server created
clusterrolebinding.rbac.authorization.k8s.io/grafana-clusterrolebinding created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding
clusterrolebinding.rbac.authorization.k8s.io/prometheus-alertmanager created
clusterrolebinding.rbac.authorization.k8s.io/prometheus-kube-state-metrics created
clusterrolebinding.rbac.authorization.k8s.io/prometheus-pushgateway created
clusterrolebinding.rbac.authorization.k8s.io/prometheus-server created
Warning: rbac.authorization.k8s.io/v1beta1 Role is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 Role
role.rbac.authorization.k8s.io/grafana created
Warning: rbac.authorization.k8s.io/v1beta1 RoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 RoleBinding
rolebinding.rbac.authorization.k8s.io/grafana created
service/grafana created
service/prometheus-alertmanager created
service/prometheus-kube-state-metrics created
service/prometheus-node-exporter created
service/prometheus-pushgateway created
service/prometheus-server created
daemonset.apps/prometheus-node-exporter created
deployment.apps/grafana created
deployment.apps/prometheus-alertmanager created
deployment.apps/prometheus-kube-state-metrics created
deployment.apps/prometheus-pushgateway created
deployment.apps/prometheus-server created

Verification

$ kubectl get pods -n kuma-metrics
NAME READY STATUS RESTARTS AGE
prometheus-server-67974d9cdc-2gxw8 0/4 Pending 0 21s
prometheus-node-exporter-n7r2w 0/1 ContainerCreating 0 22s
prometheus-node-exporter-td8vp 1/1 Running 0 22s
prometheus-node-exporter-444n4 1/1 Running 0 22s
grafana-7875d6d77f-75s9l 0/2 Init:0/1 0 22s
prometheus-kube-state-metrics-5ddc7b5dfd-4k7bb 0/2 PodInitializing 0 22s
prometheus-alertmanager-9955cbc54-scp8g 2/3 Running 0 22s
prometheus-pushgateway-5df994c7ff-bzg5v 2/2 Running 0 21s

Once powered on, we can enable metrics by updating the default mesh definition:

builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml.bak
builder@DESKTOP-QADGF36:~$ vi my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ diff -c my.default.mesh.yaml my.default.mesh.yaml.bak
*** my.default.mesh.yaml 2021-08-17 13:00:14.745672717 -0500
--- my.default.mesh.yaml.bak 2021-08-17 12:59:46.235672327 -0500
***************
***16,23****
      - name: ca-1
        type: builtin
      enabledBackend: ca-1
- metrics:
- enabledBackend: prometheus-1
- backends:
- - name: prometheus-1
- type: prometheus
--- 16,18 ----
builder@DESKTOP-QADGF36:~$ kubectl apply -f my.default.mesh.yaml
mesh.kuma.io/default configured

Which changes from

to

To generate a lot of traffic fast, we can port forward, then slam the service with curl commands:

kubectl port-forward -n kuma-demo kuma-demo-app-6787b4f7f5-f75r4 8080:8080 &
while [true]; do curl http://127.0.0.1:8080/items?q=; curl http://127.0.0.1:8080/items/1/reviews; done

We can now hop over to Grafana where we can view some of the Kuma dashboards already setup:

builder@DESKTOP-QADGF36:~$ kubectl port-forward svc/grafana -n kuma-metrics 3000:80
Forwarding from 127.0.0.1:3000 -> 3000
Forwarding from [::1]:3000 -> 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000
Handling connection for 3000

For instance the Mesh

And Dataplane:

DataDog Tracing

The first path we will try is exposing Datadogs built in tracing service from the cluster agent.  

Note: I had struggles with the built in APM via Cluster Agent as we’ll see below, but I do succeed using the Jaeger backend with OpenTelemetry to Datadog. Skip ahead to see the working solution

$ cat dd.svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: trace-svc
spec:
  selector:
    app: datadog
  ports:
    - protocol: TCP
      port: 8126
      targetPort: 8126
builder@DESKTOP-QADGF36:~$ kubectl apply -f dd.svc.yaml
service/trace-svc created

Now add  a tracing block that uses it to the mesh definition

builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml.bak
builder@DESKTOP-QADGF36:~$ vi my.default.mesh.yaml

builder@DESKTOP-QADGF36:~$ diff -c my.default.mesh.yaml my.default.mesh.yaml.bak
*** my.default.mesh.yaml 2021-08-17 13:21:24.505690104 -0500
--- my.default.mesh.yaml.bak 2021-08-17 13:20:58.755689751 -0500
***************
***26,37****
      - name: ca-1
        type: builtin
      enabledBackend: ca-1
- tracing:
- defaultBackend: datadog-collector
- backends:
- - name: datadog-collector
- type: datadog
- sampling: 100.0
- conf:
- address: trace-svc.datadog.svc.cluster.local
- port: 8126
--- 26,28 ----
builder@DESKTOP-QADGF36:~$ kubectl apply -f my.default.mesh.yaml
mesh.kuma.io/default configured

While this would work if i had APM at the Clusteragent, it doesnt presently as i only use APM via Dapr and Zipkin integration to OpenTelemetry.

Therefore, i’ll need to update my chart:

$ helm get values datadogrelease
USER-SUPPLIED VALUES:
USER-SUPPLIED VALUES: null
clusterAgent:
  enabled: true
  metricsProvider:
    enabled: true
datadog:
  apiKeyExistingSecret: dd-secret
  appKey: 60asdfasdfasdfasdfasdfasdfasdfasdf4
  logs:
    containerCollectAll: true
    enabled: true

builder@DESKTOP-QADGF36:~$ helm get values datadogrelease > myvalues.yaml
builder@DESKTOP-QADGF36:~$ vi myvalues.yaml
builder@DESKTOP-QADGF36:~$ helm get values datadogrelease > myvalues.yaml.bak
builder@DESKTOP-QADGF36:~$ diff myvalues.yaml myvalues.yaml.bak
13,16d12
< apm:
< enabled: true
< port: 8126
<

Then add the Helm repo (as this is a new WSL, it was absent).  Then upgrade the existing helm release

builder@DESKTOP-QADGF36:~$ helm repo add datadog https://helm.datadoghq.com
"datadog" has been added to your repositories

builder@DESKTOP-QADGF36:~$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "datadog" chart repository
...Successfully got an update from the "kuma" chart repository
Update Complete. ⎈Happy Helming!⎈

builder@DESKTOP-QADGF36:~$ helm upgrade -f myvalues.yaml datadogrelease datadog/datadog
Release "datadogrelease" has been upgraded. Happy Helming!
NAME: datadogrelease
LAST DEPLOYED: Tue Aug 17 13:50:12 2021
NAMESPACE: default
STATUS: deployed
REVISION: 3
TEST SUITE: None
NOTES:
Datadog agents are spinning up on each node in your cluster. After a few
minutes, you should see your agents starting in your event stream:
    https://app.datadoghq.com/event/stream
You disabled creation of Secret containing API key, therefore it is expected
that you create Secret named 'dd-secret' which includes a key called 'api-key' containing the API key.

The Datadog Agent is listening on port 8126 for APM service.

Sadly, try as I might, I could not get traces to flow.  I could see the Spike, but the service running via Dapr/Otel is an event watcher.

** the assumption was Datadog was installed in its own namespace***

checking the svc name, i figured out it is really “trace-svc. default.svc.cluster.local” not “trace-svc. datadog.svc.cluster.local”

builder@DESKTOP-QADGF36:~$ kubectl exec -it kuma-control-plane-685fc77455-f4hdq -n kuma-system -- /bin/sh
~ $ nslookup 10.43.164.51
Server: 10.43.0.10
Address: 10.43.0.10:53

51.164.43.10.in-addr.arpa name = trace-svc.default.svc.cluster.local

~ $ nslookup trace-svc.default.svc.cluster.local
Server: 10.43.0.10
Address: 10.43.0.10:53

*** Can't find trace-svc.default.svc.cluster.local: No answer

Name: trace-svc.default.svc.cluster.local
Address: 10.43.164.51

fixing

builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml.bak
builder@DESKTOP-QADGF36:~$ vi my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ diff my.default.mesh.yaml my.default.mesh.yaml.bak
48c48
< address: trace-svc.default.svc.cluster.local
---
> address: trace-svc.datadog.svc.cluster.local
builder@DESKTOP-QADGF36:~$ kubectl apply -f my.default.mesh.yaml
mesh.kuma.io/default configured
builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml | tail -n10
    enabledBackend: ca-1
  tracing:
    backends:
    - conf:
        address: trace-svc.default.svc.cluster.local
        port: 8126
      name: datadog-collector
      sampling: 100
      type: datadog
    defaultBackend: datadog-collector

But still no go…

Add traffic trace

builder@DESKTOP-QADGF36:~$ cat traffictrace.yaml
apiVersion: kuma.io/v1alpha1
kind: TrafficTrace
mesh: default
metadata:
  name: trace-all-traffic
spec:
  selectors:
  - match:
      kuma.io/service: '*'
  conf:
    backend: datadog-collector
builder@DESKTOP-QADGF36:~$ kubectl apply -f traffictrace.yaml
traffictrace.kuma.io/trace-all-traffic created

which we can see reflected in the GUI

Since this is clearly not working

$ kubectl get mesh default -o yaml | tail -n9
  tracing:
    backends:
    - conf:
        address: trace-svc.default.svc.cluster.local
        port: 8126
      name: datadog-collector
      sampling: 100
      type: datadog
    defaultBackend: datadog-collector

Via Open Telemetry

Let’s try using the OpenTelemetry endpoint that we setup to forward Dapr.io traces out to Datadog.

builder@DESKTOP-72D2D9T:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml
builder@DESKTOP-72D2D9T:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml.bak
builder@DESKTOP-72D2D9T:~$ vi my.default.mesh.yaml

$ diff -c my.default.mesh.yaml my.default.mesh.yaml.bak
*** my.default.mesh.yaml 2021-08-16 20:19:08.720000000 -0500
--- my.default.mesh.yaml.bak 2021-08-16 20:17:37.770000000 -0500
***************
***43,52****
        type: builtin
      enabledBackend: ca-1
    tracing:
- defaultBackend: jaeger-collector
      backends:
! - name: jaeger-collector
! type: zipkin
! sampling: 100.0
! conf:
! url: http://otel-collector.default.svc.cluster.local:9411/api/v2/spans
--- 43,53 ----
        type: builtin
      enabledBackend: ca-1
    tracing:
      backends:
! - conf:
! address: trace-svc.default.svc.cluster.local
! port: 8126
! name: datadog-collector
! sampling: 100
! type: datadog
! defaultBackend: datadog-collector

We also need to apply it to the traffic trace object

builder@DESKTOP-72D2D9T:~$ kubectl get traffictrace trace-all-traffic -o yaml > traffic.trace.yaml
builder@DESKTOP-72D2D9T:~$ kubectl get traffictrace trace-all-traffic -o yaml > traffic.trace.yaml.bak
builder@DESKTOP-72D2D9T:~$ vi traffic.trace.yaml
builder@DESKTOP-72D2D9T:~$ diff traffic.trace.yaml traffic.trace.yaml.bak
37c37
< backend: jaeger-collector
---
> backend: datadog-collector
$ kubectl apply -f traffic.trace.yaml
traffictrace.kuma.io/trace-all-traffic configured

doing a port-forward and hitting the website showed some trace data after a few moments

$ kubectl port-forward `kubectl get pods -l app=kuma-demo-frontend -n kuma-demo -o=jsonpath='{.items[0].metadata.name}'
` -n kuma-demo 8080:8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080

and a specific trace:

Cycling the pods gave me more details:

we can see it comes from the opentelemetry agent (otel):

which matches what we see in our traffic traces:

Logging Kuma Mesh logs to Datadog

Here we will use our DD API key to send logs via the Logstash endpoint.  In this example i’ll say my key is 123451234512345

builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml.bak
builder@DESKTOP-QADGF36:~$ kubectl get mesh default -o yaml > my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ vi my.default.mesh.yaml
builder@DESKTOP-QADGF36:~$ diff -c my.default.mesh.yaml my.default.mesh.yaml.bak
*** my.default.mesh.yaml 2021-08-17 13:34:03.395700495 -0500
--- my.default.mesh.yaml.bak 2021-08-17 13:30:52.935697887 -0500
***************
***35,59****
        sampling: 100
        type: datadog
      defaultBackend: datadog-collector
- logging:
- # TrafficLog policies may leave the `backend` field undefined.
- # In that case the logs will be forwarded into the `defaultBackend` of that Mesh.
- defaultBackend: logstash
- # List of logging backends that can be referred to by name
- # from TrafficLog policies of that Mesh.
- backends:
- - name: logstash
- # Use `format` field to adjust the access log format to your use case.
- format: '123451234512345 {"start_time": "%START_TIME%", "source": "%KUMA_SOURCE_SERVICE%", "destination": "%KUMA_DESTINATION_SERVICE%", "source_address": "%KUMA_SOURCE_ADDRESS_WITHOUT_PORT%", "destination_address": "%UPSTREAM_HOST%", "duration_millis": "%DURATION%", "bytes_received": "%BYTES_RECEIVED%", "bytes_sent": "%BYTES_SENT%"}'
- type: tcp
- # Use `config` field to co configure a TCP logging backend.
- conf:
- # Address of a log collector.
- address: intake.logs.datadoghq.com:10516
- - name: file
- type: file
- # Use `file` field to configure a file-based logging backend.
- conf:
- path: /tmp/access.log
- # When `format` field is omitted, the default access log format will be used.
-
--- 35,37 ----
builder@DESKTOP-QADGF36:~$ kubectl apply -f my.default.mesh.yaml
mesh.kuma.io/default configured

Now we can see Kuma logs (logs about/from the Kuma service) sent to Datadog:

and an error detail:

We can see logs from the app as well:

which comes from

$ kubectl describe service frontend -n kuma-demo
Name: frontend
Namespace: kuma-demo
Labels: <none>
Annotations: 8080.service.kuma.io/protocol: http
                   ingress.kubernetes.io/service-upstream: true
Selector: app=kuma-demo-frontend
Type: ClusterIP
IP Families: <none>
IP: 10.43.105.16
IPs: <none>
Port: http 8080/TCP
TargetPort: 8080/TCP
Endpoints: 10.42.2.200:8080
Session Affinity: None
Events: <none>

And we can see that matches the log

$ kubectl describe pod kuma-demo-app-6787b4f7f5-446px -n kuma-demo | grep IP | head -n1
IP: 10.42.2.200

and the other line (http://127.0.0.1:8080) was from my kubectl port-forward.

Summary

We first set up Kuma as a container then into our Kuberenetes cluster via the helm chart.  After testing basic Kuma Mesh functionality with a Demo App (and an identical non-instrumented Demo2 app) we moved forward with externalizing access to the Kuma Mesh Dashboard and securing it with basic auth via Nginx.

We examined adding logging, tracing and metrics using Prometheus and Grafana. Then worked through sending logs and traces from Kuma to Datadog (the latter we successfully solved through zipkin to an OpenTelemetry agent we use with Dapr on to Datadog).

Kuma is a pretty decent mesh that uses Spiffe under the covers.  We only scratched the surface on Kuma.  Kuma can provide circuit breakers, fault injections, rate limits and retries just to name a few.  We showed how it injects health checks but those can be modified and tweaked as well.

While I love open source, it’s always nice to see a commercial offering based on OS projects.  In this regards, Kong Mesh is a supported Enterprise instance of Kuma for those seeking a commercially supported version which you can install into AKS via the Azure Marketplace (but it’s still BYOL).

However, they hide the pricing and require a form email to sales.  Searching for Kong pricing lists mostly dog toys (which I also need, but that’s an aside).

Next steps I plan to pursue is setting up Kuma in a fresh AKS (as opposed to a ‘production’ on-prem k3s) and trying to join multi-site meshes.

service-mesh kuma datadog

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes