Skip to content

Commit

Permalink
Merge pull request #54 from secustor/add_unleash
Browse files Browse the repository at this point in the history
Prepare for 2nd workshop
  • Loading branch information
secustor committed Apr 27, 2022
2 parents 969a3a0 + 5459fe0 commit dea2482
Show file tree
Hide file tree
Showing 23 changed files with 566 additions and 38 deletions.
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
# OpenTelemetry Frontend to Backend Demo
This repo contains the example used for the `Vienna DevOps & Security` meetups.

This repo contains the example used for the `Vienna DevOps & Security` meetup.
The meetup series is build around a full example of a frontend, backend composed out of a Ingress controller, Kafka and
the corresponding consumer and producer

![Grafana trace example](./images/trace_example.png)


## Meetups
### OpenTelemetry: from frontend to backend
Slides: https://docs.google.com/presentation/d/1jPDH8Csv-Qle7Z-P7rFJgjOsBpgKeehPhjFPqTRET1Y/edit?usp=sharing

Requirements:
- [Kind](https://github.com/kubernetes-sigs/kind/issues) ( node-image >1.16.x )
- [Helmsman](https://github.com/Praqma/helmsman)
- Docker / Buildah ( with Docker alias )
### OpenTelemetry: How to debug user performance in your frontend
Slides: https://docs.google.com/presentation/d/1w1VhfGQPgCAPoT2VSB6KZlXZOXa_m5p8R-xzOzzL3hM/edit?usp=sharing

## Usage
Requirements:
- [Kind](https://github.com/kubernetes-sigs/kind/issues) ( node-image >1.16.x )
- [Helmsman](https://github.com/Praqma/helmsman)
- Docker / Buildah ( with Docker alias )

This demo deploys ingresses for `*.testing.com`.
To make it work you have to rewrite the DNS answers to localhost.

For Linux it has been as simple as adding this line to your network manager ( tested with Arch/Gnome )
`echo "address=/testing.com/127.0.0.1" > /etc/NetworkManager/dnsmasq.d/testDomains.conf`



## Usage
### Create Cluster
To create a kind cluster and add the kubecontext to your config run `make create-kind-cluster`

Expand All @@ -28,3 +37,4 @@ Build and upload the resulting images to your kind cluster

### Setup the environment
`make prepare-environment` will setup all applications in your cluster

3 changes: 2 additions & 1 deletion apps/snapshot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1"
"react-scripts": "3.4.1",
"unleash-proxy-client": "^2.0.0"
},
"scripts": {
"predeploy": "npm run build",
Expand Down
5 changes: 3 additions & 2 deletions apps/snapshot/src/App.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, { Component } from "react";
import React from "react";
import PhotoContextProvider from "./context/PhotoContext";
import { HashRouter, Route, Switch, Redirect } from "react-router-dom";
import Header from "./components/Header";
import Item from "./components/Item";
import Search from "./components/Search";
import NotFound from "./components/NotFound";
import {BaseOpenTelemetryComponent} from "@opentelemetry/plugin-react-load";

class App extends Component {
class App extends BaseOpenTelemetryComponent {
// Prevent page reload, clear input, set URL and push history on submit
handleSubmit = (e, history, searchInput) => {
e.preventDefault();
Expand Down
14 changes: 5 additions & 9 deletions apps/snapshot/src/context/PhotoContext.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import React, { createContext, useState } from "react";
import axios from "axios";
import { apiKey } from "../api/config";
import {tracer} from "../instrumentation";
import {SpanStatusCode, context, trace} from "@opentelemetry/api";
import {traceProvider} from "../instrumentation";
import {SpanStatusCode} from "@opentelemetry/api";

export const PhotoContext = createContext();

const PhotoContextProvider = props => {
const [images, setImages] = useState([]);
const [loading, setLoading] = useState(true);

const runSearchTracer = tracer
const runSearchTracer = traceProvider.getTracer('snapshot')


const runSearch = query => {
const testSpan = trace.getSpan(context.active())
console.log(`TestSpan: ${testSpan}`)
console.log(`Context:`,context.active())

runSearchTracer.startActiveSpan("search images",(span, query) => {
runSearchTracer.startActiveSpan("query images", span => { // https://github.com/open-telemetry/opentelemetry-js/issues/1923
runSearchTracer.startActiveSpan("search images",span => {
runSearchTracer.startActiveSpan("query images",{}, span => { // https://github.com/open-telemetry/opentelemetry-js/issues/1923
axios
.get(
`https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&format=json&nojsoncallback=1`
Expand Down
47 changes: 29 additions & 18 deletions apps/snapshot/src/instrumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,21 @@ import * as autoInstrumentationAPI from "@opentelemetry/auto-instrumentations-we
import * as api from "@opentelemetry/api";
import {BaseOpenTelemetryComponent} from '@opentelemetry/plugin-react-load';
import {SemanticResourceAttributes} from "@opentelemetry/semantic-conventions";
import {getUnleash} from "./unleash";
import {DiagConsoleLogger} from "@opentelemetry/api";


/* Set Global Propagator */
// support jaeger, W3C and W3C Baggage propagation
const propagator = new CompositePropagator({propagators: [new JaegerPropagator(), new W3CTraceContextPropagator(), new W3CBaggagePropagator()]})
api.propagation.setGlobalPropagator(propagator);
const traceProvider = new WebTracerProvider({
export const traceProvider = new WebTracerProvider({
resource: new Resource({
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/README.md#semantic-attributes-with-sdk-provided-default-value
[SemanticResourceAttributes.SERVICE_NAME]: "snapshot",
[SemanticResourceAttributes.SERVICE_NAMESPACE]: "example.meetup",
[SemanticResourceAttributes.SERVICE_VERSION]: "0.1.0",
[SemanticResourceAttributes.PROCESS_OWNER]: "1233"
}),
// this is the same as directly using the AlwaysOnSampler, but this shows how to respect the parent.
// https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-core#parentbased-sampler
Expand All @@ -47,21 +50,29 @@ const traceProvider = new WebTracerProvider({
// add processors
traceProvider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));

const exporter = new OTLPTraceExporter({
url: "http://collector.testing.com/v1/traces", // url is optional and can be omitted - default is http://localhost:55681/v1/traces
headers: {}, // an optional object containing custom headers to be sent with each request
concurrencyLimit: 10, // an optional limit on pending requests
});
traceProvider.addSpanProcessor(new BatchSpanProcessor(exporter, {
// The maximum queue size. After the size is reached spans are dropped.
maxQueueSize: 100,
// The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
maxExportBatchSize: 10,
// The interval between two consecutive exports
scheduledDelayMillis: 500,
// How long the export can run before it is cancelled
exportTimeoutMillis: 30000,
}));
getUnleash().then(value => {
const exporter = new OTLPTraceExporter({
url: "http://collector.testing.com/v1/traces", // url is optional and can be omitted - default is http://localhost:55681/v1/traces
headers: {}, // an optional object containing custom headers to be sent with each request
concurrencyLimit: 10, // an optional limit on pending requests
});

const enabled = value.isEnabled("opentelemetry")
console.log(`opentelemetry feature flag: ${enabled}`)

if (enabled) {
traceProvider.addSpanProcessor(new BatchSpanProcessor(exporter, {
// The maximum queue size. After the size is reached spans are dropped.
maxQueueSize: 100,
// The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
maxExportBatchSize: 20,
// The interval between two consecutive exports
scheduledDelayMillis: 500,
// How long the export can run before it is cancelled
exportTimeoutMillis: 30000,
}));
}
}).catch(reason => console.log(reason))


traceProvider.register({
Expand All @@ -83,9 +94,9 @@ registerInstrumentations({
],

});
export let tracer = traceProvider.getTracer("snapshot");

BaseOpenTelemetryComponent.setTracer("snapshot") // set global tracer for react
BaseOpenTelemetryComponent.setLogger(traceProvider.logger)
BaseOpenTelemetryComponent.setLogger(new DiagConsoleLogger())


export default function TraceProvider({children}) {
Expand Down
18 changes: 18 additions & 0 deletions apps/snapshot/src/unleash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { UnleashClient } from 'unleash-proxy-client';

export async function getUnleash() {
const unleash = new UnleashClient({
url: 'http://unleash-proxy.testing.com/proxy',
clientKey: 'clientKeyslkfsdklfkslfd',
appName: 'default',
environment: 'development'
});

// Used to set the context fields, shared with the Unleash Proxy
await unleash.updateContext({ userId: '1233' });

// Start the background polling
await unleash.start();

return unleash
}
18 changes: 18 additions & 0 deletions apps/snapshot/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10188,6 +10188,11 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=

tiny-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==

tiny-invariant@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
Expand Down Expand Up @@ -10413,6 +10418,14 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==

unleash-proxy-client@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/unleash-proxy-client/-/unleash-proxy-client-2.0.0.tgz#2668f949368805b4d949fdd83b121fa15daf23e9"
integrity sha512-7poi3txbSGjFG0e8QrIn4pGzw1de7xfA5YSLqu3abLs95wZ2gzuC/YTvzlt1Faol3PHTj9Tq0ov02Q7Z6nhE9g==
dependencies:
tiny-emitter "^2.1.0"
uuid "^8.3.2"

[email protected], unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
Expand Down Expand Up @@ -10530,6 +10543,11 @@ uuid@^3.0.1, uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==

uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==

v8-compile-cache@^2.0.3:
version "2.1.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
Expand Down
18 changes: 18 additions & 0 deletions deploy/kube-prometheus/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,24 @@ grafana:
# - "k8s.pod.name"
# lokiSearch: true

- name: Jaeger
access: proxy
editable: false
orgId: 1
type: jaeger
url: http://jaeger-query
version: 1
uid: default_jaeger
jsonData:
nodeGraph:
enabled: true
tracesToLogs:
datasourceUid: default_loki
# tags:
# - "k8s.namespace.name"
# - "k8s.pod.name"
# lokiSearch: true

serviceMonitor:
enabled: true
additionalLabels:
Expand Down
7 changes: 7 additions & 0 deletions deploy/setup.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ apps:
version: 2.6.0
valuesFiles:
- ./unleash/values.yaml
unleash-proxy:
enabled: true
namespace: unleash
chart: ./unleash-proxy
version: 0.1.0
valuesFiles:
- ./unleash-proxy/overwrites.yaml
jaeger:
enabled: true
namespace: monitoring
Expand Down
23 changes: 23 additions & 0 deletions deploy/unleash-proxy/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
24 changes: 24 additions & 0 deletions deploy/unleash-proxy/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: unleash-proxy
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.8.1"
18 changes: 18 additions & 0 deletions deploy/unleash-proxy/overwrites.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
unleash:
url: "http://unleash:4242/api"
apiToken: "*:development.354241ff38f11e42dff3de8c478b18fc07896df0104853afd0750a4e"
clientKeys: ["clientKeyslkfsdklfkslfd"]

ingress:
enabled: true

annotations:
nginx.ingress.kubernetes.io/enable-opentracing: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,traceparent,tracestate,Uber-Trace-Id

hosts:
- host: unleash-proxy.testing.com
paths:
- path: /
pathType: ImplementationSpecific
22 changes: 22 additions & 0 deletions deploy/unleash-proxy/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "unleash-proxy.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "unleash-proxy.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "unleash-proxy.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "unleash-proxy.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
Loading

0 comments on commit dea2482

Please sign in to comment.