Port Trong Mạng Máy Tính Là Gì? Tại Sao Cần Port Binding Trong Docker?
Địa chỉ IP (IP Address) giống như địa chỉ của một tòa nhà chung cư khổng lồ, còn Port (Cổng kết nối) chính là số căn hộ cụ thể trong tòa nhà đó. Nếu không có Port, gói tin mạng khi bay tới máy chủ sẽ không biết phải giao cho tiến trình (process) nào xử lý: Nginx, MySQL hay Redis?
1. Bản chất kỹ thuật của Port trong TCP/IP
- Port là một con số nguyên 16-bit, có giá trị từ 0 đến 65535.
- Trong Linux kernel, mỗi khi một ứng dụng muốn lắng nghe kết nối mạng, nó phải thực hiện một system call
bind()để liên kết socket mạng với một số hiệu Port cụ thể. - Hai tiến trình chạy trên cùng một máy chủ (cùng Network Namespace) không bao giờ được phép bind vào cùng một Port TCP tại cùng một thời điểm. Nếu cố tình, hệ điều hành sẽ ném lỗi:
bind: address already in use.
Phân vùng số hiệu Port:
- Well-known ports (0 – 1023): Dành riêng cho các dịch vụ hệ thống cốt lõi (yêu cầu quyền root để bind):
-22: SSH
-53: DNS
-80: HTTP
-443: HTTPS - Registered ports (1024 – 49151): Dành cho các phần mềm phổ biến đăng ký với IANA:
-3306: MySQL
-5432: PostgreSQL
-6379: Redis
-8080/3000: Web application - Dynamic / Ephemeral ports (49152 – 65535): Các cổng tạm thời do hệ điều hành tự động cấp phát cho phía client khi mở kết nối ra ngoài.
2. Bí ẩn Port Binding trong Docker: -p 8080:80
Khi bạn khởi chạy một container với tham số -p 8080:80:
docker run -d -p 8080:80 --name web nginx
Cú pháp chuẩn luôn là: HOST_PORT : CONTAINER_PORT
Tại sao lại cần 2 số port này?
Mỗi Docker container khi sinh ra được Linux cấp cho một Network Namespace riêng biệt và một card mạng ảo riêng (eth0 bên trong container, nối với cầu ảo docker0 của host).
- 80 (Container Port): Nginx bên trong container lắng nghe trên cổng 80 của chính network namespace của nó.
- 8080 (Host Port): Cổng mở trên card mạng vật lý của máy chủ thật để người dùng từ Internet có thể truy cập vào.
Cơ chế hoạt động ngầm bên dưới của Docker Daemon:
Khi bạn thêm cờ -p 8080:80, Docker tự động can thiệp vào bảng iptables của Linux kernel:
Client gửi request tới: 192.168.1.100:8080
│
▼
[Linux Kernel iptables PREROUTING / DOCKER chain]
│ Thực hiện DNAT (Destination Network Address Translation):
│ Biến đổi IP:Port đích thành: 172.17.0.2:80 (IP nội bộ của container)
▼
[Nginx bên trong container nhận được request trên cổng 80]
3. Các lỗi kinh điển và cách debug
Lỗi 1: driver failed programming external connectivity ... port is already allocated
Nguyên nhân: Đã có một tiến trình khác trên host đang chiếm cổng đó rồi.
Cách xử lý:
# Tìm xem tiến trình nào đang chiếm cổng 8080
sudo ss -tulpn | grep 8080
# Hoặc dùng lsof:
sudo lsof -i :8080
Lỗi 2: Container đang chạy nhưng bên ngoài không truy cập được
- Kiểm tra xem app bên trong container có lắng nghe trên
0.0.0.0hay không?
Nếu app cấu hình listen trên127.0.0.1(localhost), nó chỉ nhận kết nối từ chính bên trong container, các gói tin được NAT từ host vào sẽ bị từ chối thẳng thừng! - Quy tắc vàng: Luôn bind app container vào
0.0.0.0!
