Port Mapping
container ที่รันอยู่ไม่สามารถเข้าถึงได้จากภายนอกโดยอัตโนมัติ port mapping คือการสร้างช่องทางเชื่อมระหว่าง port บนเครื่องโฮสต์กับ port ภายใน container เพื่อให้ browser หรือแอปอื่นเข้าถึง service ได้
ภาพจำสำคัญบทนี้
-p hostPort:containerPort — ซ้ายคือภายนอก (host) ขวาคือภายใน (container) | container port ต้องตรงกับที่แอปฟังจริง | host port เลือกเองได้อิสระ
ส่วนที่ 1
Port mapping คืออะไร
โดยค่าเริ่มต้น container จะถูกแยกออกจากเครือข่ายของเครื่องโฮสต์ แม้ container จะรัน web server อยู่ข้างใน เราก็เปิด browser ไปยัง localhost แล้วเจอ service นั้นไม่ได้ทันที Port mapping คือการบอก Docker ว่า "traffic ที่มาถึง port X บนเครื่องโฮสต์ ให้ส่งต่อเข้าไปยัง port Y ภายใน container" เหมือนการติดป้ายนำทางที่หน้าประตูบ้านว่า ถ้ามาที่ประตูหน้า ให้เข้าไปที่ห้องหมายเลขเท่าไร
ประเด็นที่ 1
container network = แยกจาก host โดยค่าเริ่มต้น service ภายในไม่สามารถเข้าถึงจากภายนอกได้
ประเด็นที่ 2
port mapping = ช่องทางที่เราเปิดไว้ให้ traffic จาก host เข้าถึง service ใน container ได้
ประเด็นที่ 3
-p hostPort:containerPort คือ flag ที่ใช้สร้าง port mapping ตอนสั่ง docker run
ส่วนที่ 2
ทำไม container จึงต้องมีการ map port
Docker ออกแบบให้ container มี network namespace เป็นของตัวเอง แยกออกจาก host อย่างสมบูรณ์ ซึ่งเป็นสิ่งที่ดีในแง่ความปลอดภัย แต่ก็หมายความว่า port ที่แอปใน container ฟังอยู่ไม่ได้ถูก expose ออกมาโดยอัตโนมัติ เราต้องเปิดช่องทางนั้นเองอย่างชัดเจน
- container แต่ละตัวมี network namespace แยกกัน port 80 ใน container A ≠ port 80 ใน container B
- ความปลอดภัยเป็นค่าเริ่มต้น container ไม่ expose port ใด ๆ ออกมาหากไม่สั่ง
- เราควบคุมได้ว่า port ไหนบ้างที่ต้องการให้ภายนอกเข้าถึงได้
- ป้องกันไม่ให้ port หลาย container ชนกับ port ของ host โดยอัตโนมัติ
ส่วนที่ 3
ทบทวนพื้นฐาน: port คืออะไร
Port เปรียบได้กับหมายเลขประตูของอาคาร คอมพิวเตอร์มีที่อยู่ IP คือชื่ออาคาร แต่ภายในอาคารมีหลายประตู แต่ละประตูให้บริการต่างกัน เช่น ประตู 80 สำหรับเว็บ (HTTP) ประตู 443 สำหรับเว็บ HTTPS ประตู 5432 สำหรับ PostgreSQL เมื่อ browser เปิด http://localhost:8080 มันกำลัง "เดินไปที่เครื่อง localhost แล้วเคาะประตูหมายเลข 8080" ถ้าไม่มีอะไรรออยู่ที่ประตูนั้น ก็จะไม่ได้รับการตอบสนอง
- Port 80 = HTTP (web) / Port 443 = HTTPS / Port 3000 = Node.js dev server ที่นิยมใช้
- Port 5432 = PostgreSQL / Port 3306 = MySQL / Port 6379 = Redis
- แอปหนึ่งตัวฟังได้แค่ port เดียวในเวลาเดียว ถ้า port ถูกใช้แล้วจะ error
- port ตั้งแต่ 0–1023 คือ Well-known ports ต้องการ permission พิเศษ / 1024–65535 ใช้ได้ทั่วไป
ส่วนที่ 4
host port กับ container port — ความต่างหลัก
ใน port mapping มีสอง port ที่ต้องแยกให้ออก และมักทำให้ผู้เริ่มต้นสับสน ตารางด้านล่างอธิบายความต่างอย่างชัดเจน
| ด้าน | Host Port | Container Port |
|---|---|---|
| อยู่ที่ไหน | เครื่องโฮสต์ที่เรานั่งอยู่ | ภายใน container |
| ใครใช้ | browser หรือ client ภายนอก | process ที่รันใน container เช่น nginx, node |
| เปลี่ยนได้? | เราเลือกเองได้อย่างอิสระ | ต้องตรงกับ port ที่แอปฟังอยู่จริง |
| ตัวอย่าง | 8080, 9090, 4000 (ตามใจเรา) | 80 (nginx), 3000 (node), 5432 (postgres) |
ส่วนที่ 5
รูปแบบคำสั่ง -p hostPort:containerPort
flag -p บอก Docker ว่าต้องการสร้าง port mapping ระหว่างสองฝั่ง รูปแบบคือ host port ก่อนเสมอ ตามด้วย colon (:) แล้วจึงเป็น container port จำง่าย ๆ ว่า "ฝั่งซ้ายคือภายนอก ฝั่งขวาคือภายใน"
host port อยู่ซ้าย container port อยู่ขวา เสมอ ถ้าจำกลับกันจะเข้าไม่ถึง service
ส่วนที่ 6
ภาพ request flow: browser → host port → container port
เมื่อเราพิมพ์ http://localhost:8080 ใน browser นี่คือสิ่งที่เกิดขึ้นตามลำดับ request วิ่งมาที่เครื่องโฮสต์ port 8080 จากนั้น Docker รับและส่งต่อเข้าไปยัง port 80 ภายใน container แล้ว nginx ตอบกลับมาตามเส้นทางเดิม
Browser / Client
เปิด localhost:8080
- http://localhost:8080
- GET /index.html
Host Machine
รับที่ port 8080
- Host Port: 8080
- Docker forwards traffic
Container
nginx ฟังที่ port 80
- Container Port: 80
- nginx process
- ตอบกลับ 200 OK
ส่วนที่ 7
ตัวอย่างที่ 1: รัน nginx และ map port 8080:80
nginx ฟังที่ port 80 ภายใน container เสมอ เราเลือก map ออกมาที่ port 8080 บน host เพราะ port 80 ของ host อาจถูกใช้งานอยู่แล้ว หลังรันคำสั่งนี้ เปิด browser ไปที่ http://localhost:8080 จะเห็น nginx welcome page
-d รันเบื้องหลัง / --name web ตั้งชื่อ / -p 8080:80 เปิด port 8080 บน host ส่งต่อไปยัง port 80 ใน container
ส่วนที่ 8
ตัวอย่างที่ 2: node app — map 3000:3000
Node.js app มักฟังที่ port 3000 โดยค่าเริ่มต้น ในกรณีนี้ทั้ง host port และ container port ตรงกันที่ 3000 ซึ่งเป็นเรื่องปกติมาก โดยเฉพาะตอน dev เพื่อความสะดวกจำ แต่ไม่จำเป็นเสมอไป
host port และ container port เป็น 3000 ทั้งคู่ เปิด browser ที่ localhost:3000 ได้เลย
ส่วนที่ 9
-P (ตัวพิมพ์ใหญ่) — map port อัตโนมัติ
-P (P ตัวใหญ่) ต่างจาก -p (p ตัวเล็ก) โดยสิ้นเชิง -P จะ map ทุก port ที่ EXPOSE ไว้ใน Dockerfile ออกมาบน host แบบอัตโนมัติ โดย Docker เลือก host port สูง ๆ ให้เอง เช่น 0.0.0.0:49153->80/tcp ใช้ -P เมื่อต้องการทดสอบอย่างรวดเร็วโดยไม่สนใจ host port แต่ใช้ -p ถ้าต้องการกำหนด port เองในงานจริง
Docker จะเลือก host port สูง ๆ ให้เอง ต้องใช้ docker ps เพื่อดูว่าได้ port อะไร
ส่วนที่ 10
วิธีตรวจสอบ port mapping ด้วย docker ps
หลังรัน container ด้วย -p หรือ -P ใช้ docker ps เพื่อดูว่า port ถูก map ถูกต้องหรือไม่ คอลัมน์ PORTS แสดง mapping ทั้งหมด
docker ps แสดง PORTS column / docker port [name] แสดงเฉพาะ port mapping ของ container นั้น
ส่วนที่ 11
map หลาย port พร้อมกันในคำสั่งเดียว
container หนึ่งตัวสามารถมี port mapping หลายคู่พร้อมกันได้ โดยใส่ flag -p ซ้ำหลายครั้ง ใช้เมื่อ container ให้บริการผ่านหลาย port เช่น app ที่มีทั้ง HTTP และ HTTPS
ใส่ -p ซ้ำหลายครั้งได้ตามต้องการ แต่ host port แต่ละตัวต้องไม่ซ้ำกัน
ส่วนที่ 12
เปรียบเทียบ -p กับ -P
ทั้งสองใช้ map port แต่มีพฤติกรรมและใช้กับสถานการณ์ต่างกัน ตารางด้านล่างเปรียบเทียบให้เห็นชัดเจน
| ด้าน | -p (ตัวเล็ก) | -P (ตัวใหญ่) |
|---|---|---|
| กำหนด port เอง | ใช่ — กำหนดทั้ง host และ container port | ไม่ — Docker เลือก host port ให้อัตโนมัติ |
| port ที่ map | เฉพาะที่ระบุใน flag เท่านั้น | ทุก port ที่ EXPOSE ไว้ใน Dockerfile |
| เหมาะกับ | งานจริง ต้องการ port ที่คาดเดาได้ | ทดสอบอย่างรวดเร็ว ไม่สนใจ host port |
| ดู port ที่ได้ | รู้ล่วงหน้าจากคำสั่ง | ต้องใช้ docker ps หรือ docker port |
ส่วนที่ 13
ข้อผิดพลาดที่พบบ่อยในการ map port
ข้อผิดพลาดเหล่านี้ทำให้ container รันได้แต่เข้าถึง service ไม่ได้ หรือรันไม่ได้เลย ตรวจสอบรายการด้านล่างก่อนถามความช่วยเหลือ
- สลับฝั่ง host:container: พิมพ์ -p 80:8080 แทน -p 8080:80 ทำให้ browser ต้องเปิด port 80 แต่แอปฟังที่ 80 ใน container ซึ่งถูก map ไปยัง host port ผิด
- host port ชนกับ process อื่น: ถ้า port 8080 บน host ถูกใช้งานแล้ว Docker จะ error "address already in use" แก้โดยเปลี่ยนไปใช้ port อื่น เช่น 9090:80
- ลืม map port เลย: รัน docker run nginx โดยไม่มี -p container รันแต่เปิด browser ไม่ได้ ต้องลบ container แล้วรันใหม่พร้อม -p
- container port ผิด: ระบุ container port ไม่ตรงกับที่แอปฟังอยู่จริง เช่น nginx ฟังที่ 80 แต่เราใส่ -p 8080:8080 ต้องเช็ค EXPOSE ใน Dockerfile ของ image นั้น
- เปิดผ่าน 127.0.0.1 แทน 0.0.0.0: ถ้า container ผูก port ไว้กับ 127.0.0.1 เท่านั้น (localhost only) จะเข้าถึงจากเครื่องอื่นไม่ได้
ส่วนที่ 14
สถานการณ์จริงในการ deploy local app
ตัวอย่างสถานการณ์จริงที่นักพัฒนาใช้ port mapping ในงานประจำวัน
ประเด็นที่ 1
Local development: รัน Node.js app แบบ -p 3000:3000 เพื่อทดสอบใน browser ที่ localhost:3000 โดยไม่ต้องติดตั้ง Node บนเครื่องตรง ๆ
ประเด็นที่ 2
Multi-service dev: รัน frontend บน port 3000 และ backend API บน port 8000 พร้อมกัน โดยแต่ละ container มี -p ของตัวเอง ไม่ชนกัน
ประเด็นที่ 3
Test database: รัน postgres บน -p 5433:5432 (ใช้ 5433 บน host แทน 5432 เพื่อไม่ชนกับ postgres ที่ติดตั้งบนเครื่อง) แล้วเชื่อมต่อจาก app ได้ตามปกติ
ส่วนที่ 15
สรุปท้ายบทแบบจำง่าย
Port mapping ไม่ซับซ้อน มีสิ่งที่ต้องจำเพียงไม่กี่อย่าง กฎหลักคือ ซ้ายคือภายนอก (host) ขวาคือภายใน (container) เสมอ
- -p hostPort:containerPort คือรูปแบบมาตรฐาน ซ้าย = host, ขวา = container
- container port ต้องตรงกับ port ที่แอปใน container ฟังอยู่จริง เปลี่ยนไม่ได้
- host port เลือกได้อย่างอิสระ ขอเพียงไม่ซ้ำกับ port อื่นที่ใช้งานอยู่บน host
- -P (ตัวใหญ่) ใช้ทดสอบอย่างรวดเร็ว / -p (ตัวเล็ก) ใช้ในงานจริงที่ต้องการ port แน่นอน
- docker ps → คอลัมน์ PORTS แสดง mapping ทั้งหมด / docker port [name] แสดงของ container นั้น
- ลืม map port → เปิด browser ไม่ได้ / port ชน → Docker error ตอน run
ส่วนที่ 16
แบบฝึกหัด 3 ข้อ — กดเพื่อดูแนวเฉลย
ลองทำแต่ละข้อด้วยตัวเองก่อน จากนั้นกดที่ข้อนั้นเพื่อดูแนวเฉลย แต่ละข้อใช้เวลาไม่เกิน 5 นาที