Varnish cache – en case-studie

En kväll blev Adminor uppringd av en webbportal som hade ett akut problem.

Problemet
Deras sida hade legat nere under halva dagen och mesta delen av kvällen. När sidan väl var uppe tog det över 10 sekunder för sidan att ladda.
Mesta dels var problemet självförvållat genom ökande popularitet av deras webbsida.

De var i akut behov av att lösa problemen men hade inte möjlighet att byta server snabbt på grund av budgetbegränsningar och intern miljö.
På mjukvarusidan fanns en vanlig WordPress installation på en Apache webbserver och MySQL-databas. Utöver det hade dom själva redan försökt lösa problemet genom att köra wordpress cache plugin.

En till synes omöjlig uppgift när budget begränsar och det inte fanns mycket man kan göra på mjukvarusidan.

Förberedelser

Det första vi tittade på var serverinställningarna på Apache och MySQL. Vi kunde dock inte se några underligheter utan inställningarna var rätt hyffsade. Det var helt enkelt så att de måste frigöra resurser på något vis, antingen genom att köpa in ny  hårdvara eller förbättra applikationen.
Vi kom då med ett tredje förslaget att istället implementera Varnish som en caching proxy framför deras webbportal. På så vis skulle man kunna cacha de dynamiska förfrågningarna.

Felåtgärd

Ofta måste man göra en fullständig undersökning av kundens miljö eftersom man inte vill orsaka problem med att ändra inställningar för server.
I detta fall var det ganska enkelt när servern bara har en site som körs och sidan ändå var nere mera än den var uppe pga det höga trycket.

Vi körde därför igång på direkten. Det första vi gjorde var att göra en snabbanalys. Detta gjorde vi genom att installera grafverktyg och statistikinsamling på det systemet som var berört.

Adminor identifierade det material som MÅSTE vara dynamiskt och vilket material som kunde cachas. Det visar sig att allt förstasidematerial och dess undersidor med enkelhet kunde cachas i åtminstone 10 minuter. Restererande material som publikationsdel och administrationssidor fick inte cachas.
Sen diskuterade vi med kunden om detta verkade överenstämma. Att installera Varnish är rätt enkelt så 2h efter att kunden först tog kontakt så var en lösning färdigimplementerad.

Resultat

Graferna nedan visar de första 20 minutrarna då vi precis installerat övervakningen och sen den kraftiga resursminskning efteråt när Varnish aktiverats.


Datapunkterna på CPU-grafen visar iowait, user och system CPU-utnyttjande som låg långt över det rekommenderade. Minnesanvändningen låg över det som fanns fysiskt installerat så att server var tvungen att swappa (läsa / skriva från långsammare diskminne).
Efter Varnish-installationen så har CPU användningen minskat från >100%  till endast en 1/4 del utav de tillgängliga resurserna som högsta nivå under dagarna. Minnesgrafer är nu jämn under dagarna med avtagande ökning (normalt och hälsosamt) istället för den kaotiska höga commit nivån (5.96GB!) som låg ovanför den fysiska gränsen i början av datapunkterna.

Det ska nämnas att innan Varnish implemeterades kunde portalen endast ta emot ett tjugotal samtidiga besökare. Efter förändringen så gjorde vi ett lasttest som visade att det nu var serverns bandbreddsanslutning som kommer begränsa – men det är långt tills dess. Detta ger nu portalen en hel del utrymme att växa i utan att öka sina serverkostnader!
Efter Varnish togs i bruk laddades 100% av besökarna under 0.5sekunder enligt apache benchmark statistik.

Kommando: ab -n 1000 -c 5 http://www.domain.se/

This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.domain.se (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests

Server Software:        Apache/2.2.9
Server Hostname:        www.domain.se
Server Port:            80

Document Path:          /
Document Length:        71552 bytes

Concurrency Level:      5
Time taken for tests:   57.714 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Total transferred:      72030893 bytes
HTML transferred:       71552000 bytes
Requests per second:    17.33 [#/sec] (mean)
Time per request:       288.570 [ms] (mean)
Time per request:       57.714 [ms] (mean, across all concurrent requests)
Transfer rate:          1218.82 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       39   40   0.6     40      51
Processing:   202  248  77.6    204     447
Waiting:       40   41   2.7     41     112
Total:        241  288  77.6    244     487

Percentage of the requests served within a certain time (ms)
  50%    244
  66%    245
  75%    256
  80%    407
  90%    444
  95%    446
  98%    447
  99%    448
 100%    487 (longest request)

I nästa blogg-inlägg kommer jag gå igenom vilka Varnish konfigurations & VCL-regler vi fick definera – vad man måste göra för att få en cache att fungera och vilka undantag som måste göras för respektive del för att dynamiskt material ska fungera korrekt.

Kort om Linux- och MySQL-användarhantering.

Jag fick en fråga på en kund om skillnaden mellan linux-användare (system users) och de användare som finns i MySQL.
Vissa gånger kan det vara lätt att blanda ihop om man sköter sitt eget linux system.
T.ex. en vanlig webbserver med databas där man har MySQL som databasmotor.

Det lättaste att komma ihåg är att MySQL och systemanvändarna är helt skiljda. Det är helt enkelt olika användartabeller.

Lite förenklat så är alla systemanvändare i linux (som nås via t.ex. ssh) är definerade i olika filer (med hjälp av olika verktyg för lösenordsändring, typ passwd):

/etc/passwd – användarnamn, userid, gruppid (primär grupp som användare tillhör), hemkatalog, shell som ska köras vid inloggning typ /bin/bash

/etc/shadow – krypterade lösenorden som tillhör användare

/etc/group – gruppdefinationer, gruppid och medlemmar av sekundära grupper
Mer matnyttig information finns längst ned i denna post.

Det finns undantag, t.ex. om man använder LDAP eller andra centrala användardatabaser – men i detta inlägg förenklar jag och tar inte upp central användarhantering.

MySQL är ett program likt alla andra på ett system men körs ofta som en ”daemon” under linux och exekvieras av en vanlig systemanvändare, ofta som administratörs-användare ”root”.
När MySQL körs så tillhandahållet MySQL en egen miljö för att hantera databaser. Det finns logik , datalagring och användarhantering med mera.
I MySQL lagras användarna i en speciell reserverad tabell ”user” under databas med namnet ”mysql”. Det är mest ett sammanträffande att man använder samma namnkonvention som unix-system där superusern kallas ”root”.

mysql> show tables;
+—————————+
| Tables_in_mysql           |
+—————————+
| columns_priv              |
| db                        |
| func                      |
| help_category             |
| help_keyword              |
| help_relation             |
| help_topic                |
| host                      |
| proc                      |
| procs_priv                |
| tables_priv               |
| time_zone                 |
| time_zone_leap_second     |
| time_zone_name            |
| time_zone_transition      |
| time_zone_transition_type |
| user |
+—————————+
17 rows in set (0.01 sec)
17 rows in set (0.01 sec)

I MySQL är användare lagrade på formen permissions och privileges.  Permissions sätts upp med access per databas och även på tabellnivå för läs/skriv samt vilken ”host” som användare får ansluta från % (wildcard, alla hosts) eller per IP/subnät. En bra introduktionsguide till hur man hanterar och administrerar användare i MySQL finns länkade i slutet av denna post.

En likhet mellan systemanvändare och MySQL användare är säkerhetsaspekten. Man bör t.ex. aldrig tillåta ”root” på ett system eller ha en superuser på Mysql som kan logga in från vilket IP/domän som helst.
Man ska alltid försöka begränsa access och ge ut de privilegier som beövs till de databaser som behöver nås – per IP.
Rekommenderar att läsa följande dokumentation för användaradministration i Linux (även andra unix system är liknande): http://www.comptechdoc.org/os/linux/usersguide/linux_ugusers.html
Mer ingående information om MySQL permissions kan finnas här: http://www.databasejournal.com/features/mysql/article.php/3311731/An-introduction-to-MySQL-permissions.htm