diff --git a/.gitignore b/.gitignore index cce1ca14..63f48943 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ erl_crash.dump /.elixir_ls/ *~ *.orig + +perf_tests/src/.idea/ diff --git a/perf_tests/README.md b/perf_tests/README.md new file mode 100644 index 00000000..7213de10 --- /dev/null +++ b/perf_tests/README.md @@ -0,0 +1,58 @@ +## Requirements + +* `docker` +* `docker-compose` +* `kubectl` (>= v1.11) +* `timeout` +* `helm` (>= v3) + +## Run 1 + +Send one message, send 1M ignored messages, send one message again. + +### ENV Variables - Client +```ini +CLIENTS=1 +TIMEOUT=10m +WAIT=30s +RIG_HOST=rig +``` + +### Start +```bash +./start_run1.sh +``` + +## Run 2 + +Send 100k messages to 100 clients. + +### ENV Variables - Client +```ini +CLIENTS=100 +TIMEOUT=30m +WAIT=30s +RIG_HOST=rig +``` + +### Start +```bash +./start_run2.sh +``` + +## Run 6 + +Send 1000 messages in 100 different event types to 1000 clients. + +### ENV Variables - Client +```ini +CLIENTS=1000 +TIMEOUT=1h +WAIT=30s +RIG_HOST=rig +``` + +### Start +```bash +./start_run6.sh +``` diff --git a/perf_tests/datasource-helm.yaml b/perf_tests/datasource-helm.yaml new file mode 100644 index 00000000..a9c7e33a --- /dev/null +++ b/perf_tests/datasource-helm.yaml @@ -0,0 +1,968 @@ +datasources: + datasources.yaml: + apiVersion: 1 + datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus-server.default.svc.cluster.local + isDefault: true + +dashboardProviders: + dashboardproviders.yaml: + apiVersion: 1 + providers: + - name: "default" + orgId: 1 + folder: "" + type: file + disableDeletion: false + editable: true + options: + path: /var/lib/grafana/dashboards/default + +dashboards: + default: + rig-dashboard: + json: | + { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fill": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "container_memory_working_set_bytes{container=\"reactive-interaction-gateway\"}", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "fill": 1, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 15, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate (container_cpu_usage_seconds_total{container=\"reactive-interaction-gateway\"}[5m])", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 12, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(rig_proxy_requests_total[30s])*20", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 16 + }, + "id": 10, + "panels": [], + "title": "Total numbers", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorPostfix": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 17 + }, + "id": 4, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "pluginVersion": "6.2.4", + "postfix": " req", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rig_proxy_requests_total{target=\"http\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "legendFormat": "", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "HTTP target", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 17 + }, + "id": 5, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": " req", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rig_proxy_requests_total{target=\"kafka\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Kafka target", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 17 + }, + "id": 6, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": " req", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rig_proxy_requests_total{method=\"POST\",path=\"/api/rs/register\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Register POST Async", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 17 + }, + "id": 16, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": " req", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rig_proxy_requests_total{method=\"POST\",path=\"/api/rs/register/sync\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Register POST Sync", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorPostfix": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 2, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "pluginVersion": "6.4.2", + "postfix": " req", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rig_proxy_requests_total)", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "All HTTP requests", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "percentunit", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 25 + }, + "id": 8, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": false, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "(sum(rig_proxy_requests_total{status=\"ok\"})/sum(rig_proxy_requests_total))", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "10,30", + "timeFrom": null, + "timeShift": null, + "title": "HTTP Success Rate", + "transparent": true, + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Prometheus", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 25 + }, + "id": 7, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": " req", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(rig_proxy_requests_total{method=\"POST\",path=\"/api/kafka\"})", + "format": "time_series", + "instant": false, + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Kafka POST", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + } + ], + "refresh": "10s", + "schemaVersion": 18, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "RIG", + "uid": "Vk7MWRTZk", + "version": 1 + } \ No newline at end of file diff --git a/perf_tests/kafka/chart/values.yml b/perf_tests/kafka/chart/values.yml new file mode 100644 index 00000000..5f2f1021 --- /dev/null +++ b/perf_tests/kafka/chart/values.yml @@ -0,0 +1,12 @@ +replicas: 1 + +external: + servicePort: 9092 + +persistence: + enabled: false + +configurationOverrides: + "offsets.topic.replication.factor": 1 + "auto.create.topics.enable": true + "num.partitions": 4 \ No newline at end of file diff --git a/perf_tests/rig/chart/.helmignore b/perf_tests/rig/chart/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/rig/chart/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/rig/chart/Chart.yaml b/perf_tests/rig/chart/Chart.yaml new file mode 100644 index 00000000..fff49444 --- /dev/null +++ b/perf_tests/rig/chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: reactive-interaction-gateway +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/rig/chart/templates/_helpers.tpl b/perf_tests/rig/chart/templates/_helpers.tpl new file mode 100644 index 00000000..50dc6bf3 --- /dev/null +++ b/perf_tests/rig/chart/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "reactive-interaction-gateway.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "reactive-interaction-gateway.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "reactive-interaction-gateway.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/rig/chart/templates/deployment.yaml b/perf_tests/rig/chart/templates/deployment.yaml new file mode 100644 index 00000000..696325ab --- /dev/null +++ b/perf_tests/rig/chart/templates/deployment.yaml @@ -0,0 +1,45 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "reactive-interaction-gateway.name" . }} + labels: + app: {{ template "reactive-interaction-gateway.name" . }} + chart: {{ template "reactive-interaction-gateway.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "reactive-interaction-gateway.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "reactive-interaction-gateway.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + - name: NODE_HOST + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: API_PORT + value: {{ .Values.apiPort | quote }} + - name: INBOUND_PORT + value: {{ .Values.inboundPort | quote }} + ports: + - name: api-port + containerPort: {{ .Values.apiPort }} + protocol: TCP + - name: inbound-port + containerPort: {{ .Values.inboundPort }} + protocol: TCP \ No newline at end of file diff --git a/perf_tests/rig/chart/templates/service.yaml b/perf_tests/rig/chart/templates/service.yaml new file mode 100644 index 00000000..86a66bde --- /dev/null +++ b/perf_tests/rig/chart/templates/service.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "reactive-interaction-gateway.name" . }} + labels: + app: {{ template "reactive-interaction-gateway.name" . }} + chart: {{ template "reactive-interaction-gateway.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.inboundPort }} + targetPort: {{ .Values.inboundPort }} + protocol: TCP + name: proxy + - port: {{ .Values.apiPort }} + targetPort: {{ .Values.apiPort }} + protocol: TCP + name: internal + selector: + app: {{ template "reactive-interaction-gateway.name" . }} + release: {{ .Release.Name }} \ No newline at end of file diff --git a/perf_tests/rig/chart/templates/service_headless.yaml b/perf_tests/rig/chart/templates/service_headless.yaml new file mode 100644 index 00000000..0f894ef0 --- /dev/null +++ b/perf_tests/rig/chart/templates/service_headless.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "reactive-interaction-gateway.name" . }}-headless + labels: + app: {{ template "reactive-interaction-gateway.name" . }}-headless + chart: {{ template "reactive-interaction-gateway.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.service_headless.type }} + clusterIP: {{ .Values.service_headless.clusterIP }} + ports: + - port: {{ .Values.inboundPort }} + targetPort: {{ .Values.inboundPort }} + protocol: TCP + name: proxy + - port: {{ .Values.apiPort }} + targetPort: {{ .Values.apiPort }} + protocol: TCP + name: internal + selector: + app: {{ template "reactive-interaction-gateway.name" . }} + release: {{ .Release.Name }} \ No newline at end of file diff --git a/perf_tests/rig/chart/values.yaml b/perf_tests/rig/chart/values.yaml new file mode 100644 index 00000000..2f0bf7d6 --- /dev/null +++ b/perf_tests/rig/chart/values.yaml @@ -0,0 +1,25 @@ +replicaCount: 1 + +image: + repository: accenture/reactive-interaction-gateway + tag: latest + pullPolicy: Always + +apiPort: 4010 +inboundPort: 4000 + +service: + type: LoadBalancer + +service_headless: + type: ClusterIP + clusterIP: None + +deployment: + env: + DISCOVERY_TYPE: dns + DNS_NAME: reactive-interaction-gateway-headless.default.svc.cluster.local + LOG_LEVEL: error + KAFKA_BROKERS: kafka:9092 + API_HTTP_PORT: 4010 + INBOUND_PORT: 4000 diff --git a/perf_tests/run_all.sh b/perf_tests/run_all.sh new file mode 100755 index 00000000..199f74dc --- /dev/null +++ b/perf_tests/run_all.sh @@ -0,0 +1,13 @@ +helm install prometheus stable/prometheus --set server.global.scrape_interval="10s" +helm install grafana stable/grafana -f datasource-helm.yaml + + +echo "Grafana password: $(kubectl get secret grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo)" +echo "Prometheus: kubectl port-forward $(kubectl get pods -l 'app=prometheus,component=server' -o jsonpath='{.items[0].metadata.name}') 9090" +echo "Grafana: kubectl port-forward $(kubectl get pods -l 'app=grafana,release=grafana' -o jsonpath='{.items[0].metadata.name}') 3000" + +./start_run1.sh +#./start_run2.sh +#./start_run6.sh + +#grafana and prometheus have to be uninstalled \ No newline at end of file diff --git a/perf_tests/src/.dockerignore b/perf_tests/src/.dockerignore new file mode 100644 index 00000000..b37855d2 --- /dev/null +++ b/perf_tests/src/.dockerignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +*.pyc diff --git a/perf_tests/src/client.go b/perf_tests/src/client.go new file mode 100644 index 00000000..8c287c32 --- /dev/null +++ b/perf_tests/src/client.go @@ -0,0 +1,105 @@ +package loadtest + +import ( + "errors" + "fmt" + "io/ioutil" + "net/http" + "os" + "strconv" + "strings" + "time" + + "github.com/r3labs/sse" +) + +// Receive & time measurement loop +func Receive(i, amount, printSteps int, sub string, ctrl chan bool) (float64, error) { + var start time.Time + + count := 1 + events := make(chan *sse.Event) + client := sse.NewClient("http://" + os.Getenv("RIG_HOST") + ":4000/_rig/v1/connection/sse?subscriptions=[{\"eventType\":\"" + sub + "\"}]") + + client.SubscribeChanRaw(events) + + ctrl <- true + + for event := range events { + if string(event.Event) != sub { + continue + } + + count++ + + if count == 2 { + start = time.Now() + } + + if count%printSteps == 0 { + go fmt.Println("Count:", count, "\t| Thread:", i, "\t| Topic:", sub) + } + + if count == amount { + elapsed := time.Since(start).Seconds() + return elapsed, nil + } + } + + return 0, errors.New("") +} + +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, "OK") +} + +// StatusOk serves HTTP server to signal all is well +func StatusOk() { + http.HandleFunc("/", handler) + http.ListenAndServe(":9999", nil) +} + +// GetEnv Checks env and returns amount of goroutines to start +func GetEnv() (int, string) { + + if os.Getenv("RIG_HOST") == "" { + fmt.Println("RIG_HOST environment variable required!") + os.Exit(1) + } + + if os.Getenv("CLIENTS") == "" { + fmt.Println("CLIENTS environment variable required!") + os.Exit(1) + } + + goroutines, err := strconv.Atoi(os.Getenv("CLIENTS")) + + if err != nil { + fmt.Println("Error while parsing CLIENTS!") + os.Exit(1) + } + + if os.Getenv("TIMEOUT") == "" { + fmt.Println("TIMEOUT environment variable required!") + os.Exit(1) + } + + fmt.Println("Waiting until RIG goes online...") + + for { + resp, err := http.Get("http://" + os.Getenv("RIG_HOST") + ":4010/health") + + if err == nil { + body, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + + if err == nil { + if strings.Contains(string(body), "OK") { + break + } + } + } + } + + return goroutines, os.Getenv("TIMEOUT") +} diff --git a/perf_tests/src/load.py b/perf_tests/src/load.py new file mode 100644 index 00000000..d054badf --- /dev/null +++ b/perf_tests/src/load.py @@ -0,0 +1,53 @@ +import locale +import os +import uuid + +from confluent_kafka import Producer +from confluent_kafka.admin import AdminClient, NewTopic + +a = AdminClient({"bootstrap.servers": os.getenv("KAFKA_HOST", "localhost:9092")}) + +def delete_topic(): + for _, f in a.delete_topics(["rig"]).items(): + while not f.done(): + print("", end="") + print("Topic deleted...") + +def recreate_topic(): + for _, f in a.create_topics([NewTopic("rig", 8, replication_factor=1)]).items(): + while not f.done(): + print("", end="") + print("Topic recreated...") + + +def clear_topic(): + delete_topic() + recreate_topic() + +p = Producer({"bootstrap.servers": os.getenv("KAFKA_HOST", "localhost:9092"), "message.max.bytes": 2048}) + +# This is exactly 1 kB or 1000 bytes +payload = """ +{"specversion": "0.2", "type": "???", "id": "###", "data": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Dui vivamus arcu felis bibendum ut tristique et. +Quam nulla porttitor massa id neque aliquam vestibulum morbi. +Vestibulum sed arcu non odio euismod lacinia at quis. +Ac auctor augue mauris augue neque. +Purus gravida quis blandit turpis cursus in hac habitasse platea. +Vulputate eu scelerisque fes imperdiet proin. +Varius morbi enim nunc faucibus a pellentesque. +Nec sagittis aliquam aliquam malesuada bibendum arcu. +Ornare aenean euismod elementum nisi quis eleifend quam adipiscing. +Amet massa vitae tortor condimentum lacinia quis vel eros. +Nulla aliquet enim tortor at auctor urna nunc id.proin. +Varius morbi enim nunc faucibus a pellentesque. +Nec sagittis aliquam malesuada bibendum arcu +Diam ut venenatis tellus in metus vulputate", "source": "tutorial"} +""" + +def produce(p, topic, payload, etype): + p.produce(topic, payload.replace("???", etype).replace("###", str(uuid.uuid1())).encode("utf-8")) + +def print_progress(i, times = 1000): + num = locale.format("%d", ((i + 1) * times), grouping=True) + print(f"Loaded: {num}") \ No newline at end of file diff --git a/perf_tests/src/run1/chart/client/.helmignore b/perf_tests/src/run1/chart/client/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/src/run1/chart/client/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/src/run1/chart/client/Chart.yaml b/perf_tests/src/run1/chart/client/Chart.yaml new file mode 100644 index 00000000..05a803a0 --- /dev/null +++ b/perf_tests/src/run1/chart/client/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart creating service and deployment +name: client-run-1 +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/src/run1/chart/client/templates/_helpers.tpl b/perf_tests/src/run1/chart/client/templates/_helpers.tpl new file mode 100644 index 00000000..be4e3343 --- /dev/null +++ b/perf_tests/src/run1/chart/client/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "helm-charts.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helm-charts.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helm-charts.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/src/run1/chart/client/templates/deployment.yaml b/perf_tests/src/run1/chart/client/templates/deployment.yaml new file mode 100644 index 00000000..aafb8326 --- /dev/null +++ b/perf_tests/src/run1/chart/client/templates/deployment.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-deployment + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + template: + metadata: + labels: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ or (.Values.deployment.port) (.Values.service.port) }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + restartPolicy: Never \ No newline at end of file diff --git a/perf_tests/src/run1/chart/client/templates/service.yaml b/perf_tests/src/run1/chart/client/templates/service.yaml new file mode 100644 index 00000000..1de68252 --- /dev/null +++ b/perf_tests/src/run1/chart/client/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + ports: + - port: {{ .Values.service.port }} + name: http + targetPort: {{ .Values.service.targetPort }} + selector: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + type: {{ .Values.service.type }} \ No newline at end of file diff --git a/perf_tests/src/run1/chart/client/values.yaml b/perf_tests/src/run1/chart/client/values.yaml new file mode 100644 index 00000000..5f9df8aa --- /dev/null +++ b/perf_tests/src/run1/chart/client/values.yaml @@ -0,0 +1,18 @@ +replicaCount: 1 + +image: + repository: azer0s/rig-perf-test-run1-clients + tag: latest + pullPolicy: Always # needed for local Kubernetes cluster (docker desktop) + +service: + port: 9999 + targetPort: 9999 + type: ClusterIP + +deployment: + port: 9999 + env: + CLIENTS: 1 + TIMEOUT: 30m + RIG_HOST: reactive-interaction-gateway \ No newline at end of file diff --git a/perf_tests/src/run1/chart/loader/.helmignore b/perf_tests/src/run1/chart/loader/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/src/run1/chart/loader/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/src/run1/chart/loader/Chart.yaml b/perf_tests/src/run1/chart/loader/Chart.yaml new file mode 100644 index 00000000..81a734d4 --- /dev/null +++ b/perf_tests/src/run1/chart/loader/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart creating service and deployment +name: loader-run-1 +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/src/run1/chart/loader/templates/_helpers.tpl b/perf_tests/src/run1/chart/loader/templates/_helpers.tpl new file mode 100644 index 00000000..be4e3343 --- /dev/null +++ b/perf_tests/src/run1/chart/loader/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "helm-charts.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helm-charts.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helm-charts.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/src/run1/chart/loader/templates/deployment.yaml b/perf_tests/src/run1/chart/loader/templates/deployment.yaml new file mode 100644 index 00000000..edae8fe6 --- /dev/null +++ b/perf_tests/src/run1/chart/loader/templates/deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-deployment + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + template: + metadata: + labels: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + restartPolicy: Never \ No newline at end of file diff --git a/perf_tests/src/run1/chart/loader/values.yaml b/perf_tests/src/run1/chart/loader/values.yaml new file mode 100644 index 00000000..71b68af5 --- /dev/null +++ b/perf_tests/src/run1/chart/loader/values.yaml @@ -0,0 +1,9 @@ +image: + repository: azer0s/rig-perf-test-run1-loader + tag: latest + pullPolicy: Always # needed for local Kubernetes cluster (docker desktop) + +deployment: + env: + KAFKA_HOST: kafka:9092 + CLIENTS: run1-clients \ No newline at end of file diff --git a/perf_tests/src/run1/client.Dockerfile b/perf_tests/src/run1/client.Dockerfile new file mode 100644 index 00000000..9a97f034 --- /dev/null +++ b/perf_tests/src/run1/client.Dockerfile @@ -0,0 +1,5 @@ +FROM golang:latest +COPY . . +RUN go get github.com/r3labs/sse +RUN go build run1/client.go +CMD ["./client"] diff --git a/perf_tests/src/run1/client.go b/perf_tests/src/run1/client.go new file mode 100644 index 00000000..d0fcea4e --- /dev/null +++ b/perf_tests/src/run1/client.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "sync" + + loadtest ".." +) + +func main() { + var wg sync.WaitGroup + var mutex = &sync.Mutex{} + + goroutines, timeout := loadtest.GetEnv() + + ctrl := make(chan bool) + + fmt.Println("Starting", goroutines, "goroutines") + + for i := 1; i <= goroutines; i++ { + wg.Add(1) + go func(i int) { + elapsed, _ := loadtest.Receive(0, 3, 1, "to_be_delivered", ctrl) + + mutex.Lock() + fmt.Println("Thread", i, "finished in", elapsed, "s") + mutex.Unlock() + + wg.Done() + }(i) + <-ctrl + } + + go loadtest.StatusOk() + + fmt.Println("Waiting for goroutines to finish...") + + if loadtest.WaitTimeout(&wg, timeout) { + fmt.Println("Timed out waiting for wait group") + } else { + fmt.Println("Wait group finished") + } +} diff --git a/perf_tests/src/run1/docker-compose.yml b/perf_tests/src/run1/docker-compose.yml new file mode 100644 index 00000000..00c9eeee --- /dev/null +++ b/perf_tests/src/run1/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3' + +services: + zk: + image: confluentinc/cp-zookeeper:5.0.0 + container_name: zk + ports: + - 2181:2181 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + restart: on-failure:5 + kafka: + image: confluentinc/cp-kafka:5.0.0 + container_name: kafka + ports: + - 9092:9092 + - 9094:9094 + environment: + KAFKA_ZOOKEEPER_CONNECT: zk:2181 + KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9092,OUTSIDE://localhost:9094 + KAFKA_LISTENERS: INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9094 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" + KAFKA_NUM_PARTITIONS: 4 + restart: on-failure:5 + clients: + ports: + - 9999:9999 + build: + context: .. + dockerfile: run1/client.Dockerfile + depends_on: + - rig + environment: + CLIENTS: 1 + TIMEOUT: 30m + RIG_HOST: rig + loader: + build: + context: .. + dockerfile: run1/loader.Dockerfile + depends_on: + - clients + environment: + KAFKA_HOST: kafka:9092 + CLIENTS: clients + rig: + image: "accenture/reactive-interaction-gateway:latest" + depends_on: + - zk + - kafka + environment: + KAFKA_BROKERS: kafka:9092 + LOG_LEVEL: error diff --git a/perf_tests/src/run1/loader.Dockerfile b/perf_tests/src/run1/loader.Dockerfile new file mode 100644 index 00000000..c66b36ee --- /dev/null +++ b/perf_tests/src/run1/loader.Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:18.04 +COPY run1/loader.py loader.py +COPY run1/start_loader.sh start_loader.sh +COPY wait.sh wait.sh +COPY load.py load.py +RUN apt-get update -y +RUN apt-get install librdkafka-dev python3 python3-pip curl -y +RUN pip3 install --upgrade pip +RUN pip3 install confluent-kafka +CMD [ "sh", "start_loader.sh" ] \ No newline at end of file diff --git a/perf_tests/src/run1/loader.py b/perf_tests/src/run1/loader.py new file mode 100644 index 00000000..3fd61108 --- /dev/null +++ b/perf_tests/src/run1/loader.py @@ -0,0 +1,21 @@ +import sys +import load as l +import time + +def load_tbd(): + print("Loading to_be_delivered message...") + l.produce(l.p, "rig", l.payload, "to_be_delivered") + l.p.flush() + +def load(): + start = time.time() + + for i in range(100): + for _ in range(1000): + l.produce(l.p, "rig", l.payload, "ignored") + l.p.flush() + + l.print_progress(i) + + end = time.time() + print(end - start) \ No newline at end of file diff --git a/perf_tests/src/run1/start_k8s.sh b/perf_tests/src/run1/start_k8s.sh new file mode 100755 index 00000000..76fdbe36 --- /dev/null +++ b/perf_tests/src/run1/start_k8s.sh @@ -0,0 +1,2 @@ +helm install run1-clients ./chart/client +helm install run1-loader ./chart/loader \ No newline at end of file diff --git a/perf_tests/src/run1/start_loader.sh b/perf_tests/src/run1/start_loader.sh new file mode 100755 index 00000000..83d2f777 --- /dev/null +++ b/perf_tests/src/run1/start_loader.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -m + +bash wait.sh + +python3 -c 'import loader as run1_loader; run1_loader.load_tbd()' + +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +python3 -c 'import loader as run1_loader; run1_loader.load()' & +wait + +python3 -c 'import loader as run1_loader; run1_loader.load_tbd()' diff --git a/perf_tests/src/run2/chart/client/.helmignore b/perf_tests/src/run2/chart/client/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/src/run2/chart/client/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/src/run2/chart/client/Chart.yaml b/perf_tests/src/run2/chart/client/Chart.yaml new file mode 100644 index 00000000..42e6bd0f --- /dev/null +++ b/perf_tests/src/run2/chart/client/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart creating service and deployment +name: client-run-2 +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/src/run2/chart/client/templates/_helpers.tpl b/perf_tests/src/run2/chart/client/templates/_helpers.tpl new file mode 100644 index 00000000..be4e3343 --- /dev/null +++ b/perf_tests/src/run2/chart/client/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "helm-charts.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helm-charts.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helm-charts.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/src/run2/chart/client/templates/deployment.yaml b/perf_tests/src/run2/chart/client/templates/deployment.yaml new file mode 100644 index 00000000..aafb8326 --- /dev/null +++ b/perf_tests/src/run2/chart/client/templates/deployment.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-deployment + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + template: + metadata: + labels: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ or (.Values.deployment.port) (.Values.service.port) }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + restartPolicy: Never \ No newline at end of file diff --git a/perf_tests/src/run2/chart/client/templates/service.yaml b/perf_tests/src/run2/chart/client/templates/service.yaml new file mode 100644 index 00000000..1de68252 --- /dev/null +++ b/perf_tests/src/run2/chart/client/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + ports: + - port: {{ .Values.service.port }} + name: http + targetPort: {{ .Values.service.targetPort }} + selector: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + type: {{ .Values.service.type }} \ No newline at end of file diff --git a/perf_tests/src/run2/chart/client/values.yaml b/perf_tests/src/run2/chart/client/values.yaml new file mode 100644 index 00000000..182505fa --- /dev/null +++ b/perf_tests/src/run2/chart/client/values.yaml @@ -0,0 +1,18 @@ +replicaCount: 1 + +image: + repository: run2_clients + tag: latest + pullPolicy: Never # needed for local Kubernetes cluster (docker desktop) + +service: + port: 9999 + targetPort: 9999 + type: ClusterIP + +deployment: + port: 9999 + env: + CLIENTS: 1 + TIMEOUT: 30m + RIG_HOST: reactive-interaction-gateway \ No newline at end of file diff --git a/perf_tests/src/run2/chart/loader/.helmignore b/perf_tests/src/run2/chart/loader/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/src/run2/chart/loader/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/src/run2/chart/loader/Chart.yaml b/perf_tests/src/run2/chart/loader/Chart.yaml new file mode 100644 index 00000000..099d857c --- /dev/null +++ b/perf_tests/src/run2/chart/loader/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart creating service and deployment +name: loader-run-2 +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/src/run2/chart/loader/templates/_helpers.tpl b/perf_tests/src/run2/chart/loader/templates/_helpers.tpl new file mode 100644 index 00000000..be4e3343 --- /dev/null +++ b/perf_tests/src/run2/chart/loader/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "helm-charts.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helm-charts.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helm-charts.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/src/run2/chart/loader/templates/deployment.yaml b/perf_tests/src/run2/chart/loader/templates/deployment.yaml new file mode 100644 index 00000000..edae8fe6 --- /dev/null +++ b/perf_tests/src/run2/chart/loader/templates/deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-deployment + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + template: + metadata: + labels: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + restartPolicy: Never \ No newline at end of file diff --git a/perf_tests/src/run2/chart/loader/values.yaml b/perf_tests/src/run2/chart/loader/values.yaml new file mode 100644 index 00000000..eed8e0b6 --- /dev/null +++ b/perf_tests/src/run2/chart/loader/values.yaml @@ -0,0 +1,9 @@ +image: + repository: run2_loader + tag: latest + pullPolicy: Never # needed for local Kubernetes cluster (docker desktop) + +deployment: + env: + KAFKA_HOST: kafka:9092 + CLIENTS: run2-clients \ No newline at end of file diff --git a/perf_tests/src/run2/client.Dockerfile b/perf_tests/src/run2/client.Dockerfile new file mode 100644 index 00000000..d0a5f744 --- /dev/null +++ b/perf_tests/src/run2/client.Dockerfile @@ -0,0 +1,5 @@ +FROM golang:latest +COPY . . +RUN go get github.com/r3labs/sse +RUN go build run2/client.go +CMD ["./client"] diff --git a/perf_tests/src/run2/client.go b/perf_tests/src/run2/client.go new file mode 100644 index 00000000..d48d5b49 --- /dev/null +++ b/perf_tests/src/run2/client.go @@ -0,0 +1,43 @@ +package main + +import ( + "fmt" + "sync" + + loadtest ".." +) + +func main() { + var wg sync.WaitGroup + var mutex = &sync.Mutex{} + + goroutines, timeout := loadtest.GetEnv() + + ctrl := make(chan bool) + + fmt.Println("Starting", goroutines, "goroutines") + + for i := 1; i <= goroutines; i++ { + wg.Add(1) + go func(i int) { + elapsed, _ := loadtest.Receive(i, 100000, 1000, "chatroom_message", ctrl) + + mutex.Lock() + fmt.Println("Thread", i, "finished in", elapsed, "s") + mutex.Unlock() + + wg.Done() + }(i) + <-ctrl + } + + go loadtest.StatusOk() + + fmt.Println("Waiting for goroutines to finish...") + + if loadtest.WaitTimeout(&wg, timeout) { + fmt.Println("Timed out waiting for wait group") + } else { + fmt.Println("Wait group finished") + } +} diff --git a/perf_tests/src/run2/docker-compose.yml b/perf_tests/src/run2/docker-compose.yml new file mode 100644 index 00000000..9068044a --- /dev/null +++ b/perf_tests/src/run2/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3' + +services: + zk: + image: confluentinc/cp-zookeeper:5.0.0 + container_name: zk + ports: + - 2181:2181 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + restart: on-failure:5 + kafka: + image: confluentinc/cp-kafka:5.0.0 + container_name: kafka + ports: + - 9092:9092 + - 9094:9094 + environment: + KAFKA_ZOOKEEPER_CONNECT: zk:2181 + KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9092,OUTSIDE://localhost:9094 + KAFKA_LISTENERS: INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9094 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" + KAFKA_NUM_PARTITIONS: 4 + restart: on-failure:5 + clients: + ports: + - 9999:9999 + build: + context: .. + dockerfile: run2/client.Dockerfile + depends_on: + - rig + environment: + CLIENTS: 10 + TIMEOUT: 30m + RIG_HOST: rig + loader: + build: + context: .. + dockerfile: run2/loader.Dockerfile + depends_on: + - clients + environment: + KAFKA_HOST: kafka:9092 + CLIENTS: clients + rig: + image: "accenture/reactive-interaction-gateway:latest" + depends_on: + - zk + - kafka + environment: + KAFKA_BROKERS: kafka:9092 + LOG_LEVEL: error diff --git a/perf_tests/src/run2/loader.Dockerfile b/perf_tests/src/run2/loader.Dockerfile new file mode 100644 index 00000000..8e2e73bf --- /dev/null +++ b/perf_tests/src/run2/loader.Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:18.04 +COPY run2/loader.py loader.py +COPY run2/start_loader.sh start_loader.sh +COPY wait.sh wait.sh +COPY load.py load.py +RUN apt-get update -y +RUN apt-get install librdkafka-dev python3 python3-pip curl -y +RUN pip3 install --upgrade pip +RUN pip3 install confluent-kafka +CMD [ "sh", "start_loader.sh" ] \ No newline at end of file diff --git a/perf_tests/src/run2/loader.py b/perf_tests/src/run2/loader.py new file mode 100644 index 00000000..5c2b2978 --- /dev/null +++ b/perf_tests/src/run2/loader.py @@ -0,0 +1,14 @@ +import sys +import load as l +import time + +def load_10k_include_all(): + start = time.time() + for i in range(10): + for _ in range(1001): + l.produce(l.p, "rig", l.payload, "chatroom_message") + l.p.flush() + + l.print_progress(i) + end = time.time() + print(end - start) diff --git a/perf_tests/src/run2/start_k8s.sh b/perf_tests/src/run2/start_k8s.sh new file mode 100755 index 00000000..8f51de7d --- /dev/null +++ b/perf_tests/src/run2/start_k8s.sh @@ -0,0 +1,2 @@ +helm install run2-clients ./chart/client +helm install run2-loader ./chart/loader \ No newline at end of file diff --git a/perf_tests/src/run2/start_loader.sh b/perf_tests/src/run2/start_loader.sh new file mode 100755 index 00000000..ca405d4b --- /dev/null +++ b/perf_tests/src/run2/start_loader.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -m + +bash wait.sh + +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +python3 -c 'import loader as run2_loader; run2_loader.load_10k_include_all()' & +wait \ No newline at end of file diff --git a/perf_tests/src/run2/ws/client.go b/perf_tests/src/run2/ws/client.go new file mode 100644 index 00000000..5fcf55df --- /dev/null +++ b/perf_tests/src/run2/ws/client.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "os" + "os/signal" + "strconv" + "sync" + "time" + + "github.com/buger/jsonparser" + "github.com/sacOO7/gowebsocket" +) + +func receive(i int) { + var start time.Time + var wg sync.WaitGroup + + wg.Add(1) + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt) + + socket := gowebsocket.New("ws://localhost:4000/_rig/v1/connection/ws?subscriptions=[{\"eventType\":\"chatroom_message\"}]") + count := 1 + + socket.OnTextMessage = func(message string, socket gowebsocket.Socket) { + etype, _ := jsonparser.GetString([]byte(message), "type") + + if etype == "chatroom_message" { + count++ + } + + if count == 2 { + start = time.Now() + } + + if count%1000 == 0 { + fmt.Println(count, i) + } + + if count == 100000 { + elapsed := time.Since(start).Seconds() + fmt.Println("Thread", i, "finished in", elapsed, "s") + wg.Done() + } + } + + socket.Connect() + wg.Wait() +} + +func main() { + var wg sync.WaitGroup + + goroutines, _ := strconv.Atoi(os.Getenv("CLIENTS")) + + fmt.Println("Starting", goroutines, "goroutines") + + for i := 1; i <= goroutines; i++ { + wg.Add(1) + go func(i int) { + receive(i) + wg.Done() + }(i) + } + + fmt.Println("Waiting for goroutines to finish...") + wg.Wait() + fmt.Println("Done") +} diff --git a/perf_tests/src/run6/chart/client/.helmignore b/perf_tests/src/run6/chart/client/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/src/run6/chart/client/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/src/run6/chart/client/Chart.yaml b/perf_tests/src/run6/chart/client/Chart.yaml new file mode 100644 index 00000000..c6139abd --- /dev/null +++ b/perf_tests/src/run6/chart/client/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart creating service and deployment +name: client-run-6 +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/src/run6/chart/client/templates/_helpers.tpl b/perf_tests/src/run6/chart/client/templates/_helpers.tpl new file mode 100644 index 00000000..be4e3343 --- /dev/null +++ b/perf_tests/src/run6/chart/client/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "helm-charts.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helm-charts.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helm-charts.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/src/run6/chart/client/templates/deployment.yaml b/perf_tests/src/run6/chart/client/templates/deployment.yaml new file mode 100644 index 00000000..aafb8326 --- /dev/null +++ b/perf_tests/src/run6/chart/client/templates/deployment.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-deployment + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + template: + metadata: + labels: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ or (.Values.deployment.port) (.Values.service.port) }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + restartPolicy: Never \ No newline at end of file diff --git a/perf_tests/src/run6/chart/client/templates/service.yaml b/perf_tests/src/run6/chart/client/templates/service.yaml new file mode 100644 index 00000000..1de68252 --- /dev/null +++ b/perf_tests/src/run6/chart/client/templates/service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + ports: + - port: {{ .Values.service.port }} + name: http + targetPort: {{ .Values.service.targetPort }} + selector: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + type: {{ .Values.service.type }} \ No newline at end of file diff --git a/perf_tests/src/run6/chart/client/values.yaml b/perf_tests/src/run6/chart/client/values.yaml new file mode 100644 index 00000000..2fddabfc --- /dev/null +++ b/perf_tests/src/run6/chart/client/values.yaml @@ -0,0 +1,18 @@ +replicaCount: 1 + +image: + repository: run6_clients + tag: latest + pullPolicy: Never # needed for local Kubernetes cluster (docker desktop) + +service: + port: 9999 + targetPort: 9999 + type: ClusterIP + +deployment: + port: 9999 + env: + CLIENTS: 1 + TIMEOUT: 30m + RIG_HOST: reactive-interaction-gateway \ No newline at end of file diff --git a/perf_tests/src/run6/chart/loader/.helmignore b/perf_tests/src/run6/chart/loader/.helmignore new file mode 100644 index 00000000..c13e3c8f --- /dev/null +++ b/perf_tests/src/run6/chart/loader/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj \ No newline at end of file diff --git a/perf_tests/src/run6/chart/loader/Chart.yaml b/perf_tests/src/run6/chart/loader/Chart.yaml new file mode 100644 index 00000000..378c4c78 --- /dev/null +++ b/perf_tests/src/run6/chart/loader/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart creating service and deployment +name: loader-run-6 +version: 0.1.0 \ No newline at end of file diff --git a/perf_tests/src/run6/chart/loader/templates/_helpers.tpl b/perf_tests/src/run6/chart/loader/templates/_helpers.tpl new file mode 100644 index 00000000..be4e3343 --- /dev/null +++ b/perf_tests/src/run6/chart/loader/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "helm-charts.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "helm-charts.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "helm-charts.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} \ No newline at end of file diff --git a/perf_tests/src/run6/chart/loader/templates/deployment.yaml b/perf_tests/src/run6/chart/loader/templates/deployment.yaml new file mode 100644 index 00000000..edae8fe6 --- /dev/null +++ b/perf_tests/src/run6/chart/loader/templates/deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-deployment + labels: + app: {{ .Release.Name }} + chart: {{ template "helm-charts.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + template: + metadata: + labels: + app: {{ .Release.Name }}-deployment + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + {{- range $key, $value := .Values.deployment.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + restartPolicy: Never \ No newline at end of file diff --git a/perf_tests/src/run6/chart/loader/values.yaml b/perf_tests/src/run6/chart/loader/values.yaml new file mode 100644 index 00000000..7e52ac63 --- /dev/null +++ b/perf_tests/src/run6/chart/loader/values.yaml @@ -0,0 +1,9 @@ +image: + repository: run6_loader + tag: latest + pullPolicy: Never # needed for local Kubernetes cluster (docker desktop) + +deployment: + env: + KAFKA_HOST: kafka:9092 + CLIENTS: run6-clients \ No newline at end of file diff --git a/perf_tests/src/run6/client.Dockerfile b/perf_tests/src/run6/client.Dockerfile new file mode 100644 index 00000000..d793c83e --- /dev/null +++ b/perf_tests/src/run6/client.Dockerfile @@ -0,0 +1,5 @@ +FROM golang:latest +COPY . . +RUN go get github.com/r3labs/sse +RUN go build run6/client.go +CMD ["./client"] diff --git a/perf_tests/src/run6/client.go b/perf_tests/src/run6/client.go new file mode 100644 index 00000000..98f397aa --- /dev/null +++ b/perf_tests/src/run6/client.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "strconv" + "sync" + + loadtest ".." +) + +func main() { + var wg sync.WaitGroup + + goroutines, timeout := loadtest.GetEnv() + topic := 1 + + ctrl := make(chan bool) + + fmt.Println("Starting", goroutines, "goroutines") + + for i := 1; i <= goroutines; i++ { + wg.Add(1) + go func(i, topic int) { + elapsed, err := loadtest.Receive(i, 1000, 100, "chatroom_message"+strconv.Itoa(topic), ctrl) + + if err != nil { + fmt.Println("Error:", err) + return + } + + fmt.Println("Thread", i, "finished in", elapsed, "s") + + wg.Done() + }(i, topic) + + <-ctrl + + topic = topic + 1 + + if topic > 100 { + topic = 1 + } + } + + go loadtest.StatusOk() + + fmt.Println("Waiting for goroutines to finish...") + + if loadtest.WaitTimeout(&wg, timeout) { + fmt.Println("Timed out waiting for wait group") + } else { + fmt.Println("Wait group finished") + } +} diff --git a/perf_tests/src/run6/docker-compose.yml b/perf_tests/src/run6/docker-compose.yml new file mode 100644 index 00000000..93a24eee --- /dev/null +++ b/perf_tests/src/run6/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3' + +services: + zk: + image: confluentinc/cp-zookeeper:5.0.0 + container_name: zk + ports: + - 2181:2181 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + restart: on-failure:5 + kafka: + image: confluentinc/cp-kafka:5.0.0 + container_name: kafka + ports: + - 9092:9092 + - 9094:9094 + environment: + KAFKA_ZOOKEEPER_CONNECT: zk:2181 + KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9092,OUTSIDE://localhost:9094 + KAFKA_LISTENERS: INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9094 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" + KAFKA_NUM_PARTITIONS: 4 + restart: on-failure:5 + clients: + ports: + - 9999:9999 + build: + context: .. + dockerfile: run6/client.Dockerfile + depends_on: + - rig + environment: + CLIENTS: 1000 + TIMEOUT: 1h + RIG_HOST: rig + loader: + build: + context: .. + dockerfile: run6/loader.Dockerfile + depends_on: + - clients + environment: + KAFKA_HOST: kafka:9092 + CLIENTS: clients + rig: + image: "accenture/reactive-interaction-gateway:latest" + depends_on: + - zk + - kafka + environment: + KAFKA_BROKERS: kafka:9092 + LOG_LEVEL: error diff --git a/perf_tests/src/run6/loader.Dockerfile b/perf_tests/src/run6/loader.Dockerfile new file mode 100644 index 00000000..36f9c137 --- /dev/null +++ b/perf_tests/src/run6/loader.Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:18.04 +COPY run6/loader.py loader.py +COPY run6/start_loader.sh start_loader.sh +COPY wait.sh wait.sh +COPY load.py load.py +RUN apt-get update -y +RUN apt-get install librdkafka-dev python3 python3-pip curl -y +RUN pip3 install --upgrade pip +RUN pip3 install confluent-kafka +CMD [ "sh", "start_loader.sh" ] \ No newline at end of file diff --git a/perf_tests/src/run6/loader.py b/perf_tests/src/run6/loader.py new file mode 100644 index 00000000..2b468f09 --- /dev/null +++ b/perf_tests/src/run6/loader.py @@ -0,0 +1,22 @@ +import sys +import load as l +import time + +def load_for_multiple_topics(messages_in_k, topics): + start = time.time() + topic = 1 + for i in range(messages_in_k): + # Because we're using SSE, we can't guarantee that every message reaches its destinations + # Package loss running locally is about 4 in 100k, so I am appending 10 events just to be safe + for _ in range(1010): + l.produce(l.p, "rig", l.payload, "chatroom_message" + str(topic)) + + topic = topic + 1 + + if topic > topics: + topic = 1 + l.p.flush() + + l.print_progress(i) + end = time.time() + print(end - start) diff --git a/perf_tests/src/run6/start_k8s.sh b/perf_tests/src/run6/start_k8s.sh new file mode 100755 index 00000000..762e9fb3 --- /dev/null +++ b/perf_tests/src/run6/start_k8s.sh @@ -0,0 +1,2 @@ +helm install run6-clients ./chart/client +helm install run6-loader ./chart/loader \ No newline at end of file diff --git a/perf_tests/src/run6/start_loader.sh b/perf_tests/src/run6/start_loader.sh new file mode 100755 index 00000000..6bc62691 --- /dev/null +++ b/perf_tests/src/run6/start_loader.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -m + +bash wait.sh + +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +python3 -c 'import loader as run6_loader; run6_loader.load_for_multiple_topics(10, 100)' & +wait \ No newline at end of file diff --git a/perf_tests/src/timeout.go b/perf_tests/src/timeout.go new file mode 100644 index 00000000..ae7c97af --- /dev/null +++ b/perf_tests/src/timeout.go @@ -0,0 +1,30 @@ +package loadtest + +import ( + "fmt" + "os" + "sync" + "time" +) + +// WaitTimeout Wait for a wg to finish within a certain time, if the wg finishes before, false is returned +func WaitTimeout(wg *sync.WaitGroup, timeout string) bool { + duration, err := time.ParseDuration(timeout) + + if err != nil { + fmt.Println("Error while parsing timeout!") + os.Exit(1) + } + + c := make(chan struct{}) + go func() { + defer close(c) + wg.Wait() + }() + select { + case <-c: + return false // completed normally + case <-time.After(duration): + return true // timed out + } +} diff --git a/perf_tests/src/wait.sh b/perf_tests/src/wait.sh new file mode 100644 index 00000000..30708e53 --- /dev/null +++ b/perf_tests/src/wait.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo "Using clients $CLIENTS" +echo "Waiting for clients to come online..." + +while true; do + status=$(curl -s "$CLIENTS:9999") + + if [[ $status == *"OK"* ]]; then + break + else + echo "." + fi + + sleep 1 +done + +echo "Starting loader..." \ No newline at end of file diff --git a/perf_tests/start_run1.sh b/perf_tests/start_run1.sh new file mode 100755 index 00000000..d667a848 --- /dev/null +++ b/perf_tests/start_run1.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -m + +helm install kafka incubator/kafka -f ./kafka/chart/values.yml + +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-0 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-1 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-2 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-0 + +helm install rig ./rig/chart + +cd src/run1 +docker-compose build + +chmod +x start_k8s.sh +./start_k8s.sh + +kubectl wait --for=condition=Ready --timeout=5m "pod/$(kubectl get pods -l 'app=run1-clients-deployment' -o jsonpath='{.items[0].metadata.name}')" +kubectl wait --for=condition=Ready --timeout=5m "pod/$(kubectl get pods -l 'app=run1-loader-deployment' -o jsonpath='{.items[0].metadata.name}')" + +echo "Starting log collection..." + +cd ../.. + +timeout 10m bash < run1.client.log +EOT + +helm uninstall rig +helm uninstall kafka +helm uninstall run1-clients +helm uninstall run1-loader \ No newline at end of file diff --git a/perf_tests/start_run2.sh b/perf_tests/start_run2.sh new file mode 100755 index 00000000..336af0c9 --- /dev/null +++ b/perf_tests/start_run2.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -m + +helm install kafka incubator/kafka -f ./kafka/chart/values.yml + +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-0 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-1 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-2 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-0 + +helm install rig ./rig/chart + +cd src/run2 +docker-compose build + +chmod +x start_k8s.sh +./start_k8s.sh + +kubectl wait --for=condition=Ready --timeout=5m "pod/$(kubectl get pods -l 'app=run2-clients-deployment' -o jsonpath='{.items[0].metadata.name}')" +kubectl wait --for=condition=Ready --timeout=5m "pod/$(kubectl get pods -l 'app=run2-loader-deployment' -o jsonpath='{.items[0].metadata.name}')" + +echo "Starting log collection..." + +cd ../.. + +timeout 10m bash < run2.client.log +EOT + +helm uninstall rig +helm uninstall kafka +helm uninstall run2-clients +helm uninstall run2-loader \ No newline at end of file diff --git a/perf_tests/start_run6.sh b/perf_tests/start_run6.sh new file mode 100755 index 00000000..6d17c4ff --- /dev/null +++ b/perf_tests/start_run6.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -m + +helm install kafka incubator/kafka -f ./kafka/chart/values.yml + +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-0 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-1 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-zookeeper-2 +kubectl wait --for=condition=Ready --timeout=5m pod/kafka-0 + +helm install rig ./rig/chart + +cd src/run6 +docker-compose build + +chmod +x start_k8s.sh +./start_k8s.sh + +kubectl wait --for=condition=Ready --timeout=5m "pod/$(kubectl get pods -l 'app=run6-clients-deployment' -o jsonpath='{.items[0].metadata.name}')" +kubectl wait --for=condition=Ready --timeout=5m "pod/$(kubectl get pods -l 'app=run6-loader-deployment' -o jsonpath='{.items[0].metadata.name}')" + +echo "Starting log collection..." + +cd ../.. + +timeout 10m bash < run6xw.client.log +EOT + +helm uninstall rig +helm uninstall kafka +helm uninstall run6-clients +helm uninstall run6-loader \ No newline at end of file