1. Home
  2. Learn
  3. Docker
  4. Docker คืออะไร และเหมาะกับงานแบบไหนในระบบจริง
Docker

Docker คืออะไร และเหมาะกับงานแบบไหนในระบบจริง

อธิบาย Docker แบบเริ่มต้นให้เข้าใจว่ามันแก้ปัญหาอะไร ทำงานอย่างไร ต่างจาก VM ตรงไหน และเหมาะกับงาน dev, test, deploy และ production แบบใด

Docker คืออะไร และเหมาะกับงานแบบไหนในระบบจริง

เวลาทีมเริ่มทำระบบจริง ปัญหาที่เจอบ่อยมากไม่ใช่แค่เรื่องเขียนโค้ด แต่คือการทำให้แอปทำงานเหมือนกันทุกเครื่อง ตั้งแต่เครื่อง developer, เครื่อง test, CI pipeline ไปจนถึง server ปลายทาง ปัญหาแบบนี้มักเริ่มจากประโยคที่ทุกทีมเคยได้ยิน เช่น “บนเครื่องผมมันรันได้” หรือ “เครื่อง production ใช้ dependency คนละเวอร์ชัน” หรือ “ตอน build ผ่าน แต่ตอน deploy พัง”

Docker เข้ามาช่วยแก้ปัญหากลุ่มนี้โดยทำให้เราสามารถแพ็กแอปพร้อม runtime, library, config บางส่วน และคำสั่งเริ่มต้นให้อยู่ในรูปแบบที่ย้ายไปใช้ที่ไหนก็ได้ง่ายขึ้น ถ้าปลายทางมี Docker runtime รองรับ เราก็สามารถรันแอปด้วย environment ที่ใกล้เคียงกันมากกว่าการติดตั้งทุกอย่างแบบ manual ทีละเครื่อง

Docker ไม่ได้แก้ทุกปัญหาในโลกของ infrastructure แต่สำหรับหลายทีม มันช่วยลดความต่างระหว่าง development กับ deployment ได้มาก และช่วยให้การ build, test, release และ rollback เป็นระบบมากขึ้นอย่างชัดเจน

Docker คืออะไร

Docker เป็นแพลตฟอร์มสำหรับ build, package และ run application ในรูปแบบ container โดย container คือหน่วยรันที่รวมตัวแอปและสิ่งที่มันต้องใช้ไว้ด้วยกันในระดับหนึ่ง เช่น runtime, package dependency, system library ที่จำเป็น และคำสั่งสำหรับเริ่มทำงาน

ถ้าจะอธิบายแบบตรงที่สุด Docker ทำให้เราเปลี่ยนจากแนวคิดว่า “ไปติดตั้งแอปบนเครื่องนั้น” เป็น “เอา image ที่ build แล้วไปรันเป็น container บนเครื่องนั้น”

ภาพรวมแบบง่ายที่สุดคือ

source code -> Docker image -> Docker container

image คือแพ็กเกจที่ถูก build แล้ว ส่วน container คือ instance ที่กำลังรันจริงจาก image นั้น

Docker กำลังแก้ปัญหาอะไร

ก่อนมี container workflow ทีมจำนวนมาก deploy แอปด้วยการติดตั้ง dependency ลงเครื่องตรง ๆ ไม่ว่าจะเป็น Node.js, Python, Nginx, system package หรือ library อื่น ๆ วิธีนี้ไม่ได้ผิด แต่พอระบบโตขึ้น ความต่างของ environment จะเริ่มกลายเป็นต้นทุนสะสมทันที

ตัวอย่างที่เจอบ่อยคือเครื่อง dev ใช้ Node 20 แต่ server ใช้ Node 18, local มี library ที่ image production ไม่มี, CI ผ่านเพราะ runner ตัวหนึ่งมี package ติดมาก่อน หรือบางครั้งเครื่องใหม่ของทีม onboarding ไม่สามารถรันโปรเจกต์ได้เพราะขาด native dependency บางตัว

Docker ช่วยลดปัญหาพวกนี้ด้วยการทำให้ขั้นตอน build environment ถูกนิยามเป็นไฟล์ เช่น Dockerfile แทนที่จะพึ่งการจำหรือพึ่ง wiki ที่ไม่อัปเดต เมื่อ environment ถูกนิยามเป็น code การทำซ้ำจึงเสถียรกว่าและ audit ได้ง่ายกว่า

Docker ทำงานอย่างไร

แกนหลักของ Docker มีอยู่สามชิ้นที่ต้องแยกให้ออก คือ Dockerfile, image และ container

Dockerfile คือไฟล์คำสั่งสำหรับบอกว่าจะสร้าง image อย่างไร เช่น ใช้ base image ตัวไหน คัดลอกไฟล์อะไร ติดตั้ง dependency แบบไหน เปิด port ไหน และใช้คำสั่งอะไรตอนเริ่มรัน

ตัวอย่างง่ายมากของแอป Node.js:

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

EXPOSE 3000
CMD ["npm", "start"]

เมื่อเรา build ไฟล์นี้ Docker จะสร้าง image ออกมา เช่น

docker build -t my-node-app .

จากนั้นเรานำ image นี้ไปรันเป็น container ได้ เช่น

docker run -p 3000:3000 my-node-app

ถ้าแอปใน container ฟังที่ port 3000 เราก็จะเปิดใช้งานจากเครื่อง host ได้ผ่าน port 3000 เช่นกัน

Image กับ Container ต่างกันอย่างไร

จุดนี้เป็นเรื่องที่มือใหม่สับสนบ่อยที่สุด

image คือแม่แบบที่ถูก build เรียบร้อยแล้ว ส่วน container คือ process ที่รันจริงจาก image นั้น ถ้าเทียบแบบบ้าน ๆ image เหมือนแม่พิมพ์ ส่วน container คือชิ้นงานที่ถูกกดออกมาจากแม่พิมพ์อีกที

ดังนั้น image เดียวสามารถสร้าง container ได้หลายตัว และถ้า container ถูกลบ image ก็ยังอยู่ ถ้าอยากรันใหม่ก็ใช้ image เดิมสร้าง container ใหม่ได้

เช็ก image ที่มีอยู่:

docker images

เช็ก container ที่กำลังรัน:

docker ps

เช็ก container ทั้งหมดรวมที่หยุดแล้ว:

docker ps -a

Docker ต่างจาก Virtual Machine อย่างไร

Docker กับ VM ไม่ได้เป็นของชนิดเดียวกันเสียทีเดียว แม้ทั้งคู่จะช่วยแยก environment ออกจากกันได้ แต่ระดับการแยกและต้นทุนต่างกัน

VM จำลองทั้งเครื่องรวม OS เต็มชุด จึงหนักกว่า ใช้ resource มากกว่า และ boot ช้ากว่า แต่ก็แยกตัวได้ลึกกว่าในหลายกรณี ส่วน Docker container ใช้ kernel ของ host ร่วมกัน จึงเบากว่า เริ่มเร็วกว่า และเหมาะกับงาน application packaging มากกว่า

ถ้ามองแบบสั้นที่สุด

VM = virtualize ทั้งเครื่อง
Docker = package และ isolate ที่ระดับ process/application

นี่เป็นเหตุผลว่าทำไม Docker จึงเหมาะมากกับการรัน service จำนวนมาก การทำ CI/CD และการ deploy แอปที่ต้องการ portability สูง

โครงสร้างโปรเจกต์ที่ใช้ Docker มักหน้าตาแบบไหน

ตัวอย่าง Node.js backend ธรรมดาอาจมีโครงสร้างประมาณนี้

my-app/
├─ src/
├─ package.json
├─ package-lock.json
├─ Dockerfile
├─ .dockerignore
└─ docker-compose.yml

ไฟล์ .dockerignore สำคัญมากเพราะช่วยลดขยะที่ไม่จำเป็นไม่ให้ถูก copy เข้า build context เช่น

node_modules
npm-debug.log
.env
.git
coverage
.dist

ถ้าไม่ใส่ .dockerignore build context จะใหญ่ขึ้นโดยไม่จำเป็น และบางครั้งลากไฟล์ลับหรือไฟล์หนัก ๆ เข้า image ไปด้วยโดยไม่รู้ตัว

เริ่มต้นกับ Docker แบบง่ายที่สุด

ถ้าแค่ต้องการ build และ run แอปตัวเดียว ขั้นตอนมักมีเท่านี้

สร้าง Dockerfile

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["python", "app.py"]

build image

docker build -t my-python-app .

run container

docker run -p 8000:8000 my-python-app

ถ้าแอปรันได้เหมือน local จุดสำคัญคือ environment สำหรับแอปตัวนี้เริ่มมีรูปแบบที่ย้ายข้ามเครื่องได้ดีขึ้นแล้ว

เมื่อมีหลาย service ควรใช้อะไร

ในระบบจริงเราไม่ได้มีแค่แอปตัวเดียว บ่อยครั้งจะมี web app, API, database, Redis, worker และ service อื่น ๆ ถ้าต้องรันทีละตัวด้วย docker run จะเริ่มจัดการยากมาก ตรงนี้จึงมักใช้ Docker Compose เพื่ออธิบายหลาย service ในไฟล์เดียว

ตัวอย่างเช่น backend + postgres

services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgresql://postgres:postgres@db:5432/appdb
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

สั่งรันได้ด้วย

docker compose up --build

ข้อดีคือเราเริ่มนิยาม local stack ได้ครบขึ้น และ onboarding คนใหม่ในทีมจะง่ายขึ้นมาก

Docker เหมาะกับงานแบบไหน

Docker เหมาะมากกับงาน development environment ที่ต้องการให้ทุกคนในทีมใช้ runtime ใกล้กัน เช่น backend ที่ต้องใช้ dependency native, service ที่ต้องพึ่ง database หลายตัว หรือระบบที่ setup manual ยุ่งเกินไป

มันยังเหมาะมากกับ CI/CD เพราะ pipeline สามารถ build image แล้วทดสอบบน image ชุดเดียวกับที่จะ deploy จริง ทำให้ลดความเสี่ยงจากความต่างของ environment ระหว่างขั้นตอน test กับ runtime ปลายทาง

ใน production Docker ก็เหมาะกับงานที่ต้องการ deploy แบบ reproducible, rollback ได้ง่าย, scale เป็นหลาย instance, หรือ integrate เข้ากับระบบ orchestrator เช่น Kubernetes หรือ platform ที่รับ container โดยตรง

นอกจากนี้ Docker ยังเหมาะกับงานประเภท one-off tooling เช่น data migration, cron jobs, batch processor, admin scripts และ service ที่อยากแพ็กทุก dependency ให้ชัดเจนใน image เดียว

Docker ไม่ได้เหมาะกับทุกกรณี

แม้ Docker จะมีประโยชน์มาก แต่มันไม่ใช่คำตอบของทุกอย่าง ถ้าระบบเล็กมาก มีแอปเดียว ไม่มี dependency ซับซ้อน และ deploy ไปยังเครื่องเดียวแบบง่าย ๆ การใช้ Docker อาจเพิ่มความซับซ้อนเกินจำเป็น

อีกกรณีหนึ่งคือทีมที่ยังไม่เข้าใจพื้นฐาน network, volume, environment variable, image layer หรือ process lifecycle เลย การโยน Docker เข้าไปเร็วเกินไปอาจทำให้ debugging ยากขึ้น เพราะปัญหาที่เคยอยู่แค่ระดับแอป จะถูกซ้อนด้วยปัญหาระดับ container เพิ่มเข้ามาอีกชั้น

Docker ยังไม่ใช่ตัวแทนของ observability, security, secret management หรือ orchestration โดยตัวมันเอง การใส่แอปลง container ไม่ได้แปลว่าแอปนั้นพร้อม production โดยอัตโนมัติ

จุดที่คนเริ่มใช้ Docker แล้วพังบ่อย

จุดแรกคือเอาไฟล์ทุกอย่างเข้า image โดยไม่จำเป็น เช่น node_modules, .env, .git, test artifact หรือไฟล์ขนาดใหญ่ ผลคือ image โตช้าและเสี่ยงหลุดข้อมูลที่ไม่ควรอยู่ใน runtime

จุดที่สองคือใช้ image ตัวใหญ่เกินจำเป็น เช่นเอา image เต็มชุดมาใช้ทั้งที่แอปต้องการแค่ runtime เล็ก ๆ ทำให้ build ช้า ดึงช้า และเพิ่ม attack surface โดยไม่จำเป็น

จุดที่สามคือเก็บ secret ไว้ใน image เช่นเขียน API key ตรงใน Dockerfile หรือ copy .env เข้า image ซึ่งเป็นพฤติกรรมที่อันตรายมาก เพราะ image มักถูก push และเก็บไว้นานกว่าที่คิด

จุดที่สี่คือเข้าใจผิดว่า container คือ VM แล้วพยายามใช้มันแบบเครื่องเต็มรูปแบบ เช่นรันหลาย process ที่ไม่เกี่ยวกันใน container เดียว หรือใช้ container เป็น shell environment ระยะยาวโดยไม่มี lifecycle ที่ชัดเจน

ตัวอย่าง Dockerfile ที่เหมาะกว่าในระบบจริง

ตัวอย่าง Node.js ที่ production-minded ขึ้นมักใช้ multi-stage build เพื่อลดขนาด image

FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD ["node", "dist/index.js"]

แนวคิดคือ stage แรกใช้ build source code ส่วน stage สุดท้ายเอาเฉพาะสิ่งที่จำเป็นต่อ runtime จริงเข้ามา ทำให้ image สะอาดขึ้นและเล็กลง

Volume คืออะไร และสำคัญอย่างไร

container โดยธรรมชาติถูกออกแบบให้ replace ได้ง่าย ถ้า container ถูกลบ ข้อมูลภายในบางส่วนก็หายตามไปด้วยถ้าเราไม่ได้ mount volume ไว้ ดังนั้น service ที่มี state เช่น database, uploaded file หรือ persistent cache ต้องคิดเรื่อง volume ให้ชัด

ตัวอย่างการ mount volume:

docker run -p 5432:5432 \
  -v postgres_data:/var/lib/postgresql/data \
  postgres:16

ถ้าไม่แยก persistent data ออกจาก lifecycle ของ container วันหนึ่งเวลา rebuild หรือ recreate container ข้อมูลอาจหายโดยไม่ตั้งใจ

การส่งค่า config และ environment

อีกจุดที่ต้องทำให้เป็นระบบคือการส่งค่า config เข้า container เช่น database URL, API endpoint, feature flag หรือ runtime mode ค่าพวกนี้ไม่ควรถูก hardcode ใน image ถ้าเปลี่ยนตาม environment

ตัวอย่าง:

docker run -p 3000:3000 \
  -e NODE_ENV=production \
  -e PORT=3000 \
  my-node-app

หรือถ้าใช้ Compose

environment:
  NODE_ENV: production
  PORT: 3000

หลักคิดคือ image ควร reusable ส่วนค่าที่เปลี่ยนตาม environment ควรถูก inject ตอน run มากกว่า bake ลงไปในตัว image

วิธี debug เมื่อ container รันไม่ขึ้น

เวลารันแล้วพัง อย่าเดาสุ่ม ให้แยกทีละชั้น เริ่มจากดู log ของ container ก่อน

docker logs <container_id>

ถ้าต้องการเข้าไปดูภายใน container

docker exec -it <container_id> sh

แล้วค่อยเช็กทีละเรื่อง เช่นไฟล์อยู่ครบไหม, port ตรงไหม, environment variable ถูกไหม, process เริ่มจริงไหม, dependency ลงครบหรือเปล่า

หลายครั้งปัญหาไม่ได้อยู่ที่ Docker เอง แต่เป็นตัวแอปที่ bind ผิด host เช่นฟังแค่ 127.0.0.1 ภายใน container ทำให้จากข้างนอกเข้าไม่ได้ ทั้งที่จริงควร bind เป็น 0.0.0.0

ตัวอย่าง Node.js:

app.listen(3000, "0.0.0.0", () => {
  console.log("server running on port 3000");
});

มุมมอง production-minded

ถ้าจะใช้ Docker ใน production จริง คำถามที่ควรถามไม่ใช่แค่ “มันรันได้ไหม” แต่คือ “มัน build ซ้ำได้ไหม”, “roll back ง่ายไหม”, “image เล็กและปลอดภัยพอไหม”, “secret ถูกจัดการถูกที่หรือยัง”, “health check มีหรือยัง” และ “เราเห็น log กับ metric จาก container นี้อย่างไร”

Docker ทำให้การแพ็กและ deploy แอปเป็นระบบขึ้น แต่ถ้าระบบยังไม่มี health check, ไม่มี restart policy, ไม่มี resource limit, ไม่มี monitoring หรือไม่มี image scanning มันก็ยังไม่ใช่ production workflow ที่สมบูรณ์

Checklist ก่อนใช้ Docker กับ service ใหม่

ก่อนตัดสินใจเอา service ตัวใหม่ใส่ Docker ควรตอบให้ได้อย่างน้อยว่าแอปนี้ต้องใช้ dependency อะไรบ้าง, state อยู่ตรงไหน, config ใดต้อง inject ตอน runtime, มีไฟล์ไหนไม่ควรถูก copy เข้า image, port ที่แอปฟังคืออะไร และถ้า container พัง เราจะ debug จาก log หรือ shell อย่างไร

คำถามพวกนี้ฟังดูพื้นฐาน แต่เป็นตัวแบ่งชัดมากระหว่าง container ที่ “แค่รันได้” กับ container ที่ “ทีมดูแลได้จริง”

สรุป

Docker เป็นเครื่องมือที่ช่วยทำให้แอปถูกแพ็กและรันด้วย environment ที่สม่ำเสมอมากขึ้น ลดปัญหาเครื่องไม่เหมือนกัน ลด friction ในการ onboarding และช่วยให้ build, test และ deploy เป็นระบบมากขึ้น โดยเฉพาะเมื่อระบบมี dependency หลายชั้นหรือมีหลาย service ที่ต้องทำงานร่วมกัน

แต่หัวใจสำคัญคืออย่ามอง Docker เป็นเวทมนตร์ที่แก้ทุกปัญหา เพราะมันช่วยเรื่อง packaging และ runtime consistency เป็นหลัก ไม่ได้แทนเรื่อง architecture, observability, security หรือ operational discipline ทั้งระบบ

ถ้าจะสรุปสั้นที่สุดอีกครั้ง Docker คือการเปลี่ยนจากการ “ติดตั้งแอปลงเครื่อง” ไปเป็นการ “แพ็กแอปเป็น image แล้วเอา image นั้นไปรันเป็น container” ซึ่งสำหรับงานจำนวนมากในโลกจริง นี่คือความต่างที่มีผลมากกว่าที่เห็นในตอนเริ่มต้น

💬 Chat (ตอบเร็ว)