Tình huống
2 giờ sáng và container ứng dụng của bạn liên tục ném ra lỗi này mỗi vài giây:
dial tcp: lookup db on 127.0.0.1:53: no such host
File docker-compose.yml của bạn có một service tên db. Ứng dụng rõ ràng đang cố kết nối tới đó. Container DB đang chạy — bạn đã kiểm tra rồi. Vậy chuyện gì đang xảy ra?
Câu trả lời ngắn gọn: container ứng dụng của bạn đang dùng 127.0.0.1:53 làm DNS server. Đó là resolver loopback của máy host — không phải DNS nội bộ của Docker. Tính năng phân giải tên service của Docker chạy qua DNS nhúng riêng tại 127.0.0.11. Resolver của host chưa bao giờ biết đến db, nên nó thất bại ngay lập tức.
Điều này xảy ra khi container không nằm trên một network do Docker quản lý, hoặc khi cấu hình DNS của nó bị hỏng. Dù thế nào, nó cũng fallback về resolver hệ thống — và hệ thống không có khái niệm gì về db.
Quy trình debug
Bước 1: Kiểm tra xem cả hai container có cùng network không
Chín trên mười lần, đây chính là vấn đề. Chạy lệnh:
docker network ls
docker inspect <your_container_name> | grep -A 20 '"Networks"'
Xem phần Networks. Container app đang ở bridge (network legacy mặc định) nhưng DB lại ở myapp_default? Chúng bị cô lập với nhau — tên service sẽ không phân giải được giữa các network khác nhau.
Bước 2: Xác minh Docker DNS nội bộ đang được sử dụng
Exec vào container app và kiểm tra:
docker exec -it <app_container> cat /etc/resolv.conf
Một container Docker hoạt động bình thường sẽ hiển thị:
nameserver 127.0.0.11
options ndots:0
Thấy nameserver 127.0.0.1 hoặc một resolver công cộng như 8.8.8.8? DNS của Docker chưa được kết nối. Điều này xảy ra khi container chạy với --network host, hoặc khi chúng không được gắn vào một user-defined network.
Bước 3: Kiểm tra phân giải tên thủ công
docker exec -it <app_container> nslookup db
docker exec -it <app_container> ping db
nslookup thất bại nhưng container DB đang chạy? Đó là vấn đề gắn network — không phải vấn đề cấu hình ứng dụng.
Giải pháp
Giải pháp 1: Đặt cả hai service vào cùng một named network (Docker Compose)
Bắt đầu từ đây — cách này giải quyết được 90% trường hợp. Trong file docker-compose.yml của bạn:
services:
app:
build: .
networks:
- backend
environment:
DATABASE_URL: postgres://user:pass@db:5432/mydb
db:
image: postgres:15
networks:
- backend
networks:
backend:
driver: bridge
Khi cả hai service đều nằm trong cùng một file Compose mà không có cấu hình network tường minh, Docker Compose tự động tạo một shared network tên <project>_default và đặt tất cả vào đó. Tên service được phân giải tự động. Không cần cấu hình thêm.
Lỗi thường xuất hiện khi:
- Bạn ghi đè
networks:ở một service nhưng quên service kia - Một container được khởi động thủ công bằng
docker runthay vì qua Compose - Bạn có nhiều file Compose với các service nằm trên các project network khác nhau
Giải pháp 2: Kết nối container chạy thủ công vào Compose network
Đã khởi động app bằng docker run trong khi DB nằm trong một Compose stack? Kết nối chúng lại:
# Tìm tên network
docker network ls
# Kết nối container standalone của bạn
docker network connect myproject_default <app_container_name>
Không cần khởi động lại. Phân giải tên service có hiệu lực ngay sau khi kết nối.
Giải pháp 3: Bỏ --network host
--network host khiến container chia sẻ toàn bộ network stack của máy host. DNS nội bộ của Docker sẽ không áp dụng. Tên service sẽ không phân giải được — hoàn toàn.
Trừ khi bạn thực sự cần host networking (raw socket, truy cập phần cứng, các tình huống bind port rất đặc thù), hãy bỏ nó đi:
# Sai
docker run --network host myapp
# Đúng — dùng named network
docker run --network myproject_default myapp
Giải pháp 4: Nhiều file Compose — tạo một shared external network
Đang chạy các Compose stack riêng biệt cần giao tiếp với nhau? Định nghĩa một shared network tường minh:
# Trong DB stack (docker-compose.db.yml)
networks:
shared:
name: shared_backend
driver: bridge
# Trong app stack (docker-compose.app.yml)
networks:
shared:
external: true
name: shared_backend
Cả hai stack giờ dùng chung một network. Tên service phân giải được giữa các stack mà không cần thêm thủ thuật nào.
Kiểm tra lại
Chạy các lệnh sau để xác nhận việc sửa đã có hiệu lực:
# Kiểm tra container đang ở đúng network
docker inspect <app_container> | grep -A 5 '"Networks"'
# Xác nhận Docker DNS đang hoạt động
docker exec -it <app_container> cat /etc/resolv.conf
# Phải hiển thị: nameserver 127.0.0.11
# Kiểm tra phân giải tên
docker exec -it <app_container> nslookup db
# Phải phân giải ra một IP nội bộ dạng 172.18.0.x
nslookup db trả về một IP? Bạn đã xong. Vẫn thấy lỗi trong ứng dụng? Hãy khởi động lại container app — một số runtime (đặc biệt là ứng dụng Go và Java) cache lỗi DNS trong bộ nhớ và sẽ không thử lại cho đến khi tiến trình được khởi động lại.
Mẹo: Debug xung đột subnet
DNS đã phân giải được nhưng kết nối vẫn thất bại? Kiểm tra xung đột subnet. Docker tự động chọn dải CIDR — đôi khi chúng trùng với VPN hoặc mạng văn phòng của bạn và làm gián đoạn routing hoàn toàn. Kiểm tra subnet của network:
docker network inspect <network_name> | grep Subnet
Một thủ phạm phổ biến: Docker mặc định dùng 172.17.0.0/16 trong khi VPN của bạn dùng cùng dải đó. Hãy cố định một subnet tùy chỉnh trong file Compose để tránh xung đột:
networks:
backend:
driver: bridge
ipam:
config:
- subnet: 10.50.0.0/24
Khi cần xác định địa chỉ nào thuộc subnet nào, tôi thường dùng Subnet Calculator trên ToolCraft — chạy trên trình duyệt, hiển thị địa chỉ broadcast, dải địa chỉ khả dụng và wildcard mask mà không cần gửi dữ liệu ra ngoài.
Bài học rút ra
- DNS theo tên service chỉ hoạt động trên user-defined network. Network
bridgemặc định không hỗ trợ service discovery. Hãy đặt tên network trong Compose — luôn luôn. - Trộn lẫn
docker runvàdocker composegây ra lỗi này một cách âm thầm. Container khởi động thủ công sẽ không tự động tham gia Compose network. Bạn phải kết nối thủ công bằngdocker network connect. --network hostlà một cái búa tạ. Nó vượt qua Docker DNS hoàn toàn. Chỉ dùng khi bạn thực sự cần, không phải như một cách sửa nhanh cho các vấn đề bind port.- Dòng nameserver trong
/etc/resolv.confnói lên tất cả.127.0.0.11nghĩa là Docker DNS đang hoạt động và tên service sẽ dùng được. Bất kỳ giá trị nào khác đều có nghĩa là bạn đang phân giải bên ngoài Docker — vàdbkhông có nghĩa gì ở ngoài đó.

