1. Tổng quan
Khi phát triển ứng dụng backend hoặc triển khai hệ thống phân tán, một trong những lỗi phổ biến mà nhiều kỹ sư gặp phải là:
OSError: [Errno 98] Address already in use
Thông báo trên thường xuất hiện khi một ứng dụng cố gắng mở dịch vụ (LISTEN) trên một cổng đã bị chiếm dụng. Tuy nhiên, trong một số trường hợp, dù không có tiến trình nào rõ ràng đang “LISTEN”, lỗi này vẫn xảy ra. Nguyên nhân sâu xa có thể đến từ một cơ chế của hệ điều hành Linux: ip_local_port_range
.
2. ip_local_port_range
là gì?
ip_local_port_range
là dải cổng (port range) mà hệ điều hành sử dụng để gán ngẫu nhiên cho các kết nối outbound (ví dụ: khi máy A kết nối đến máy B).
Ví dụ:
Khi server thực hiện kết nối TCP outbound:
[ 10.10.10.1:60001 ] ---> [ 10.10.10.2:5432 ]
60001
là cổng tạm thời (ephemeral port) do kernel chọn từip_local_port_range
5432
là cổng của PostgreSQL mà máy đích đang lắng nghe
2.1 Cách kiểm tra
cat /proc/sys/net/ipv4/ip_local_port_range
Kết quả:
1025 65000
Tức là Linux sẽ chọn các cổng từ 1025 đến 65000 để làm cổng nguồn cho các kết nối outbound.
3. Minh họa luồng kết nối
┌────────────────────────┐ ┌────────────────────────────┐
│ APP (Flask, etc.) │ connect to │ Database/PostgreSQL │
│ │ ─────────────▶ │ 10.10.10.2:5432 │
│ 10.10.10.1:60001 │ │ │
└────────────────────────┘ └────────────────────────────┘
- Cổng
60001
được lấy từip_local_port_range
để tạo socket kết nối ra ngoài - Nếu một ứng dụng khác cố tình bind vào cổng
60001
, xung đột có thể xảy ra
4. Ví dụ thực tế
4.1 Flask bị lỗi do port bị chiếm
Bạn chạy Flask trên port 60001:
python3 app.py
Lỗi:
OSError: [Errno 98] Address already in use
Bạn kiểm tra:
netstat -tlnp | grep 60001
Không thấy gì. Nhưng khi chạy:
lsof -i :60001
Thấy:
ceph-osd 3013825 ceph 628u TCP 10.10.10.1:60001->10.10.10.2:6836 (ESTABLISHED)
Tức là cổng 60001 đang được dùng làm outbound TCP socket.
5. Vì sao không nên bind dịch vụ vào các port trong dải này?
5.1 Nguy cơ xung đột
Hệ điều hành có quyền chọn bất kỳ cổng nào trong ip_local_port_range
để dùng làm source port. Nếu bạn cố bind dịch vụ vào cổng đó, sẽ dễ gây lỗi.
5.2 Khó debug
Các kết nối outbound không ở trạng thái LISTEN
, khiến các công cụ như netstat -tlnp
không phát hiện được, dẫn đến hiểu nhầm là port còn trống.
6. Giải pháp
6.1 Tránh dùng cổng trong ip_local_port_range
- Kiểm tra dải cổng:
cat /proc/sys/net/ipv4/ip_local_port_range
- Tránh dùng các port trong khoảng này để chạy dịch vụ backend, frontend, proxy, etc.
6.2 Sử dụng cổng ngoài dải ephemeral
Một số cổng gợi ý:
Cổng | Ứng dụng thường dùng | Ghi chú |
---|---|---|
80 | HTTP | Cần root |
443 | HTTPS | Cần root |
5000 | Flask | Mặc định |
8000 | Django, uvicorn | Rất phổ biến |
8080 | Dev server, reverse proxy | Thường dùng |
8888 | Jupyter Notebook | An toàn |
≥65001 | Nếu dải ephemeral kết thúc tại 65000 | Có thể dùng |
6.3 Thay đổi dải ephemeral port (nếu cần thiết)
Chỉ thực hiện khi bạn kiểm soát hoàn toàn hệ thống và có lý do chính đáng.
Tạm thời:
echo "20000 40000" > /proc/sys/net/ipv4/ip_local_port_range
Vĩnh viễn:
echo "net.ipv4.ip_local_port_range = 20000 40000" >> /etc/sysctl.conf
sysctl -p
7. Giải pháp tương đương nếu bạn cần chạy nhiều dịch vụ
Giải pháp | Miễn phí | License | Ghi chú |
---|---|---|---|
NGINX reverse proxy | Có | BSD | Gộp nhiều dịch vụ qua 1 port như 80/443 |
HAProxy | Có | GPL | Cân bằng tải, routing nâng cao |
Caddy | Có | Apache | Tự động HTTPS, cấu hình đơn giản |
Systemd socket activation | Có | GPL | Chia sẻ socket giữa các service (nâng cao) |
8. Lời khuyên
- Không nên dùng bất kỳ cổng nào trong
ip_local_port_range
để bind dịch vụ. - Luôn kiểm tra dải ephemeral trước khi chọn port cho ứng dụng backend.
- Nếu bạn cần nhiều cổng, nên dùng reverse proxy để gom về 1 port chuẩn.
- Tránh hardcode port trùng với cổng của các service phổ biến như Ceph, Docker, Kubernetes…
9. Tài liệu tham khảo
- Linux ephemeral ports explained:
https://www.baeldung.com/linux/ephemeral-ports - Flask OSError “Address already in use”原因分析:
https://stackoverflow.com/questions/43456237 - Ceph OSD port usage:
https://docs.ceph.com/en/latest/rados/configuration/network-config-ref/ - ip_local_port_range – Linux kernel documentation:
https://man7.org/linux/man-pages/man7/ip.7.html