การใช้ Docker Compose สำหรับ PHP Development
ในบทนี้ เราจะมาเรียนรู้เกี่ยวกับการใช้ Docker Compose เพื่อจัดการสภาพแวดล้อมการพัฒนา PHP ของเราให้ง่ายและมีประสิทธิภาพมากยิ่งขึ้น Docker Compose ช่วยให้เราสามารถกำหนดและจัดการ multi-container Docker applications ได้อย่างง่ายดาย โดยการใช้ไฟล์ YAML เพื่อกำหนด services, networks, และ volumes ที่จำเป็นสำหรับแอปพลิเคชันของเรา
ทำไมต้องใช้ Docker Compose ในการพัฒนา PHP?
การพัฒนาแอปพลิเคชัน PHP มักจะต้องพึ่งพา services อื่นๆ เช่น ฐานข้อมูล (MySQL, PostgreSQL), web server (Apache, Nginx), และ caching system (Redis, Memcached) การติดตั้งและ configure services เหล่านี้ด้วยตัวเองอาจเป็นเรื่องที่ยุ่งยากและเสียเวลา แถมยังอาจทำให้เกิดปัญหาความไม่สอดคล้องกันของสภาพแวดล้อมระหว่างเครื่อง development, staging, และ production อีกด้วย
Docker Compose ช่วยแก้ปัญหาเหล่านี้ได้โดย:
- Simplified Environment Setup: กำหนด services ทั้งหมดที่จำเป็นในไฟล์
docker-compose.ymlไฟล์เดียว ทำให้การสร้างสภาพแวดล้อมการพัฒนาเป็นเรื่องง่าย เพียงแค่รันคำสั่งdocker-compose up - Consistent Environments: ทำให้มั่นใจได้ว่าสภาพแวดล้อมการพัฒนา, staging, และ production มีความสอดคล้องกัน ลดโอกาสที่จะเกิดปัญหา "It works on my machine"
- Isolation: แต่ละ service จะทำงานใน container ของตัวเอง ทำให้แยกออกจากกัน และลดผลกระทบจาก dependencies ที่ขัดแย้งกัน
- Reproducibility: สามารถสร้างสภาพแวดล้อมการพัฒนาขึ้นใหม่ได้ง่ายๆ บนเครื่องใดก็ได้ที่มี Docker และ Docker Compose ติดตั้ง
Defining Services
Service คือ building block หลักของ Docker Compose แต่ละ service จะ correspond กับ container ที่รัน image ตัวใดตัวหนึ่ง ในไฟล์ docker-compose.yml เราจะกำหนด services ต่างๆ ที่จำเป็นสำหรับแอปพลิเคชันของเรา
ตัวอย่างไฟล์ docker-compose.yml:
version: "3.8"
services:
app:
image: php:8.1-apache
ports:
- "8000:80"
volumes:
- ./src:/var/www/html
depends_on:
- db
environment:
DB_HOST: db
DB_USER: root
DB_PASSWORD: password
DB_NAME: mydatabase
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: mydatabase
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
อธิบายส่วนต่างๆ:
version: "3.8": กำหนด version ของ Docker Compose file formatservices:: section นี้จะ define services ต่างๆapp:: ชื่อของ service สำหรับ PHP application ของเราimage: php:8.1-apache: ใช้ PHP 8.1 image ที่มี Apache web server ติดตั้งports:: map port 8000 บน host machine ไปยัง port 80 ใน containervolumes:: mount directory./srcบน host machine ไปยัง/var/www/htmlใน container ทำให้เราสามารถแก้ไขโค้ดบน host machine และเห็นผลลัพธ์ได้ทันทีใน containerdepends_on:: กำหนดว่า serviceappขึ้นอยู่กับ servicedbDocker Compose จะเริ่ม servicedbก่อน serviceappenvironment:: กำหนด environment variables สำหรับ serviceappdb:: ชื่อของ service สำหรับ MySQL databaseimage: mysql:8.0: ใช้ MySQL 8.0 imageenvironment:: กำหนด environment variables สำหรับ MySQLvolumes:: สร้าง volume ชื่อdb_dataเพื่อเก็บข้อมูล database อย่างถาวร แม้ว่า container จะถูก restartvolumes:: section นี้จะ define volumes ต่างๆdb_data:: ชื่อของ volume ที่ใช้สำหรับเก็บข้อมูล database
เหตุผลเชิงออกแบบ: การใช้ volumes ช่วยให้ข้อมูลยังคงอยู่แม้ container จะถูกลบหรือสร้างใหม่ สำหรับการพัฒนา เรามักจะ mount source code directory จาก host machine เข้าไปใน container เพื่อให้สามารถแก้ไข code และเห็นผลลัพธ์ได้ทันที
Trade-offs: การ mount source code volume อาจทำให้ประสิทธิภาพลดลงเล็กน้อยเมื่อเทียบกับการ copy code เข้าไปใน container โดยตรง แต่ tradeoff นี้คุ้มค่าสำหรับการพัฒนาเพราะช่วยให้ workflow รวดเร็วขึ้นมาก
Networking
Docker Compose สร้าง network โดยอัตโนมัติเพื่อให้ services ใน docker-compose.yml สามารถสื่อสารกันได้ โดย default, services จะสามารถเข้าถึงกันได้ผ่านชื่อ service ของตัวเอง (เช่น db ในตัวอย่างข้างต้น)
ในตัวอย่างข้างต้น, PHP application (service app) สามารถเข้าถึง MySQL database (service db) ได้โดยใช้ hostname db ซึ่ง Docker Compose จะ resolve ให้เป็น IP address ของ container ที่รัน MySQL
เราสามารถกำหนด networks เองได้ใน docker-compose.yml ถ้าต้องการ control การสื่อสารระหว่าง services ให้ละเอียดยิ่งขึ้น:
version: "3.8"
services:
app:
image: php:8.1-apache
ports:
- "8000:80"
volumes:
- ./src:/var/www/html
depends_on:
- db
environment:
DB_HOST: db
DB_USER: root
DB_PASSWORD: password
DB_NAME: mydatabase
networks:
- app-network
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: mydatabase
volumes:
- db_data:/var/lib/mysql
networks:
- app-network
networks:
app-network:
ในตัวอย่างนี้, เราได้สร้าง network ชื่อ app-network และ assign ทั้ง service app และ db ให้กับ network นี้
เหตุผลเชิงออกแบบ: การสร้าง network เองช่วยให้เราสามารถแยก services ออกจาก network default และ control การสื่อสารระหว่าง services ได้อย่างชัดเจน
Volumes
Volumes ใช้สำหรับเก็บข้อมูลอย่างถาวร แม้ว่า container จะถูกลบหรือสร้างใหม่ Docker Compose มีสองประเภทหลักๆ ของ volumes:
- Named volumes: สร้างและจัดการโดย Docker Compose Volumes เหล่านี้เหมาะสำหรับเก็บข้อมูลที่ต้องการ persist ข้าม container lifecycles (เช่น ข้อมูล database)
- Bind mounts: Mount directory หรือ file จาก host machine เข้าไปใน container Bind mounts เหมาะสำหรับ development workflow เพราะช่วยให้เราสามารถแก้ไข code บน host machine และเห็นผลลัพธ์ได้ทันทีใน container
ในตัวอย่าง docker-compose.yml ก่อนหน้านี้ เราได้ใช้ทั้ง named volume (db_data) และ bind mount (./src:/var/www/html)
เหตุผลเชิงออกแบบ: การใช้ named volumes ช่วยให้ข้อมูล database ของเราปลอดภัย แม้ว่า container จะถูกลบหรือสร้างใหม่ การใช้ bind mounts ช่วยให้ development workflow ของเรารวดเร็วและมีประสิทธิภาพ
การใช้งาน Docker Compose
หลังจากที่เราได้สร้างไฟล์ docker-compose.yml แล้ว เราสามารถรันคำสั่งต่อไปนี้เพื่อสร้างและ start services ทั้งหมด:
docker-compose up -d
option -d หมายถึง "detached mode" ซึ่งจะรัน containers ใน background
เราสามารถดู logs ของแต่ละ service ได้โดยใช้คำสั่ง:
docker-compose logs app
เราสามารถ stop services ทั้งหมดได้โดยใช้คำสั่ง:
docker-compose down
คำสั่งนี้จะ stop และ remove containers, networks, และ volumes ที่สร้างโดย Docker Compose
สรุป
Docker Compose เป็นเครื่องมือที่ทรงพลังสำหรับการจัดการสภาพแวดล้อมการพัฒนา PHP การใช้ Docker Compose ช่วยให้เราสามารถสร้างสภาพแวดล้อมที่สอดคล้องกัน, แยก services ออกจากกัน, และทำให้ development workflow ของเรารวดเร็วและมีประสิทธิภาพมากยิ่งขึ้น ในบทต่อไป เราจะมาเรียนรู้เกี่ยวกับการ Monitoring application PHP ใน production