สร้าง LEMP STACK (PHP7-FPM, NGINX, MariaDB) ด้วย Docker Run

Sathit Seethaphon
Sathit Seethaphon
Published in
5 min readDec 18, 2016

แนะนำการใช้งาน Docker run เพื่อสร้าง LEMP STACK

ออกตัวก่อนนะครับ ว่าผมใช้ Linux เป็นแค่ผิวเผินเท่านั้น! ตรงใหนผิดแนะนำได้เลยนะครับ

คราวที่แล้วได้ผมได้แนะนำการใช้งาน Docker Compose ไป 3 ตอน (ต่อไป blog จะย้ายมาที่นี่หมดครับ)

ก็พบว่า คนที่เริ่มใช้งานส่วนใหญ่ จะงงนิดนึง (จริงๆ ไม่น่าจะนิดเยอะเลย 555 T_T’ ) เพราะผมไม่ได้อธิบายคำสั่งของการใช้งาน Docker run พื้นฐานเลย เพราะตอนคิดว่าน่าจะพอเข้าใจ เลยไม่ได้อธิบายไป

จริงๆ แล้วคำสั่งต่างๆ ที่ docker compose ใช้นั้น ก็มาจาก Docker นั่นเอง เพราะ compose มันเป็นชุดคำสั่งที่เอาไว้คุม docker อีกที เพื่อให้สามารถสั่งและควบคุมการทำงานของ container ได้ทีละหลายๆ ตัว เช่น สั่ง start, stop container ส่วน docker run จะทำได้เพียงแค่ทีละตัว ต่อ 1 คำสั่ง

วันนี้ ได้มีโอกาสแบ่งปันการใช้งาน Docker run ให้กับพี่แทน เป็นพี่ที่รู้จักกัน จึงจะนำมาสรุปให้อ่านกันนิดหน่อย คิดว่าน่าจะเป็นประโยชน์ไม่มากก็น้อย

รูปแบบคำสั่ง

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

ในตัวอย่างนี้จะแนะนำการใช้งาน Docker run ที่เป็นคำสั่งพื้นฐาน โดยมีขั้นตอนดังนี้

  • สร้าง Network
  • สร้าง Dockerfile และ build image
  • รัน Container

หากมองเป็นภาพรวมจะได้ดังภาพนี้

ขั้นตอนแรกเราจะสร้าง network ขึ้นมาก่อนเพื่อให้ container ทั้ง 3 (NGINX, PHP-FPM, MariaD) คุยกันได้ จากนั้นในตอนที่สั่ง composer run ให้เราระบุ options ชื่อ network ที่เราได้สร้างขึ้นมาเพิ่มเข้าไปด้วย ในทุกๆ container ที่ใช้งาน

Initiation Project

เราจะสร้างโฟลเดอร์ขึ้นมาเก็บไฟล์โปรเจคทั้งหมด โดยใช้ชื่อว่า docker-php และ cd เข้าไปที่ docker-php

mkdir docker-hhp && $_

จากนั้นสร้างไฟล์ชื่อว่า index.php และใส่ phpinfo() เข้าไปเพื่อเอาไว้ทดสอบการใช้งาน php

echo "<?php phpinfo() ?>" > index.php

จะได้ไฟล์มาประมาณนี้

docker-php
├── index.php
0 directory, 1 files

Create Netowork

เราจะสร้าง network ชื่อว่า front-nw เพื่อเป็น network ไว้ใช้งานร่วมกันระหว่าง container ทั้ง 3 ตัว โดยใช้คำสั่ง docker network create

docker network create  front-nw

เมื่อสร้างเสร็จเรียบร้อย เราสามารถตรวจสอบได้ว่ามี network ชื่อ front-nw ขึ้นมาหรือยังด้วยคำสั่ง

docker network lsNETWORK ID          NAME                    DRIVER              SCOPE
04adde0bc34d bridge bridge local
6184c2685b86 front-nw bridge local
65a4ef10060f host host local
6395ea52a290 none null local

เมื่อได้ network มาแล้ว ในตอนที่ใช้คำสั่ง docker run ให้ระบุ options — net ด้วยทุกครั้ง

--net front-nw

เพื่อให้ container มันเชื่อมเข้าไปยัง network วงนี้

Run Mariadb

ต่อไปเราจะสร้างฐานข้อมูลด้วย image Mariadb โดยมี options ที่จำเป็นต้องใช้งานดังนี้ -e คือค่า environment ที่ image mariadb กำหนดมาให้ว่ามีอะไรให้เราสามารถกำหนดค่าได้บ้าง ซึ่งจะบอกไว้แล้วที่ official image ของ mariadb

ค่า environment แต่ละ image จะไม่เหมือนกันแล้วแต่ image นั้นๆ จะกำหนดไว้ว่ามีอะไรให้ใช้ได้บ้าง ซึ่งของ mariadb มีดังนี้

  • MYSQL_ROOT_PASSWORD = กำหนดค่า root password ของ mariadb
  • MYSQL_USER=กำหนดชื่อ user ที่จะใช้งาน
  • MYSQL_PASSWORD = กำหนดรหัสผ่านที่ใช้กับ MYSQL_USER
  • MYSQL_DATABASE = กำหนดชื่อ db

ปกติเค้าจะแนะนำให้ 1 container db นั้นควรมีเพียงแค่ 1 ฐานข้อมูลเท่านั้น! เพื่อความยืดหยุ่น จริงๆ แล้วเราสามารถสร้างเพิ่มได้ แต่ options -e ของ mariadb จะไม่มีให้ระบุชื่อ db อื่นๆ หากอยากเพิ่มต้องเข้าไปสร้างเอง หรืออีกวิธีให้สร้าง .sql ไว้แล้ว mount volume ไปที่พาท /docker-entrypoint-initdb.d เดี่ยวมันจะนำเข้าให้เองในครั้งแรก

ตัวสุดท้าย — network-alias db เป็นชื่อแทนในการเรียกใช้งานฐานข้อมูลนี้ ตอนที่ระบุในตัว container php ตอน connect ฐานข้อมูลปกติเราจะใส่เป็น localhost หรือ ip ฐานข้อมูล ใน docker เราก็ระบบชื่อ — network-alias ที่เราตั้งไว้ไปแทน ซึ่งในตัวอย่างนี้เราใช้ชื่อว่า db

หากมีการย้าย server หรือการปรับปรุงอื่นๆ เราก็ยังสามารถใช้งานชื่อนี้ได้ ไม่ว่า container จะได้รับเป็น ip อะไรก็ตาม แต่ถ้าหากระบุเป็น ip ในการติดต่อฐานข้อมูล เราก็อาจต้องตามแก้ การตั้งค่าต่างๆ เพราะ ip ของ container อาจมีการเปลี่ยนแปลงได้ ถ้าเราย้ายไปที่อื่นๆ

สั่งรัน mariadb เราจะใช้ image mariadb:10.1.19 พร้อมกำหนดค่า options ต่างๆ

docker run -d --name Mariadb -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_USER=php -e MYSQL_PASSWORD=1234 -e MYSQL_DATABASE=php --net front-nw -p 3306:3306 --network-alias db --restart always mariadb:10.1.19

Create Dockerfile PHP-FPM

ตัวอย่างนี้จะใช้ image php:7.1.0-fpm-alpine โดยทำการติดตั้ง library และ php extension ที่จำเป็นต้องใช้งาน ตั้งค่า timezone และติดตั้ง composer

ให้สร้างไฟล์ Dockerfile และใส่โค้ดตามนี้

สังเกตว่าเราจะเปิดเฉพาะ port 9000 เอาไว้ให้ container nginx ส่ง php มารันที่นี่ เมื่อสร้างแล้วจะได้โครงสร้างโฟลเดอร์ประมาณนี้

docker-php
├── Dockerfile
├── index.php
0 directory, 2 files

Run & Build Dockerfile PHP-FPM

เมื่อสร้าง Dockerfile เรียบร้อย เราสามารถทำการ build และสร้าง image ได้ โดยเราจะตั้งชื่อ image นี้ว่า php7fpm

docker build -t php7fpm .

ขั้นตอนนี้ จะใช้เวลาพอสมควร

เมื่อ build เสร็จ เราสามารถตรวจสอบ image ด้วยคำสั่ง docker images มันจะ list รายการ image ที่มีทั้งหมดในเครื่องของเรามาให้ดู ก็จะพบกับ image php7fpm

docker imagesREPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
php7fpm latest d967b780c21c 11 hours ago 70.6 MB

เมื่อได้ image php7fpm มาแล้วเราสามารถสร้าง container จาก image php7fpm ได้ทันที

docker run -d --name php7-fpm -v ${PWD}:/var/www/html/ --expose 9001 --net front-nw --restart always --network-alias phpfpm php7fpm

เราได้ทำการกำหนดค่า — network-alias phpfpm เพื่อเอาไว้เป็นชื่อให้เราสามารถระบุมายัง container นี้ได้แทนการใช้ ip ซึ่งเรากำหนดไว้ให้ nginx เรียกชื่อนี้เวลาจะส่ง php มาที่ port 9000 ที่เราเปิดรอไว้

-v ${PWD}:/var/www/html/

ส่วน -v เป็นการ mount ไฟล์จากโฟลเดอร์ปัจจุบันที่รันคำสั่งอยู่ไปไว้ที่ /var/www/html เมื่อ mount แล้วหากมีการแก้ไขไฟล์ต่างๆ ใน container ก็จะถูกเปลี่ยนไปด้วยเพราะเป็นการ mount ไฟล์เข้าไปตรงๆ ใน container

เราสามารถระบุให้ docker ไม่ mount ไฟล์บางตัวได้ด้วยการใช้ dockerignore คล้ายๆ กับ gitignore

Run NGINX & config

รายละเอียดการ config nginx มีเยอะพอสมควรตรงนี้ลองไปศึกษาเพิ่มเติมเอาเอง ในที่นี้จะแนะนำแค่การสร้าง config สำหรับ site ที่เราต้องการให้ nginx รัน คล้ายๆ กับการทำ virtualHost ของ apache

สร้างโฟลเดอร์ชื่อว่า nginx.d เพื่อเก็บไฟล์ config และสร้างไฟล์ config docker.conf

docker-php
├── Dockerfile
├── index.php
└── nginx.d
└── docker.conf
1 directory, 3 files

ไฟล์นี้ตั้งชื่ออะไรก็ได้ แต่นามสกุลต้องเป็น .conf

server {
charset utf-8;
client_max_body_size 128M;
listen 80; ## listen for ipv4
#listen [::]:80 default_server ipv6only=on; ## listen for ipv6
server_name app-frontend.dev;
root /var/www/html;
index index.php;
access_log /var/log/nginx/frontend-access.log;
error_log /var/log/nginx/frontend-error.log;
location / {
# Redirect everything that isn't a real file to index.php
try_files $uri $uri/ /index.php$is_args$args;
}
# uncomment to avoid processing of calls to non-existing static files by Yii
#location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
# try_files $uri =404;
#}
#error_page 404 /404.html;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass phpfpm:9000;
try_files $uri =404;
}
location ~ /\.(ht|svn|git) {
deny all;
}
}

ในส่วนของ block location นี้จะเป็นส่วนที่เช็คว่าไฟล์ที่รันเข้ามาเป็น .php ให้ทำการส่งไฟล์นี้ไปประมวลผลที่ container phpfpm ที่ port 9000

location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass phpfpm:9000;
try_files $uri =404;
}

จะเห็นว่าบรรทัดนี้คือ fastcgi_pass phpfpm:9000; ที่เราตั้ง network-alias ให้กับ container phpfpm

— volumes-from php7-fpm เป็น options ให้เรากำหนดค่าเชื่อ volume จาก container อื่นๆได้ หาก ssh เข้าไปดูใน nginx จะมี /var/www/html เหมือนกับ container php7fpm เลย

ทำการสั่ง run container พร้อมระบุค่า options ต่างๆ พร้อมระบุ port ให้ออก ที่ 80

docker run -d --name nginx --volumes-from php7-fpm --net front-nw --restart always -p 80:80 -v ${PWD}/nginx.d:/etc/nginx/conf.d nginx:1.10.2-alpine

เข้าใช้งาน

ลองเข้าใช้งาน ip ผ่าน browser ผมใช้ docker for mac สามารถเรียกผ่าน localhost ได้เลย เพราะเราระบุ Nginx ให้ออกพอร์ต 80 อยู่แล้ว กรณีระบุพอร์อื่นๆ ให้ : แล้วตามด้วยชื่อพอร์ตครับ

หากอยากทดสอบว่า container มันเชื่อมกับ container mariadb ได้หรือไม่ให้เรา exec เข้าไปใน container phpfpm เพื่อ ping container mariadb ซึ่งตอนรัน mariadb เราตั้งชื่อ — network-alias ว่า db

docker exec -it php7-fpm sh

เมื่อเข้าไปใน container แล้วสามารถ ping db ได้

/var/www/html # ping db
PING db (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.123 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.165 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.168 ms

คำสั่งที่จำเป็นต้องใช้

docker start [OPTIONS] CONTAINER [CONTAINER…]

สั่ง start container

docker start phpfpm 

docker stop [OPTIONS] CONTAINER [CONTAINER…]

สั่ง stop container

docker stop phpfpm

docker rm [OPTIONS] CONTAINER [CONTAINER…]

สั่งลบ container

docker rm phpfpm

กรณีที่ container รันอยู่แต่อยากลบ container ให้เพิ่ม -f เข้าไป

docker rm -f phpfpm

docker inspect [OPTIONS] NAME|ID [NAME|ID…]

ดูรายละเอียด conatiner

docker inspect phpfpm

docker exec [OPTIONS] CONTAINER COMMAND [ARG…]

ssh เข้าไปใน container

docker exec -it phpfpm bash

ถ้าเป็น alpine

docker exec -it phpfpm sh

คำสั่งทั้งหมด

สร้าง network

docker network create  front-nw

Run Mariadb

docker run -d --name Mariadb -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_USER=php -e MYSQL_PASSWORD=1234 -e MYSQL_DATABASE=php --net front-nw -p 3306:3306 --network-alias db --restart always mariadb:10.1.19

สร้าง Dockerfile และ build

(ไปเอาด้านบน)

docker build -t php7fpm .

Run php7-fpm

docker run -d --name php7-fpm -v ${PWD}:/var/www/html/ --expose 9001 --net front-nw --restart always --network-alias phpfpm php7fpm

สร้าง config nginx docker.conf

(ไปเอาด้านบน)

Run nginx

docker run -d --name nginx --volumes-from php7-fpm --net front-nw --restart always -p 80:80 -v ${PWD}/nginx.d:/etc/nginx/conf.d nginx:1.10.2-alpine

Conclusion

เมื่อเปรียบเทียบการใช้งานกับ docker compose จะเห็นว่า compose สะดวกกว่าเยอะเลย แต่ทั้งนี้ทั้งนั้นเราก็ควรจะเรียนรู้การใช้ docker run ง่ายๆ ก่อน เพราะเป็นพื้นฐานที่สำคัญมาก อีกอย่างถ้าใครคิดจะใช้ Docker Swarm ตอนนี้มันไม่สามารถใช้ร่วมกับ Compose ได้นะครับ เพราะฉะนั้นก็จะต้องเรียกคำสั่งสร้าง service คล้ายๆ กับ docker run นี่แหละ ถ้าเข้าใจตรงนี้แล้ว ก็คงไปต่อได้ไม่ยาก ^^

#update ตอนนี้ docker-swarm ใช้กับ docker-compose v3 ได้แล้วนะครับ

Source code https://github.com/dixonsatit/docker-run-lempstack ถ้าชอบก็กด start บน github เป็นกำลังใจให้ด้วยนะครับ ^^

--

--