คอนเทนเนอร์สำหรับเว็บไซต์ standalone: ตั้งค่า Docker, ปรับแต่งอิมเมจ, จัดการ orchestration และจำกัดทรัพยากร
1. ทำไมเว็บไซต์ standalone ต้องทำคอนเทนเนอร์ ไม่ใช่ติดตั้งลงเซิร์ฟเวอร์ตรงๆ
ติดตั้ง PHP, Nginx, MySQL ลงบน VPS ตรงๆ ก็รันได้ แต่ปัญหาสามข้อจะระเบิดออกมาภายในครึ่งปี: หนึ่ง พนักงานใหม่รับงานต้องใช้เวลาสองวันตั้งค่าสภาพแวดล้อม สอง อยากย้ายจาก Alibaba Cloud ไป Tencent Cloud ต้องติดตั้ง dependency ใหม่ทั้งหมด สาม เวลาอัปเดต security patch ไม่กล้าแตะเวอร์ชันที่รันอยู่จริง Docker แพ็กแอปกับ dependency เป็นอิมเมจที่แก้ไขไม่ได้ ใช้แก้ปัญหาสามข้อนี้
เกณฑ์ตัดสินง่ายๆ: ถ้าเอกสาร deploy ของคุณมีข้อความว่า “ติดตั้ง php8.3-fpm ด้วย apt ก่อน จากนั้นติดตั้ง 12 extension นี้ แล้วแก้ php.ini 8 ข้อนี้” แสดงว่าคุณต้องทำคอนเทนเนอร์ เขียน 12 extension กับ 8 การตั้งค่าลงใน Dockerfile ต่อไปเครื่องไหนก็รัน docker run ครั้งเดียวก็ได้ผลเหมือนเดิม ดูการทำงานร่วมกับ CDN และ static asset ได้ที่CDN เร่งความเร็วทั่วโลก。
| วิธีติดตั้ง | ต้นทุนการทำสภาพแวดล้อมซ้ำ | ย้ายเซิร์ฟเวอร์ | ความเร็วในการ rollback | ขนาดที่เหมาะ |
|---|---|---|---|---|
| ติดตั้งบน VPS ตรงๆ | 2 คน-วัน | ติดตั้ง dependency ใหม่ทั้งหมด | ใช้ snapshot 30 นาที | ช่วง prototype |
| ติดตั้งสภาพแวดล้อมด้วย shell script | 0.5 คน-วัน | รันสคริปต์ แต่ยังมีความต่าง | 15 นาที | เว็บเดียวรัน 1 ปี |
| อิมเมจ Docker | 5 นาที | ดึงอิมเมจแล้วรันได้เลย | สลับ tag อิมเมจ 3 นาที | เว็บจริง หลายสภาพแวดล้อม |
| จัด orchestration ด้วย Docker Compose | 5 นาที | คัดลอก yaml ไปแล้วรันได้เลย | rollback ด้วยคลิกเดียว | แอป + ฐานข้อมูล + คิว |
2. เขียน Dockerfile อย่างไรไม่ให้บวม
ข้อผิดพลาดที่พบบ่อยที่สุดใน Dockerfile ของเว็บไซต์ standalone คือ single-stage build: FROM ubuntu แล้วติดตั้ง PHP, ติดตั้ง Composer, composer install, COPY ซอร์สโค้ดเข้าไป, expose port วิธีนี้มักได้อิมเมจเกิน 1GB เพราะมีเครื่องมือคอมไพล์, แคช npm และไดเรกทอรี .git วิธีที่ถูกต้องคือ multi-stage build: ขั้นแรกใช้ builder image คอมไพล์ frontend และติดตั้ง dependency ของ composer ขั้นที่สองคัดลอกแค่ผลลัพธ์จากการคอมไพล์กับรันไทม์เข้า alpine image
กฎเหล็กไม่กี่ข้อ: เลือก alpine เป็น base image แทน ubuntu; .dockerignore ต้องตัด node_modules, .git, .env และโฟลเดอร์อัปโหลดออก; ตอน npm/composer install ให้ใส่ –no-dev และ –no-cache; ตอนรันให้ใช้ user ที่ไม่ใช่ root เป้าหมายขนาด image: แอป PHP/Node ล้วนคุมให้ไม่เกิน 200MB, ถ้ามี static asset ไม่เกิน 400MB การสแกน image และ security baseline ดูได้ที่การสำรองและกู้คืนเว็บไซต์。
เคสจริงเคสหนึ่ง: ทีมหนึ่งออนไลน์ไปครึ่งปีแล้วพบว่า image ใหญ่ถึง 1.4GB พอใช้ docker history ไล่ดูทีละ layer ก็พบว่า composer install ไม่ได้ใส่ –no-dev เลยเอา phpunit กับ dev dependency ทั้งหมดยัดเข้า production image ไปด้วย พร้อมกับ COPY . . ที่คัดลอกโฟลเดอร์ .git เข้าไปด้วย พอเปลี่ยนเป็น multi-stage build โดย stage builder ติดตั้ง dependency ส่วน stage runtime คัดลอกแค่โฟลเดอร์ vendor กับ public ขนาด image ก็ลดลงเหลือ 180MB ทันที เวลาที่ใช้ deploy จาก 3 นาทีเหลือ 40 วินาที
| ชั้น | วิธีเขียนที่ผิดบ่อย | วิธีที่ปรับ | ประหยัดขนาด |
|---|---|---|---|
| Base image | FROM ubuntu:22.04 | FROM php:8.3-fpm-alpine | ประมาณ 400MB |
| System dependency | apt-get install build-essential | Multi-stage build ติดตั้งใน stage builder | ประมาณ 200MB |
| PHP dependency | composer install | composer install –no-dev –no-cache | ประมาณ 80MB |
| Frontend dependency | COPY . . | npm ci && npm run build ใน stage builder | ประมาณ 300MB |
| โฟลเดอร์ซอร์สโค้ด | COPY . /var/www | COPY –from=builder /app/public ./public | ประมาณ 100MB |
| User ที่รัน | รันด้วย root ตรงๆ | RUN adduser -D app | ได้ความปลอดภัย ไม่ได้ลดขนาด |
สาม: จัดการเว็บไซต์ standalone ด้วย docker compose อย่างไร
สถาปัตยกรรมทั่วไปของเว็บไซต์ standalone คือสี่ container: Nginx reverse proxy, แอป PHP-FPM หรือ Node, MySQL/PostgreSQL และ Redis ใช้ docker-compose.yml ไฟล์เดียวเชื่อมสี่ service นี้เข้าด้วยกัน แยก data volume ออกมาเพื่อ persist ข้อมูล MySQL เปิดพอร์ตแค่ 80 และ 443 ของ Nginx ส่วน service อื่นสื่อสารผ่าน internal network ห้ามเปิดพอร์ต MySQL ออกสู่อินเทอร์เน็ต
ตัวอย่าง config ที่ใช้งานได้จริง: Nginx depends_on php, php depends_on db และ redis; health check ใช้คำสั่ง curl -fsS http://localhost/health รันทุก 30 วินาที; restart policy ใช้ unless-stopped เหมือนกันหมด กลยุทธ์การ backup คือ mount data volume ของ MySQL ไปยังโฟลเดอร์ backup บนโฮสต์ตามเวลาที่ตั้งไว้ แล้ว sync ไปต่างพื้นที่ด้วย rsync การ monitor สำหรับ multi-container ดูได้ที่การมอนิเตอร์และแจ้งเตือนเว็บไซต์。
ตัวอย่างสถานการณ์: Alibaba Cloud Lightweight Application Server 4 vCPU 8GB เครื่องหนึ่ง รัน WordPress กับ MySQL กับ Redis สาม container container MySQL mount named volume เก็บข้อมูล Nginx map พอร์ต 80 และ 443 ไปยังโฮสต์ ส่วน container WordPress ไม่เปิดพอร์ต สื่อสารผ่าน internal network เท่านั้น หลังรีสตาร์ตโฮสต์ docker compose up -d จะดึงทั้งสาม service ขึ้นมาอัตโนมัติ ทั้งกระบวนการไม่ต้องล็อกอินเข้าไปสั่งเปิดอะไรด้วยมือเลย นี่คือคุณค่าพื้นฐานที่สุดของ compose
| บริการ | Base image | จำกัด CPU | จำกัดหน่วยความจำ | Persist หรือไม่ |
|---|---|---|---|---|
| nginx | nginx:1.27-alpine | 0.5 vCPU | 128MB | ไม่ |
| php-fpm / node | php:8.3-fpm-alpine | 1 ถึง 2 vCPU | 512MB ถึง 1GB | ไม่ (mount volume สำหรับโฟลเดอร์อัปโหลด) |
| mysql | mysql:8.4 | 1 ถึง 2 vCPU | 1 ถึง 2GB | ใช่ (โฟลเดอร์ data) |
| redis | redis:7-alpine | 0.5 vCPU | 256MB | ใช่ (persist ด้วย AOF) |
| cron/queue | ใช้ image เดียวกับแอป | 0.5 vCPU | 256MB | ไม่ |
4. ข้อจำกัดทรัพยากร: คอนเทนเนอร์ที่ไม่จำกัดจะลากเครื่องทั้งเครื่องให้ล่ม
หลายคนใช้ Docker เพราะอยากประหยัดเวลา ตอนสตาร์ทเลยไม่ใส่ –cpus และ –memory ผลก็คือพอ PHP-FPM เจอ slow query ครั้งเดียว หน่วยความจำพุ่งจาก 512MB ไป 4GB ไปเบียด MySQL บนเครื่องเดียวกันจน OOM ทุกคอนเทนเนอร์ต้องประกาศขีดจำกัดทรัพยากรอย่างชัดเจน นี่เป็นข้อบังคับ
โควตาทรัพยากรอ้างอิงสำหรับเว็บไซต์ของตัวเอง: คอนเทนเนอร์แอป 1 ถึง 2 คอร์ CPU, หน่วยความจำ 512MB ถึง 1GB; คอนเทนเนอร์ MySQL ให้ตามปริมาณข้อมูล ข้อมูลต่ำกว่า 10GB ให้ 1 คอร์ 1GB ก็เพียงพอ; Nginx และ Redis อย่างละ 0.5 คอร์, 128 ถึง 256MB เหลือสำรองไว้ 20% ของเครื่องโฮสต์ให้ Docker daemon และระบบ เกณฑ์ตัดสินง่ายๆ: จัดสรรหน่วยความจำรวมของเครื่องโฮสต์ 80% ให้คอนเทนเนอร์ เหลือ 20% ให้ระบบ ถ้าเกินสัดส่วนนี้ให้เพิ่มเครื่อง CPU ให้ตามพีก ไม่ใช่ตามค่าเฉลี่ย เพราะทราฟฟิกเว็บไซต์ของตัวเองมีพีกช่วงวันทำงานและช่วงต่ำตอนกลางคืน ถ้าจัด CPU ตามค่าเฉลี่ยจะทำให้ช่วงพีกต้องต่อคิว
| ขนาดเว็บไซต์ของตัวเอง | คอนฟิกที่แนะนำ | การจัดสรร CPU | การจัดสรรหน่วยความจำ | ยอดเข้าชมต่อเดือน (อ้างอิง) |
|---|---|---|---|---|
| ช่วงเริ่มต้นทดสอบ | VPS 2 คอร์ 2GB 1 เครื่อง | แอป 1 คอร์ + DB 0.5 คอร์ | แอป 512MB + DB 1GB | <5 หมื่น |
| ช่วงเติบโตมั่นคง | VPS 4 คอร์ 8GB 1 เครื่อง | แอป 2 คอร์ + DB 1.5 คอร์ | แอป 2GB + DB 4GB | 5 หมื่นถึง 3 แสน |
| ช่วงพีกหลายภาษา | 4 คอร์ 8GB 2 เครื่อง | แอปเครื่องละ 2 คอร์ | เครื่องละ 2GB, DB แยก 4GB | 3 แสนถึง 1 ล้าน |
| พีกวันชาติ | ขยายแอปแนวนอนเป็น 3 ถึง 4 เครื่อง | เครื่องละ 2 คอร์ | เครื่องละ 2GB | ทราฟฟิกโปรโมชัน/งานแสดงสินค้า |
5. เมื่อไหร่ควรขึ้น Swarm หรือ K8s
ข้อสรุปที่ขัดกับสัญชาตญาณคือ: 90% ของเว็บไซต์ของตัวเองไม่จำเป็นต้องใช้ K8s docker compose บน VPS เครื่องเดียวรันได้ 4 ถึง 8 คอนเทนเนอร์ เพียงพอรองรับเว็บ B2B ที่มียอดเข้าชมไม่เกิน 1 แสนต่อวัน จะพิจารณาอัปเกรดเป็น Swarm ก็ต่อเมื่อมีสัญญาณใดสัญญาณหนึ่งในสามข้อนี้: ต้องรันแอปข้าม VPS ตั้งแต่สองเครื่องขึ้นไป, ต้องทำ blue-green deployment ที่สลับทราฟฟิกอัตโนมัติ, ต้องมี health check ที่รีสตาร์ทคอนเทนเนอร์ที่ล่มอัตโนมัติ
เกณฑ์ตัดสินง่ายมาก: ถ้า docker-compose.yml ของคุณมีเกิน 30 เซอร์วิส หรือทีมเกิน 5 คนช่วยกันดูแลสภาพแวดล้อมนี้ จึงค่อยคิดเรื่องระบบ orchestration ไม่อย่างนั้น compose คือคำตอบที่ดีที่สุด เอาแรงที่ต้องไปดูแล K8s ไปลงกับเนื้อหาและ SEO ให้ผลตอบแทนสูงกว่า
กำแพงของ K8s อยู่ที่: คุณต้องมีคนหนึ่งคนดูแล etcd, ปลั๊กอินเครือข่าย, Ingress controller โดยเฉพาะ แค่แก้ปัญหาก็กินเวลาสัปดาห์ละหนึ่งวันแล้ว สำหรับทีมเล็ก เอาเวลาส่วนนี้ไปลงกับเนื้อหาและ SEO ให้ผลตอบแทนสูงกว่า ถ้าในอนาคตต้อง deploy หลายอินสแตนซ์จริงๆ ดูการออกแบบโหลดบาลานซ์ได้ที่โหลดบาลานซ์และการ deploy หลายอินสแตนซ์。
| เครื่องมือ orchestration | ต้นทุนการเรียนรู้ | ต้นทุนการดูแล | จำนวนโหนดที่เหมาะ | เมื่อไหร่ควรเลือก |
|---|---|---|---|---|
| docker compose | 1 ถึง 2 วัน | ต่ำมาก | 1 เครื่อง | ตัวเลือกแรกของ 90% ของเว็บไซต์ของตัวเอง |
| docker swarm | 3 ถึง 5 วัน | ต่ำ | 2 ถึง 10 เครื่อง | ต้อง orchestration ข้ามเครื่องและ rolling update |
| K8s | 2 ถึง 4 สัปดาห์ | สูง | 5 เครื่องขึ้นไป | หลายทีม หลายเซอร์วิส ปรับสเกลอัตโนมัติ |
| K8s แบบ managed ของคลาวด์ | 1 ถึง 2 สัปดาห์ | ปานกลาง | 10 เครื่องขึ้นไป | มี DevOps ประจำอยู่แล้ว |
FAQ คำถามที่พบบ่อย
เว็บไซต์ของตัวเองใช้ Docker ช้ากว่าติดตั้งบนเซิร์ฟเวอร์ตรงๆ ไหม?
อิมเมจใหญ่แค่ไหนถึงถือว่าสมเหตุสมผล?
docker compose รับไหวไหมถ้ามีคนเข้า 1 แสนต่อวัน?
ล็อกและไฟล์อัปโหลดในคอนเทนเนอร์จัดการยังไง?
เมื่อไหร่ควรใช้ K8s เมื่อไหร่ใช้ compose ต่อ?
อิมเมจ alpine จะขาด dependency ทำให้รันไม่ขึ้นไหม?
ทิ้งท้าย
คุณค่าของ containerization สำหรับเว็บไซต์แบรนด์ตัวเอง 80% อยู่ที่ “ความสม่ำเสมอของสภาพแวดล้อม” อีก 20% อยู่ที่ “ความสามารถในการ orchestrate” เขียน Dockerfile ให้ถูกก่อน บีบอิมเมจให้เหลือ 200MB จัดการ compose ให้ดูแลสี่ service ให้ดี แค่นี้ก็แก้ปัญหาสภาพแวดล้อมไม่ตรงกันซึ่งเป็นปัญหาที่เจ็บที่สุดในสแตกเทคนิคของเว็บไซต์แบรนด์ตัวเองได้แล้ว อย่าเพิ่งไปไล่ตาม K8s ตั้งแต่แรก นั่นเป็นงานเต็มเวลาอีกงานหนึ่ง
เส้นทางลงมือทำ: สัปดาห์แรกเขียน Dockerfile แบบ multi-stage ให้เสร็จ ลดขนาด image จาก 1GB เหลือ 200MB; สัปดาห์ที่สองใช้ compose จัดระเบียบ Nginx + แอป + MySQL + Redis พร้อมผูก data volume ให้เรียบร้อย; สัปดาห์ที่สามต่อ CI/CD pipeline เข้ากับ image registry ให้ push โค้ดแล้ว build image อัตโนมัติ ทำครบสามขั้น ย้ายเซิร์ฟเวอร์จากสองวันเหลือสองชั่วโมง การเชื่อมกับ CDN และระบบมอนิเตอร์ดูที่CDN เร่งความเร็วทั่วโลก ในการดูแลระบบประจำวัน การดู docker system df สัปดาห์ละครั้งเพื่อล้าง dangling image ช่วยไม่ให้ดิสก์ค่อยๆ ถูก image ที่ไม่ได้ใช้กินจนเต็ม
ประโยคเดียวที่จำง่ายๆ: ก้าวแรกของ containerization ไม่ใช่การขึ้น K8s แต่คือการทำให้คุณกล้าเปิดเว็บด้วยคำสั่งเดียวบนเซิร์ฟเวอร์สะอาดๆ เครื่องไหนก็ได้
