DNS — phân giải tên miền và những lỗi nhớ đời

Có một câu meme nội bộ trong giới DevOps: "It's always DNS." Nửa đùa nửa thật — DNS chiếm 30% sự cố production trong sự nghiệp mình. Service không gọi…

04 tháng 5, 2026 · 90 phút đọc · Tiền đề: Hiểu IP, UDP/TCP (Bài 02, 11, 12).

Phần 1 — Tại sao bài này quan trọng với DevOps?

Có một câu meme nội bộ trong giới DevOps: "It's always DNS." Nửa đùa nửa thật — DNS chiếm ~30% sự cố production trong sự nghiệp mình. Service không gọi được nhau? DNS. App chậm? DNS lookup chậm. Microservice mới deploy không được khách hàng truy cập? DNS chưa propagate.

Bài này dạy đủ để bạn:

  1. Đọc và sửa /etc/resolv.conf, nsswitch.conf — file config DNS trên Linux.
  2. Phân biệt resolver và authoritative server.
  3. Dùng dig thành thạo — debug DNS issue.
  4. Hiểu DNS trong K8s (CoreDNS) — vì sao pod gọi service.namespace.svc.cluster.local.

Sau bài này, khi gặp Name or service not known, bạn không còn ngơ ngác.


Phần 2 — Bản chất

DNS giải quyết vấn đề: con người nhớ tên (google.com), máy tính dùng IP (142.250.x.x). DNS là "danh bạ" — bạn cho tên, nó trả IP.

Cấu trúc phân tầng từ phải sang trái:

www.example.com.
└────────────┴── root (.)
       └─────────  TLD (.com)
              └──── domain (example)
                  └── subdomain (www)

Quá trình resolve một domain:

Client
  │ "IP của www.example.com là gì?"
  ↓
Resolver (DNS server gần nhất, ví dụ 8.8.8.8)
  │ "Hỏi root: TLD .com server ở đâu?"
  ↓
Root server (a.root-servers.net, ...)
  │ "TLD .com ở 192.5.6.30, ..."
  ↓
TLD server (.com)
  │ "Authoritative cho example.com là ns.example.com (1.2.3.4)"
  ↓
Authoritative server (ns.example.com)
  │ "www.example.com là 93.184.216.34"
  ↓
Trả về Resolver → cache → trả về Client

Mỗi bước có TTL (Time-To-Live) — bao lâu thì cache hết hạn, phải hỏi lại.


Phần 3 — Đi sâu kỹ thuật

3.1 Các loại DNS record DevOps phải biết

Record Ý nghĩa Ví dụ
A Tên → IPv4 example.com → 93.184.216.34
AAAA Tên → IPv6 example.com → 2606:2800:...
CNAME Alias (tên này = tên kia) www → example.com
MX Mail server example.com mail → mail1.example.com
TXT Text bất kỳ (SPF, DKIM, verification) SPF record
SRV Service location (host + port) K8s, Active Directory
NS Name server cho domain example.com NS ns1.example.com
PTR IP → tên (reverse DNS) 34.216.184.93.in-addr.arpa → example.com

DevOps đụng nhiều nhất: A, CNAME, TXT (TLS verification), SRV (K8s service discovery).

3.2 Resolver vs Authoritative

  • Authoritative server: "Sở hữu" zone, trả lời câu hỏi cuối cùng. Ví dụ ns1.example.com authoritative cho example.com.
  • Resolver (recursive): thay mặt client đi hỏi từ root → TLD → authoritative, cache kết quả. 8.8.8.8, 1.1.1.1, hoặc DNS server nội bộ của công ty.

Trong DC on-prem, thường có internal DNS (BIND, dnsmasq, hoặc Active Directory) phân giải cả tên nội bộ (db1.internal) và tên public (forward đến 8.8.8.8).

3.3 /etc/resolv.conf — file quyền lực

# /etc/resolv.conf
nameserver 192.168.1.1     # IP DNS server dùng
nameserver 8.8.8.8         # fallback
search internal.company    # tự động thêm khi gõ tên ngắn
options timeout:2 attempts:3

search rất quan trọng. Nếu có search internal.company và bạn gõ ping db1, Linux thử:

  1. db1.internal.company (search domain trước)
  2. db1. (tên gốc)

Cấu hình sai search domain → cả lookup nội bộ và public bị chậm/lỗi.

Lưu ý Ubuntu modern: /etc/resolv.conf thường là symlink đến file của systemd-resolved. Đừng edit trực tiếp — dùng netplan hoặc disable systemd-resolved.

3.4 nsswitch.conf — thứ tự lookup

# /etc/nsswitch.conf
hosts: files dns

Nghĩa là: tra /etc/hosts trước, không có thì tra DNS. Đó là vì sao bạn có thể "fake" DNS cho test bằng cách thêm vào /etc/hosts.

3.5 DNS trong K8s — CoreDNS

K8s có DNS server riêng (CoreDNS), service được resolve qua tên <service>.<namespace>.svc.<cluster-domain>. Ví dụ:

my-app.production.svc.cluster.local

Pod có /etc/resolv.conf được kubelet tự config:

nameserver 10.96.0.10           # IP của CoreDNS service
search production.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

ndots:5 nghĩa là: nếu tên có ít hơn 5 dấu chấm, thử search domain trước. Đây là nguyên nhân của một class bug nổi tiếng — sẽ thấy ở câu chuyện.

3.6 DNS cache

Cache có ở nhiều tầng:

  1. Browser cache (~1 phút).
  2. OS resolver cache (systemd-resolved, nscd).
  3. App cache (Java JVM cache forever nếu không config — bug nổi tiếng).
  4. Recursive resolver cache (theo TTL).

TTL nhỏ (60s) → propagate nhanh nhưng tải DNS server cao. TTL lớn (3600s) → propagate chậm, nhưng tải nhẹ.

3.7 dig — vũ khí debug DNS

# Query A record
dig example.com

# Query loại record cụ thể
dig MX example.com
dig TXT example.com

# Hỏi DNS server cụ thể (bypass /etc/resolv.conf)
dig @8.8.8.8 example.com

# Trace toàn bộ chain (root → TLD → authoritative)
dig +trace example.com

# Reverse lookup
dig -x 93.184.216.34

dig show ra section ANSWER, AUTHORITY, ADDITIONAL — đọc được là debug được.


Phần 4 — Câu chuyện hiện trường

💡 Một lần ở production...

Tháng 11/2023, cluster K8s on-prem. Một service mới (let's call payment-api) deploy vào namespace production. Service expose qua ClusterIP. Service khác (order-api) cùng namespace gọi payment-api qua tên ngắn payment-api.

Triệu chứng: lookup chậm. Thay vì <1ms, mỗi DNS query mất ~5 giây. Toàn bộ flow order-to-payment chậm.

SSH vào pod order-api:

cat /etc/resolv.conf
nameserver 10.96.0.10
search production.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

Test:

time nslookup payment-api
# → 5.012s

Bật tcpdump trên pod xem DNS query đi đâu:

sudo tcpdump -i any -n port 53

Output: pod gửi 5 query DNS liên tiếp:

payment-api.production.svc.cluster.local  → A record OK
payment-api.production.svc.cluster.local  → AAAA (IPv6) — CoreDNS cố lookup → fail timeout
payment-api.svc.cluster.local             → NXDOMAIN
payment-api.cluster.local                 → NXDOMAIN
payment-api.                              → NXDOMAIN (sau timeout)

Lý do: ndots:5 + glibc resolver mặc định query cả A và AAAA song song. Một số pod app dùng musl libc (Alpine image) query tuần tự → mỗi search domain mất 1s timeout.

Fix: pod gọi service bằng FQDN tuyệt đốipayment-api.production.svc.cluster.local. (chú ý dấu chấm cuối). Khi tên ≥ 5 dấu chấm hoặc kết thúc bằng ., glibc skip search domain → 1 query duy nhất → <1ms.

Để tránh phải sửa code, có thể đặt ndots:1 trong pod spec, hoặc dùng FQDN trong config.

Bài học:

  1. K8s DNS chậm = thường do ndots:5 + search domain × số record types.
  2. Tcpdump trên pod là vũ khí — thấy được số query thật.
  3. Lookup tên ngắn trong K8s nên dùng FQDN tuyệt đối nếu service ở namespace cố định.

Phần 5 — Lab thực hành

Mục tiêu: Dùng dig thành thạo, hiểu DNS resolver chain, cấu hình DNS server riêng.

Bước 1 — Cài tool

sudo apt install -y dnsutils  # đã cài từ Bài 01

Bước 2 — Cơ bản với dig

# A record
dig example.com

# Đọc output:
# ;; ANSWER SECTION:
# example.com.    3600    IN    A    93.184.216.34
#                 │       │     │    └── value
#                 │       │     └─ class (IN = Internet)
#                 │       └── TTL
#                 └── name

Bước 3 — So sánh các DNS server

# Google DNS
dig @8.8.8.8 google.com +short

# Cloudflare
dig @1.1.1.1 google.com +short

# DNS server hiện tại (theo /etc/resolv.conf)
dig google.com +short

# Đo thời gian
dig @8.8.8.8 example.com | grep "Query time"

Bước 4 — Query loại record khác

# MX (mail server)
dig MX gmail.com

# TXT (SPF, verification)
dig TXT google.com

# NS (authoritative server)
dig NS google.com

# Reverse DNS
dig -x 8.8.8.8

Bước 5 — Trace toàn bộ resolution chain

dig +trace google.com

Output bắt đầu từ root (.), xuống TLD (com.), xuống authoritative (google.com). Đây là cách thấy "đường đi" của DNS lookup.

Bước 6 — Test /etc/hosts override

# Thêm fake entry
echo "1.2.3.4 fake-test.local" | sudo tee -a /etc/hosts

# Resolve
ping -c 1 fake-test.local
# → ping 1.2.3.4

# Cleanup
sudo sed -i '/fake-test.local/d' /etc/hosts

/etc/hosts lookup trước DNS — hữu ích cho local testing.

Bước 7 — Setup dnsmasq đơn giản

Cài DNS server local cho lab:

sudo apt install -y dnsmasq

# Cấu hình
sudo tee /etc/dnsmasq.d/lab.conf <<EOF
# Forward query không match đến 8.8.8.8
server=8.8.8.8

# Custom record
address=/db1.lab.local/192.168.100.10
address=/app1.lab.local/192.168.100.20
EOF

sudo systemctl restart dnsmasq

# Test
dig @127.0.0.1 db1.lab.local
# → 192.168.100.10

Bạn vừa setup DNS authoritative cho domain lab.local — y chang cách công ty setup internal DNS.

Self-check:

  1. dig +trace cho thấy bao nhiêu hop từ root đến answer?
  2. Vì sao /etc/hosts đè được DNS? Cấu hình ở đâu?
  3. Trong câu chuyện Phần 4, vì sao FQDN có dấu chấm cuối lại nhanh hơn?

Phần 6 — Best practices

  1. DevOps phải có internal DNS server riêng. Mỗi machine /etc/hosts → không scale. dnsmasq, BIND, hoặc tích hợp với Active Directory.

  2. Set TTL hợp lý. Production stable: 300-3600s. Trước migration: hạ xuống 60s ~24h trước để propagate nhanh.

  3. Monitor DNS resolver latency. Prometheus + node_exporter có metric. Latency tăng = vấn đề.

  4. K8s pod gọi service: dùng FQDN tuyệt đối nếu service cross-namespace. Tránh ndots:5 waste.

  5. JVM app: set networkaddress.cache.ttl. Mặc định Java cache DNS forever — service IP đổi thì app không cập nhật. Set 60 hoặc thấp hơn.


Phần 7 — Pitfalls

  1. Edit /etc/resolv.conf trực tiếp trên Ubuntu modern — bị systemd-resolved override. Phòng tránh: edit netplan hoặc /etc/systemd/resolved.conf.

  2. Cache DNS quá hung. App cache DNS forever (Java default), service IP đổi → app gọi sai IP. Phòng tránh: set TTL cache ngắn ở app.

  3. Search domain gây slow lookup (như câu chuyện). Phòng tránh: FQDN hoặc ndots:1.

  4. DNS server đơn lẻ. Một DNS server = single point of failure. Toàn corporate sập DNS = sập everything. Phòng tránh: ≥ 2 DNS server, có failover.

  5. Reverse DNS không khớp. Một số service (mail server, SSH host verification) check PTR record — nếu PTR không khớp với forward, fail/slow. Phòng tránh: setup PTR cho mọi server quan trọng.


Phần 8 — Liên kết DevOps thực tế

  • K8s CoreDNS: Bài này là tiền đề để hiểu CoreDNS config.
  • Bài 17 (TLS): SAN trong cert phải khớp tên DNS — DNS sai = TLS error.
  • Bài 18 (Load Balancing): DNS round-robin là kiểu LB cũ.
  • Service Mesh (Istio, Linkerd): dùng DNS extensively cho service discovery.
  • CDN, GeoDNS: DNS trả IP khác nhau theo location.

Phần 9 — Dùng AI

Prompt tốt:

Pod K8s base image Alpine, gi service `redis.cache.svc.cluster.local` 
chậm ~3s. Pod cùng cluster image Ubuntu thì <1ms. tcpdump cho thy Alpine 
pod gi 4 query DNS, Ubuntu gi 2 query song song. Nguyên nhân và fix?

Cảnh báo: AI hay nhầm nscd (legacy) và systemd-resolved (modern). Hỏi rõ Ubuntu version.


Phần 10 — 3 điều khắc cốt

  1. DNS lookup đi qua chain: client → resolver → root → TLD → authoritative. TTL quyết định cache thời gian.
  2. dig là vũ khí số 1. /etc/resolv.confnsswitch.conf là 2 file phải biết.
  3. K8s ndots:5 + search domain gây slow lookup. FQDN tuyệt đối hoặc ndots:1 là fix.

Phần 11 — Câu hỏi tự kiểm tra

  1. Kể 5 loại DNS record và ý nghĩa.
  2. Khác biệt giữa resolver (recursive) và authoritative server?
  3. Trong câu chuyện Phần 4, vì sao Alpine pod chậm hơn Ubuntu pod?
  4. /etc/resolv.conf có dòng search company.local — gõ ping db1 thực tế Linux query gì?
  5. TTL DNS = 60s. Bạn vừa update A record. Sau bao lâu thì client trên thế giới biết IP mới?
  6. JVM app nhớ DNS forever — fix ở đâu, đặt giá trị bao nhiêu?
  7. Lệnh nào để query DNS server cụ thể, bypass /etc/resolv.conf?

Bài tiếp theo: Bài 14 — DHCP — cấp IP tự động và pitfall thường gặp.

← Bài trước
UDP và khi nào dùng nó
Bài tiếp theo →
DHCP — cấp IP tự động và pitfall thường gặp
Zalo tư vấn