DNS: từ tên miền ra IP
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 đề
- Hardcode IP thì đổi máy là phải sửa mọi client.
- Dùng tên thì cần một hệ thống tra cứu tên → IP, và hệ thống đó có cache ở nhiều tầng.
- Không hiểu cache thì không hiểu vì sao đổi DNS "không ăn ngay".
3. Giải pháp: DNS
DNS là "danh bạ" phân tán. Các thành phần:
- Stub resolver: trong OS/JVM của client, hỏi thay ứng dụng.
- Recursive resolver: DNS của ISP, công ty, 8.8.8.8, hoặc Azure DNS
168.63.129.16. Đi hỏi hộ và cache kết quả. - Authoritative server: nơi giữ bản ghi thật của
example.com(ví dụ Azure DNS zone).
| Record | Ý nghĩa | Ví dụ |
|---|---|---|
| A | Tên → IPv4 | api.example.com A 203.0.113.10 |
| AAAA | Tên → IPv6 | api.example.com AAAA 2001:db8::10 |
| CNAME | Tên → tên khác (alias) | www.example.com CNAME api.example.com |
| MX / TXT / NS | Mail, 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 | Ưu | Nhược |
|---|---|---|
| Hardcode IP | Khô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 |
| CNAME | Trỏ 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/hosts | Test 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 Pod | Chỉ trong cluster/network đó |
7. Khi nào dùng
- Luôn gọi bằng tên: public domain cho client, private DNS zone (
db-vm.internal) cho nội bộ. - TTL ngắn (60–300s) cho bản ghi hay đổi; dài (giờ) cho bản ghi ổn định để giảm tải.
/etc/hostschỉ để test, không dùng làm cấu hình production.
api.example.com thật và dữ liệu không bị đọc trộm? Đó là TLS.Đổ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-servicehoặcorder-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-serviceororder-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
- DNS = danh bạ: A (IPv4), AAAA (IPv6), CNAME (alias).
- Cache ở mọi tầng theo TTL: đổi DNS phải chờ.
dighỏi DNS, bỏ qua hosts;getentđi đúng đường app.- JVM có DNS cache riêng:
networkaddress.cache.ttl. - Docker/K8s: gọi bằng tên service.
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.