สร้าง LEMP STACK (PHP7-FPM, NGINX, MariaDB) ด้วย Docker Run
แนะนำการใช้งาน Docker run เพื่อสร้าง LEMP STACK
ออกตัวก่อนนะครับ ว่าผมใช้ Linux เป็นแค่ผิวเผินเท่านั้น! ตรงใหนผิดแนะนำได้เลยนะครับ
คราวที่แล้วได้ผมได้แนะนำการใช้งาน Docker Compose ไป 3 ตอน (ต่อไป blog จะย้ายมาที่นี่หมดครับ)
- มาลองสร้าง LEMP Stack ง่ายๆ ด้วย Docker กัน (Part1)
- มาลองสร้าง LEMP Stack ง่ายๆ ด้วย Docker กัน (Part2)
- มาลองสร้าง LEMP Stack ง่ายๆ ด้วย Docker กัน (Part3)
ก็พบว่า คนที่เริ่มใช้งานส่วนใหญ่ จะงงนิดนึง (จริงๆ ไม่น่าจะนิดเยอะเลย 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.php0 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.php0 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.conf1 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 ipv6server_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 เป็นกำลังใจให้ด้วยนะครับ ^^