114 —Operations
Legacy-sites bewaken: vier signalen voor solo-shops
Dertig legacy sites, één persoon, en een zondagochtend die begon met een Slack: 'checkout kapot sinds vrijdag'. Vier signalen hadden het donderdag al gevangen.
Een zondag in november, 09:14. Een Slack-ping van een Nederlands bureau waar we mee werken: de WooCommerce-checkout van hun klant faalt al stilletjes sinds vrijdagmiddag. Twee dagen orders, weg. De oorzaak, eenmaal binnen op de bak: MySQL-binlogs hadden /var/lib/mysql tot 99% volgezet. Het PHP error log was vrijdag om 16:22 gestopt met schrijven. Geen enkele monitor had iets opgemerkt.
Dertig legacy sites en één persoon om ze in de lucht te houden. Je kunt niet elke ochtend elk log doorlezen. Wat je wel kunt doen is vier signalen kiezen die luid genoeg falen om de soorten incidenten te vangen die ook echt voorkomen, ze in één middag opzetten, en er daarna niet meer aan denken.
De vier signalen die er echt toe doen
In tien jaar andermans WordPress, Drupal, Joomla en Magento beheren, kwam vrijwel elke noodsituatie waar we bij werden geroepen uit één van vier hoeken:
- De site is down, of geeft HTTP 200 terug met een witte pagina. Uptime, maar content-bewust.
- Schijf vol. wp-content/uploads, de MySQL-datadirectory, /var/log, of een op hol geslagen cache.
- Een query duurt ineens 8 seconden. Het slowlog vangt het lang voordat de klant het merkt.
- Uitgaande mail begint te bouncen. Orderbevestigingen, wachtwoord-resets, antwoorden op contactformulieren. Stil totdat een klant het opmerkt.
Geen van deze heeft Datadog nodig. Geen ervan heeft een dashboard nodig. Ze hebben vier luide alarmen nodig.
Uptime, content-bewust
Een simpele HTTP 200-check vertelt je dat de webserver draait. Hij vertelt je niet of wp-config.php net een fatal is gaan gooien omdat iemands plugin een deprecated each() aanroept op PHP 8.2. De site geeft 200, de pagina is blanco, de checkout is dood.
Twee manieren om dit zonder kosten te doen. Self-host Uptime Kuma op een VPS van vijf euro, of gebruik de gratis tier van UptimeRobot (50 monitors, ruim voldoende voor dertig sites met twee checks per stuk).
Zet voor elke site twee probes op:
curl -s https://example.com/ | grep -q "Add to cart" || alert
curl -sI https://example.com/wp-login.php | grep -q "200 OK" || alert
De eerste probe vangt het white-screen-of-death (200 met lege body). De tweede vangt het geval waar de voorpagina uit cache komt maar PHP eigenlijk al bij parsen is gestorven, wat wp-login.php verraadt omdat die niet gecached kan worden. Elke vijf minuten, alert pas na drie opeenvolgende failures, zodat je niet om 03:00 wordt gewekt door een CDN-hikje van 30 seconden.
Schijfruimte, voor het je nekt
Een volgelopen schijf is veruit de meest voorkomende oorzaak van "de site doet het ineens niet meer"-tickets op legacy WordPress- en Magento-bakken. De boosdoeners, op volgorde van hoe vaak we ze zijn tegengekomen:
/var/lib/mysqlbinary logs waarvan niemand de expiry heeft ingesteldwp-content/uploadsals een back-upplugin daar archieven naartoe schrijft/var/log/apache2/of/var/log/nginx/als logrotate stuk is- Magento
var/cacheenvar/sessionop pre-2.4-installaties - Joomla
tmp/als een mislukte extensie-upload een tarball van 4GB heeft achtergelaten
Een cron job, één keer per dag, is voldoende:
#!/bin/bash
# /etc/cron.daily/disk-watch
THRESHOLD=85
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
{
echo "Disk at ${USAGE}% on $(hostname)"
echo
df -h
echo
du -sh /var/lib/mysql /var/log /home/*/public_html/wp-content/uploads 2>/dev/null
} | mail -s "DISK ${USAGE}% $(hostname)" you@yourdomain.tld
fi
Zet de drempel op 85, niet 95. Je wilt een geeuw tijdens de lunch, geen brand om middernacht. Nu je er toch zit, voeg expire_logs_days = 7 toe aan my.cnf en draai logrotate -d /etc/logrotate.conf om te bevestigen dat rotatie ook echt werkt. De docs van MySQL over binlog-expiry zijn vijf minuten leeswerk waard.
MySQL slowlog als vroege waarschuwing
De slowlog is het goedkoopste production-grade signaal dat je ooit aan zult zetten. Een query die eerst 80ms duurde, doet er nu 3,2 seconden over omdat iemand een plugin heeft toegevoegd die naar wp_options schreef met autoload = yes voor een geserialiseerde blob van 6MB. De site is nog steeds up. De checkout werkt nog steeds. Maar elke pageload sleept, en over twee weken gaat de hosting de database throttlen.
In my.cnf:
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 0
Laat log_queries_not_using_indexes uit, tenzij je een muur van WordPress-ruis wilt. Twee seconden is de juiste drempel voor legacy sites. Daaronder lees je core WP-queries, daarboven lees je echte problemen.
Eens per week, draai pt-query-digest:
pt-query-digest /var/log/mysql/slow.log | head -120
De eerste drie queries in de digest zijn bijna altijd het hele verhaal. Op een Magento 1-site die we nog onderhouden voor een kleine klant, was de top-slow-query zes maanden lang een EAV_ENTITY_ATTRIBUTE-join die op piekmomenten 11 seconden draaide. Eén covering index, probleem weg.
Mail-bounces waar niemand naar kijkt
Orderbevestigingen zijn de stille moordenaar. De site ziet er prima uit. De klant betaalt. Geen mail komt aan. Ze wachten twee dagen, dan volgt een chargeback. Ondertussen bounced de server al weken omdat noreply@theirdomain.com SPF kwijt is geraakt na een DNS-wijziging drie weken geleden, en jij wist het nooit.
Op elke Postfix-bak:
grep "status=bounced" /var/log/mail.log | tail -50
pflogsumm -d today /var/log/mail.log | head -40
pflogsumm geeft je een dagelijkse samenvatting per afzender, ontvanger en reden. Een wekelijkse cron die deze digest naar jezelf mailt, vangt de sluipende problemen voordat ze een reputatie-issue worden. Vuistregel in de industrie: een bouncepercentage boven 2% beschadigt je sender-reputatie, boven 5% ga je richting een blocklist. Verkeerd ingestelde SPF en DKIM zijn de meest voorkomende oorzaak, en het makkelijkst op te lossen zodra je het ook echt ziet.
Voor sites die het uitbesteden aan een transactionele provider, richt hun bounce-webhook op dezelfde alert-mailbox en forward alles boven je drempel.
Opzetten, en daarna vergeten
De hele stack is één VPS waar Uptime Kuma op draait, vier cron jobs (schijf, slowlog-digest, mail-samenvatting, weekrecap), en één alert-mailbox die je ook echt leest. Geen dashboard. Geen on-call-rooster. Vier luide alarmen die afgaan voordat de klant het merkt, en stilte de rest van de tijd.
Toen we Pier bouwden, liepen we steeds tegen dezelfde kloof aan bij klantsites: de mensen die monitoring het hardst nodig hadden, konden de tijd om het op te zetten niet rechtvaardigen, omdat hun gereedschap om het probleem daadwerkelijk te fixen (SSH'en, slow query vinden, foute plugin patchen) al een rondreis van negentig minuten was. Hoe we het uiteindelijk aanpakten was de fix-tools, de MySQL editor en een bijgehouden version history op elk bestand, pal naast de FTP-verbinding te zetten, zodat een slowlog-alert om 14:00 een fix van vijf minuten wordt in plaats van een avond werk.
Kies vanmiddag één site. Zet de slowlog erop aan, met long_query_time = 2. Lees morgenochtend de eerste tien regels van pt-query-digest. Je weet dan meer over die site dan gisteren.
— Vragen —
Hoeveel uptime-monitors heb ik eigenlijk nodig per site?
Twee. Eén content-match-probe op de homepage om witte pagina's te vangen, en één HTTP 200-check op de login-URL om PHP-parsefouten te vangen die de page cache verbergt.
Welke slow_query_time-drempel is een goed startpunt?
Twee seconden. Daaronder lees je normale WordPress-ruis, daarboven lees je echte problemen. Zet hem op één seconde zodra de eerste indexeerronde achter de rug is.
Heb ik een betaalde monitoring-dienst nodig voor 30 legacy sites?
Nee. De gratis tier van UptimeRobot dekt 50 monitors, Uptime Kuma is gratis te self-hosten, en de andere drie signalen zijn cron jobs die naar mail schrijven. Totale kosten: één kleine VPS.