Recently we felt the need to collect anonymous metrics about how our users are visiting our websites.
After some investigation, the development team proposed using Matomo: a free and open source software project which collects usage metrics while guaranteeing 100% privacy alongside the chance to be self-hosted.
We decided to install it on OpenShift, since Matomo provides several ways to do that.
The first alternative we tried was matomo-openshift which was supposed to work out of the box, but what we didn’t like was the necessity to also install openshift-developer-tools. Furthermore the solution didn’t work as expected, requiring quite a lot of time, and finally we gave up. Furthermore that project has only 2 stars on GitHub and its last update is more than one year old as of writing this blog post.
A second alternative was to use bitnami helm chart. We succeeded in installing Matomo from console, but we didn’t like granting anyuid permission to the console to make it work properly.
Finally we decided to go for a Matomo Docker container and deploy it via OpenShift GitOps (aka ArgoCD)
To do this you need to create:
kind: Deployment
apiVersion: apps/v1
metadata:
name: matomo
namespace: matomo
labels:
app: matomo
spec:
replicas: 1
selector:
matchLabels:
app: matomo
template:
metadata:
labels:
app: matomo
spec:
containers:
- name: matomo
image: matomo:latest
ports:
- containerPort: 80
protocol: TCP
securityContext:
runAsUser: 0
privileged: false
imagePullPolicy: Always
env:
- name: MATOMO_LOG_LOG_WRITERS
value: "file"
- name: MATOMO_LOG_LOG_LEVEL
value: "DEBUG"
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: matomo
key: "***"
- name: MYSQL_DATABASE
value: "matomo"
- name: MYSQL_USER
value: "***"
- name: MATOMO_DATABASE_ADAPTER
value: "mysql"
- name: MATOMO_DATABASE_TABLES_PREFIX
value: "matomo_"
- name: MATOMO_DATABASE_USERNAME
value: "***"
- name: MATOMO_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: matomo
key: "***"
- name: MATOMO_DATABASE_DBNAME
value: "***"
- name: MARIADB_AUTO_UPGRADE
value: "1"
- name: MARIADB_INITDB_SKIP_TZINFO
value: "1"
volumeMounts:
- name: matomo
mountPath: "/var/www/html"
restartPolicy: Always
volumes:
- name: matomo
persistentVolumeClaim:
claimName: matomo
Please note some details like:
You can also create a similar deployment for MariaDB and store the root password in a secret. If you want to use a single pod you can basically reuse the same “container” section and
kind: Deployment
apiVersion: apps/v1
metadata:
name: matomo-db
namespace: matomo
labels:
app: matomo-db
spec:
replicas: 1
selector:
matchLabels:
app: matomo-db
template:
metadata:
labels:
app: matomo-db
spec:
containers:
- name: db
image: mariadb:10.11
ports:
- containerPort: 3306
env:
- name: MARIADB_AUTO_UPGRADE
value: "1"
- name: MARIADB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: matomo
key: "root-pass"
- name: MARIADB_DISABLE_UPGRADE_BACKUP
value: "1"
volumeMounts:
- name: matomo-db
mountPath: "/var/lib/mysql"
restartPolicy: Always
volumes:
- name: matomo-db
persistentVolumeClaim:
claimName: matomo-db
To expose deployments you can just create two services: one to expose MariaDB port 3306 (if you deploy it as a separate pod) and the other one to expose Matomo’s port 80: in our case we perform TLS termination at ingress level so then Matomo will be reachable on port 443 with TLS. Since this is a pretty standard setup we won’t show it here.
Due to security reasons we decided to expose the Matomo GUI only on the internal network, but Matomo must be reachable from outside to collect metrics since it works at the browser level. To achieve that we exposed only the two files required:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: matomo-external-ingress
namespace: matomo
spec:
rules:
- host: ...
http:
paths:
- backend:
service:
name: matomo
port:
number: 80
path: /matomo.php
pathType: Prefix
- backend:
service:
name: matomo
port:
number: 80
path: /matomo.js
pathType: Prefix
Also in this case we performed TLS termination at ingress level, so we have to expose port 80 for OpenShift.
As mentioned before Matomo needs to run as user 0, this doesn’t mean that it needs to be privileged but it requires anyuid permissions. You can create a role binding like the following:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: 'matomo-anyuid'
namespace: matomo
subjects:
- kind: ServiceAccount
name: matomo-sa
namespace: matomo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: 'system:openshift:scc:anyuid'
Here we described our solution for deploying Matomo on an OpenShift cluster. We hope that in the future we’ll see an official operator for that, but at the moment we’ve been using this solution in production for several weeks and it’s still working.
Did you find this article interesting? Are you an “under the hood” kind of person? We’re really big on automation and we’re always looking for people in a similar vein to fill roles like this one as well as other roles here at Würth Phoenix.