Docker Compose ขั้นสูง: กำหนดค่า Network และ Volume
ในบทความก่อนหน้า เราได้เรียนรู้เกี่ยวกับ Docker Volume ซึ่งเป็นวิธีจัดการข้อมูลแบบถาวรใน Container บทความนี้จะพาคุณไปอีกขั้นด้วย Docker Compose ซึ่งเป็นเครื่องมือที่ช่วยให้คุณจัดการ Container หลายตัวที่ทำงานร่วมกันได้ง่ายขึ้น โดยเฉพาะอย่างยิ่งการกำหนดค่า Network และ Volume ให้กับ Container เหล่านั้น
ทำไมต้อง Docker Compose?
ลองจินตนาการว่าคุณมีเว็บแอปพลิเคชันที่ต้องพึ่งพาฐานข้อมูล และอาจมี Redis สำหรับ Cache ด้วย การรัน Container เหล่านี้แยกกันด้วยคำสั่ง docker run จะเริ่มซับซ้อนขึ้นเรื่อยๆ ทั้งในแง่ของการเชื่อมต่อ Container เหล่านี้เข้าด้วยกัน การจัดการ Volume และการกำหนดค่า Network Docker Compose เข้ามาช่วยแก้ปัญหานี้ โดยการกำหนดค่าทั้งหมดไว้ในไฟล์ docker-compose.yml ไฟล์เดียว ทำให้การจัดการแอปพลิเคชันที่ประกอบด้วยหลาย Container ง่ายขึ้นมาก
กำหนด Network ให้กับ Container ใน Docker Compose
โดยปกติแล้ว Docker จะสร้าง Network ให้กับ Container โดยอัตโนมัติ แต่ในบางครั้งเราต้องการควบคุม Network มากขึ้น เช่น ต้องการให้ Container อยู่ใน Network เดียวกันเพื่อให้สื่อสารกันได้ง่าย หรือต้องการแยก Network เพื่อความปลอดภัย Docker Compose ช่วยให้เรากำหนดค่า Network ได้ตามต้องการ
การกำหนด Network แบบ Default
เมื่อคุณรัน docker-compose up โดยไม่มีการกำหนด Network เอง Docker Compose จะสร้าง Network default ให้โดยอัตโนมัติ Container ที่อยู่ในไฟล์ docker-compose.yml เดียวกันจะอยู่ใน Network นี้ และสามารถสื่อสารกันได้โดยใช้ชื่อ service เป็น hostname
การกำหนด Network เอง
เราสามารถกำหนด Network เองได้โดยเพิ่มส่วน networks เข้าไปในไฟล์ docker-compose.yml
version: "3.9"
services:
web:
image: nginx:latest
ports:
- "80:80"
networks:
- my_network
app:
image: your-node-app:latest
networks:
- my_network
networks:
my_network:
driver: bridge
ในตัวอย่างนี้ เราได้สร้าง Network ชื่อ my_network โดยใช้ driver แบบ bridge (ซึ่งเป็นค่า default) และกำหนดให้ทั้ง web และ app service อยู่ใน Network นี้ ทำให้ Container ทั้งสองสามารถสื่อสารกันได้โดยใช้ชื่อ service เป็น hostname เช่น app สามารถเข้าถึง web ได้โดยใช้ URL http://web
Trade-off: การใช้ Network default จะง่ายและรวดเร็ว แต่การกำหนด Network เองจะช่วยให้เราควบคุม Network ได้มากขึ้น และสามารถปรับแต่งให้เหมาะสมกับความต้องการของแอปพลิเคชัน
ใช้งาน Volume ร่วมกับ Docker Compose
เราได้เรียนรู้เรื่อง Docker Volume ในบทความก่อนหน้า Docker Compose ช่วยให้การใช้งาน Volume ง่ายขึ้น โดยเราสามารถกำหนด Volume ในไฟล์ docker-compose.yml ได้เลย
การกำหนด Volume
เราสามารถกำหนด Volume ได้สองวิธีหลักๆ คือ:
- Named Volume: Docker จะจัดการตำแหน่งของ Volume ให้เอง
- Bind Mount: เรากำหนดตำแหน่งของ Volume เอง (เหมือนกับที่เรียนในบทก่อนหน้า)
ตัวอย่างการใช้ Named Volume
version: "3.9"
services:
db:
image: postgres:14
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
ในตัวอย่างนี้ เราได้สร้าง Volume ชื่อ db_data และ mount ไปที่ /var/lib/postgresql/data ใน Container db Docker จะจัดการตำแหน่งของ Volume db_data ให้เอง และข้อมูลใน Volume นี้จะคงอยู่แม้ Container db จะถูกลบ
ตัวอย่างการใช้ Bind Mount
version: "3.9"
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
volumes:
# No need to define volumes here for bind mounts
ในตัวอย่างนี้ เราได้ mount directory ./html ใน host machine ไปที่ /usr/share/nginx/html ใน Container web การเปลี่ยนแปลงไฟล์ใน directory ./html จะมีผลกับไฟล์ใน Container web ทันที และในทางกลับกัน
Trade-off: Named Volume จะง่ายต่อการใช้งาน และ Docker จะจัดการให้ทั้งหมด แต่ Bind Mount จะให้ความยืดหยุ่นมากกว่า เพราะเราสามารถกำหนดตำแหน่งของ Volume เองได้
จัดการ Dependencies ระหว่าง Container
ในแอปพลิเคชันที่ซับซ้อน Container หลายตัวอาจต้องพึ่งพากัน เช่น เว็บแอปพลิเคชันต้องรอให้ฐานข้อมูลพร้อมใช้งานก่อน Docker Compose มีกลไกที่ช่วยจัดการ dependencies ระหว่าง Container ได้
การใช้ depends_on
depends_on เป็นคำสั่งที่ใช้ระบุว่า Container หนึ่งต้องรอให้ Container อื่นพร้อมใช้งานก่อน
version: "3.9"
services:
web:
image: your-node-app:latest
ports:
- "3000:3000"
depends_on:
- db
environment:
DATABASE_URL: postgres://user:password@db:5432/database
db:
image: postgres:14
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: database
ในตัวอย่างนี้ เราได้กำหนดให้ web service depends on db service หมายความว่า Docker Compose จะรอให้ Container db เริ่มทำงานและ "healthy" (ถ้ามีการกำหนด healthcheck) ก่อนที่จะเริ่ม Container web
ข้อควรระวัง: depends_on ไม่ได้ guarantee ว่า Container ที่ถูก depends on จะ "ready" จริงๆ (เช่น ฐานข้อมูลพร้อมรับ connection) มันแค่ guarantee ว่า Container นั้นเริ่มทำงานแล้ว ดังนั้น เราอาจต้องใช้ healthcheck หรือ retry logic เพิ่มเติมในแอปพลิเคชันของเรา
Healthcheck
Healthcheck เป็นคำสั่งที่ใช้ตรวจสอบว่า Container นั้นพร้อมใช้งานหรือไม่ เราสามารถกำหนด healthcheck ในไฟล์ docker-compose.yml ได้
version: "3.9"
services:
db:
image: postgres:14
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: database
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d database"]
interval: 10s
timeout: 5s
retries: 5
ในตัวอย่างนี้ เราได้กำหนด healthcheck ให้กับ db service โดยใช้คำสั่ง pg_isready เพื่อตรวจสอบว่า PostgreSQL พร้อมรับ connection หรือไม่ Docker Compose จะถือว่า Container db "healthy" เมื่อ healthcheck สำเร็จติดต่อกันหลายครั้ง (ตามที่กำหนดใน retries)
Trade-off: depends_on เป็นวิธีที่ง่ายในการจัดการ dependencies แต่ healthcheck จะช่วยให้เรามั่นใจได้มากขึ้นว่า Container นั้นพร้อมใช้งานจริงๆ
สรุป
ในบทความนี้ เราได้เรียนรู้เกี่ยวกับการใช้ Docker Compose เพื่อจัดการ Network และ Volume ของ Container รวมถึงการจัดการ dependencies ระหว่าง Container Docker Compose เป็นเครื่องมือที่สำคัญสำหรับนักพัฒนาและ DevOps ที่ต้องการจัดการแอปพลิเคชันที่ประกอบด้วยหลาย Container การใช้ Docker Compose จะช่วยให้การจัดการแอปพลิเคชันง่ายขึ้น ลดความซับซ้อน และเพิ่มประสิทธิภาพในการทำงาน
ในบทความถัดไป เราจะเจาะลึกเรื่อง Dockerfile Optimization เพื่อให้ Image ของคุณมีขนาดเล็กลงและ Build ได้เร็วขึ้น