ข้ามไปยังเนื้อหา

การ Publish และ Expose port

มีความสับสนที่พบบ่อยใน 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 หรืออินเทอร์เน็ตเข้าถึงมันได้

flag -p มีรูปแบบ host_port:container_port:

Terminal window
docker run -d --name webserver -p 8080:80 nginx:alpine

สิ่งนี้ผูก port 8080 ของ host กับ port 80 ของ container การ request ที่มาถึง http://localhost:8080 บน host จะถูกส่งต่อไปยัง port 80 ของ container

ใช้ docker port เพื่อดู port binding ทั้งหมดของ container ที่ตั้งชื่อไว้:

Terminal window
docker port webserver

ตัวอย่างผลลัพธ์:

80/tcp -> 0.0.0.0:8080

เมื่อ publish แล้ว port จะเข้าถึงได้จาก host:

Terminal window
curl http://localhost:8080

โดยค่าเริ่มต้น Docker จะ bind กับทุก interface ของ host (0.0.0.0) หากต้องการจำกัดให้ bind เฉพาะ loopback interface (ไม่มีการเข้าถึงจากภายนอก):

Terminal window
docker run -d --name local-only -p 127.0.0.1:9090:80 nginx:alpine

วิธีนี้มีประโยชน์สำหรับ environment การพัฒนาที่คุณต้องการให้ service เข้าถึงได้เฉพาะจาก machine เดียวกันเท่านั้น

ใช้ flag -p หลายครั้งเพื่อ publish มากกว่าหนึ่ง port:

Terminal window
docker run -d --name multi -p 8080:80 -p 8443:443 nginx:alpine

ละ host port ไว้เพื่อให้ Docker กำหนด port ที่ว่างให้อัตโนมัติ:

Terminal window
docker run -p 80 nginx:alpine

Docker จะเลือก port ว่างในช่วง ephemeral (โดยทั่วไปคือ 32768–60999) ใช้ docker port เพื่อดูว่า port ใดถูกกำหนดให้

# 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
ตัวเลือกBenefitCost
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 ภายใน ทำให้พื้นที่ที่เปิดสู่ภายนอกมีแค่จุดเดียว

คำสั่ง EXPOSE ใน Dockerfile จริงๆ แล้วทำอะไร?
คุณรัน: docker run -p 9000:3000 myapp ข้อใดต่อไปนี้อธิบาย port mapping ได้ถูกต้อง?
จะจำกัด published port ให้เข้าถึงได้เฉพาะจาก local machine (loopback interface) ได้อย่างไร?
คำสั่งใดแสดงรายการ port mapping ของ container ชื่อ 'webserver'?