Aquestes són les diapositives de la presentació de Cassandra que vaig fer al SudoersBCN la setmana passada. Crítiques, comentaris i correccions seran benvingudes!
Monitoritzant l’estat d’un sistema linux amb graphite
Ja vam veure en anteriors posts com instal·lar graphite. Ara haure’m d’enviar-li algunes dades perquè les emmagatzemi i les poguem consultar, per començar a jugar amb la webapp (si no, no ens serveix de res!). El graphite es pot fer servir per fer gràfiques de moltes coses, però començarem pel més bàsic que ens pot interessar: l’estat d’un servidor linux.
Per decidir si l’estat d’un servidor és correcte, ens fixarem en les coses típiques que es monitoritzen a un linux: memòria, espai en disc, CPU, iowait, i operacions read/write a disc. Hi ha moltes maneres de recollir aquestes dades. Jo he escollit un shell-script que executa comandes standard (free, sar i awk, bàsicament) i les envia al carbon via netcat. Podeu trobar el codi del script a github dintre del repositori graphite-monitoring , però també el poso aquí perquè es senzill i curtet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#!/bin/bash #Carbon server where data should be stored for graphite to show - El servidor carbon on s'han de guardar les dades que mostra el graphite carbon_server=graphite.domain.tld # Tree structure where we want information to be stored - L'estructura de l'arbre on volem que es guardin les dades a graphite. tree="servers" #In this case, info will be shown in graphite as "servers.servername.loadavg_1min". We could use "pro" and "pre" to separate environments: "servers.pro.servername.loadavg_1min" - En el nostre cas es veuran a "servers.servername.loadavg_1min". Podriem posar "prod" i "pre" per separar entorns: "servers.pro.servername.loadavg_1min" now=`date +%s` host=`hostname` #Load average - Carrega read un cinc quinze resta < /proc/loadavg data="$tree.$host.loadavg_1min $un $now n $tree.$host.loadavg_5min $cinc $now n $tree.$host.loadavg_15min $quinze $now n" #Memory - Memoria data="$data `free -o|awk -v host=$host -v now=$now '(NR==2) {printf("servers.%s.memory %s %s \n ", host, $3/$2*100, now)} (NR==3) {printf("servers.%s.swap %s %s\n ", host, $3/$2*100, now)}'`" #CPU Used - Recollim CPU data="$data `sar -u 3|awk -v host=$host -v now=$now 'END {printf("servers.%s.cpu %s %s \n ", host, 100-$8, now)}'`" #Disk data - Recollim dades de disc data="$data `sar -b 3|awk -v host=$host -v now=$now 'END {printf("servers.%s.disk.totalops %s %s \n servers.%s.disk.readops %s %s \n servers.%s.disk.writeops %s %s \n servers.%s.disk.breads %s %s \n servers.%s.disk.bwrites %s %s \n ", host, $2, now, host, $3, now, host, $4, now, host, $5, now, host, $6, now)}'`" #Show data for debugging purpose - Mostrem les dades per depurar errors echo $data #Send data to graphite - Enviem dades a graphite echo -e $data |nc -w 5 $carbon_server 2003 2>&2 exit $? |
Ara només hem de fer que això s’executi periòdicament, per exemple posant un cron al sistema. Jo ho executo amb monit (del que ja en parlaré en un altre moment), perquè em gestioni els errors en l’execució. Amb això ja disposarem de dades útils al graphite per començar a jugar.
Configuració bàsica de Varnish amb http cache i stale-while
En entorns d’alta demanda arriba un moment on les peticions php (o similar, CGI en general) que volem servir a traves del nostre apache httpd son més de les que el nostre servidor pot processar. Per solucionar-ho podem fer el més senzill, que és afegir més servidors a la granja de balanceig i disminuir així la càrrega (es reparteixen les peticions entre més servidors). Però el més senzill no necessariament és el més eficient. En comptes de repartir la càrrega entre més servidors, no podriem fer que cada servidor pogués servir més peticions?
I tant. D’una banda podem fer més ràpid el processament dels PHP (o similar, CGI en general) amb FastCGI. D’altra banda podem fer que el nostre servidor HTTP sigui més ràpid, canviant-lo per un de mes lleuger, com per exemple nginx. Una altra aproximació, de la que parlarem aquí, es tracta de mantenir una cache en memòria dels continguts, en comptes de processar-los cada vegada, evitant així el consum de CPU i accelerant enormement el temps que es triga a servir-los. Ho farem fent servir varnish .
Mantenir una cache és força delicat perquè s’han de tenir en compte moltes coses. No s’hauria de fer cache si hi ha cookies pel mig, per exemple, o si hi la petició http és de tipus POST. Però això es una cosa a avaluar dintre de cada aplicació, els desenvolupadors han de poder indicar què es pot guardar a la cache i què no, i els administradors configurar els servidors en conseqüencia.Aleshores, suposem que comencem de zero, que no tenim res en la cache del varnish, i volem començar posant-hi una URL en concret, de la que sabem que no te risc. Això farem aquí, caché selectiva d’una sola URL.
Per il·lustrar la nostra demostració, farem servir un fitxer PHP senzill que triga 10 segons a tornar el resultat, i que té una capçalera que el fa caducar en 5 segons. L’anomenarem sleep.php:
1 2 3 4 |
<?php header("Cache-control: max-age=5, must-revalidate"); sleep(10); ?> |
Si el demanem comprovarem que efectivament triga 10 segons a servir-se:
1 2 |
$ curl http://localhost/sleep.php -w %{time_total} 10,001 |
El primer que hem de fer es instal·lar varnish amb el nostre gestor de paquets (apt-get install varnish, yum install varnish, el que sigui). Després voldrem que sigui el varnish qui escolti pel port 80 en comptes de l’apache. Per això mourem el apache de port (directiva “Listen: “), i el posarem al 8080, per exemple. Després canviarem el port del varnish (directiva VARNISH_LISTEN_PORT= , normalment esta al fitxer /etc/default/varnish o /etc/sysconfig/varnish, depenent de la distro). Necessitem dir-li al varnish quins servidors tindrà darrera, als que ha de reenviar les peticions (els servidors backend). Per això crearem el fitxer /etc/varnish/default.vcl amb el següent contingut:
1 2 3 4 |
backend default { .host = "127.0.0.1"; .port = "8080"; } |
Amb tot això reiniciem tant el apache com el varnish, i podrem comprobar que estan ambdos en marxa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$ curl http://localhost/sleep.php -IXGET HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Fri, 30 Nov 2012 13:56:33 GMT X-Varnish: 1538615861 Age: 0 Via: 1.1 varnish Connection: keep-alive $ curl http://localhost:8080/sleep.php -IXGET HTTP/1.1 200 OK Date: Fri, 30 Nov 2012 13:56:59 GMT Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Length: 0 Content-Type: text/html |
Veiem que les capçaleres que retornen un i l’altre són diferents. Quan demanem al varnish apareixen “Via: 1.1 varnish” i “Age: 0”, entre d’altres que amb l’apache no apareixen. Si ho tenim així, ja tenim la base configurada.
El comportament que tindrem ara mateix es de fer cache de tot.
1 2 3 4 |
$ curl http://localhost/sleep.php -w %{time_total} 10,002 $ curl http://localhost/sleep.php -w %{time_total} 0,001 |
Però com que volem ser selectius i no volem fer cache de tot, sino de algunes URL en concret, evitant que es faci cache de cookies i coses semblants, canviarem el comportament de la rutina sub vcl_recv perque no faci cache, afegint això al fitxer /etc/varnish/default.vcl:
1 2 3 |
sub vcl_recv { return(pass); } |
Ho comprovem:
1 2 3 4 |
$ curl http://localhost/sleep.php -w %{time_total} 10,002 $ curl http://localhost/sleep.php -w %{time_total} 10,001 |
Ara fem que faci cache només del fitxer sleep.php, afegint aixo al default.vcl:
1 2 3 4 5 6 7 8 9 10 |
sub vcl_recv { if (req.url == "/sleep.php") { return(lookup); } else { return(pass); } } |
Ho podem comprovar:
1 2 3 4 5 6 7 8 9 |
$ cp /var/www/sleep.php /var/www/sleep2.php $ curl http://localhost/sleep.php -w %{time_total} 10,002 $ curl http://localhost/sleep.php -w %{time_total} 0,001 $ curl http://localhost/sleep2.php -w %{time_total} 10,002 $ curl http://localhost/sleep2.php -w %{time_total} 10,001 |
També comprovem que la capçalera “Age:” va pujant, i quan arriba a 5 (el max-age que li hem configurat), torna a trigar 10 segons:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
$ curl http://localhost/sleep.php -IXGET -w %{time_total} HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Mon, 03 Dec 2012 10:53:54 GMT X-Varnish: 500945303 Age: 0 Via: 1.1 varnish Connection: keep-alive 10,002 $ curl http://localhost/sleep.php -IXGET -w %{time_total} HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Mon, 03 Dec 2012 10:53:56 GMT X-Varnish: 500945305 500945303 Age: 2 Via: 1.1 varnish Connection: keep-alive 0,001 $ curl http://localhost/sleep.php -IXGET -w %{time_total} HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Mon, 03 Dec 2012 10:53:59 GMT X-Varnish: 500945309 500945303 Age: 5 Via: 1.1 varnish Connection: keep-alive 0,001 $ curl http://localhost/sleep.php -IXGET -w %{time_total} HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Mon, 03 Dec 2012 10:54:09 GMT X-Varnish: 500945310 Age: 0 Via: 1.1 varnish Connection: keep-alive 10,002 |
Veiem que quan el contingut caduca, el torna a demanar i triga 10 segons. Pero què passa en aquest interval? La resta de peticions que arriben al varnish mentre es fa aquesta petició al backend, s’han d’esperar? Doncs no. Hi ha un periode de gràcia de 10 segons, durant els quals el varnish continuará servint l’objecte antic (o “ranci”, “stale” en anglés). Ho podem comprobar si fem dos curl alhora, en comptes d’un, i veurem com n’hi ha un que s’atura mentre l’altre continua servint les pagines ràpid, amb la capçalera “Age” per sobre dels 5 segons que li hem assignat:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ while :;do curl http://localhost/sleep.php -IXGET;sleep 1;done (...) HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Mon, 03 Dec 2012 11:16:29 GMT X-Varnish: 500952300 500952287 Age: 8 Via: 1.1 varnish Connection: keep-alive |
També podem comprovar-ho amb el siege, posant dos usuaris concurrents, i veurem com durant una estona només un d’ells serveix contingut, pero que en cap moment es deixa de rebre continguts:
1 |
$ siege -t 30s -c 2 -d 1 localhost/sleep.php |
Si ens sembla que 10 segons de gràcia és massa poc, podem canviar aquest valor amb la directiva beresp.grace dintre de la rutina vcl_fetch al fitxer default.vcl. Per exemple, si volem posar un minut:
1 2 3 |
sub vcl_fetch { set beresp.grace = 60s; } |
I si cau el servidor backend? Continuará servint el contingut antic (“stale“)? Doncs tal i com ho tenim, no. Perquè tal i com ho tenim, al varnish no li hem configurat res per distingir entre un backend saludable i un que no ho és, per tant els considerarà tots saludables. Per tant, si el backend cau, i el contingut caduca, tornarem error 503:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
$ $ sudo /etc/init.d/apache2 stop [sudo] password: * Stopping web server apache2 apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName ... waiting [OK] $ sudo /etc/init.d/apache2 status Apache2 is NOT running. $ while :;do curl http://localhost/sleep.php -IXGET;sleep 1;done (...) HTTP/1.1 200 OK Server: Apache/2.2.22 (Ubuntu) X-Powered-By: PHP/5.3.10-1ubuntu3.4 Cache-control: max-age=5, must-revalidate Vary: Accept-Encoding Content-Type: text/html Transfer-Encoding: chunked Date: Fri, 30 Nov 2012 14:19:15 GMT X-Varnish: 1538616905 1538616860 Age: 5 Via: 1.1 varnish Connection: keep-alive HTTP/1.1 503 Service Unavailable Server: Varnish Content-Type: text/html; charset=utf-8 Retry-After: 5 Content-Length: 419 Accept-Ranges: bytes Date: Fri, 30 Nov 2012 14:19:15 GMT X-Varnish: 1538616906 Age: 0 Via: 1.1 varnish Connection: close <span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; line-height: 1.5;">Per fer que el periode de gràcia també s'apliqui quan el servidor està caigut, primer hem de dir-li al varnish com ha de comprovar si el apache esta en marxa o no (healthy). Això es fa configurant la directiva "probe" al backend:</span> <pre class="lang:sh decode:true">backend default { .host = "127.0.0.1"; .port = "8080"; .probe = { .url = "/"; .timeout = 100 ms; .interval = 1s; .window = 10; .threshold = 8; } } |
D’aquesta manera es continua servint el contingut antic quan el backend cau, i el servim fins que s’aixequi i el varnish pugui tornar a demanar-li el contingut.
Fent proves amb el siege i curl, podem veure que tant en el cas del contingut que caduca com en el cas del servidor que cau, sempre hi ha un fil que “pringa”. La primera vegada que el varnish es troba amb el contingut caducat, el demana i espera que acabi. Mentrestant, la resta de fils reben el contingut antic, pero aquest fil “pringa”. El mateix passa amb el servidor caigut. Hi ha molta literatura intentant trobar la manera d’evitar que això passi, i pots llegir molt al respecte, pero el resum es que no hi ha manera d’evitar-ho. Que passa i punt. Un d’els fils “pringa”.
Amb això cobrim dos casos en els quals continuarem servint continguts antics (stale):
– No hi ha cap backend disponible, per tant servim contingut antic.
– Hi ha backends disponibles, i un fil ja ha demanat nou contingut. Mentre aquest nou contingut arriba des del backend, el varnish continua servint l’antic a la resta de fils.
I si volem que aquests dos casos tinguin un timeout diferent? Per exemple, pot donar-se el cas que volguem que, si el backend està disponible, els continguts caducats triguin un temps màxim. Passat aquest temps, deixem de servir el contingut antic i forcem a que s’esperin per al contingut nou. Aquest timeout normalment serà d’uns segons. I alhora, volem que si els backends estan caiguts, i per tant no hi ha manera d’aconseguir contingut nou, que el varnish continui servint el contingut antic durant molta estona, que normalment és millor que estar servint una pàgina d’error 503. Això s’ha de configurar a la rutina vcl_recv del default.vcl, i es fa aixi:
1 2 3 4 5 6 7 8 9 10 11 |
sub vcl_recv { if (req.backend.healthy) { set req.grace = 30s; } else { set req.grace = 1h; } } sub vcl_fetch { set beresp.grace = 1h; } |
Per tant, el nostre fitxer default.vcl complet tindra el seguent contingut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
$ cat /etc/varnish/default.vcl backend default { .host = "127.0.0.1"; .port = "8080"; .probe = { .url = "/"; .timeout = 100 ms; .interval = 1s; .window = 10; .threshold = 8; } } sub vcl_recv { if (req.backend.healthy) { set req.grace = 30s; } else { set req.grace = 1h; } if (req.url == "/sleep.php") { return(lookup); } else { return(pass); } } sub vcl_fetch { set beresp.grace = 30s; } |
Comprovar la data d’expiració d’un domini: check_domain
No ens agradaria que el nostre domini caduqués i vingués un ciberespeculador (també mal anomenats “ciberokupes”) i ens demanés 1.000$ per ell, quan en realitat val 20 (ho he viscut amb un domini personal que tenia).
No és un greu problema, al cap i a la fi els registrars sempre avisen amb temps d’antelació per donar-te totes les facilitats de renovació (que és el que a ells els interessa). Però… i si l’adreça de correu que vau configurar el seu dia, ja no està activa? I si la nova secretaria del jefe ho confon amb spam i ho ignora (cas real)? I si l’empresa és tan gran que ningú no sap qui llegeix aquella adreça de correu?
Per assegurar-nos que estem al dia dels nostres dominis, he creat un plugin pel nagios que es diu “check_domain”. És força senzill (si ens hi fixem veurem que hi ha més tros parsejant paràmetres que no pas fent coses ), però cobreix la necessitat i t’avisa quan el domini està a prop de caducar.
A l’article complet (“llegir més”) hi apareix el codi i l’arxiu descarregable.
Continue reading Comprovar la data d’expiració d’un domini: check_domain