DNS: từ tên miền ra IP

4 câu hỏi~25 phútTừ khóa: resolver, A, AAAA, CNAME, TTL, /etc/hosts, dig, nslookup, CoreDNS, Docker DNS, networkaddress.cache.ttl

Tiếp đi. Mình gõ api.example.com chứ không ai gõ IP, vậy tên đó thành IP thế nào?

Bài trước bạn gọi order-service bằng IP 10.0.1.4. Thực tế client gọi api.example.com, và order-service gọi DB bằng db-vm.internal. Đây là chặng 1 trên bản đồ.

1. Bài toán

Client     -> https://api.example.com/orders/123
order-svc  -> jdbc:postgresql://db-vm.internal:5432/orders

Hôm nay team chuyển order-service sang VM mới có IP public mới. Đã sửa DNS, nhưng một nửa user vẫn vào VM cũ suốt 1 giờ.

2. Vấn đề

3. Giải pháp: DNS

DNS là "danh bạ" phân tán. Các thành phần:

RecordÝ nghĩaVí dụ
ATên → IPv4api.example.com A 203.0.113.10
AAAATên → IPv6api.example.com AAAA 2001:db8::10
CNAMETên → tên khác (alias)www.example.com CNAME api.example.com
MX / TXT / NSMail, xác minh domain, nameserver

TTL (giây) cho biết resolver được cache bản ghi bao lâu.

4. Từng bước

Bước 1: Tra cứu

dig api.example.com
;; ANSWER SECTION:
api.example.com.   300   IN   A   203.0.113.10
#                  ^ TTL còn lại (giây)

dig +short www.example.com
api.example.com.
203.0.113.10

# Windows / chung
nslookup api.example.com
Resolve-DnsName api.example.com   # PowerShell

Bước 2: Ghi đè cục bộ bằng /etc/hosts (Windows: C:\Windows\System32\drivers\etc\hosts), hữu ích để test VM mới trước khi đổi DNS:

203.0.113.20   api.example.com

Thứ tự tra: thường là hosts file trước, rồi mới DNS (/etc/nsswitch.conf). Lưu ý dig hỏi thẳng DNS, bỏ qua hosts file; getent hosts api.example.com mới đi đúng đường của ứng dụng.

Bước 3: Đổi IP an toàn

T-1 ngày : hạ TTL từ 3600 xuống 60
T        : đổi A record sang IP mới, giữ VM cũ chạy
T + vài TTL: traffic chuyển hết, tắt VM cũ

Bước 4: DNS cache trong JVM

JVM có cache riêng trong InetAddress. Khi không có security manager, mặc định cache kết quả thành công khoảng 30 giây; kết quả thất bại 10 giây. Muốn chỉnh (file $JAVA_HOME/conf/security/java.security hoặc code khởi động):

// đặt sớm, trước khi resolve lần đầu
java.security.Security.setProperty("networkaddress.cache.ttl", "60");
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "5");

Không bao giờ để -1 (cache vĩnh viễn) với service gọi tới endpoint cloud đổi IP. Thêm nữa, connection pool (Hikari, HTTP client) giữ connection đã mở tới IP cũ; đổi DNS không làm connection cũ tự chuyển. Đặt maxLifetime hợp lý.

Bước 5: Test từ Java

for (InetAddress a : InetAddress.getAllByName("api.example.com")) {
    System.out.println(a.getHostAddress());
}

5. Cách hoạt động

App (JVM cache?) -> OS (hosts file, OS cache)
     |
     v
Recursive resolver (cache?)  -- có rồi và chưa hết TTL -> trả luôn
     |  chưa có
     v
Root "." -> "Hỏi .com ở đây"
     v
.com TLD -> "Hỏi ns của example.com ở đây"
     v
Authoritative example.com -> "api.example.com A 203.0.113.10, TTL 300"
     |
     v
Cache ở resolver, OS, JVM ... rồi app mới mở TCP tới 203.0.113.10:443

Vì có cache ở nhiều tầng, đổi bản ghi cần chờ ít nhất TTL cũ hết hạn ở mọi tầng. Đó là chuyện "một nửa user vẫn vào VM cũ".

DNS trong Docker và Kubernetes

Docker Compose (user-defined network):
  order-service -> jdbc:postgresql://postgres:5432/orders
                   "postgres" = tên service, Docker embedded DNS 127.0.0.11 trả IP container

Kubernetes (CoreDNS):
  http://order-service                       (cùng namespace)
  http://order-service.shop.svc.cluster.local (đầy đủ)
  -> ClusterIP ổn định của Service, kube-proxy chuyển tới Pod

Pod/container đổi IP liên tục, nên luôn gọi bằng tên service, không hardcode IP.

6. So sánh

CáchƯuNhược
Hardcode IPKhông phụ thuộc DNSĐổi máy là sửa mọi nơi
A recordĐơn giản, nhanhĐổi IP phụ thuộc TTL
CNAMETrỏ vào tên do cloud quản lý (LB, App Service)Thêm một lượt tra; không dùng ở zone apex
/etc/hostsTest nhanh, ghi đè cục bộChỉ một máy, dễ quên xóa
Service name (Docker/K8s)Tự cập nhật theo PodChỉ trong cluster/network đó

7. Khi nào dùng

Đổi DNS rồi mà máy mình vẫn ra IP cũ, làm sao?

Đúng rồi, đó là cache. Xóa từng tầng:

Windows:  ipconfig /flushdns
Linux (systemd-resolved): resolvectl flush-caches
Browser:  chrome://net-internals/#dns -> Clear host cache
JVM:      restart app hoặc chờ networkaddress.cache.ttl

Kiểm tra authoritative đã đúng chưa bằng dig @ns1.example-dns.net api.example.com (hỏi thẳng nameserver của zone). Nếu authoritative đúng mà resolver công ty còn cũ, chỉ có thể chờ TTL.

Ping được bằng IP mà curl http://db-vm.internal báo Could not resolve host, có phải firewall không?

Không, đây là lỗi DNS, xảy ra trước khi có gói TCP nào được gửi. Liên hệ bài 9.3: Unknown host nằm ở tầng tên, không phải tầng port. Kiểm tra: tên có trong private DNS zone không, VNet đã link với zone chưa, máy client có dùng đúng DNS server không (resolvectl status).

Phỏng vấn hỏi về DNS thì trả lời sao?

Q1. DNS resolution hoạt động thế nào?

🇻🇳 Tiếng Việt:

Ứng dụng hỏi stub resolver của OS, OS xem hosts file và cache, rồi hỏi recursive resolver. Nếu resolver chưa cache, nó đi từ root, tới TLD .com, tới authoritative server của domain để lấy bản ghi A/AAAA hoặc CNAME. Kết quả được cache theo TTL ở resolver, OS và cả JVM.

🇬🇧 English:

The application asks the OS stub resolver, which checks the hosts file and its cache, then asks a recursive resolver. If the resolver has no cached answer, it walks from the root servers to the TLD and then to the domain's authoritative server to get the A, AAAA or CNAME record. The answer is cached according to its TTL in the resolver, the OS and the JVM.

Q2. Vì sao đổi DNS không có hiệu lực ngay? Migrate server thế nào?

🇻🇳 Tiếng Việt:

Vì bản ghi bị cache theo TTL ở nhiều tầng. Em hạ TTL trước một khoảng bằng TTL cũ, đổi bản ghi, giữ server cũ chạy song song cho tới khi traffic chuyển hết. Phía Java cần chú ý DNS cache của JVM và connection pool vẫn giữ connection tới IP cũ.

🇬🇧 English:

Because records are cached for their TTL at several layers. I lower the TTL in advance, at least one old TTL before the change, switch the record, and keep the old server running until traffic has moved. On the Java side I watch the JVM DNS cache and connection pools that still hold connections to the old IP.

Q3. Trong Kubernetes, service gọi nhau bằng gì?

🇻🇳 Tiếng Việt:

Bằng tên Service, ví dụ order-service hoặc order-service.shop.svc.cluster.local. CoreDNS trả về ClusterIP ổn định, kube-proxy chuyển tới các Pod. Không hardcode IP Pod vì Pod đổi IP khi restart.

🇬🇧 English:

By Service name, for example order-service or order-service.shop.svc.cluster.local. CoreDNS returns a stable ClusterIP and kube-proxy forwards to the Pods. We never hardcode Pod IPs because they change on restart.

Cách nhớ ngắn gọn


Tự kiểm tra

A và CNAME khác nhau thế nào?

A trỏ tên tới IPv4. CNAME trỏ tên tới một tên khác, resolver phải tra tiếp tên đó.

TTL 3600 nghĩa là gì khi bạn đổi IP?

Resolver có thể giữ IP cũ tới 1 giờ. Nên hạ TTL trước khi đổi.

Vì sao dig và ứng dụng có thể ra kết quả khác nhau?

dig hỏi thẳng DNS server, bỏ qua /etc/hosts và cache của OS/JVM.

Thuộc tính nào chỉnh DNS cache của JVM?

networkaddress.cache.ttl (và networkaddress.cache.negative.ttl cho kết quả lỗi).

Trong Docker Compose, order-service tìm Postgres bằng gì?

Tên service (ví dụ postgres), qua DNS nội bộ của Docker trên user-defined network.