Software Berater Logo Software Berater #neuland seit 1993

Matomo mit Docker

Ich habe die Software Matomo (früher Piwik) mit Docker auf meinem eigenen Server installiert.

Matomo (früher “Piwik”) ist eine Software zur Analyse von Zugriffen auf Websites, ähnlich wie Google Analytics. Im Unterschied dazu betreibt man Matomo auf dem eigenen Server, gibt also für die Analyse keine Daten an Dritte weiter. Auch wenn ich privat zahlreiche Google-Dienste nutze, wollte ich für diese Webseite lieber nicht Analytics einsetzen, um die Privatsphäre meiner Leser zu schützen.

Matomo Ansicht

Nun ist Matomo durchaus recht umfangreich, eine ordentliche Installation nicht mal eben so gemacht. Hier bietet sich Docker an, um möglichst wenige Komponenten auf der eigenen Maschine installieren (und pflegen!) zu müssen. Schematisch schaut das dann so aus:

Matomo Setup

Der nginx-Server oben nimmt Webanfragen entgegen, den habe ich sowieso installiert. Dort sind auch SSL-Zertifikate hinterlegt. Nginx wiederum gibt als reverse proxy die Anfragen weiter an 3 separate Prozesse, die als Docker Container laufen:

  • Mysql Datenbank
  • Matomo Anwendung
  • nginx Frontend

Man fragt sich: Wozu der zusätzliche nginx-Prozess? Das Matomo-Docker-Image stellt keinen Webserver bereit, sondern ein FastCGI Interface. Es bedarf also eines zusätzlichen Webservers, der http-Anfragen an FastCGI übersetzt. Dazu kommen die statischen HTML-Bestandteile von Matomo, die im Docker-Image verborgen sind und einem externen Webserver nicht zugänglich sind.

Installation

Auf meinem Server ist Debian 9.6 (Stretch) installiert. Zusätzlich findest du hier eine Anleitung zur Installation von Docker und hier eine für die Installation von docker-compose. Um die Installation zu vollenden, sind diese Schritte auszuführen:

  1. User auf dem Server anlegen
  2. Docker-Dateien im $HOME dieses Users anlegen
  3. systemd-Eintrag erzeugen
  4. Frontend-Webserver konfigurieren

1. User anlegen

Ich lege einen Nutzer matomo an, dem die Dateien später gehören sollen. Dieser Account darf sich nicht interaktiv anmelden und erhält daher kein Passwort. Der Nutzer ist Mitglied der Gruppe docker, was dem Nutzer weitreichende Rechte einräumt. Achtung!

adduser --disabled-login matomo
adduser matomo docker
su - matomo

2. Docker-Dateien erzeugen

Wir brauchen drei Docker-Prozesse für Datenbank, Anwendung und Frontend. Um diese gebündelt zu definieren und zu starten, verwende ich docker-compose. Das Composefile docker-compose.yml schaut so aus:

db:
  image: mariadb:latest
  volumes:
    - ./mysql/runtime2:/var/lib/mysql
  environment:
    - MYSQL_ALLOW_EMPTY_PASSWORD=1
app:
  image: matomo:fpm
  links:
    - db
  volumes:
    - ./config:/var/www/html/config:rw
    - ./logs:/var/www/html/logs
  env_file:
    - ./matomo.env
web:
  image: nginx:latest
  volumes:
    - ./docker-nginx.conf:/etc/nginx/nginx.conf:ro
  links:
    - app
  volumes_from:
    - app
  ports:
    - 127.0.0.1:8001:80

Die Datei docker-nginx.conf konfiguriert den inneren Nginx-Server. Der Pfad /var/www/html bezeichnet den Pfad innerhalb des Docker-Containers der Anwendung, so schaut das dann aus:

user www-data;

events {
  worker_connections 768;
}

http {
  upstream backend {
    server app:9000;
  }

  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  gzip on;
  gzip_disable "msie6";
  
  server {
    listen 80;

    root /var/www/html/;
    index index.php index.html index.htm;

    location / {
      try_files $uri $uri/ =404;
    }

    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
      root /usr/share/nginx/html;
    }

    location = /favicon.ico {
      log_not_found off;
      access_log off;
    }
   
    location ~ \.php$ {
      fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
      fastcgi_param  SERVER_SOFTWARE    nginx;
      fastcgi_param  QUERY_STRING       $query_string;
      fastcgi_param  REQUEST_METHOD     $request_method;
      fastcgi_param  CONTENT_TYPE       $content_type;
      fastcgi_param  CONTENT_LENGTH     $content_length;
      fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
      fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
      fastcgi_param  REQUEST_URI        $request_uri;
      fastcgi_param  DOCUMENT_URI       $document_uri;
      fastcgi_param  DOCUMENT_ROOT      $document_root;
      fastcgi_param  SERVER_PROTOCOL    $server_protocol;
      fastcgi_param  REMOTE_ADDR        $remote_addr;
      fastcgi_param  REMOTE_PORT        $remote_port;
      fastcgi_param  SERVER_ADDR        $server_addr;
      fastcgi_param  SERVER_PORT        $server_port;
      fastcgi_param  SERVER_NAME        $server_name;
      fastcgi_intercept_errors on;
      fastcgi_pass backend;
    }
  }
}

Zuletzt brauchen wir noch eine Datei namens matomo.env mit der Umgebungsvariablen der Anwendung gesetzt werden können:

TZ=Europe/Berlin
REAL_IP_HEADER=X-Forwarded-For
LOG_LEVEL=DEBUG

Wenn alles fertig ist, solltest du jetzt diese Dateien angelegt haben:

/home/matomo# ls -l
total 24
-rw-r--r-- 1 matomo   matomo  443 Jan  4 15:45 docker-compose.yml
-rw-r--r-- 1 matomo   matomo   66 Jan  4 15:40 matomo.env
-rw-r--r-- 1 matomo   matomo 1649 Jan  4 15:34 nginx.conf

Alles testen

Um deine Installation zu testen, kannst du sie als Nutzer matomo starten und stoppen:

$ docker-compose up
...
# jetzt sollte der lokale Port 127.0.0.1:8081 verfügbar sein
$ docker-compose down

Welcher Port genau verwendet wird, ergibt sich aus der letzten Zeile der Datei docker-compose.yml die den (Docker-internen) Port 80 des Frontend-nginx auf den nach aussen sichtbaren Port 8081 des Hosts umleitet. Beim ersten Start sollte dir Matomo die Konfigurationsseite präsentieren, in der man zB die Verbindung zur Datenbank angeben muss. Verwende dazu folgende Werte:

NameWert
Hostdb
DBmatomo
user root
Passwort (leer)

Der Datenbankhost heisst db, weil das in der Datei docker-compose.yml oben so definiert ist. Die Container können untereinander diese Namen auflösen.

3. systemd Service definieren

Damit meine Matomo Instanz automatisch und im Hintergrund startet, verwende ich unter Debian 9.6 (stretch) systemd. Meine Dienstdefinition in /etc/systemd/system/matomo.service schaut so aus:

[Unit]
Description=Matomo service with docker compose
Requires=docker.service
After=docker.service nginx.service

[Service]
RestartSec=1
Restart=on-failure

User=matomo
WorkingDirectory=/home/matomo

ExecStart=/usr/local/bin/docker-compose -f /home/matomo/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f /home/matomo/docker-compose.yml stop

# use syslog for logging
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=matomo

[Install]
WantedBy=multi-user.target

Dieser Dienst muss jetzt noch durch folgende zwei Kommandos aktiviert und gestartet werden:

systemctl enable matomo
systemctl start matomo

Weitere Informationen zu systemd findest du zB unter https://wiki.debian.org/systemd

4. Frontend Webserver mit nginx

Nachdem jetzt die drei Docker-Container definiert, verbunden und gestartet sind, ist der Port 8081 absichtlich so gestaltet, dass er von außen nicht zugänglich ist. Das erlauben wir über den Standard-Webserver, im Schaubild ganz oben die oberste Box.

Es gibt wahrscheinlich eine Millionen Wege, nginx oder Apache zu diesem Zweck zu konfigurieren. In meinem Setup verwende ich eine nginx-Datei pro virtuellem Server: Der Hostname lautet hier stats.software-berater.net und die Datei dazu liegt in /etc/nginx/sites-available/stats.software-berater.net.conf. So schaut sie aus:

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;

  server_name stats.example.com;

  # managed by Certbot, redacted

  ssl_certificate foo;
  ssl_certificate_key bar;

  location / {
    proxy_pass http://127.0.0.1:8001;
  }
}

Das SSL-Zertifikat erstelle ich mit LetsEncrypt, mit dem Kommando certbot --nginx lasse ich mir neue Zertifikate erstellen und hinterlegen. Jetzt sollte deine brandneue Matomo-Instanz auf dem Hostnamen deiner Wahl SSL-geschützt im Netz sein. Herzlichen Glückwunsch!