CMD vs ENTRYPOINT
สองวิธีในการระบุสิ่งที่ container จะรัน
หัวข้อที่มีชื่อว่า “สองวิธีในการระบุสิ่งที่ container จะรัน”Docker ให้ instruction สองตัวเพื่อกำหนดว่าจะเกิดอะไรขึ้นเมื่อ container เริ่มทำงาน:
CMD— คำสั่งเริ่มต้น (หรืออาร์กิวเมนต์เริ่มต้น) override ได้ง่ายตอนdocker runENTRYPOINT— executable ที่ตายตัว override ได้ยากกว่า ทำให้ container ทำตัวเหมือนโปรแกรมเฉพาะตัวหนึ่ง
ทั้งสองมักถูกสับสนกัน การเข้าใจความต่างจะช่วยให้คุณสร้าง container ที่ใช้งานอย่างถูกต้องได้ง่าย
exec form กับ shell form
หัวข้อที่มีชื่อว่า “exec form กับ shell form”ทั้ง CMD และ ENTRYPOINT รับ syntax ได้สองรูปแบบ
exec form (แนะนำ)
หัวข้อที่มีชื่อว่า “exec form (แนะนำ)”CMD ["node", "server.js"]ENTRYPOINT ["node"]อาร์กิวเมนต์อยู่ในรูป JSON array Docker จะรันโปรแกรม โดยตรง — ไม่มี shell มาเกี่ยวข้อง PID 1 ภายใน container คือโปรเซสของคุณ นั่นหมายความว่าจะรับสัญญาณ OS (SIGTERM, SIGINT) ได้อย่างถูกต้อง ซึ่งสำคัญมากต่อการ shutdown แบบ graceful
shell form
หัวข้อที่มีชื่อว่า “shell form”CMD node server.jsENTRYPOINT nodeDocker จะห่อคำสั่งไว้ใน /bin/sh -c "..." shell จะกลายเป็น PID 1 แทนที่จะเป็นโปรเซสของคุณ แอปของคุณจะรันเป็น child ของ shell และสัญญาณ OS จะไม่ถูกส่งต่ออย่างเชื่อถือได้ หลีกเลี่ยงรูปแบบนี้สำหรับ production service
CMD — คำสั่งเริ่มต้น
หัวข้อที่มีชื่อว่า “CMD — คำสั่งเริ่มต้น”CMD กำหนดคำสั่งเริ่มต้นที่ container จะรัน ถ้าผู้ใช้ส่งคำสั่งมาตอน docker run จะ แทนที่ CMD ทั้งหมด
# syntax=docker/dockerfile:1FROM alpine:3.20CMD ["echo", "Hello from CMD!"]# Uses CMDdocker run --rm myimage# Output: Hello from CMD!
# Overrides CMD completelydocker run --rm myimage echo "Custom message"# Output: Custom messageENTRYPOINT — executable ที่ตายตัว
หัวข้อที่มีชื่อว่า “ENTRYPOINT — executable ที่ตายตัว”ENTRYPOINT กำหนด executable ที่จะรันเสมอ อาร์กิวเมนต์ที่ส่งมาตอน docker run จะถูก ต่อท้าย entry point — ไม่ใช่แทนที่
# syntax=docker/dockerfile:1FROM alpine:3.20ENTRYPOINT ["ping"]CMD ["-c", "3", "8.8.8.8"]# Uses ENTRYPOINT + CMD defaultsdocker run --rm pinger# Runs: ping -c 3 8.8.8.8
# Appends to ENTRYPOINT, replaces CMDdocker run --rm pinger -c 1 1.1.1.1# Runs: ping -c 1 1.1.1.1ENTRYPOINT + CMD ใช้ร่วมกัน (แพตเทิร์น)
หัวข้อที่มีชื่อว่า “ENTRYPOINT + CMD ใช้ร่วมกัน (แพตเทิร์น)”แพตเทิร์นที่ยืดหยุ่นที่สุดและเป็นสำนวนที่นิยมใช้:
ENTRYPOINT= executable (ตายตัว)CMD= อาร์กิวเมนต์เริ่มต้น (ผู้ใช้แทนที่ได้)
# syntax=docker/dockerfile:1FROM node:22-alpineWORKDIR /appCOPY server.js .ENTRYPOINT ["node"]CMD ["server.js"]# Default: node server.jsdocker run --rm myapp
# Override: node --versiondocker run --rm myapp --versionการ override ENTRYPOINT
หัวข้อที่มีชื่อว่า “การ override ENTRYPOINT”คุณยังสามารถ override ENTRYPOINT ได้เมื่อจำเป็นโดยใช้แฟล็ก --entrypoint:
docker run --rm --entrypoint sh myappคำสั่งนี้จะพาคุณเข้าสู่ shell ภายใน container — มีประโยชน์สำหรับการ debug
ตารางสรุป
หัวข้อที่มีชื่อว่า “ตารางสรุป”| ไม่ override | docker run img args | docker run --entrypoint X img | |
|---|---|---|---|
| มีแค่ CMD | รัน CMD | แทนที่ CMD ด้วย args | n/a — ใช้ entrypoint ใหม่ |
| มีแค่ ENTRYPOINT | รัน ENTRYPOINT | ต่อ args ท้าย ENTRYPOINT | แทนที่ ENTRYPOINT |
| ENTRYPOINT + CMD | รัน ENTRYPOINT + CMD เริ่มต้น | แทนที่ CMD ด้วย args | แทนที่ ENTRYPOINT |
ฝึกลงมือทำ
หัวข้อที่มีชื่อว่า “ฝึกลงมือทำ”# syntax=docker/dockerfile:1
FROM alpine:3.20
# ENTRYPOINT sets the fixed executable
# CMD sets the default arguments
ENTRYPOINT ["echo"]
CMD ["Hello from ENTRYPOINT + CMD!"]
# --- build ---
# docker build -t greet .
# Run with defaults (uses CMD):
# docker run --rm greet
# Override CMD with your own message:
# docker run --rm greet "My custom message"
# Override ENTRYPOINT entirely:
# docker run --rm --entrypoint sh greetข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
CMD | override ได้ง่ายตอน docker run เหมาะกับ dev/debug | ผู้ใช้เผลอเปลี่ยนพฤติกรรม default ทั้งหมดโดยไม่ตั้งใจได้ |
ENTRYPOINT | ตายตัว บังคับให้ container ทำหน้าที่เดียวเสมอ ปลอดภัยกว่าใน production | override ยากขึ้น ต้องใช้ --entrypoint ทำให้ debug ลำบากกว่า |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ใช้ shell form แทน exec form ทำให้โปรเซสไม่ได้เป็น PID 1 รับสัญญาณ OS ไม่ได้ shutdown ไม่ graceful
- ใส่ทั้ง executable และอาร์กิวเมนต์ไว้ใน
ENTRYPOINTเดียวกันหมด ทำให้CMDoverride อะไรไม่ได้เลย - เข้าใจผิดว่าอาร์กิวเมนต์ที่ส่งตอน
docker runจะถูกต่อท้ายCMDเสมอ ทั้งที่จริงอาร์กิวเมนต์พวกนั้นแทนที่CMDทั้งหมด
💡 ตัวอย่างจากของจริง
Official image ของ
postgresใช้ENTRYPOINT ["docker-entrypoint.sh"]เป็น executable ตายตัว และใช้CMD ["postgres"]เป็นอาร์กิวเมนต์เริ่มต้น ผู้ใช้จึง override เฉพาะอาร์กิวเมนต์ของpostgresได้โดยไม่ต้องแตะ entrypoint script