Etikettarkiv: MySQL

Docker med WordPress

Ni som läser denna blogg vet hur förälskad jag är i Docker. Jag tänkte nu försöka förklara hur du kan köra WordPress tillsammans med Docker. Det är ingen lätt process men det finns en stor uppsida:

  • Flexibilitet – Möjlighet att flytta komponenter mellan servrar lätt. Exempelvis lyfta ur databasmotorn eller webbservern.
  • Säkerhet – Allt separeras med hjälp av Linux Containers. Inte lika säkert som separata fysiska servrar eller virtuella maskiner.
  • Skalbarhet – Att växla upp delkomponenter är snabbt och enkelt. Lägga till och ta bort memcached exempelvis.
  • Driftsättning – Gå från utveckling, stage-miljö till driftsättning går snabbt och smidigt. Inga jobbiga beroenden som saknas eller är felaktiga.

Men hur gör man då? Jo först och främst är det några saker man måste tänka på:

  • En docker med Nginx + WordPress
  • Skrivbart filsystem för wp-content utanför dockern till WordPress.
  • En docker med MySQL (eller MariaDB, Percona)
  • Skrivbart filsystem för MySQL-databasen utanför dockern
  • Framtid: En docker med memcached.

Då sätter vi igång. Jag går ej igenom hur du installerar Docker utan det kan du läsa här eller köra Vagrant som jag gör på Mac OS X.

Installera NginxNginx

Den första dockern vi skapar ska ha Nginx samt WordPress installerat. Port 80 för http ska exponeras utåt.

Först och främst kör vi igång kommandotolken bash i Ubuntu 13.10 samt installerar Nginx.

# mkdir -p /data/wordpress/

Först enligt ovan kommando så skapar vi en gemensam katalog för våra containrar samt värdsystemet som ligger under /data.

# docker run -v /data/wordpress:/data/wordpress -p 80:80 -i -t ubuntu:13.10 /bin/bash
# apt-get update
# apt-get -y dist-upgrade
# apt-get -y install php5-fpm php5-gd php5-mysqlnd nginx php5-cli php5-curl php5-json

Ändra sedan /etc/nginx/sites-enabled/default och lägg till konfigurationsdirektiv enligt exempelvis den konfigg-template du hittar här. Du kan även konfigurera tonvis med andra inställningar i Nginx för att uppnå optimal prestanda, men det är ligger utanför denna guide.

Du måste även sätta root till den delade katalogen /data/wordpress/ i Nginx-konfen samt lägga till nedan i /etc/nginx/nginx.conf:

daemon off;

Vi avslutar dockern genom att trycka ctrl-D eller skriva exit.

Sedan kör vi följande för att spara ändringarna vi har genomfört i vår docker. Obs detta är viktigt annars går allt förlorat:

# docker ps -a

Kontrollera sedan vilket container ID den dockern vi just jobbade med och skriv ner ändringarna permanent genom en commit:

# docker commit 5d jonaslejon/nginx-wp:version1

Det räcker att skriva exempelvis de första två tecken i container ID. Ovan exempel så skriver jag bara 5d.

WordPress installationwordpress

Nästa steg är att ladda hem och installera WordPress. Detta görs genom att vi ansluter till dockern som vi just installerade Nginx på:

# docker run -v /data/wordpress:/data/wordpress -p 80:80 -i -t jonaslejon/nginx-wp:version1 bash

Sedan installerar vi wget samt går vi till root-katalogen för webbservern vilket i mitt fall är enligt följande:

# apt-get -y install wget
# chown www-data /data/wordpress/
# cd /data/wordpress/
# wget http://sv.wordpress.org/wordpress-3.8.1-sv_SE.tar.gz
# tar xvfz wordpress-3.8.1-sv_SE.tar.gz
# mv wordpress/* .
# exit

Då var WordPress och Nginx installerat. Och glöm framförallt inte att göra en commit på ändringarna:

# docker ps -a
# docker commit 7e6b550faa50 jonaslejon/nginx-wp:version3

Installation av MySQL, MariaDB eller Percona Server

Här ska vi inte glömma att skapa MySQL dockern i en helt ny docker och inte fortsätta med föregående Nginx-docker.

# mkdir -p /data/mysql

Först skapar vi en gemensam katalog där filerna till databasen ska leva kvar. Sen kör vi igång en docker och delar katalogen:

# docker run -v /data/mysql:/data/mysql -p 3306:3306 -i -t ubuntu:13.10 /bin/bash
# apt-get -y install mysql-server
# chown ubuntu /data/mysql
# vi /etc/mysql/my.cnf
# service mysql start
# echo "create database wordpress"|mysql -plösenord
# echo "GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'localhost' IDENTIFIED BY 'wplösenord'" | mysql -plösenord
# exit 
# docker ps -a 
# docker commit 1f9b38696a7f jonaslejon/mysql:version1

Ovan så ändrar du även i konfigg-filen /etc/mysql/my.cnf där datadir finns (jag gör det med vi). Ändra katalogen från /var/lib/mysql till /data/mysql/.

Jag skapar även ovan en ny databas med namnet wordpress samt en användare med namnet wordpress och lösenordet wplösenord.

Om allt sedan fungerar och du har gjort en commit så kan du testa denna docker med:

# docker run -v /data/mysql:/data/mysql -p 3306:3306 -i -t jonaslejon/mysql:version1 /usr/bin/mysqld_safe

Starta upp allt

Då var två stycken Docker-containers skapade. Vi ska nu starta upp dessa och hoppas på att allt går vägen. Skillnaden här är att vi anger -d så att containrarna läggs i bakgrunden:

# docker run -d -v /data/mysql:/data/mysql -p 3306:3306 -i -t jonaslejon/mysql:version1 /usr/bin/mysqld_safe
Det vi gör sedan är att titta med docker ps:
docker ps
Samt kontrollera loggarna från containern:
docker logs

Ser bra ut! Då ska vi starta upp webbservern Nginx:

# docker run -v /data/wordpress:/data/wordpress -p 80:80 -i -t jonaslejon/nginx-wp:version3 sh -c "service php5-fpm start && service nginx start"
Verifiera att allt körs med hjälp av docker ps-kommandot.

Avslutande anmärkningar

Nu behöver du bara konfigurera WordPress genom att surfa till localhost:80. Detta står även under PORTS när du kör docker ps.

IP-adressen som du behöver när du konfigurerar WordPress-installationen till MySQL-databasen kan du få ut genom att köra inspect på rätt container:

# docker inspect c6e0c1e4e5f0|grep -i ipaddress
 "IPAddress": "172.17.0.2",

Om du ansluter via Vagrant mot containrarna så måste du även konfigurera i Vagrantfile alternativt när du kör vagrant up på följande sätt:

# FORWARD_PORTS=80,3306 vagrant [up|reload]

Övrigt

  • Du behöver inte ha en delad /data-katalog som jag kör med. Men bra om du vill ha data som ska leva en längre tid.
  • Du kan även köra Nginx och MySQL på samma container.
  • Supervisord kan vara bra att använda för att starta upp och hålla koll på processer.
  • Det finns färdiga containers i det centrala docker-indexet. Sök genom kommandot docker search wordpress eller klicka här >
  • Det finns ett fiffigt sätt att automatiskt koppla ihop två containers genom att använda -link argumentet. Då läggs miljövariablar innehållandes port samt IP-nummer till automatiskt på följande sätt:

docker link

Gillar du detta blogginlägg? Donera gärna till min Bitcoin-adress nedan eller dela detta inlägg i sociala medier.

  • 13cxyC2sFvNfAAShcAaSs2YMW3Q4ygstD4

Docker är kittet i DevOps

Docker

Då och då dyker det upp mjukvara som underlättar livet avsevärt, då man nästan utropar Heureka! När jag först lärde mig använda Fabric så tycke jag att detta var ju guld värt: Att slippa göra samma sak manuellt på flera servrar. DevOps-filosofin i ett nötskal

Men! Nu tog jag mig tid och försökte lära mig Docker. Docker har tagit det hela lite längre och isolerar filer samt processer från underliggande operativsystem samt innehar versionshantering. Vad innebär detta då?

Jo, för sådana som mig som har beroenden långt utanför vad vanliga webbhotell kan leverera såsom Redis, Sphinx och Memcache så är Docker en mjukvara som löser detta.

Man kan kort och gott säga att Docker är ett operativsystem i operativsystemet. Lite som Xen eller Vmware men mycket ”lättare”.

I detta underliggande operativsystem så utgår du från en spegelbild som fungerar som bas och allt du lägger till får en ny revision. Detta gör exempelvis om du vill flytta en webbsajt med Redis, MySQL och Sphinx Search från en server till en annan så behöver du enbart flytta det som ändrats från spegelbilden.

Docker är så klart öppen källkod och helt gratis att använda. Och inte nog med det så är det ett MeetUp om några veckor i Stockholm om just DevOps och Docker med talare från bl.a. Spotify:

Så fixar du en krashad MySQL-databas

Flertalet gånger så har jag stött på problem då mysqld vägrar starta på grund av olika orsaker. Den absolut vanligaste orsaken är att hårddisken blivit full. Den näst vanligaste orsaken är att AppArmor gör DENIED då datakatalogen ligger utanför standardkatalogen för MySQL.

Men, om du nu har felsökt och stöter på problem som ger felmeddelanden såsom:

130423 13:08:09 InnoDB: Database was not shut down normally!
InnoDB: Starting crash recovery.
InnoDB: Reading tablespace information from the .ibd files...
InnoDB: Restoring possible half-written data pages from the doublewrite
InnoDB: buffer...
InnoDB: Doing recovery: scanned up to log sequence number 709 555107725
InnoDB: 1 transaction(s) which must be rolled back or cleaned up
InnoDB: in total 1 row operations to undo
InnoDB: Trx id counter is 1 3738915328
130423 13:08:09 InnoDB: Starting an apply batch of log records to the database...
InnoDB: Progress in percents: 1 2 3 4 5 InnoDB: Probable data corruption on page 14686502
InnoDB: Original record (compact record)
InnoDB: on that page.
InnoDB: Cannot find the dir slot for record (compact record)
InnoDB: on that page!
130423 13:08:09 InnoDB: Page dump in ascii and hex (16384 bytes):
InnoDB: stored checksum 1683902533, prior-to-4.0.14-form stored checksum 3027438534
InnoDB: Page lsn 709 555059595, low 4 bytes of lsn at page end 555059595
InnoDB: Page number (if stored to page already) 14686502,
InnoDB: space id (if created with >= MySQL-4.1.1 and stored already) 0
InnoDB: Page may be an index page where index id is 0 375
130423 13:08:10 InnoDB: Assertion failure in thread 139759355893504 in file ../../../storage/innobase/page/page0page.c line 133
InnoDB: We intentionally generate a memory trap.
InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
InnoDB: If you get repeated assertion failures or crashes, even
InnoDB: immediately after the mysqld startup, there may be
InnoDB: corruption in the InnoDB tablespace. Please refer to
InnoDB: http://dev.mysql.com/doc/refman/5.1/en/forcing-innodb-recovery.html
InnoDB: about forcing recovery.
11:08:10 UTC - mysqld got signal 6 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
We will try our best to scrape up some info that will hopefully help
diagnose the problem, but since we have already crashed,
something is definitely wrong and this may fail.

Då rekommenderar jag att du testar InnoDB recovery tool. Som återfinnes här:  InnoDB Recovery Tool. Obs du bör ha minst lika mycket hårddisk ledigt som storleken på din databas.

Webbutveckling för framtiden

HTML5Att webbutveckla för framtiden eller åtminstone anamma nya tekniker är något som jag gör ständigt. Här är några av de förbättringar/framsteg jag gör:

  • Från MySQL till Redis
  • Från HTML4 till HTML5
  • Från Apache till Nginx
  • Från sed/awk etc till MapReduce med Hadoop

Sen försöker jag i viss mån även att förbättra följande:

  • Nyttja PHP-frameworks i större omfattning
  • Webb-frameworks (typ Twitter Bootstrap)
  • Från PayPal till DIBS eller motsvarande för betalningslösningar
  • Och även uppgradera gammal kod att fungera med nya PHP-versioner, några saker jag ändrar är bl.a. mysql_* till mysqli_ och php short tags
  • Cufón

Så man slipper uppfinna hjulet allt för många gånger och att kod slutar fungera vid uppgraderingar. Men det finns även ett antal nya tekniker som jag önskar att jag hade mer tid att testa:

Något fundamentalt som jag missat?

Populära inlägg på Utvbloggen

Genom alla år som jag har bloggat här på Utvbloggen.se så har jag skrivit mer och mindre populära blogginlägg. Här kommer en mindre sammanställning på några av de blogginlägg som är populärast sett gällande hänvisningar från Google:

WordPress prestanda 101

Under Sweden Social Web Camp så försökte jag på kort under en session tid förklara hur man kan analysera och hitta flaskhalsar i WordPress gällande prestanda. Så därför försökte jag här sammanfatta det jag pratade om på några rader.

Först och främst så bör du stänga av alla plugins och då se om prestandan fortfarande är dålig, för tyvärr finns det många dåliga plugins (se här då Computer Sweden intervjuade mig och Nikke, år 2009).

Okej, nästa grej att testa är att slå på något cache-plugin i form av W3 Total Cache eller WP Super Cache. Du kan även testa att ”städa upp” i databasen då många plugins lägger temporär eller onödig information i databasen (kommentera om du vet något plugin som gör detta).

Använder du ett cache-plugin så bör du även installera memcached samt titta i källkoden längst ner på sidan hur många SQL-anrop som cachas, exempelvis (skriver WP Super Cache också ut detta?):

Object Caching 730/765 objects using memcached

En annan grej man kan testa är att använda strace direkt från kommandotolken i Linux tillsammans med PHP, ungefär så här:

$ cd /min/blogg/

$ strace php index.php

Så får du lite loggrader utspottade på skärmen och om det står still länge på ett ställe så ser du exakt vad som händer på den raden. Samt så bör du även testa Xdebug så kan berätta exakt vilken rad i PHP-koden som går långsamt, tar lite tid att installera och använda.

Du kan även titta i MySQL:s slow-log som kan aktiveras på eller hittas i /etc/mysql/my.cnf (sök efter log_slow_queries).

Sist men inte minst det som jag brukar göra är att lägga upp Varnish som en front-end proxy framför Apache exempelvis, detta är en helt mer eller mindre transparent lösning. Kom även ihåg att Apache slukar massor av minne jämfört med Nginx som istället kan användas till memcached. Detta är nog det bästa tipset jag kan ge samt att testa använda ett CDN.

Sen finns det så klart mycket man kan göra på själva sidorna som WordPress spottar ut (sprites, javascript etc), men det är utanför detta blogginlägg.

Uppdatering: Glömde skriva två saker som jag nämnde på sessionen, dels är det att använda sig av en OP-code cache typ APC eller eAccelerator samt så kan även URL-strukturen i WordPress slöa ner prestandan.

Så tolkar du MySQL:s felkoder

Rätt ofta så stöter jag problem med MySQL som då spottar ur sig någon typ av felkod. Dessa felkoder och hur de uppkommer brukar jag tyvärr hinna glömma bort mellan varven så därför skriver jag ner dem här för att underlätta för mig och dig:

Följande felmeddelande:

  • ERROR 3 (HY000) at line 11619: Error writing file ’/tmp/STUkun6Z’ (Errcode: 28)

Avkodas lättast genom att man skriver perror 28 vid en kommandotolk i Linux så bör något liknande komma fram:

  • OS error code  28:  No space left on device
Vilket är tämligen självförklarande. Slut på hårddiskutrymme på den monteringspunkten.
Om du sedemera utför någon typ av import eller dylikt som kan ta sjukt lång tid så brukar jag använda två bra trick:
  • Visa hur lång tid som är kvar av importen med följande kommandorad: pv filnamn.sql | mysql -u root -p (pv är ett användbart litet program som kan installeras via apt-get install pv)
Output blir ungefär följande då:
  • 10,9GB 1:24:51 [ 1,1kB/s] [=====>             ] 19% ETA 5:59:32
Och det andra tricket är att undersöka vad som händer genom SHOW PROCESSLIST vilket med fördel kan köras på följande sätt:
  • echo ’show processlist’|mysql -u root -plösenord -B
Nu till nästa felmeddelande:
  • ERROR 1071 (42000) at line 11673: Specified key was too long; max key length is 1000 bytes
Det tog ett tag innan jag insåg hur detta felmeddelande uppstod och det var pga att InnoDB-motorn i min MySQL installation ej startades pga felaktiga konfigurationsvariabler i my.cnf och då gick MySQL automatiskt över till MyISAM. Denna ”bugg” har funnits i många år och upprört en hel del (se sista kommentaren här). Kan vara rätt jobbigt om man gör mysqldump på ett system för att sedan läsa in dumpen på ett annat system och det ej fungerar.
Nästa problematik är inte direkt ett felmeddelande utan någon jag stött på då en import tog sjuk lång tid. Vid undersökning så stod det ”repair by sorting” vilket är något som kan ta många timmar. Efter lite googlande så hittade jag att en snabbare repair finns och då skall följande meddelande visas vid SHOW PROCESSLIST: repair using keycache. Fixas genom att sätta myisam_max_sort_file_size=10G i my.cnf.
Om du använder dig av mysqlimport eller LOAD DATA INFILE och får något av följande felmeddelanden:
  • Error: 13, Can’t get stat of ’/var/lib/mysql/Desktop/abc123’ (Errcode: 2)
  • File ’/home/standage/Desktop/abc213’ not found (Errcode: 13)

Så beror det troligtvis på att du ej har behörighet eller att du ej angett hela sökvägen till filen. Brukar jag lösa genom att genomföra importen med ett mysql-root konto.

Uppdatering: Något annat som kan ställa till fel hos MySQL är nya AppArmor som standard används i senare versioner av Ubuntu. Om du ändrar exempelvis datadir så kommer det ej att fungera. Då måste du ändra i apparmor-profilen för MySQL.

Serverstrul

Denna vecka så har mer eller mindre alla mina servrar strulat. Jag fick för mig att uppgradera Redis till en lite nyare version som har buggfixar samt äter mindre CPU och passade samtidigt på att uppgardera det PHP bibliotek jag använder för att prata med Redis till owlient/phpredis. Så klart så gick något fel och cachningen i Redis ballade ut och servern dog sakta men säkert pga alla SQL-frågor som ej cachades.

Nästa problem dyker upp på Bloggy då flertalet InnoDB-databasfiler försvinner vid en hastig omstart av servern och MySQL vägrar starta så jag får manuellt använda strace till att lista ut vilken databas som strular och bygga om flertalet tabeller. Då passar jag även på att uppgradera Varnish till senaste versionen och lägger in lite kod för att ej cacha WordPress-adminsidor vilket får till följd att inloggningen på Bloggy misslyckas för alla användare.

Samt så har även den server som sköter bloggsökmotorn Bloggz strulat.. vilket lärdom kan man dra av detta då? Jo, att om du uppgraderar eller ändrar minsta lilla så testa att det verkligen fungerar, lägg till exemeplevis dold HTML-kod som berättar om sidans element verkligen cachas eller ej.

Bildkälla: http://retecool.com/post/computable-awards--fail/1/

Crawler 2.0

Som många vet så har jag två tjänster som visar information om domäner, nämligen DomainDB.com samt DomainTool.se. Jag var i behov av att uppdatera den informationen som fanns så det var dags att starta den crawler som jag skapat för några år sedan.

Crawlern (eller webcrawler, spider som det också heter) var tämligen ouppdaterad så jag tog tillfället i akt och använder nu enbart Redis. Tidigare så använde jag MySQL + Sphinx:

  • db0 – Kö för domäner som skall besökas
  • db1 – Koppling mellan namnserver och IP-nummer
  • db2 – Koppling mellan www.domän och IP-nummer
  • db3 – Sidtitlar

Db0 rensas och fylls på ungefär varannan dag med runt 25 miljoner nya domäner som besöks så efter cirka en vecka så har jag besökt 100 miljoner domäner. För server-kapacitet så använder jag Amazon EC2 och deras spot instances som är lite billigare och utnyttjar oanvända EC2-instanser.