---
# System prepended metadata

title: Labo TICK stack (2)

---

# Labo TICK stack (2)

## Labo inhoud

### Opgave

Je gaat aan de slag met de TICK stack. Je zal de verschillende componenten leren kennen en gebruiken. Het labo is een integratie-oefening, m.a.w. **je zal gebruik maken van de reeds eerder vergaarde kennis en kunde**. De bedoeling is dat dit labo wordt uitgewerkt gedurende **twee lesweken**. 

### Deadline

Ten laatste voor de start van het volgende labo (deel 1).

### Indienen

Je dient het verslag in via Leho – Opdrachten (als PDF-bestand).

## Inleiding


### Doestelling

De bedoeling van dit labo is **niet** om na te gaan hoe snel je kan lezen, commando’s kan invoeren en de output kan plakken in een document.

De bedoeling van dit labo is **wel** dat je de tijd neemt om rustig op verkenning te gaan en bij te leren over het systeem waarmee we werken. De opgave dient enkel als leidraad voor jouw zoektocht. Maak er dus geen race tegen de klok van.

### Benodigdheden

* Je eigen laptop
* Je krijgt toegang tot een VM met:
    * Ubuntu 18.04.4
    * Docker 19.03.8
    * docker-compose 1.25.4
    * De credentials zijn teurg te vinden op Leho

## Theoretische achtergrond

Gezien de omstandigheden lijkt me een stukje zelfstudie aangewezen. Bekijk zeker eens ["glossary"](https://docs.influxdata.com/influxdb/v1.7/concepts/glossary/). Verklaar volgende termen i.f.v. van InfluxDB zo conreet mogelijk.

### InfluxDB Glossary

Wat is een database?
> Antwoord
> ...
> ...

Wat is een measurement?
> Antwoord
> ...
> ...
 
Wat is een tag?
> Antwoord
> ...
> ...

Wat is een field?
> Antwoord
> ...
> ...

Wat is een tag set?
> Antwoord
> ...
> ...

Wat is een field set?
> Antwoord
> ...
> ...

### De TICK stack

TICK staat voor... Telegraf, InfluxDB, Chronograf, Kapacitor. Leg kort uit wat de verschillende componenten precies doen:

InfluxDB is / doet:
> Antwoord
> ...
> ...

Telegraf is / doet:
> Antwoord
> ...
> ...

Chronograf is / doet:
> Antwoord
> ...
> ...

Kapacitor is / doet:
> Antwoord
> ...
> ...


## TICK stack opzetten - deel 1

We zullen stap voor stap de volledige TICK stack opzetten, configureren en uiteraard gebruiken. 

### InfluxDB

Starten doen we met de *i* van InfluxDB. Op je VM staat reeds de nodige software, alsook zijn de Docker images reeds *"gepulled"*.

Maak volgende mappenstructuur aan:

```
student@tick-01:~$ tree
.
.
└── labo
    └── configs
        └── tick
            ├── env
            ├── influxdb.conf
            └── orig
                └── influxdb.conf

```

In de directory `labo` maak je het bestand `docker-compose.yml`:

```
version: '3.7'

services:
  influxdb01:
    image: influxdb:1.7.8-alpine
    container_name: influxdb01
    ports:
      - "8086:8086"
    expose:
      - 8089
    volumes:
      - influxdb01:/var/lib/influxdb
      - ./configs/tick/influxdb.conf:/etc/influxdb/influxdb.conf:ro
      
volumes:
  influxdb01:
```

Leg bovenstaande uit (tip: [documentatie Docker image](https://hub.docker.com/_/influxdb)). 

> Antwoord
> ...
> ...

Je kan een configuratiebestand volledig van scratch aanmaken of je kan dit "genereren" (zie [documentatie](https://hub.docker.com/_/influxdb)).

```
docker run --rm influxdb:1.7.8-alpine influxd config > ~/labo/configs/tick/orig/influxdb.conf
```

Maak een kopie van deze config in `labo/configs/tick`. 

Pas volgende setting aan (zie [documentatie](https://docs.influxdata.com/influxdb/v1.7/administration/config#reporting-disabled-false) waarom):

```
reporting-disabled = true
```

Start daarna de services beschreven in `docker-compose.yml` op. Hoe doe je dit ook alweer? Uitstekende vraag:

> Antwoord
> ...
> ...
 
Je InfluxDB is nu normaal gezien actief. Het image bevat naast de "database server" ook de client: *influx*. Het Docker image zelf is gebaseerd op Alpine. Dat maakt dat we in principe gebruik kunnen maken van *sh*. We kunnen echter ook rechtstreeks de client starten:

```
docker-compose exec influxdb01 influx
```

Probeer volgende zaken uit, documenteer, gebruik de **juiste** terminologie. Merk op dat de commando's niet hoofdlettergevoelig zijn (de tags, fields, measurements, ... wel), alsook een ';' is optioneel.

```
> SHOW DATABASES
> CREATE DATABASE testdb
> USE testdb
> SHOW MEASUREMENTS
> INSERT cpu,host=test value=25.3
> SHOW MEASUREMENTS
> SELECT * FROM cpu
> SHOW TAG KEYS
> SHOW FIELD KEYS

> INSERT cpu,host=test,rack=production-01 value=25.3
> SHOW MEASUREMENTS
> SHOW TAG KEYS
> SHOW FIELD KEYS
> SELECT * FROM cpu
> SELECT * FROM cpu WHERE rack = 'production-01'
> SELECT value FROM cpu WHERE rack = 'production-01'
> SELECT host,value FROM cpu WHERE rack = 'production-01'
```

Documenteer! Zijn er dingen die je opvielen?

> Antwoord
> ...
> ...

Moeilijk? Makkelijk? Geef gerust je feedback. Mijn persoonlijke mening: relatief eenvoudig, gelijkaardige aan "klassieke" SQL.

> Antwoord
> ...
> ...

### Authenticatie

Momenteel kan in principe iedereen aan de InfluxDB instantie (poort staat open), zonder enige autorisatie en authenticatie. Dat kan natuurlijk niet de bedoeling zijn... . 

Pas `docker-compose.yml` aan, voeg volgende toe (bv. onder `expose`):

```
    env_file:
      - configs/tick/env/.env.influxdb
```

Maak daarna `configs/tick/env/.env.influxdb` aan:

```
INFLUXDB_HTTP_AUTH_ENABLED=true
INFLUXDB_ADMIN_USER=admin
INFLUXDB_ADMIN_PASSWORD=admin
INFLUXDB_USER=telegraf
INFLUXDB_USER_PASSWORD=labo
INFLUXDB_DB=telegraf
```

Pas daarnaast de InfluxDB configuratie aan:

```
[http]
  auth-enabled = true
  pprof-auth-enabled = true
  ping-auth-enabled = true
```

De combinatie van beiden zal zorgen dat je InfluxDB ietwat beter afgeschermd is. Merk op dat environment variables prioritair zijn t.o.v. de config. Er zal een admin gebruiker aangemaakt worden, evenals een gebruiker voor *Telegraf* en bijhorende database. Dit zal echter enkel en alleen gebeuren wanneer de database voor het eerst geïnitialiseerd wordt. 

We hebben nog niets cruciaal opgeslagen. Stop alle services. Wis het Docker volume.

```
docker volume ls
docker system prune --volumes
```

Start nu opnieuw de services (voor de duidelijkheid: dat is er momenteel nog steeds maar eentje). Bekijk de output (dus niet detached uitvoeren, `screen` is een geweldige tool). 

Welke query's zie je passeren?

> Antwoord
> ...
> ...

### Telegraf

Voeg volgende service toe aan `docker-compose.yml`:

```
  telegraf:
    image: telegraf:1.13.4-alpine
    container_name: telegraf
    env_file:
      - configs/tick/env/.env.telegraf
    volumes:
      - ./configs/tick/telegraf.conf:/etc/telegraf/telegraf.conf:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
```

Wat zou het nut van de laatste lijn kunnen zijn?

> Antwoord
> ...
> ...

Genereer ook hier een config file:

```
docker run --rm telegraf:1.13.4-alpine telegraf config > ~/labo/configs/tick/orig/telegraf.conf
```

Maak een kopie, `~/labo/configs/tick/telegraf.conf` en zoek, pas volgende zaken aan (je text-editor vlot beheersen zal hier zeker helpen): 

```
[[outputs.influxdb]]
  ...
  urls = ["http://influxdb01:8086"]
  database = "${INFLUXDB_DB}"
  
  ...
  
## HTTP Basic Auth
  username = "${INFLUXDB_USER}"
  password = "${INFLUXDB_USER_PASSWORD}"
  
  
[[inputs.docker]]
   ...
   endpoint = "unix:///var/run/docker.sock"
   ...
   total = true
```

Merk op dat er gebruik gemaakt wordt van variabelen. De documentatie daarvan is terug te vinden in het config bestand zelf:

```
# Environment variables can be used anywhere in this config file, simply surround
# them with ${}. For strings the variable must be within quotes (ie, "${STR_VAR}"),
# for numbers and booleans they should be plain (ie, ${INT_VAR}, ${BOOL_VAR})
```

Zorg dat `~/labo/configs/tick/env/.env.telegraf` volgende bevat:

```
INFLUXDB_USER=telegraf
INFLUXDB_USER_PASSWORD=labo
INFLUXDB_DB=telegraf
```

Start alle services. Start opnieuw de client, vergeet niet te authenticeren, controleer of er een database telegraf is, bekijk de measurements, bekijk data, ... .

```
docker-compose exec influxdb01 influx
auth
SHOW DATABASES
USE telegraf
SHOW ...
SELECT * FROM ...
```

> Antwoord
> ...
> ...

### Chronograf

We gaan verder... Voeg volgende toe aan `docker-compose.yml`:

```
  chronograf:
    image: chronograf:1.8.0-alpine
    container_name: chronograf
    ports:
      - "8888:8888"
    env_file:
      - configs/tick/env/.env.chronograf
    volumes:
      - chronograf:/var/lib/chronograf
```

Vergeet geen `volume` te registreren (`chronograf`). 

Het environment file (`.env.chronograf`) ziet er deze keer als volgt uit:

```
INFLUXDB_URL=http://influxdb01:8086
INFLUXDB_USERNAME=admin
INFLUXDB_PASSWORD=admin
REPORTING_DISABLED=true
```

Start alles op en surf naar http://172.23.82.60:\$JOUW_POORT\$. 

#### Eerste visualisatie

Voer volgende stappen uit:

* Ga naar "Explore"
* Selecteer telegraf.autogen
* Klik CPU open
* Klik CPU - 5 open
* Kies cpu-total
* Kies usage-user (bij Fields)

Tada! Je zou nu een grafiek moeten hebben. Rechts bovenaan kan je zorgen dat deze automatisch wordt bijgewerkt. Je ziet daar tevens staan welke query er gebruikt wordt. Maak een screenshot.

> Antwoord
> ...
> ...

Je kan eventueel nog meerdere query's toevoegen, de visualisatie verder aanpassen, ... Probeer in ieder geval de chart op te slaan in een dashboard. Maak opnieuw een screenshot.

> Antwoord
> ...
> ...

#### Dashboards

Je kan zelf dashboards maken, maar je kan er ook importeren. Voer volgende stappen uit:

* Ga naar Configuration
* Klik op je InfluxDB connectie
* Klik op update
* Selecteer "System" en "Docker"
* Kapacitor "skip" je
* Finish

Ga naar Dashboards. Klik bv. het Docker dashboard open. Maak een screenshot.

> Antwoord
> ...
> ...
 
#### Verken zelf!

Klik nog wat rond, bekijk zeker eens al de mogelijkheden!

### Kapacitor

Voeg volgende snipper toe aan je `docker-compose.yml`:

```
  kapacitor:
    image: kapacitor:1.5.4-alpine
    container_name: kapacitor
    expose:
      - 9092
    env_file:
      - configs/tick/env/.env.kapacitor
    volumes:
      - ./configs/tick/kapacitor.conf:/etc/kapacitor/kapacitor.conf:ro
```

Genereer zoals gebruikelijk een config:

```
docker run --rm kapacitor:1.5.4-alpine kapacitord config > ~/labo/configs/tick/orig/kapacitor.conf
```

Maak een kopie, plaats deze in `configs/tick`. Je hoeft momenteel niets aan te passen. We overschrijven bepaalde settings met environment variabels:

```
KAPACITOR_INFLUXDB_0_USERNAME=admin
KAPACITOR_INFLUXDB_0_PASSWORD=admin
KAPACITOR_INFLUXDB_0_URLS_0=http://influxdb01:8086
KAPACITOR_REPORTING_0_enabled=false
```

Sla dit op als `~/labo/configs/tick/env/.env.kapacitor`. Pas `.env.chronograf` aan, voeg volgende zaken toe:

```
KAPACITOR_URL=http://kapacitor:9092
KAPACITOR_USERNAME=admin
KAPACITOR_PASSWORD=admin
```

Start alle services op.

#### Mailgun

Maak een account aan op Mailgun (gebruikt eventueel https://10minutemail.com).

![](https://i.imgur.com/8YwAvFP.png)

Voor de verificatie kan je bv. https://www.spoofbox.com of een gelijkaardige service gebruiken.

Ga naar "Sending", klik daar op je "Sandbox" domein en kies voor "SMTP". Hou de credentials bij. Aan de rechterkant vul je je e-mailadres in, voeg dit toe als een "authorized recipient".

In Chronograf ga je naar "Configuration". Bewerk je Kapacitor connectie. Ga naar "SMTP" en voer de credentials in. Zorg dat dit je *default* server is door "Configuration Enabled" af te vinken. Sla op, stuur een "Test Alert". 

![](https://i.imgur.com/eBBgCbR.png)


#### Alerting

Ga naar "Alerting" (in Chronograf). Maak een alert aan, die bv. een mail stuurt wanneer de CPU gebruik groter dan een bepaalde *treshold* is. 

Maak een screenshot van een ontvangen "Alert Mail".

> Antwoord
> ...
> ...

## TICK stack opzetten - deel 2

### High availability & scalability

In principe heb je intussen een volledig werkende TICK stack. Deze week gaan we de stack uitgebreider gaan gebruiken. Daarnaast gaan we een complete(re) pipeline uitbouwen. 

In de vorige labo's hebben we meestal rekening gehouden met "high availability" en "scalability". 

We kunnen bv. een service scalen (meer containers maken). We kunnen dezelfde service meermaals opnemen in `docker-compose.yml`. We kunnen oplossingen zoals Docker Swarm en Kubernetes inzetten. We gaan hier niet verder op in, dit is een les / labo op zichzelf ;-). 

In het labo "Elastic Stack" hebben we bijvoorbeeld drie Elasticsearch nodes gebruikt. We hebben daar gezien hoe availabiltiy en scalability wordt behaald door gebruik te maken van sharding.

In het Kafka labo hebben we een gelijkaardig principe gezien: partitions. Weet dat we ook perfect meerdere message brokers (meerdere Kafka's) kunnen inzetten. 

### InfluxDB Relay

Bij InfluxDB ligt dat verhaal wat moeilijker. In de open-source wereld en meer bepaald in de wereld van "Big Data" is InfluxDB een beetje een vreemde eend in de bijt. De "gratis" versie is niet zomaar te gebruiken voor productiedoeleinden. Men probeert eigenlijk jou ten alle tijden InfluxDB Enterprise / InfluxDB Cloud aan te praten.

Begrijpelijk. Het is hun hun verdienmodel. Om toch ergens de community tegemoet te komen hebben de mensen achter InfluxDB, InfluxData een projectje genaamd [InfluxDB Relay](https://github.com/influxdata/influxdb-relay) uitgebracht. 

Het kernidee daarachter is dat je met InfluxDB Relay de data kan gaan repliceren naar verschillende InfluxDB instanties. De bedoeling is om dan minstens twee InfluxDB instanties draaiende te hebben, alsook twee InfluxDB Relay instanties en daarbovenop een high available load balancer (kan bv. met HAProxy of Nginx).

```
        ┌─────────────────┐                 
        │writes & queries │                 
        └─────────────────┘                 
                 │                          
                 ▼                          
         ┌───────────────┐                  
         │               │                  
┌────────│ Load Balancer │─────────┐        
│        │               │         │        
│        └──────┬─┬──────┘         │        
│               │ │                │        
│               │ │                │        
│        ┌──────┘ └────────┐       │        
│        │ ┌─────────────┐ │       │┌──────┐
│        │ │/write or UDP│ │       ││/query│
│        ▼ └─────────────┘ ▼       │└──────┘
│  ┌──────────┐      ┌──────────┐  │        
│  │ InfluxDB │      │ InfluxDB │  │        
│  │ Relay    │      │ Relay    │  │        
│  └──┬────┬──┘      └────┬──┬──┘  │        
│     │    |              |  │     │        
│     |  ┌─┼──────────────┘  |     │        
│     │  │ └──────────────┐  │     │        
│     ▼  ▼                ▼  ▼     │        
│  ┌──────────┐      ┌──────────┐  │        
│  │          │      │          │  │        
└▸ │ InfluxDB │      │ InfluxDB │◂─┘        
   │          │      │          │           
   └──────────┘      └──────────┘           
 ```
 <small>[Bron InfluxDB Relay documentatie](https://github.com/influxdata/influxdb-relay)</small>

Dat lost weliswaar het availability probleem op, maar dat geeft ons nog geen volwaardige schaalbare cluster. Dat oplossen is dan weer een stukje complexer. Er zijn in principe twee "voor de hand liggende oplossingen".

We kunnen bovenstaande oplossing meermaals naast mekaar gaan opzetten en op niveau van de load balancer het verkeer verdelen. Prima oplossing op relatief kleine schaal. Zeker haalbaar in een container gebaseerde omgeving. Op grote schaal wordt dat (te) complex.

De tweede optie is om de InfluxDB Relay component intelligenter te maken. Eigenlijk zorgen dat op die plaats al een eerste vorm van load balancing plaatsvindt. 

Het InfluxDB project wordt niet meer onderhouden door Influxdata. Gelukkig zijn er tal van forks die dat wel nog goed doen en die eveneens bovenstaande aanpak implementeren. 

Volgende twee lijken mij persoonlijk het interessants:

* https://github.com/strike-team/influxdb-relay
* https://github.com/toni-moreno/influxdb-srelay

De eerste oplossing is de eenvoudigste. Kort door de bocht kan je zeggen dat deze fork vooral zorgt voor een up2date versie. De tweede optie is een stuk uitgebreider (eveneens een fork van strike-team), maar ietwat minder onderhouden. De "srelay" heeft in principe alles aan boord om de boel high available en netjes load balanced op te zetten.

Tijdens dit labo kiezen we voor de eerste oplossing. Dit omwille van boven vermelde voordelen / redenen. Daarnaast is het belangrijk op te merken dat InfluxDB 2 al een tijdje in beta-fase is. Het ziet er naar uit dat het dan een stuk eenvoudiger wordt om een mooie (gratis) cluster op te zetten. 

Momenteel is je docent daarmee bezig in onderzoek, indien interesse, vraag mij het eens op het einde van het semester ;-).

### InfluxDB high available

#### Extra InfluxDB node

We starten met het toevoegen van een extra InfluxDB node. Voeg aan je `docker-compose.yml` een extra `service` toe, genaamd `influxdb02`. Baseer je op `influxdb01`. 

#### InfluxDB Relay

Maak een config aan, genaamd `configs/tick/influxdb-relay.conf`, de inhoud ziet er als volgt uit:

```
# InfluxDB
[[http]]
name = "Labo-HTTP"
bind-addr = "0.0.0.0:9096"

[[http.output]]
name = "influxdb01"
location = "http://influxdb01:8086/"
endpoints = {write="/write", ping="/ping", query="/query"}
timeout = "10s"

[[http.output]]
name = "influxdb02"
location = "http://influxdb02:8086/"
endpoints = {write="/write", ping="/ping", query="/query"}
timeout = "10s"

[[udp]]
name = "Labo-UDP"
bind-addr = "0.0.0.0:9096"
read-buffer = 0 # default

[[udp.output]]
name = "influxdb01"
location = "influxdb01:8089"
mtu = 512

[[udp.output]]
name = "influxdb02"
location = "influxdb02:8089"
mtu = 512
```

Merk op dat er zowel een HTTP- evenals een UDP-endpoint wordt geconfigureerd. 

Wat is het voordeel van UDP te gebruiken t.o.v. HTTP?
> Antwoord
> ...
> ...

Voeg volgende toe aan `docker-compose.yml`:

```
  relay:
    image: vptech/influxdb-relay:latest
    container_name: relay
    volumes:
      - ./configs/tick/influxdb-relay.conf:/etc/influxdb-relay/influxdb-relay.conf:ro
    ports:
      - "9096:9096"
```

Herstart al je services. Surf naar http://172.23.82.60:JOUW_POORT/health

Als alles goed gaat ziet de output er ongeveer uit als volgt:

```
{
status: "healthy",
  healthy: {
    influxdb01: "OK. Time taken 2.264959ms",
    influxdb02: "OK. Time taken 1.624863ms"
  }
}
```

Vanaf nu kan je via de Relay de data **wegschrijven**. De documentatie van Relay is wat dat betreft ~~misschien~~ wat onduidelijk / verwarrend. Het is **niet** mogelijk om data te bevragen via de Relay, er kan enkel geschreven worden. Op niveau van load balancer dien je te bepalen of er data moet geschreven en/of gelezen worden. Afhankelijk daarvan stuur je het door naar de juiste endpoint (in het schema duidelijk weergegeven). 


#### Telegraf data wegschrijven

Momenteel schrijft Telegraf de data weg naar `influxdb01`. Pas `telegraf.conf` aan zodanig de data via de Relay wordt weggeschreven.

#### Load balancing met Traefik

Zoals reeds vermeld zijn er tal van oplossingen om load te verdelen. Persoonlijk ben ik wel fan van de enerzijds de simpliciteit en anderzijds de kracht die Nginx te bieden heeft. Daarnaast is het prachtig (ja, je docent meent dat echt) als dat dan ook nog eens mooi hand-in-hand gaat met Docker(-compose). 

Dat is exact wat Traefik te bieden heeft. Zonder in al te veel detail te gaan (het is echt een vrij uitgebreid geweldig stukje software) kan het Nginx en Docker op een zeer dynamische manier combineren. Traefik zal bv. automatisch nieuwe Docker containers detecteren. Daarnaast kan het bijvoorbeeld ook automatisch SSL-certificaten aanvragen en configureren (via [Let's encrypt](https://letsencrypt.org/)).

Voeg volgende toe aan `docker-compose.yml`:

```
  traefik:
    container_name: traefik
    # The official Traefik docker image
    image: traefik:v2.1.9
    # Enables the web UI and tells Traefik to listen to docker
    command:
      - "--api"
      - "--providers.docker=true"
      # Traefik will listen to incoming request on the port 80 (http)
      - "--entrypoints.web.address=:80"
    ports:
      - "80:80"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      # Dashboard
      - "traefik.http.routers.traefik.rule=Host(`traefik.${HOSTNAME}.local`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=admin"

      - "traefik.http.middlewares.admin.basicauth.users=admin:$$2y$$05$$XMIfFc6/5wN5OAPCAuiDSOuRBR62nmUZVKURSC08LIguvgudc1EkK"
    networks:
      - web
```

Merk op dat er gebruik wordt van een middleware `admin`, die gebruik maakt van `basic auth`. De gebruikersnaam is `admin`, het wachtwoord (htpasswd syntaxis) is tevens `admin`. Merk op dat er tevens een apart netwerk wordt gebruikt (`web`).

Voeg dit onderaan toe aan `docker-compose.yml`:

```
networks:
  web:
    external: true
  internal:
    external: false
```

Wat doet dit precies?
> Antwoord
> ...
> ...

M.b.v labels kunnen we de services eenvoudig configureren. 

Voeg volgende toe aan `influxdb01` en `influxdb02`:

```
    labels:
      - "traefik.http.routers.influxdb_http.entrypoints=web"
      - "traefik.http.routers.influxdb_http.rule=Host(`influxdb.${HOSTNAME}.local`)"
      - "traefik.docker.network=web"
      - "traefik.http.services.influxdb.loadbalancer.server.port=8086"
    networks:
      - web
      - internal
```

Probeer zo goed mogelijk uit te zoeken wat dit precies doet.
> Antwoord
> ...
> ...

Bij `telegraf` en `kapacitor` voeg je het volgende toe:

```
    labels:
      - traefik.enable=false
    networks:
      - internal
```

Bij `chronograf` voeg je het volgende toe:

```
    labels:
      - "traefik.http.routers.chronograf_http.entrypoints=web"
      - "traefik.http.routers.chronograf_http.rule=Host(`chronograf.${HOSTNAME}.local`)"
      - "traefik.docker.network=web"
    networks:
      - web
      - internal
```

Bij `relay` voeg je het volgende toe:

```
    labels:
      - "traefik.http.routers.relay_http.entrypoints=web"
      - "traefik.http.routers.relay_http.rule=Host(`relay.${HOSTNAME}.local`)"
      - "traefik.docker.network=web"
    networks:
      - web
      - internal
```

Herstart al je services. 

Pas je Windows / Linux hosts file als volgt aan:
(Tip: [How to Windows](https://support.vip.nl/nl/support/solutions/articles/1000127980-hosts-file-aanpassen-in-windows) - [How to Linux](https://support.rackspace.com/how-to/modify-your-hosts-file/))

```
172.23.82.60		traefik.JOUW-HOSTNAME.local
172.23.82.60		relay.JOUW-HOSTNAME.local
172.23.82.60		influxdb.JOUW-HOSTNAME.local
172.23.82.60		chronograf.JOUW-HOSTNAME.local
```

Als alles gaat kan je nu surfen naar http://traefik.JOUW-HOSTNAME.local:JOUW-POORT. Login en kijk eens rond in de interface. 

Maak een screenshot van alles wat volgens jou belangrijk is.
> Antwoord
> ...
> ...

Surf ook eens naar http://influxdb.JOUW-HOSTNAME.local:JOUW-POORT/query?db=telegraf&pretty=true&q=SELECT%20usage_user%20FROM%20%22cpu%22%20LIMIT%201.

Paste de output. Wat is er nu eigenlijk gebeurd (wat heeft Traefik gedaan)?
> Antwoord
> ...
> ...

##### ✓ High available
In principe zou je er nu voor kunnen zorgen dat je via Traefik zowel kan schrijven en lezen naar je InfluxDB's. Als je dit aan de praat krijgt: dat is zeker een extra puntje op het labo-examen.

### Data anlyseren met de Elastic Stack

De data die nu in InfluxDB zit is perfect te visualiseren met Chronograf. Analyses kan je doen via de InfluxQL. Dit taaltje is sterk gelijkend op klassieke SQL. Daarom is het misschien interessanter om nog eens je Elasticsearch skills te testen.

#### Elastic Stack opzetten 
Daarnaast is het niet ondenkbaar dat je meer complexe analyses wil (en sneller) kan uitvoeren met Elasticsearch. Voeg volgende toe aan `docker-compose.yml` om een "single-node Elasticsearch cluster" en Kibana op te zetten:

```
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.1
    container_name: es01
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.type=single-node
      - bootstrap.memory_lock=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
    labels:
      - traefik.enable=false
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data
      - ./configs/elastic/jvm-elasticsearch.options:/usr/share/elasticsearch/config/jvm.options:ro
    ports:
      - 9200:9200
    networks:
      - internal

  kibana:
    image: docker.elastic.co/kibana/kibana:7.6.1
    container_name: kibana
    environment:
      SERVER_NAME: labo-big-data
      ELASTICSEARCH_HOSTS: http://es01:9200
    ports:
      - 5601:5601
    labels:
      - "traefik.http.routers.kibana_http.entrypoints=web"
      - "traefik.http.routers.kibana_http.rule=Host(`kibana.${HOSTNAME}.local`)"
      - "traefik.docker.network=web"
    networks:
      - web
      - internal
```

Dit zou je in principe allemaal moeten begrijpen. Vergeet geen `volume` voor Elasticsearch te registeren. Maak daarnaast volgende config bestanden aan met bijhorende inhoud:

`configs/elastic/jvm-elasticsearch.options`

```
## JVM configuration

################################################################
## IMPORTANT: JVM heap size
################################################################
##
## You should always set the min and max JVM heap
## size to the same value. For example, to set
## the heap to 4 GB, set:
##
## -Xms4g
## -Xmx4g
##
## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
## for more information
##
################################################################

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms256m
-Xmx256m

################################################################
## Expert settings
################################################################
##
## All settings below this section are considered
## expert settings. Don't tamper with them unless
## you understand what you are doing
##
################################################################

## GC configuration
8-13:-XX:+UseConcMarkSweepGC
8-13:-XX:CMSInitiatingOccupancyFraction=75
8-13:-XX:+UseCMSInitiatingOccupancyOnly

## G1GC Configuration
# NOTE: G1 GC is only supported on JDK version 10 or later
# to use G1GC, uncomment the next two lines and update the version on the
# following three lines to your version of the JDK
# 10-13:-XX:-UseConcMarkSweepGC
# 10-13:-XX:-UseCMSInitiatingOccupancyOnly
14-:-XX:+UseG1GC
14-:-XX:G1ReservePercent=25
14-:-XX:InitiatingHeapOccupancyPercent=30

## JVM temporary directory
-Djava.io.tmpdir=${ES_TMPDIR}

## heap dumps

# generate a heap dump when an allocation from the Java heap fails
# heap dumps are created in the working directory of the JVM
-XX:+HeapDumpOnOutOfMemoryError

# specify an alternative path for heap dumps; ensure the directory exists and
# has sufficient space
-XX:HeapDumpPath=data

# specify an alternative path for JVM fatal error logs
-XX:ErrorFile=logs/hs_err_pid%p.log

## JDK 8 GC logging
8:-XX:+PrintGCDetails
8:-XX:+PrintGCDateStamps
8:-XX:+PrintTenuringDistribution
8:-XX:+PrintGCApplicationStoppedTime
8:-Xloggc:logs/gc.log
8:-XX:+UseGCLogFileRotation
8:-XX:NumberOfGCLogFiles=32
8:-XX:GCLogFileSize=64m

# JDK 9+ GC logging
9-:-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m
```
Voeg aan een record toe aan je `hosts` file, zodanig je ook kan surfen naar Kibana.

#### Elasticsearch metrics naar InfluxDB pushen.

Pas `.env.telegraf` aan, voeg volgende regel toe:

```
ELASTICSEARCH_HOST=http://elasticsearch:9200
```

Activeer `inputs.elasticsearch` (haal uit commentaar) in `telegraf.conf`:

```
[[inputs.elasticsearch]]
servers = ["${ELASTICSEARCH_HOST}"]
```

Herstart al je services. Als alles goed gaat worden er vanaf heden metrics van Elasticsearch bijgehouden in je InfluxDB's.

##### Elasticsearch dashboard

Surf naar Chronograf. Pas je connectie aan (zoals eerder beschreven in dit document) en zorg dat jet het Elasticsearch dashboard te zien krijgt.

#### Data van InfluxDB naar Elasticsearch pompen.

Om de data in Kibana te analyseren dien je uiteraard de data eerst naar Elasticsearch te sturen. Daarvoor kunnen we gebruik maken van een Telegraf output.

Pas `telegraf.conf` als volgt aan:

```
[[outputs.elasticsearch]]
urls = [ "http://elasticsearch:9200" ] # required.
index_name = "telegraf-%Y.%m.%d" # required.
manage_template = true
template_name = "telegraf"
overwrite_template = true
```

Jammer genoeg zit er een fout in de mapping die Telegraf genereert voor Elasticsearch. Meer bepaald in de mapping voor metrics van Docker. Wat nog erger is, is dat we niet precies kunnen kiezen welke data er al dan niet naar Elasticsearch moet doorgestuurd worden. Gelukkig is er een oplossing.

De mooiste oplossing zou natuurlijk zijn om de mapping te debuggen (dat is zeker een pluspuntje waardig). Maar dat is out-of-scope voor deze module (al moeilijk genoeg, niet?). Daarom maken we het ons makkelijk: schakel de Docker input plugin uit (zet in commentaar).

Maar... we vinden die metrics wel cool, mooi ook om weer te geven in Chronograf. Daarom voegen we een 2de Telegraf node toe!

Voeg volgende toe aan `docker-compose.yml`:

```
  telegraf-docker:
    image: telegraf:1.13.4-alpine
    container_name: telegraf-docker
    env_file:
      - configs/tick/env/.env.telegraf
    labels:
      - traefik.enable=false
    volumes:
      - ./configs/tick/telegraf-docker.conf:/etc/telegraf/telegraf.conf:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - internal
```

`configs/tick/telegraf-docker.conf` ziet er als volgt uit (sterk opgepoest):

```
# Global tags can be specified here in key="value" format.
[global_tags]

# Configuration for telegraf agent
[agent]
  interval = "10s"
  round_interval = true
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  collection_jitter = "0s"
  flush_interval = "10s"
  flush_jitter = "0s"
  precision = ""
  hostname = ""
  omit_hostname = false
  
[[outputs.influxdb]]
  urls = ["http://relay:9096"]
  database = "${INFLUXDB_DB}"
  username = "${INFLUXDB_USER}"
  password = "${INFLUXDB_USER_PASSWORD}"

[[inputs.docker]]
endpoint = "unix:///var/run/docker.sock"
perdevice = true
total = true
```

Herstart nu alle services. Als alles goed gaat hebben we nu dus nog steeds alle metrics in beide InfluxDB's en hebben we alle metrics over ons "systeem" en Elasticsearch in Elasticsearch.

#### Kibana - queries

Surf naar je Kibana node (http://kibana.JOUW-HOSTNAME:jouw-poort). Ga naar de "Dev Tools".

Probeer volgende vraagstellingen op te lossen. Vermeld ook steeds je gebruikte query.

Geef alle indices weer.
> Antwoord
> ...
> ...

Voer eerst volgende query uit (en wacht even):

```
PUT /jouw_telegraf_index/_settings
{
    "index" : {
        "number_of_replicas" : 2
    }
}
```

Geef alle shards weer. Waarom zijn sommige shards "unassigned"?
> Antwoord
> ...
> ...

Geef het totaal aantal hits in je "Telegraf index" (zet size op 0).
> Antwoord
> ...
> ...

Geef 5 *documents* terug waarin de `cpu.usage_user` groter of gelijks is aan 5. (Dus momenten waarop er meer dan 5% CPU gebruik is op `user` niveau.) 
> Antwoord
> ...
> ...

Zoek de maximale `cpu.usage_user` waarde.
> Antwoord
> ...
> ...

Zoek de maximale index tijd voor een record in Elasticsearch.
(tip: elasticsearch_indices.indexing_index_time_in_millis).
> Antwoord
> ...
> ...

Probeer een gelijkaardig resultaat te krijgen aan het vorige. Maar deze keer doe je dat niet met `query` maar met een "sorteer" opdracht. Dit heb je nog niet gezien.
> Antwoord
> ...
> ...

### Traefik metrics

Traefik is geweldig. Fantastisch. En biedt dus ook logs aan, die we rechtstreeks naar InfluxDB kunnen shippen. 

Vervang in `docker-compose.yml` het `command` van de `traefik` service:

```
    command:
      - "--api"
      - "--providers.docker=true"
      # Traefik will listen to incoming request on the port 80 (http)
      - "--entrypoints.web.address=:80"
      - "--metrics"
      - "--metrics.influxDB.address=http://relay:9096"
      - "--metrics.influxDB.protocol=http"
      - "--metrics.influxDB.database=traefik"
      - "--metrics.influxDB.username=admin"
      - "--metrics.influxDB.password=admin"
```

Wanneer je de services herstart zouden de metrics nu in de database `traefik` moeten komen. 

Of toch niet...
![](https://i.imgur.com/ZWBmBGN.png)

De database bestaat nog niet. Relay kan deze voor jou niet zomaar aanmaken. Waarom (tip: zie documentatie Relay)?

> Antwoord
> ...
> ...

De oplossing:

```
curl -u admin:admin -X POST "http://127.0.0.1:9096/admin" --data-urlencode 'q=CREATE DATABASE traefik'
```

Als je dit commando in de terminal uitvoert zal het wel lukken. Je hoeft de services niet te herstarten. Wat doet dit commando eigenlijk?

> Antwoord
> ...
> ...

### Python

Voer volgende tutorial uit: https://www.influxdata.com/blog/getting-started-python-influxdb/. Deze tutorial legt zeer snel en duidelijk uit hoe je Python kan gebruiken om te interageren met InfluxDB.

Een aantal opmerkingen:

* Gebruik niet de Python terminal, maar werk in een script
    * Dien dit samen met je opdracht in
* Je zal moeten werken met twee `clients`.
    * Maak eentje voor writes: `write_client`
    * Maak eentje voor read: `read_client`
* Maak de database `pyexamples` op voorhand aan (zie vorig deeltje)
* De `WHERE`-clause klopt uiteraard niet (meer) i.f.v. voorbeeld JSON

### Grafana

Ja, beste student, je ben er bijna! Zoals tijdens de inleiding van dit labo gezegd, is dit een uitgebreid labo, maar wel eentje waarin zowat alle leerstof tot dusver verwerkt is!

Grafana is een erg populaire visualisatie tool. De tool staat eigenlijk op zichzelf. Je komt het bijvoorbeeld al eens tegen i.p.v. Kibana (of toch voor de visualiaties). Zeer populaire combinatie met Prometheus. Eveneens zeer populair in combinatie, met jawel ..., InfluxDB. Je zal meer "default" dashboards vinden voor Grafana dan voor Chronograf. 

Voeg een `grafana` service toe aan `docker-compose.yml`:

```
  grafana:
    image: grafana/grafana:6.6.2
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
     - grafana:/var/lib/grafana
    environment:
     - GF_SECURITY_ADMIN_PASSWORD=admin
     - GF_USERS_ALLOW_SIGN_UP=false
    labels:
      - "traefik.http.routers.grafana_http.entrypoints=web"
      - "traefik.http.routers.grafana_http.rule=Host(`grafana.${HOSTNAME}.local`)"
      - "traefik.docker.network=web"
    networks:
      - web
      - default
```

Vergeet je `hosts` file niet aan te passen:

```
172.23.82.60		grafana.influxdb-test.local
```

Surf naar Grafana: http://grafana.JOUW-HOSTNAME:JOUW-POORT. Inloggen kan met de gebruikelijke credentials: `admin` / `admin` (kies nooit voor deze **credentials** in een **productieomgeving!**).

Maak een connectie aan als volgt:

![](https://i.imgur.com/HE8ylDp.png)

Maak nog een 2de connectie aan, eentje die toegang heeft tot de `traefik` database (je hoeft geen extra nieuw gebruiker aan te maken, ...). 

Probeer daarna volgende Grafana Dashboards te importeren:

![](https://i.imgur.com/xWpkMtC.png)


https://grafana.com/grafana/dashboards/928
https://grafana.com/grafana/dashboards/10585
https://grafana.com/grafana/dashboards/11584

Maak een screenshot van de dashboards:

> Antwoord
> ...
> ...

#### Extra (optioneel)

Probeer zelf is een dashboard aan te maken.

> Antwoord
> ...
> ...

### Loose ends

Ik wil / wou jullie gerust nog meer werk geven. Fun!? Maar, ik denk dat we ergens moeten stoppen. Hieronder toch nog een aantal opmerkingen / bedenkingen die je (kan ook wel een extra puntje opleveren) zeker mag oplossen.

Wat gaat er gebeuren als InfxluDB niet meer kan volgen? Relay heeft mechanismen om te bufferen, die zijn echter ook niet eindig. Hoe zou je dit kunnen oplossen? Tip: denk aan de vorige labo's.

> Antwoord
> ...
> ...

#### Echte echte loose ends (optional read)

Securitywise gezien kunnen er wel nog heel wat zaken beter. Men kan bv. HTTPS inschakelen waar mogelijk. Betere wachtwoorden gebruiken. Meer users gebruiken, fine-grained rechten, ... Chronograf authenticatie is misschien nog het meest ontbrekende in deze set-up. Zoek gerust uit hoe je dat kan regelen.

Daarnaast staan de wachtwoord simpelweg in `docker-compose.yml` of de desbetreffende config files. Het zou netter zijn moest je deze in environment variabelen steken (dan bedoel ik niet de `env_files`). Als je dit project dan online zet, staat er nergens geen wachtwoord meer in de code. Uiteraard moet je dan wel documenteren welke env vars ingesteld moeten worden. 

De load balancer staat nog niet volledig op punt. Momenteel aanvaarden we enkel HTTP. UDP is tevens gewenst. Daarnaast is de load balancer nog niet opgezet zoals weergegeven in het scema (zie Relay). Dit zou nog een stuk netter kunnen, we zouden dan bv. geen twee clients meer moeten aanmaken in de Python code. Daarnaast zijn Cronograf en Kapacitor nu tevens uitsluitend verbonden met `influxdb01`. Merk ook op dat Kapacitor in principe ook data kan verwerken, gelijkaardig aan Logstash.

Wellicht wil je niet al deze containers op een VM / fysieke machine hebben draaien. Je zou eens kunnen kijken naar Docker Swarm (eenvoudigste oplossing i.s.m. docker-compose) of Kubernetes (Rancher is een interessant stukje software).

De code kan ook nog een stuk netter opgedeeld worden. `docker-compose.yml` is nu toch wel een redelijk stevig bestand geworden. Eigenlijk kan dit opgesplitst worden in verschillende `docker-compose.yml`'en. Bijvoorbeeld eentje apart voor Traefik, eentje voor de TICK-stack, eentje voor de Elastic Stack, eentje voor Kafka / een andere message queue en eentje voor Grafana. Merk dan wel op dat je telkens de `networks` onderaan moet opnoemen. Merk dan ook op dat elke "stack" zijn eigen `internal` netwerk zal hebben. Je zal er wellicht dus nog minstens eentje extra maken waarmee bepaalde componenten met elkaar kunnen communiceren.

Daarnaast hebben we nog maar het topje van de ijsberg gezien wat betreft InfluxDB. De InfluxQL is weliswaar heel gelijkaardig aan klassieke SQL, het heeft toch wel wat interessante toevoegingen. Denk daarbij bijvoorbeeld aan ["InfluxQL Continuous Queries"](https://docs.influxdata.com/influxdb/v1.7/query_language/continuous_queries/).

### Extra

Voor wie er maar niet genoeg van kan krijgen... . Vraag gerust meer info aan je docent. Die zal heel blij zijn.

Ik zou zeggen, probeer de vorige puntjes op te lossen. Daarnaast, misschien nog een ander interessant stukje software (ook los van dit labo) is PiHole. Dit is simpelweg gezegd een "AdBlocker" op DNS niveau. 

Je zou dat als volgt kunnen toevoegen aan je `docker-compose.yml`:

```
  pihole:
    container_name: pihole
    image: pihole/pihole:v4.4
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    expose:
      - 80
      - 443
    environment:
      TZ: "Europe/Brussels"
      WEBPASSWORD: "od3%6^n7C4*AvcFIr^bR"
      DNS1: "1.1.1.1"
      DNS2: "8.8.8.8"
      DNSSEC: "True"
      VIRTUAL_HOST: "pihole.${HOSTNAME}.local"
    # Volumes store your data between container upgrades
    volumes:
      - './pihole/etc-pihole/:/etc/pihole/'
      - './pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/'
    dns:
      - "127.0.0.1"
      - "1.1.1.1"
      - "8.8.8.8"
      - "8.8.4.4"
    # Recommended but not required (DHCP needs NET_ADMIN)
    #   https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
    # --cap-add NET_ADMIN no longer required unless using DHCP, leaving in examples for consistency
    # cap_add:
    #   - NET_ADMIN
    restart: unless-stopped
    labels:
      - "traefik.http.routers.pihole_http.entrypoints=web"
      - "traefik.http.routers.pihole_http.rule=Host(`pihole.${HOSTNAME}.local`)"

      - "traefik.http.middlewares.pihole.addprefix.prefix=/admin"

      - "traefik.http.services.pihole.loadbalancer.server.port=80"
      - "traefik.docker.network=web"
    networks:
      - web
```

In het mapje `pihole/etc-dnsmasq.q` kan je een bestand aanmaken genaamd `02-labo.conf` met volgende inhoud:

```
address=/.JOUW-HOSTNAME.local/JOUW-INTERN-IP
```

Als je dan je DNS-server van je host aanpast, kan je eigenlijk op heel simpele manier alles mooie via DNS accessen (lukt natuurlijk niet achter de NAT in dit labo). Andere hosts in je netwerk, die dus vanaf heden naar de PiHole DNS zullen luisteren kunnen daar dan ook probleemloos aan.

Concreet moet je netplan config er bv. als volgt uitzien:

```
network:
  version: 2
  renderer: networkd
  ethernets:
    ens192:
      dhcp4: yes
      dhcp4-overrides:
        use-dns: false
      nameservers:
        addresses: [127.0.0.1, 8.8.8.8]
      dhcp6: no
```

Om de service te draaien op PiHole zal je Ubuntu overigens ook nog een klein beetje moeten "tunen". Gelukkig staat dat heel snel en duidelijk uitgelegd in [de documenatie](https://github.com/pi-hole/docker-pi-hole#installing-on-ubuntu) van PiHole.

