Erwin Müller

Mon­i­tor­ing Word­Press with Prometheus in a Ku­ber­netes Clus­ter

In­tro­duc­tion

Grafana Dash­board for Word­Press Met­rics

This ar­ti­cle will de­scribe how to mon­i­tor a Word­Press in­stal­la­tion with Prometheus. The set­up re­quires a work­ing Ku­ber­netes clus­ter with the Prometheus Op­er­a­tor in­stalled. This ar­ti­cle will de­scribe ba­sic three use cas­es.

Use Cas­es

Prometheus met­ric use cas­es

Word­Press Met­rics

We want to see ba­sic met­rics about out site, like how many posts are pub­lished, how many com­ments there are on our site and how many users are reg­is­tered.

Site La­ten­cy

We want to see the site la­ten­cy in mil­lisec­onds to our site.

Down Alert­ing

We want to be in­formed if our site goes of­fline for what­ev­er rea­sons.

Met­rics Projects

Sur­pris­ing­ly, there are not many Prometheus mon­i­tor­ing plu­g­ins for Word­Press. A search in Google shows just four re­sults. Here is a list of projects that we will use for our use case.

https://​github​.com/​k​o​t​s​i​s​/​w​o​r​d​p​r​e​s​s​_​e​x​p​o​r​ter

This project pro­vides Word­Press met­rics for Prometheus by read­ing di­rect­ly the MySQL data­base and is writ­ten in Go. This project is in­ter­est­ing be­cause we can learn some ba­sic Go and we will use it to cre­ate a Dock­er con­tain­er that we run along­side Word­Press in our clus­ter to pro­vide met­rics to Prometheus.

https://​github​.com/​o​r​i​g​a​m​a​/​w​o​r​d​p​r​e​s​s​-​e​x​p​o​r​t​e​r​-​p​r​o​m​e​t​h​eus

This is a very sim­ple Word­Press plu­g­in that can pro­vide met­rics to Prometheus. We will al­so use this plu­g­in be­cause then we can have the la­ten­cy of our Word­Press site in ad­di­tion to the usu­al Word­Press met­rics. But more on that lat­er.

Here are some projects that are not use­ful for our use case, but are men­tioned for com­plete­ness.

https://​github​.com/​P​e​t​e​r​B​o​o​k​e​r​/​w​p​-​p​r​o​m​p​r​ess

The au­thor writes that it sup­pose to be a Word­Press Prometheus client, but the last com­mit is still just “ini­tial file struc­ture” one year ego. My guess is that this project is not de­vel­oped any­more.

https://​de​.word​press​.org/​p​l​u​g​i​n​s​/​w​o​o​-​p​r​o​m​e​t​h​e​u​s​-​m​e​t​r​i​cs/

This is a Word­Press plu­g­in that pro­vides met­rics specif­i­cal­ly for Woocom­merce. This is not what we want here of course.

wordpress_exporter

https://​github​.com/​d​e​v​e​n​t​/​w​o​r​d​p​r​e​s​s​_​e​x​p​o​r​ter

https://​cloud​.dock​er​.com/​u​/​e​r​w​i​n​8​2​/​r​e​p​o​s​i​t​o​r​y​/​d​o​c​k​e​r​/​e​r​w​i​n​8​2​/​w​o​r​d​p​r​e​s​s​_​e​x​p​o​r​ter

The “wordpress_exporter” project will pro­vide ba­sic Word­Press met­rics by read­ing the MySQL data­base and it will cov­er our first use case. It can not give us the site la­ten­cy since the “wordpress_exporter” runs as a sep­a­rate con­tain­er and for the same rea­son it can not give us the down alert­ing. As the first step I have forged the orig­i­nal project on Github and cre­at­ed a Dock­er­file for it. We set­up Dock­er Hub to cre­ate au­to­mat­ic builds for it. For that I have fol­lowed the guide pro­vid­ed: Build­ing Dock­er Con­tain­ers for Go Ap­pli­ca­tions.

The re­sult is a Dock­er im­age that will run a con­tain­er for the Go “wordpress_exporter”. It will pro­vide a met­ric at the URL http://wordpress-exporter:8888/metrics.

Now we can de­ploy the “wordpress_exporter” on our Ku­ber­netes clus­ter.

---

apiVersion: v1
kind: Service
metadata:
  name: wordpress-exporter
  namespace: www-muellerpublic-de
  labels:
    app: wordpress-exporter
    tier: metrics
spec:
  ports:
  - name: "metrics"
    port: 8888
    targetPort: 8888
  selector:
    app: wordpress-exporter

---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress-exporter
  namespace: www-muellerpublic-de
  labels:
    app: wordpress-exporter
    tier: metrics
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress-exporter
      tier: metrics
  template:
    metadata:
      labels:
        app: wordpress-exporter
        tier: metrics
    spec:
      containers:
      - image: erwin82/wordpress_exporter:latest
        name: wordpress-exporter
        env:
        - name: WORDPRESS_DB_NAME
          value: "wordpressdb"
        - name: WORDPRESS_DB_USER
          value: "wordpress"
        - name: WORDPRESS_DB_PASSWORD
          value: "wordpress1234"
        - name: WORDPRESS_DB_HOST
          value: "db"
        - name: WORDPRESS_DB_PORT
          value: "3306"
        ports:
        - containerPort: 8888
          name: "metrics"
      restartPolicy: Always

---

We will cre­ate a new Promethus Ser­vice­Mon­i­tor for the Prometheus op­er­a­tor.

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: wordpress-exporter
  namespace: www-muellerpublic-de
spec:
  selector:
    matchLabels:
      app: wordpress-exporter
  endpoints:
- port: metrics

Since the Promethus serv­er runs in the name­space “mon­i­tor­ing” we need to cre­ate RoleBind­ing and a Role to al­low the serv­er ac­cess to the name­space where our Word­Press de­ploy­ment is.

---

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: prometheus-k8s
  namespace: www-muellerpublic-de
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: prometheus-k8s
subjects:
- kind: ServiceAccount
  name: prometheus-k8s
  namespace: monitoring

---

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: prometheus-k8s
  namespace: www-muellerpublic-de
rules:
- apiGroups:
  - ""
  resources:
  - services
  - endpoints
  - pods
  verbs:
  - get
  - list
  - watch

---

If every­thing was cor­rect­ly de­ployed then we should now see a new tar­get in our Prometheus serv­er.

http://prometheus/targets

The “wordpress_exporter” ex­ports the fol­low­ing met­rics:

wp_num_comments_metric{endpoint=“metrics”,instance=“10.244.1.40:8888”,job=“wordpress-exporter”,namespace=“www-muellerpublic-de”,pod=“wordpress-exporter-5f647597-drvzf”,service=“wordpress-exporter”}

wp_num_posts_metric{endpoint=“metrics”,instance=“10.244.1.40:8888”,job=“wordpress-exporter”,namespace=“www-muellerpublic-de”,pod=“wordpress-exporter-5f647597-drvzf”,service=“wordpress-exporter”}

wp_num_users_metric{endpoint=“metrics”,instance=“10.244.1.40:8888”,job=“wordpress-exporter”,namespace=“www-muellerpublic-de”,pod=“wordpress-exporter-5f647597-drvzf”,service=“wordpress-exporter”}

origa­ma/­word­press-ex­porter-prometheus

The project is a Word­Press plu­g­in that pro­vides met­rics un­der the end­point http://wordpress/wp-json/metrics and we will use the Prometheus Ad­di­tion­al Scrape Con­fig­u­ra­tion to add the end­point to our Prometheus server.The in­stal­la­tion is very sim­ple. Just down­load the zip archive some­where and use Word­Press to in­stall the archive as a new plu­g­in. The plu­g­in will use the Word­Press API to pro­vide ba­sic Word­Press met­rics like num­ber of posts, com­ments and users. What im­por­tant for us is that the plu­g­ins runs in­side Word­Press so we can use it to re­turn the site la­ten­cy time and cre­ate an alert if the site be­comes un­reach­able.

Af­ter the in­stal­la­tion of the Word­Press plu­g­in we add the ad­di­tion­al scrape con­fig­u­ra­tion. For that we cre­ate the file “prometheus-additional.yaml” with the fol­low­ing con­tent.

- job_name: "www-muellerpublic-de"
  static_configs:
  - targets: ["www.muellerpublic.de", "www.mueller-public.de"]
  scrape_interval: "30s"
  metrics_path: "/wp-json/metrics"
  scheme: "https"

Now we cre­ate a Ku­ber­netes se­cret from this file.

kubectl create secret generic additional-scrape-configs --from-file=prometheus-additional.yaml --dry-run -oyaml > additional-scrape-configs.yaml

We de­ploy it in­to the “mon­i­tor­ing” name­space.

kubectl -n monitoring apply -f additional-scrape-configs.yaml

We ed­it the Prometheus cus­tom re­source de­f­i­n­i­tion to ref­er­ence our se­cret and add the fol­low­ing snip­pet.

kubectl -n monitoring edit prometheus k8s
spec:
  additionalScrapeConfigs:
    key: prometheus-additional.yaml
    name: additional-scrape-configs

We should now have an ad­di­tion­al scrape tar­get in our Prometheus serv­er.

http://prometheus/targets

The plu­g­in ex­ports sim­i­lar met­rics as the “wordpress_exporter” con­tain­er.

Grafana

Af­ter we have con­fig­ures our scrape tar­gets we can now set­up a nice Grafana dash­board to see the met­rics and to be in­formed if our Word­Press site be­comes un­reach­able for some rea­son. I have pre­pared a Grafana dash­board for that. It re­quires Grafana 6 and a Prometheus serv­er as the da­ta source.

{
  "__inputs": [
    {
      "name": "DS_PROMETHEUS",
      "label": "prometheus",
      "description": "",
      "type": "datasource",
      "pluginId": "prometheus",
      "pluginName": "Prometheus"
    }
  ],
  "__requires": [
    {
      "type": "grafana",
      "id": "grafana",
      "name": "Grafana",
      "version": "6.0.1"
    },
    {
      "type": "panel",
      "id": "graph",
      "name": "Graph",
      "version": "5.0.0"
    },
    {
      "type": "datasource",
      "id": "prometheus",
      "name": "Prometheus",
      "version": "5.0.0"
    },
    {
      "type": "panel",
      "id": "table",
      "name": "Table",
      "version": "5.0.0"
    }
  ],
  "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": null,
  "iteration": 1558268165521,
  "links": [],
  "panels": [
    {
      "aliasColors": {},
      "bars": false,
      "dashLength": 10,
      "dashes": false,
      "datasource": "${DS_PROMETHEUS}",
      "fill": 1,
      "gridPos": {
        "h": 8,
        "w": 12,
        "x": 0,
        "y": 0
      },
      "id": 14,
      "interval": "",
      "legend": {
        "avg": true,
        "current": false,
        "max": false,
        "min": false,
        "show": true,
        "total": false,
        "values": true
      },
      "lines": true,
      "linewidth": 1,
      "links": [],
      "nullPointMode": "null",
      "paceLength": 10,
      "percentage": false,
      "pointradius": 2,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "stack": false,
      "steppedLine": false,
      "targets": [
        {
          "expr": "avg_over_time(scrape_duration_seconds{job=\"$namespace\"}[30m])",
          "format": "time_series",
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "{{instance}}",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeFrom": null,
      "timeRegions": [],
      "timeShift": null,
      "title": "Scrape Duration",
      "tooltip": {
        "shared": true,
        "sort": 0,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "buckets": null,
        "mode": "time",
        "name": null,
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "format": "s",
          "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
      }
    },
    {
      "cacheTimeout": null,
      "columns": [
        {
          "text": "Current",
          "value": "current"
        }
      ],
      "datasource": "${DS_PROMETHEUS}",
      "fontSize": "100%",
      "gridPos": {
        "h": 8,
        "w": 7,
        "x": 12,
        "y": 0
      },
      "id": 12,
      "links": [],
      "pageSize": null,
      "scroll": true,
      "showHeader": true,
      "sort": {
        "col": 0,
        "desc": true
      },
      "styles": [
        {
          "alias": "Up",
          "colorMode": "cell",
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "decimals": 0,
          "pattern": "Current",
          "thresholds": [
            "0",
            "1"
          ],
          "type": "number",
          "unit": "short"
        },
        {
          "alias": "Instance",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": 2,
          "mappingType": 1,
          "pattern": "Metric",
          "sanitize": false,
          "thresholds": [],
          "type": "string",
          "unit": "short",
          "valueMaps": []
        }
      ],
      "targets": [
        {
          "expr": "up{job=\"$namespace\"}",
          "format": "time_series",
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "A"
        }
      ],
      "timeFrom": null,
      "timeShift": null,
      "title": "Up",
      "transform": "timeseries_aggregations",
      "type": "table"
    },
    {
      "gridPos": {
        "h": 1,
        "w": 24,
        "x": 0,
        "y": 8
      },
      "id": 8,
      "title": "Statistics",
      "type": "row"
    },
    {
      "aliasColors": {},
      "bars": true,
      "cacheTimeout": null,
      "dashLength": 10,
      "dashes": false,
      "datasource": "${DS_PROMETHEUS}",
      "decimals": 0,
      "fill": 1,
      "gridPos": {
        "h": 9,
        "w": 7,
        "x": 0,
        "y": 9
      },
      "id": 6,
      "legend": {
        "avg": false,
        "current": false,
        "max": false,
        "min": false,
        "show": true,
        "total": false,
        "values": false
      },
      "lines": true,
      "linewidth": 1,
      "links": [],
      "nullPointMode": "null",
      "paceLength": 10,
      "percentage": false,
      "pointradius": 2,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "stack": false,
      "steppedLine": true,
      "targets": [
        {
          "expr": "wp_num_comments_metric{namespace=\"$namespace\"}",
          "format": "time_series",
          "instant": false,
          "interval": "",
          "intervalFactor": 10,
          "legendFormat": "$namespace",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeFrom": null,
      "timeRegions": [],
      "timeShift": null,
      "title": "Number of comments",
      "tooltip": {
        "shared": true,
        "sort": 0,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "buckets": null,
        "mode": "time",
        "name": null,
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "decimals": 0,
          "format": "short",
          "label": "",
          "logBase": 1,
          "max": null,
          "min": null,
          "show": true
        },
        {
          "decimals": null,
          "format": "short",
          "label": "",
          "logBase": 1,
          "max": null,
          "min": null,
          "show": true
        }
      ],
      "yaxis": {
        "align": false,
        "alignLevel": null
      }
    },
    {
      "aliasColors": {},
      "bars": true,
      "cacheTimeout": null,
      "dashLength": 10,
      "dashes": false,
      "datasource": "${DS_PROMETHEUS}",
      "fill": 1,
      "gridPos": {
        "h": 9,
        "w": 6,
        "x": 7,
        "y": 9
      },
      "id": 2,
      "legend": {
        "avg": false,
        "current": false,
        "max": false,
        "min": false,
        "show": true,
        "total": false,
        "values": false
      },
      "lines": true,
      "linewidth": 1,
      "links": [],
      "nullPointMode": "null",
      "paceLength": 10,
      "percentage": false,
      "pointradius": 2,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "stack": false,
      "steppedLine": true,
      "targets": [
        {
          "expr": "wp_num_users_metric{namespace=\"$namespace\"}",
          "format": "time_series",
          "interval": "",
          "intervalFactor": 10,
          "legendFormat": "$namespace",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeFrom": null,
      "timeRegions": [],
      "timeShift": null,
      "title": "WordPress number of registered users",
      "tooltip": {
        "shared": true,
        "sort": 0,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "buckets": null,
        "mode": "time",
        "name": null,
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "decimals": 0,
          "format": "short",
          "label": "",
          "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": true,
      "dashLength": 10,
      "dashes": false,
      "datasource": "${DS_PROMETHEUS}",
      "fill": 1,
      "gridPos": {
        "h": 9,
        "w": 6,
        "x": 13,
        "y": 9
      },
      "id": 4,
      "legend": {
        "avg": false,
        "current": false,
        "max": false,
        "min": false,
        "show": true,
        "total": false,
        "values": false
      },
      "lines": true,
      "linewidth": 1,
      "links": [],
      "nullPointMode": "null",
      "paceLength": 10,
      "percentage": false,
      "pointradius": 5,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "spaceLength": 10,
      "stack": false,
      "steppedLine": true,
      "targets": [
        {
          "expr": "wp_num_posts_metric{namespace=\"$namespace\"}",
          "format": "time_series",
          "interval": "",
          "intervalFactor": 10,
          "legendFormat": "$namespace",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeFrom": null,
      "timeRegions": [],
      "timeShift": null,
      "title": "Number of WordPress posts",
      "tooltip": {
        "shared": true,
        "sort": 0,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "buckets": null,
        "mode": "time",
        "name": null,
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "decimals": 0,
          "format": "short",
          "label": "",
          "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
      }
    }
  ],
  "refresh": "5m",
  "schemaVersion": 18,
  "style": "dark",
  "tags": [],
  "templating": {
    "list": [
      {
        "allValue": null,
        "current": {},
        "datasource": "${DS_PROMETHEUS}",
        "definition": "label_values(wp_users_total, job)",
        "hide": 0,
        "includeAll": false,
        "label": "Namespace",
        "multi": false,
        "name": "namespace",
        "options": [],
        "query": "label_values(wp_users_total, job)",
        "refresh": 1,
        "regex": "",
        "skipUrlSync": false,
        "sort": 0,
        "tagValuesQuery": "",
        "tags": [],
        "tagsQuery": "",
        "type": "query",
        "useTags": false
      }
    ]
  },
  "time": {
    "from": "now-24h",
    "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": "WordPress exporter dashboard",
  "uid": "qtFzy1dik",
  "version": 12
}

Leave a Reply

Your email address will not be published. Required fields are marked *