Blog

Einrichten eines Servers für die cloudbasierte Webentwicklung (4/4)

Veröffentlicht vor einem Monat

authelia
code-server
docker
grafana
traefik
Illustration zum Blogeintrag

Alle Teile der Serie

Im vorangegangenen Teil haben wir die Einrichtung von code-server abgeschlossen. Damit wäre die serverbasierte Entwicklungsumgebung schon voll einsatzfähig. Darüber hinaus ist es aber durchaus sinnvoll, sich die Leistungsdaten (z.B. CPU- Auslatung, RAM- Nutzung und Netzwerkdurchsatz ) des Servers regelmäßig anzuschauen. Da sieht man auf den ersten Blick, ob noch alles ordnungsgemäß läuft. Damit kann man auch abschätzen, ob der gewählte Server für den beabsichtigten Einsatzzweck ausreichend ist oder vielleicht sogar überdimensioniert ist.

Bestandteile des Monitorings

Das Server- Monitoring, das wir einrichten werden, besteht im Wesentlichen aus drei Bestandteilen:

  • Zeitreihendatenbank (Prometheus) zum Speichern der Messwerte
  • ein oder mehrere sogenannte "Exporter" zum Auslesen der Leistungsdaten des Servers
  • Grafana zum Auswerten und Darstellen der Messwerte

Prometheus

Prometheus ist eine sehr beliebte Anwendung im Bereich des Server- Monitorings, da es nicht nur eine Zeitreihendatenbank ist, sondern auch noch das regelmäßige Abfragen der Messwerte von vordefinierten Datenquellen, sogenannte "Exporter", ("Polling") übernimmt.

Da hierfür aber eine etwas aufwändigere Konfiguration notwendig ist, kommt wieder das inwzischen bekannte Schema zum Einsatz, bei dem wir das Basis- Image von Prometheus mit Dockerfile und Github Actions um die Konfigurationsdateien erweitern.

Zuerst erstellen wir also den neuen Ordner prometheus/config im Stammverezeichnis des Repos und dann die Datei prometheus-config.yml im Pfad `prometheus/config/ mit dem folgenden Inhalt:

# ./prometheus/config/prometheus-config.yml

global:
  scrape_interval: 15s # By default, scrape targets every 15 seconds.

scrape_configs:
  # prometheus (optional)
  # - job_name: 'prometheus'
  #   static_configs:
  #     - targets: ['localhost:9090']

  # node_exporter
  - job_name: 'node_exporter'
    static_configs:
      - targets: ['node_exporter:9100']

  # cadvisor
  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

Mit dieser Konfigurationsdatei wird zunächst ein globales Zeitintervall für das Abfragen der Messwerte von 15 Sekunden definiert. Sollte bei bestimmten Messwerten ein anderes Zeitintervall gewünscht werden, kann dieses mit der Direktive scrape_interval auch bei den einzelnen Datenquellen konfiguriert werden.

Unter scrape_configs werden dann die Datenquellen ("Exporter") definiert, von denen Messwerte abgefragt werden sollen. Diese sind in unserem Fall:

  • node_exporter: Daten des Servers
  • cadvisor: Daten der einzelnen Docker- Container

Für die Exporter müssen wir dann später noch die entsprechenden Docker- Container einrichten und starten.

Als nächstes brauchen wir noch ein Dockerfile für die Anweisungen, wie wir das Base Image von Prometheus mit der Konfigurationsdatei erweitern:

# ./prometheus/Dockerfile

# base image
FROM prom/prometheus:v2.33.1

# copy configuration file
RUN mkdir -p /etc/prometheus
COPY ./config/prometheus-config.yml /etc/prometheus/prometheus-config.yml

Hier wird einfach die Konfigurationsdatei, die wir weiter oben erstellt haben während des Build- Prozesses an die richtige Stelle im Container kopiert.

Außerdem brauchen wir noch eine Github Action die unser Docker Image für Prometheus erstellt und in der Github Container Registry bereitstellt. Diese Github Action ist nach demselben Schema aufgebaut, wie auch die Actions zum Erstellen der adneren Docker- Images:

# ./.github/workflows/docker-build-prometheus.yml

name: build custom image for prometheus

on:
  push:
    branches:
      - 'master'
    paths:
      - 'prometheus/**'

jobs:
  build-prometheus:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v2

      - name: Set up Docker BuildX
        uses: docker/setup-buildx-action@v1

      - name: login to GitHub Container Registry
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push the custom Prometheus Image
        uses: docker/build-push-action@v2
        with:
          context: ./prometheus
          push: true
          tags: ghcr.io/<GITHUB_USERNAME>/prometheus:latest

Weiterhin ergänzen wir die docker-compose.yml um die Direktiven zum Starten des Prometheus- Containers:

# ./docker-compose.yml

networks:
  # ...
  monitoring:
    driver: bridge

volumes:
  # ...
  prometheus-data:
    driver: local

services:
  # ...
  ##############
  # Monitoring #
  ##############
  prometheus:
    image: ghcr.io/<GITHUB_USERNAME>/prometheus:latest
    container_name: prometheus
    networks:
      - monitoring
    expose:
      - 9090
    volumes:
      - prometheus-data:/prometheus
    restart: unless-stopped
    labels:
      - 'com.centurylinklabs.watchtower.enable=true'
    command:
      - '--config.file=/etc/prometheus/prometheus-config.yml'

Für die Kommunikation der Container des Monitorings fügen wir ein weiteres lokales Netzwerk mit dem Namen monitoring hinzu und erstellen ein weiteres Volume für die dauerhafte Speicherung der Prometheus Datenbank.

Die Direktiven für den Prometheus- Container sind ja relativ selbsterklärend. Ein wichtiger Punkt ist jedoch, dass wir unter der command- Direktive den Pfad zur Konfigurationsdatei angeben, die wir während des Build- Prozesses in das Image kopiert haben.

Datenquellen ("Exporter")

Wie oben schon erwähnt nutzen wir zwei Datenquellen für unser Monitoring. Die Konfiguration dieser Datenquellen ist aber so einfach, dass wir lediglich unsere docker-compose.yml um die folgenden Zeilen erweiteren müssen:

# ./docker-compose.yml

services:
  # ...
  node_exporter:
    image: quay.io/prometheus/node-exporter:latest
    container_name: node_exporter
    command:
      - '--path.rootfs=/host'
    pid: host
    restart: unless-stopped
    volumes:
      - '/:/host:ro,rslave'
    labels:
      - 'com.centurylinklabs.watchtower.enable=true'
    networks:
      - monitoring

  cadvisor:
    image: google/cadvisor:latest
    container_name: cadvisor
    networks:
      - monitoring
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    labels:
      - 'com.centurylinklabs.watchtower.enable=true'
    devices:
      - /dev/kmsg
    restart: unless-stopped

Beiden Containern müssen wir umfangreichen Zugriff auf das Host- System gewähren, damit die gewünschten Daten auch ausgelesen werden können. Dazu werden jeweils mehrere Volumes definiert, für die aber mit dem Zusatz :ro nur Leserechte gewährt werden.

Grafana

Auch die Definition des Containers für Grafana erfolgt ausschließlich in der docker-compose.yml. Wir fügen dort also noch folgende Zeilen ein:

# ./docker-compose.yml

volumes:
  #...
  grafana-data:
    driver: local

services:
  # ...
  grafana:
    image: grafana/grafana-oss:latest
    container_name: grafana
    networks:
      - proxy
      - monitoring
    expose:
      - 3000
    volumes:
      - grafana-data:/var/lib/grafana
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.grafana.rule=Host(`grafana.server.<MY-DOMAIN>`)"
      - "traefik.http.routers.grafana.entrypoints=https"
      - "traefik.http.routers.grafana.tls=true"
      - "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
      - "traefik.http.routers.grafana.middlewares=authelia@docker"
      - "traefik.docker.network=proxy"
      - "com.centurylinklabs.watchtower.enable=true"
    environment:
      - "GF_AUTH_ANONYMOUS_ENABLED=true"
      - "GF_AUTH_ORG_ROLE=Editor"
      - "GF_AUTH_DISABLE_LOGIN_FORM=true"
      - "GF_AUTH_ANONYMOUS_ORG_ROLE=Admin"

Da wir ja bereits mit Authelia eine Authentifizierung für Aufrufe des Grafana Dashboards eingerichtet haben, nutzen wir die Umgebungsvariablen, um das in Grafana eingebaute Authentifizierungssystem auszuschalten. Außerdem fügen wir den Grafana- Container neben dem Monitoring- Netzwerk auch noch zum Netzwerk Proxy hinzu, um Verbindungen aus dem Internet über den Traefik Reverse Proxy zu ermöglichen.

Monitoring- Container starten

Nachdem wir unsere Änderungen am Repo committed und auf Github gepusht haben, müssen wir noch ein bisschen warten, bis das neue Prometheus- Image erstellt wurde. Danach können wir uns per SSH auf dem Server einloggen, den Ordner mit dem Repo aufrufen und mit dem folgenden Befehl docker-compose down alle laufenden Container stoppen.

Als nächsten holen wir uns die neueste Version mit git pull origin main von Github und starten mit docker-compose up -d alle Container wieder (inklusive der neuen Monitoring- Container).

Grafana- Dashboards einrichten

Nun können wir unsers Grafana- Instanz im Browser aufrufen (grafana.server.<MY-DOMAIN>) und in der GUI noch die Einrichtung abschließen sowie die gewünschten Dashboards konfigurieren.

Zunächst fügen wir Prometheus als Datenquelle für Grafana hinzu. Hierzu fahren wir mit der Maus auf das Zahnrad in der linkeren unteren Ecke des Bildschirms und wählen im Menü "Data Sources" aus. Ein Klick auf "Add Data Source" bringt uns zu einer Liste mit verschieden Datenquellen, von der wir natürlich "Prometheus" auswählen. Die Einstellungen könnenfast alle auf der Voreinstellung verbleiben. Nur bei URL tragen wir http://prometheus:9090 ein, um auf den Prometheus- Container zugreifen zu können.

Nun könnten wir unsere Dashboards mit den Daten aus Prometheus selbst erstellen. Das ist aber eine Menge Arbeit, die sich zum Glück schon andere gemacht haben. Auf der Seite https://grafana.com/grafana/dashboards/ gibt es eine Vielzahl von fertigen Dashboards, die sich sehr einfach in Grafana einbinden lassen. Ich nutze die folgende beiden Dashboards:

Die Einbindung dieser beiden Dashboards erfolgt ganz einfach über die ID, die auf der Seite in der Seitenleiste rechts angegeben ist. Für "Node Exporter Full" ist das 1860 und für "Cadvisor Exporter" 14282. In Grafana klicken wir auf der linken Seite auf das Icon für die Dashboards (vier Quadrate), danach auf den Button "New" und wählen aus dem Menü "Import" aus. Im Feld "Import via grafana.com" muss die ID des gewünschten Dashboards eingfügt und mit Klick auf "Load" bestätigt werden. Nach einigen Augenblicken öffnet sich das Dashboard und die ersten Messwerte werden angezeigt.

Grafana Dashboard

Da die Container für die Erfassung der Messwerte ja erst seit ein paar Minuten laufen, gibt es natürlich auch nur für diesen kurzen Bereich Messwerte und es könnte sein, dass mit dem standardmäßig eingestellten Zeitintervall von 24 Stunden für die Anzeige noch nichts zu erkennen ist. Über einen Button in der oberen rechten Ecke kann aber das Zeitintervall angepasst werden.

Fazit

Das war jetzt zwar ne ganze Menge Arbeit, aber nun haben wir einen eigenen Server, mit dem wir die Webentwicklung vom Endgerät entkoppeln und in die Cloud verlegen können. Außerdem ist ein solcher Server auch ein toller Startpunkt, um weitere Dienste wie zum Beispiel einer Datenbank oder einer eigenen Nextcloud- Instanz hinzuzufügen. Den Server könnte man aber auch nutzen, um selbst entwickelte Apps und Webseiten zu hosten. Nach meiner Erfahrung ist der Server mit der beschriebenen Einrichtung noch keineswegs ausgelastet und bietet noch viel Raum für Erweiterungen.

Den gesamten Code und die Konfigurationsdateien, die in den vier Blogeinträgen dieser Serie beschrieben wurden, können auch in meinem Github Repository eingesehen und heruntergeladen werden.

Zum Abschluss möchte ich noch Danke sagen, dass du dir die Zeit zum Lesen dieser Serie genommen hast, und hoffe, dass du dabei auch die ein oder andere Sache gelernt hast.