การ Publish และ Expose port
EXPOSE กับ -p ต่างกันอย่างไร
หัวข้อที่มีชื่อว่า “EXPOSE กับ -p ต่างกันอย่างไร”มีความสับสนที่พบบ่อยใน Docker networking: ความแตกต่างระหว่าง EXPOSE ใน Dockerfile กับ -p ตอน docker run
EXPOSEคือเอกสาร บอกทั้งคนและเครื่องมือว่า application ภายใน container ฟังที่ port ไหน แต่ไม่ได้เปิด port หรือทำให้เข้าถึงได้จากภายนอกแต่อย่างใด-p(หรือ--publish) ผูก port ของ host กับ port ของ container จริง ๆ นี่คือสิ่งที่ทำให้ traffic จากภายนอก container เข้าถึง application ของคุณได้
หากคุณใส่แค่ EXPOSE 80 ใน Dockerfile แล้วลืม -p application ภายใน container ยังคงฟังอยู่ แต่ไม่มีอะไรจาก host หรืออินเทอร์เน็ตเข้าถึงมันได้
Syntax การ Publish port
หัวข้อที่มีชื่อว่า “Syntax การ Publish port”flag -p มีรูปแบบ host_port:container_port:
docker run -d --name webserver -p 8080:80 nginx:alpineสิ่งนี้ผูก port 8080 ของ host กับ port 80 ของ container การ request ที่มาถึง http://localhost:8080 บน host จะถูกส่งต่อไปยัง port 80 ของ container
ดูรายการ Port Mapping
หัวข้อที่มีชื่อว่า “ดูรายการ Port Mapping”ใช้ docker port เพื่อดู port binding ทั้งหมดของ container ที่ตั้งชื่อไว้:
docker port webserverตัวอย่างผลลัพธ์:
80/tcp -> 0.0.0.0:8080ทดสอบ Published Port
หัวข้อที่มีชื่อว่า “ทดสอบ Published Port”เมื่อ publish แล้ว port จะเข้าถึงได้จาก host:
curl http://localhost:8080ผูกกับ Interface เฉพาะ
หัวข้อที่มีชื่อว่า “ผูกกับ Interface เฉพาะ”โดยค่าเริ่มต้น Docker จะ bind กับทุก interface ของ host (0.0.0.0) หากต้องการจำกัดให้ bind เฉพาะ loopback interface (ไม่มีการเข้าถึงจากภายนอก):
docker run -d --name local-only -p 127.0.0.1:9090:80 nginx:alpineวิธีนี้มีประโยชน์สำหรับ environment การพัฒนาที่คุณต้องการให้ service เข้าถึงได้เฉพาะจาก machine เดียวกันเท่านั้น
Publish หลาย Port พร้อมกัน
หัวข้อที่มีชื่อว่า “Publish หลาย Port พร้อมกัน”ใช้ flag -p หลายครั้งเพื่อ publish มากกว่าหนึ่ง port:
docker run -d --name multi -p 8080:80 -p 8443:443 nginx:alpineให้ Docker เลือก host port ให้ (ephemeral ports)
หัวข้อที่มีชื่อว่า “ให้ Docker เลือก host port ให้ (ephemeral ports)”ละ host port ไว้เพื่อให้ Docker กำหนด port ที่ว่างให้อัตโนมัติ:
docker run -p 80 nginx:alpineDocker จะเลือก port ว่างในช่วง ephemeral (โดยทั่วไปคือ 32768–60999) ใช้ docker port เพื่อดูว่า port ใดถูกกำหนดให้
ลงมือปฏิบัติ: Publishing Ports
หัวข้อที่มีชื่อว่า “ลงมือปฏิบัติ: Publishing Ports”# Publish port 80 of nginx to host port 8080
docker run -d --name webserver -p 8080:80 nginx:alpine
# List port mappings
docker port webserver
# Curl the published port
curl http://localhost:8080
# Bind to loopback only
docker run -d --name local-only -p 127.0.0.1:9090:80 nginx:alpine
# Multiple ports
docker run -d --name multi -p 8080:80 -p 8443:443 nginx:alpine
# Cleanup
docker stop webserver local-only multi && docker rm webserver local-only multiข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
Publish ไปที่ 0.0.0.0 (ทุก interface) | เข้าถึงได้จากทุกเครื่องในเครือข่าย เหมาะกับ service ที่ต้องเปิดสู่ภายนอกจริง | เปิดช่องเข้าถึงกว้างเกินจำเป็นถ้า service นั้นควรใช้แค่ภายในเครื่อง |
Publish ไปที่ 127.0.0.1 เท่านั้น | จำกัดการเข้าถึงเฉพาะจาก host เดียวกัน ปลอดภัยกว่า | เข้าถึงจากเครื่องอื่นในเครือข่ายไม่ได้เลย ต้องแก้ flag ถ้าจะเปิดออกภายหลัง |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- เข้าใจผิดว่า
EXPOSEใน Dockerfile publish port ออกมาจริง ทั้งที่เป็นแค่เอกสารบอกว่า container ฟัง port ไหน ต้องใช้-p/--publishเท่านั้นถึงจะ map port ออกมาให้เข้าถึงได้จริง - Publish port ไปที่
0.0.0.0โดยไม่ตั้งใจสำหรับ service ที่ควรเข้าถึงได้แค่จาก localhost เช่น database หรือ admin panel ทำให้เปิดสู่เครือข่ายภายนอกโดยไม่จำเป็น - Publish port เดียวกันซ้ำจากหลาย container บน host เดียว ทำให้
docker runล้มเหลวเพราะ port ถูกจองไปแล้วโดย container ตัวอื่น
💡 ตัวอย่างจากของจริง
Pattern ทั่วไปในการ deploy คือ publish port แค่จาก reverse proxy ตัวเดียว (เช่น Traefik หรือ nginx) ที่
0.0.0.0:80และ0.0.0.0:443ส่วน backend service อื่น ๆ ไม่ publish port เลย แต่คุยกันผ่าน internal network ภายใน ทำให้พื้นที่ที่เปิดสู่ภายนอกมีแค่จุดเดียว