ติดตั้ง WordPress บน Docker LEMP Stack

Pattanapong Cherthong
6 min readAug 28, 2016

--

โดยปกติแล้วการที่เราจะติดตั้ง WordPress สัก 1 เว็บใน Server นั้น มันช่างเสียเวลาพอสมควรไปกับการติดตั้ง Application ต่างๆ รวมไปถึงการแก้ Configulation มากมาย จะดีกว่าไหมถ้าสร้าง Configulation ไว้ใช้ซ้ำๆได้ บทความนี้จะมาเล่าถึงการติดตั้ง WordPress โดยนำมารันอยู่บน LEMP Stack (Linux, Nginx, MySQL PHP-fpm) ด้วย Docker ทำไมผมถึงเลือกใช้ Docker มันดียังไง มันช่วยอะไรได้บ้าง
บทความนี้ถ้าเป็นมือใหม่เรื่อง Docker อาจจะต้องศึกษาเพิ่มเติมครับ เนื่องจากในเนื้อหาไม่ได้แนะนำ Docker เริ่มต้นจาก basic

ลองศึกษาและทำความเข้าใจ Docker ได้จากบทความในลิงค์นี้ครับ

ปัญหาที่มักจะเจอกันบ่อยๆ เมื่อต้องติดตั้งใช้งาน WordPress
1. เสียเวลา ติดตั้งและ Config Server Application
สิ่งที่สำคัญเมื่อต้องการใช้งาน WordPress บน Server สักตัวนึง จะต้องติดตั้ง Application ต่างๆมากมายในฝั่ง Server รวมถึงการแก้ Config มากมายเพื่อให้ Application ทำงานได้ตามที่เราต้องการ บางทีแค่ setup ก็เสียเวลากันเป็นวันๆ
2. เครื่อง Development กับ Production ไม่เหมือนกัน
เวลาเราใช้งาน WordPress ในเครื่องของเราเอง (Localhost) อาจจะใช้คอมพิวเตอร์ที่เป็น Windows หรือ Mac แน่นอนครับว่า Web Server ที่ใช้งานจริงมักจะเป็น Linux ซะส่วนมาก แต่เมื่อเอาเว็บมารันบนเครื่องตัวเองก็อาจจะใช้งานได้ปกติ แต่พอเอาขึ้นไปรันบน Server แล้ว ใช้ OS แตกต่างกัน อาจจะใช้ PHP ต่างเวอร์ชั่น หรือการ Config บนเครื่อง Dev กับ Production ไม่เหมือนกัน ทำให้เว็บพังหรือหน้าเว็บมีปัญหาได้

ปัญหาดังกล่าวจะไม่เกิดขึ้นถ้าเราใช้ Docker เพราะ Docker จะช่วยแพ็ค application เหล่านี้อยู่ในรูปแบบของ Container ซึ่งเราสามารถเอา Container ไปรันในเครื่องไหนก็ได้โดย Application ของเรายังทำงานได้ปกติไม่ผิดเพี้ยนจากเดิม ไม่ว่าจะไปรันในเครื่องตัวเอง หรืออยู่บน Server เครื่องไหนก็ได้บนโลกที่ติดตั้ง Docker ไว้แล้ว และถ้าเกิดว่า Server มีปัญหา เราสามารถโยกย้ายเว็บ WordPress ไปรันอยู่บน Server อื่นได้ในระยะเวลาอันสั้น ย้ายไฟล์เสร็จกลับมารันต่อได้ทันที โดย WordPress ยังคงทำงานได้เหมือนเดิมเป๊ะ

มาปูพื้นคำศัพท์เกี่ยวกับ Docker กันก่อน

Docker image
คือไฟล์ image ที่ติดตั้ง application เอาไว้ เกิดมาจากการ build ไฟล์ Dockerfile ขึ้นมาเป็น image (เป็นไฟล์ต้นแบบของ Container)

Docker container
เกิดจากการรันจาก Docker Image เกิดเป็น container โดยเป็นกล่องที่บรรจุ Application ที่สามารถเรียกใช้งานได้ทันที

Docker Hub
เป็นเว็บสำหรับเก็บ Docker Image ฟรี เมื่อมีการเรียกใช้งาน docker image ตัว docker machine จะดาวน์โหลด image มาจาก Docker Hub

Docker Compose
คือ tool อำนวยความสะดวกในการรัน Container โดยสามารถสั่งรันหลายๆ Container ได้พร้อมๆกัน ตั้งค่าต่างๆ รวมไปถึงเชื่อมโยง Container ให้สามารถทำงานร่วมกันได้

ก่อนที่จะติดตั้งและใช้งาน WordPress บน Docker นั้น เราต้องรู้ก่อนว่า WordPress มี system requirement อย่างไรบ้างตามที่เว็บ wordpress.org ได้แนะนำไว้ดังนี้

สำหรับ PHP extension ที่จำเป็นต้องใช้กับ WordPress ในเว็บ wordpress.org ก็ไม่ได้บอกเอาไว้ใน system requirement ไว้เลยว่าต้องใช้อะไรบ้าง แต่ก็มีผู้ใช้งานส่วนหนึ่งรวบรวม PHP extension ที่จำเป็นเอาไว้แล้วจากการแกะดู code ของ WordPess ดูได้จากลิงค์นี้ http://wordpress.stackexchange.com/questions/42098/what-are-php-extensions-and-libraries-wp-needs-and-or-uses

WordPress เป็น CMS ที่เขียนด้วยภาษา PHP เป็นหลัก ตัว Web Server จะต้องติดตั้ง PHP เข้าไปด้วย เพื่อประมวลผลไฟล์ PHP ให้แสดงผลออกมาเป็น HTML บน Web Server และต้องใช้ MySQL เป็นฐานข้อมูลหลัก ซึ่งก็มีให้เลือกอีก 2 ค่ายได้แก่ MySQL โดยบริษัท Oracle และ MariaDB จากอดีตผู้สร้าง MySQL ฐานข้อมูล 2 ตัวนี้สามารถใช้ทำงานได้เหมือนกันทั้งคู่ และ Web Server ตามที่ WordPress แนะนำก็มี 2 ค่าย คือ Apache และ Nginx และในที่นี้ผมจะเลือกติดตั้ง Nginx, MariaDB, PHP-FPM รวมกันเป็น LEMP Stack สำหรับใช้งาน WordPress โดยรันอยู่บน Docker

โครงสร้างในโปรเจคนี้มีอะไรบ้าง

  • แยก LEMP Stack (Nginx, MariaDB, PHP-FPM) แต่ละ Service เป็นอย่างละ 1 container ทำงานแยกกันเป็น microservice เชื่อมต่อกันด้วย Docker Compose
  • เก็บไฟล์ทุกอย่างที่ต้องใช้ได้แก่ config, docker-compose.yml, Dockerimage ขึ้นไว้ใน git private repo (ไม่รวม web directory) อาจจะแยก config แต่ละเว็บไว้เป็นแต่ละ repo เมื่อเรียกใช้ครั้งต่อไปก็สามารถ clone จาก git มารันได้ทันที
  • เรียกใช้ Docker Image ทั้งหมดมาจาก Official Images ใน Dokcer Hub เลือกใช้ Docker Image ที่เป็น alpine linux ซึ่งจะได้ image ที่มีขนาดเล็กกว่า Docker Images ทั่วไป
    - nginx:stable-alpine
    - php:7.0-fpm-alpine
    - mariadb:10.1
  • เขียน Dockerfile เพื่อสร้าง Docker image จาก php:7.0-fpm-alpine นำมาติดตั้ง PHP extension เพิ่มเติมสำหรับ WordPress
  • ใช้ Docker Compose สำหรับช่วยสร้าง Docker Container ได้ง่ายๆ
  • web directory สำหรับเก็บไฟล์ WordPress ในที่นี้คือโฟลเดอร์ www
  • mount volume ไฟล์ nginx cache, mysql data และ access_log, error_log ออกมาข้างนอก container
Docker LEMP Stack Architecture

ติดตั้งและใช้งาน

  1. ก่อนอื่นในเครื่องเราจะต้องมี Docker ติดตั้งไว้ ถ้ายังไม่มี ลองดูวิธีติดตั้งที่นี่เลยครับ https://docs.docker.com/engine/installation/
  2. พอเราติดตั้ง Docker เสร็จแล้ว ให้ติดตั้ง Docker Compose ด้วย
    https://docs.docker.com/compose/install/ (สำหรับ Docker for Mac, Windows จะมีการติดตั้ง Docker Compose มาให้พร้อมแล้ว ไม่ต้องติดตั้งเพิ่ม)
  3. เตรียมไฟล์ Config ต่างๆสำหรับ nginx และ php ได้แก่ vhost.conf, php.ini, php-fpm.conf
ตัวอย่างไฟล์ nginx config

ในไฟล์นี้จะเป็นการ config nginx virtual host ในตัวอย่างจะเป็นการตั้งค่าสำหรับโดเมน domain.com และ www.domain.com เชื่อมต่อกับ PHP-FPM ด้วย fastcgi_pass โดยทำการ link กับ container phpfpm ผ่านตัวแปร phpfpm มีการทำ caching (fastcgi_cache) เพื่อประสิทธิภาพในการเปิดหน้าเว็บที่ดีขึ้น ไม่จำเป็นต้องประมวลผลไฟล์ php อยู่บ่อยๆช่วยประหยัดทรัพยากรเครื่องได้อีก ช่วยให้หน้าเว็บรับโหลดได้มากขึ้น รองรับ connection ได้มากขึ้น
ถ้าหากต้องการปรับแต่ง nginx ให้เข้ากับ WordPress ได้ดี ทำงานกับ Cache Plugin ได้ดี ทางเว็บ WordPress ก็มีแนะนำเรื่องการ Config Nginx อยู่ด้วยนะครับลองเข้าไปศึกษาดูได้ที่ https://codex.wordpress.org/Nginx

สำหรับไฟล์ php.ini และ php-fpm.conf จะแนะนำการ config เฉพาะส่วนที่เป็นค่าพื้นฐานเท่านั้น การตั้งค่าอื่นๆในไฟล์นี้ไม่ขอพูดถึงละกันครับ แนะนำให้ไปศึกษาเพิ่มเติมด้วยนะครับ

  • ไฟล์ php.iniให้แก้ไขจาก ;cgi.fix_pathinfo=1 ให้ลบ ; ออก และเปลี่ยนจากเลข 1 เป็น 0 ดังนี้
cgi.fix_pathinfo=0
  • ไฟล์ php-fpm.conf ควรจะตั้งค่า process manager โดยแต่ละเว็บอาจจะตั้งค่าไม่เหมือนกันขึ้นอยู่กับลักษณะการใช้งาน
[www]user = www-data
group = www-data
listen = [::]:9000pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 500

pm = สามารถเลือกได้ว่าจะรัน process เป็น static, ondemand หรือ dynamic ซึ่ง static จะรัน child processes ตามจำนวนที่เราตั้งค่าไว้
pm.max_children = จำนวน child processes ที่รันได้สูงสุด
pm.start_servers = จำนวน child processes เริ่มต้น
pm.min_spare_servers = จำนวน idle server processes ขั้นต่ำ
pm.max_spare_servers = จำนวน idle server processes สูงสุด
pm.max_requests = จำนวน request สูงสุดในแต่ละ process เพื่อป้องกันไม่ให้ memory leak
ดูรายละเอียดเพิ่มเติมเรื่องการตั้งค่า PHP-FPM ได้ในลิงค์นี้ครับ http://php.net/manual/en/install.fpm.configuration.php

4. สร้าง Dockerfile สำหรับการ Build Docker Image ในที่นี้เราจะต้องสร้าง Docker Image ที่ติดตั้ง PHP extension ที่จำเป็นสำหรับ WordPress

ตัวอย่าง Dockerfile สำหรับ build image php-fpm

5. สร้างไฟล์ docker-compose.yml

จากตัวอย่างไฟล์ docker-compose.yml มีการเรียกใช้ docker image จาก docker hub เพื่อนำมาสร้างเป็น container มีการเชื่อมต่อ network แต่ละ container เข้าด้วยกันโดยใช้ชื่อ service เป็นตัวกลางในการเชื่อมต่อ ประกอบกันเป็น LEMP Stack ไฟล์ Config และ web directory (www) ที่เตรียมไว้จะเชื่อมเข้าไปใน Docker Container ด้วยการ mount volume เข้าไปแทนที่ไฟล์เดิมใน Container (คือการ Map ไฟล์หรือโฟลเดอร์ข้างนอกเข้าไปแทนไฟล์ที่อยู่ใน Container) และนอกจากนี้ยังมีการ mount volume ไฟล์จากแต่ละ container ออกมาข้างนอกด้วย เช่น log, nginx cache, mysql data

ตัวอย่างไฟล์ docker-compose.yml

สำหรับ Docker Image ในบาง Application จะมี environment variables ให้เรากำหนด เพื่อตั้งค่าต่างๆใน Docker Container เช่น เราสามารถกำหนด username และ password หรือชื่อ database สำหรับ MySQL server โดยการกำหนด environment variables ได้
(ในที่นี้ผมเลือกใช้ Docker Image เป็น MariaDB ซึ่งมันก็คือ MySQL server เช่นเดียวกัน)

ตัวอย่าง environment variables ที่กำหนดไว่้ในไฟล์ docker-compose.yml
MYSQL_ROOT_PASSWORD: 123456
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_DATABASE: database

ถ้าหากใครเป็นห่วงเรื่อง security ถ้าใส่ user กับ password ไว้ในไฟล์ docker-compose.yml ถ้าไฟล์นี้หลุดออกไปข้างนอกก็เกรงจะไม่ปลอดภัย แนะนำให้เขียน environment variables แยกออกมาเป็นไฟล์ .env ดีกว่า ตามที่เว็บ docker เค้าแนะนำไว้ https://docs.docker.com/compose/environment-variables/

6. เตรียมไฟล์ WordPress ไว้ในโฟลเดอร์ www และแก้ไข config database ในไฟล์ wp-config.php ตามที่กำหนดไว้ใน environment variables
(ถ้าหาจะใช้บน Production อย่าตั้ง password แบบนี้ละกัน ขอหละ)

phpfpm:    
restart: always
build: ./php-fpm-7
links:
- db:dockerdb

db
restart: always
image: mariadb:10.1

อ้างอิงจากไฟล์ docker-compose.yml
ส่วนนี้สังเกตได้ว่าจะมีการ link network กันระหว่าง container ที่ชื่อว่า phpfpm กับ db ในที่นี้เรากำหนดตัวแปร dockerdb สำหรับเรียกใช้ในไฟล์ wp-config.php เพื่อตั้งค่า DB_HOST ซึ่งการตั้งค่าในลักษณะเดิมจะต้องใส่ ip address ของฐานข้อมูลนั้นๆ

ผมได้นำไฟล์ config ทุกๆไฟล์อัพไว้ใน github สามารถเข้าไปดูตัวอย่างไฟล์ได้ในลิงค์นี้ครับ https://github.com/teampat/docker-lemp

7. หลังจากที่เราเตรียมไฟล์พร้อมสำหรับรัน Docker เสร็จแล้ว สั่งรันคำสั่งนี้ได้เลย

docker-compose up -d

docker-compose จะทำการ build Dockerfile ที่เราเตรียมไว้ (ในโปรเจคนี้คือโฟลเดอร์ php-fpm-7) ทำการ pull docker image จาก docker hub ที่เราเรียกใช้ ทำการตั้งค่าต่างๆและรัน docker container

ใช้คำสั่ง docker ps เพื่อดู Container ที่รันอยู่ จะเห็นว่าตอนนี้ มี Docker Container ทำงานอยู่พร้อมกัน 3 Container แล้ว

ลองเปิดดูใน browser จะพบว่า WordPress สามารถใช้งานบน Docker ได้แล้ว

เนื่องจากอาจจะมีปัญหาเรื่อง owner ของไฟล์ ทำให้ไม่สามารถอัพโหลดรูปหรือ ไม่สามารถติดตั้ง plugin WordPress ได้ ต้องให้สิทธิ user ของ container ที่เป็น phpfpm ให้สามารถเขียนหรือแก้ไขไฟล์ให้ได้ก่อน ให้สิทธิ์ไฟล์ด้วยคำสั่งนี้ครับ

chown -R 82:82 www/

(user id ของ www-data ใน docker image php:7.0-fpm-alpine คือ 82 )

สุดท้ายแล้วสำหรับใครที่มีเว็บ WordPress เดิมอยู่แล้ว สามารถนำไฟล์เว็บมาวางไว้ในโฟลเดอร์ www และแก้ไข wp-config.php ให้ตรงตาม environment variables ที่ได้เซ็ตไว้ใน docker-compose และ database เดิมก็จะต้อง export ออกมาเป็นไฟล์ .sql เพื่อ import เข้าไปยัง mysql container ได้โดยใช้คำสั่ง

docker exec -i containerid mysql -u'user' -p'password' database < dump.sql

ก่อนจะทำการ import ไฟล์ .sql เข้าไปยัง mysql container เราจะต้องรู้ id ของ container นั้นซะก่อน เพื่อนำ id มาเป็น reference ในการเข้าถึง container หรือจะใช้ชื่อ container ในการ reference ได้เช่นกันครับ
ปล. ใช้คำสั่ง docker ps เพื่อเช็คดูว่า mysql container ที่รันอยู่เป็น id ไหน

สรุปผลจากการใช้งาน WordPress บน Docker

  • ไม่เสียเวลาติดตั้ง Application มากมายใน Server สำหรับการรัน WordPress หรือ Service อื่นๆ ถ้ามีความจำเป็นต้องย้าย Server สามารถทำได้ในระยะเวลาอันสั้น
  • เมื่อมีการอัพเดท หรือเปลี่ยนเวอร์ชั่น Application ได้ทันที เช่นต้องการเปลี่ยนจาก php 7 เป็น HHVM หรือ downgrade ไปใช้ php 5.6,5.5 ก็สามารถทำได้โดยไม่ต้องไปยุ่งเกี่ยวกับ Container อื่นๆ
  • เราสามารถต่อยอดทำ Load balancing รวมกันเป็นหลายๆ server ทำให้ระบบมีความสามารถในการรองรับผู้ใช้งานจำนวนมหาศาลได้ ทำงานต่อเนื่องตลอดเวลาแบบ 24x7 หรือเรียกว่า High availability โดยใช้ Docker Swarm
    ปล. หวังว่า docker-compose เวอร์ชั่นใหม่จะรองรับ Swarm Mode นะ

--

--