系统学习 演练学习路径:35 个 lab 的难度排序、4 周基础计划与复盘方法

版本信息最后核对:2026-10-25

Lab 状态:实跑通过2026-10-25 · Docker Compose + CoreDNS

TTL 预降、区导入、NS 切换、回滚与探针均通过;宿主 5353 被占用时用 15353 等价验证

在本地 CoreDNS 中模拟权威 DNS 提供商切换:预降 TTL、导出导入 zone、验证委派与解析、检查 DNSSEC/DS,并完成可回滚的切换

演练目标

本 Lab 用本地 CoreDNS 文件区和显式 upstream 模拟委派与递归解析,真实环境还涉及注册商控制面、父区缓存、Anycast、运营商解析器和 DNSSEC 验证器,不能直接外推结论。

安全边界:只允许在一次性虚拟机或本地 Docker Compose 中进行。不要修改公司域名、真实注册商 NS、生产 DNS、线上 zone 或证书。所有命令都带 timeout;实验结束只清理本项目,保留其他 Compose 项目。任何真实切换都必须有变更单、回滚窗口和决策人。

环境准备

准备 Docker/Compose v2、dig、curl 和 GNU timeout。本页按 Linux/bash 编写;macOS 默认没有 timeout,先安装 coreutils,并把命令中的 timeout 替换为 gtimeout。所有文件放在 /tmp/lab-dns-failover,Compose 项目名固定为 lab-dns-failover,网络名固定为 lab-dns-failover-net,避免误删其他项目。

启动前先检查宿主机端口:lsof -nP -iUDP:5353 -iUDP:5354 -iUDP:5355 -iUDP:5356。macOS 的 mDNS(Chrome、avahi 等)常占用 UDP 5353,docker compose up 会报 bind: address already in use;此时只需把 parent 的宿主机端口改为 127.0.0.1:15353:53/udp 和 127.0.0.1:15353:53/tcp,并把本页所有父区查询的 -p 5353 改为 -p 15353,容器 IP、委派和缓存逻辑不变。

网络使用 172.29.0.0/24:父区 172.29.0.10,旧权威 172.29.0.11,新权威 172.29.0.12,递归缓存 172.29.0.13,旧应用 172.29.0.20,新应用 172.29.0.21,探针 172.29.0.22。父区和旧权威通过回环端口 5353、5355 暴露,新权威和递归缓存分别用 5356、5354。

先创建目录、父区、旧权威和递归缓存配置。父区只负责 example. 委派,旧权威负责 lab.example.;初始委派指向 ns1.old.example.:

timeout 10 mkdir -p /tmp/lab-dns-failover/{parent,old,new,resolver,www/old,www/new,backup}
timeout 10 tee /tmp/lab-dns-failover/parent/Corefile >/dev/null <<'EOF'
example:53 {
    file /etc/coredns/example.zone
    log
    errors
}
EOF
timeout 10 tee /tmp/lab-dns-failover/parent/example.zone >/dev/null <<'EOF'
$ORIGIN example.
$TTL 60
@ IN SOA ns1.example. hostmaster.example. (2026102501 60 30 300 60)
  IN NS ns1.example.
ns1 IN A 172.29.0.10
lab IN NS ns1.old.example.
ns1.old IN A 172.29.0.11
ns1.new IN A 172.29.0.12
EOF
timeout 10 tee /tmp/lab-dns-failover/old/Corefile >/dev/null <<'EOF'
lab.example:53 {
    file /etc/coredns/lab.example.zone
    log
    errors
}
EOF
timeout 10 tee /tmp/lab-dns-failover/old/lab.example.zone >/dev/null <<'EOF'
$ORIGIN lab.example.
$TTL 30
@ IN SOA ns1.old.example. hostmaster.lab.example. (2026102501 60 30 300 30)
  IN NS ns1.old.example.
  IN MX 10 mail.lab.example.
  IN TXT "v=spf1 -all"
  IN CAA 0 issue "letsencrypt.org"
app IN A 172.29.0.20
www IN CNAME app.lab.example.
mail IN A 172.29.0.20
_verify IN TXT "lab-dns-failover"
_acme-challenge IN TXT "lab-token"
*.apps IN A 172.29.0.20
EOF
timeout 10 tee /tmp/lab-dns-failover/new/Corefile >/dev/null <<'EOF'
lab.example:53 {
    file /etc/coredns/lab.example.zone
    log
    errors
}
EOF
timeout 10 tee /tmp/lab-dns-failover/resolver/Corefile >/dev/null <<'EOF'
.:53 {
    cache 300
    forward . 172.29.0.11
    log
    errors
}
EOF
timeout 10 tee /tmp/lab-dns-failover/www/old/index.html >/dev/null <<'EOF'
old
EOF
timeout 10 tee /tmp/lab-dns-failover/www/new/index.html >/dev/null <<'EOF'
new
EOF

写 Compose。new-auth 先定义但不启动,等记录导入后再加入;探针通过本地递归缓存解析 app.lab.example,应用端口只绑定回环地址,便于从宿主机核对旧、新后端:

timeout 10 tee /tmp/lab-dns-failover/compose.yaml >/dev/null <<'EOF'
name: lab-dns-failover
services:
  parent:
    image: coredns/coredns:1.11.3
    container_name: lab-dns-parent
    command: -conf /etc/coredns/Corefile
    ports:
      - "127.0.0.1:5353:53/udp"
      - "127.0.0.1:5353:53/tcp"
    volumes:
      - ./parent:/etc/coredns:ro
    networks:
      lab:
        ipv4_address: 172.29.0.10
  old-auth:
    image: coredns/coredns:1.11.3
    container_name: lab-dns-old
    command: -conf /etc/coredns/Corefile
    ports:
      - "127.0.0.1:5355:53/udp"
      - "127.0.0.1:5355:53/tcp"
    volumes:
      - ./old:/etc/coredns:ro
    networks:
      lab:
        ipv4_address: 172.29.0.11
  new-auth:
    image: coredns/coredns:1.11.3
    container_name: lab-dns-new
    command: -conf /etc/coredns/Corefile
    ports:
      - "127.0.0.1:5356:53/udp"
      - "127.0.0.1:5356:53/tcp"
    volumes:
      - ./new:/etc/coredns:ro
    networks:
      lab:
        ipv4_address: 172.29.0.12
  resolver:
    image: coredns/coredns:1.11.3
    container_name: lab-dns-resolver
    command: -conf /etc/coredns/Corefile
    ports:
      - "127.0.0.1:5354:53/udp"
      - "127.0.0.1:5354:53/tcp"
    volumes:
      - ./resolver:/etc/coredns:ro
    networks:
      lab:
        ipv4_address: 172.29.0.13
  app-old:
    image: nginx:stable
    container_name: lab-dns-app-old
    ports:
      - "127.0.0.1:8080:80"
    volumes:
      - ./www/old:/usr/share/nginx/html:ro
    networks:
      lab:
        ipv4_address: 172.29.0.20
  app-new:
    image: nginx:stable
    container_name: lab-dns-app-new
    ports:
      - "127.0.0.1:8081:80"
    volumes:
      - ./www/new:/usr/share/nginx/html:ro
    networks:
      lab:
        ipv4_address: 172.29.0.21
  probe:
    image: curlimages/curl:8.10.1
    container_name: lab-dns-probe
    dns:
      - 172.29.0.13
    depends_on: [resolver, app-old, app-new]
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        while :; do
          timeout 5 curl -sS --max-time 4 http://app.lab.example/ || echo probe_failed
          timeout 5 sleep 2
        done
    networks:
      lab:
        ipv4_address: 172.29.0.22
networks:
  lab:
    name: lab-dns-failover-net
    ipam:
      config:
        - subnet: 172.29.0.0/24
EOF

启动父区、旧权威、缓存、应用和探针,并用循环等待 DNS 与 HTTP readiness,不要只等容器 Running:

timeout 300 docker compose -f /tmp/lab-dns-failover/compose.yaml up -d parent old-auth resolver app-old app-new probe
timeout 120 bash -c 'until timeout 5 dig @127.0.0.1 -p 5353 +short ns1.old.example A | grep -q 172.29.0.11; do timeout 5 sleep 2; done'
timeout 120 bash -c 'until timeout 5 dig @127.0.0.1 -p 5355 +short app.lab.example A | grep -q 172.29.0.20; do timeout 5 sleep 2; done'
timeout 120 bash -c 'until timeout 5 dig @127.0.0.1 -p 5354 +short app.lab.example A | grep -q 172.29.0.20; do timeout 5 sleep 2; done'
timeout 120 bash -c 'until timeout 5 curl -fsS http://127.0.0.1:8080/ >/dev/null; do timeout 5 sleep 1; done'
timeout 120 bash -c 'until timeout 5 curl -fsS http://127.0.0.1:8081/ >/dev/null; do timeout 5 sleep 1; done'

固定基线并导出旧 zone。成功基线应同时满足:父区委派到旧 NS、递归缓存返回 .20、旧权威 SOA 序列为 2026102501、探针日志输出 old。基线不通过就不要继续:

timeout 20 dig @127.0.0.1 -p 5353 +noall +answer +authority lab.example NS | tee /tmp/lab-dns-failover/backup/parent-ns-before.txt
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer app.lab.example A | tee /tmp/lab-dns-failover/backup/app-before.txt
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer lab.example SOA | tee /tmp/lab-dns-failover/backup/soa-before.txt
timeout 20 dig @127.0.0.1 -p 5355 +noall +answer lab.example MX lab.example TXT lab.example CAA | tee /tmp/lab-dns-failover/backup/records-before.txt
timeout 20 dig @127.0.0.1 -p 5355 +noall +answer www.lab.example CNAME demo.apps.lab.example A | tee /tmp/lab-dns-failover/backup/alias-before.txt
timeout 20 dig @127.0.0.1 -p 5353 +dnssec +norecurse DS lab.example | tee /tmp/lab-dns-failover/backup/ds-before.txt
timeout 20 dig @127.0.0.1 -p 5355 +dnssec DNSKEY lab.example | tee /tmp/lab-dns-failover/backup/dnskey-old.txt
timeout 10 cp /tmp/lab-dns-failover/old/lab.example.zone /tmp/lab-dns-failover/backup/zone-export-before.txt
timeout 20 docker compose -f /tmp/lab-dns-failover/compose.yaml logs --tail=10 probe | tee /tmp/lab-dns-failover/backup/probe-before.txt
timeout 20 curl -sS http://127.0.0.1:8080/ | tee /tmp/lab-dns-failover/backup/app-old.txt
timeout 5 sh -c 'date -u +"%FT%TZ stage=baseline_ok" >> /tmp/lab-dns-failover/backup/timeline.log'

演练步骤

1. 预降 TTL 并等待缓存过期

把旧权威的默认 TTL 从 30 秒降到 5 秒,同时降低 SOA minimum,覆盖正向和负向缓存。真实环境通过提供商 API 修改,等待时间取旧 TTL 的上限;只改权威记录而不等递归缓存过期,切换后仍会返回旧地址:

timeout 10 sed -E 's/^\$TTL 30$/$TTL 5/; s/\(2026102501 60 30 300 30\)/(2026102501 60 30 300 5)/' /tmp/lab-dns-failover/old/lab.example.zone > /tmp/lab-dns-failover/old/lab.example.zone.tmp
timeout 10 mv /tmp/lab-dns-failover/old/lab.example.zone.tmp /tmp/lab-dns-failover/old/lab.example.zone
timeout 90 docker compose -f /tmp/lab-dns-failover/compose.yaml restart old-auth
timeout 20 dig @127.0.0.1 -p 5355 +noall +answer app.lab.example A
timeout 60 sleep 31
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer app.lab.example A
timeout 10 sleep 2
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer app.lab.example A
timeout 5 sh -c 'date -u +"%FT%TZ stage=ttl_lowered" >> /tmp/lab-dns-failover/backup/timeline.log'

成功判据:旧权威和递归缓存的 Answer TTL 均小于等于 5;两次缓存查询的 TTL 递减;探针没有持续失败。失败判据:TTL 仍为 30、解析中断超过 10 秒、探针连续失败。回滚:恢复 $TTL 30 和原 SOA minimum,重启旧权威,确认解析恢复后再继续。

2. 导出旧 zone 并导入新提供商

从旧权威导出 zone 文件,复制后只改三项计划内差异:SOA 主服务器与 NS 改为 ns1.new.example.、序列号递增、app 从 .20 改为 .21。其余 A、CNAME、MX、TXT、CAA、通配符和 TTL 必须保持一致:

timeout 10 cp /tmp/lab-dns-failover/old/lab.example.zone /tmp/lab-dns-failover/backup/zone-export-ttl5.txt
timeout 10 sed -E -e 's/ns1[.]old[.]example[.]/ns1.new.example./g' -e 's/2026102501/2026102502/' -e 's/^app IN A 172[.]29[.]0[.]20$/app IN A 172.29.0.21/' /tmp/lab-dns-failover/old/lab.example.zone > /tmp/lab-dns-failover/new/lab.example.zone
timeout 120 docker compose -f /tmp/lab-dns-failover/compose.yaml up -d new-auth
timeout 120 bash -c 'until timeout 5 dig @127.0.0.1 -p 5356 +short app.lab.example A | grep -q 172.29.0.21; do timeout 5 sleep 2; done'
timeout 20 dig @127.0.0.1 -p 5356 +noall +answer app.lab.example A
timeout 20 dig @127.0.0.1 -p 5356 +noall +answer lab.example SOA
timeout 20 dig @127.0.0.1 -p 5356 +noall +answer lab.example MX lab.example TXT lab.example CAA
timeout 20 dig @127.0.0.1 -p 5356 +noall +answer www.lab.example CNAME demo.apps.lab.example A
timeout 10 grep -Ev '^(@ IN (SOA|NS)|[[:space:]]+IN NS|app IN A)' /tmp/lab-dns-failover/old/lab.example.zone > /tmp/lab-dns-failover/backup/old-rest.txt
timeout 10 grep -Ev '^(@ IN (SOA|NS)|[[:space:]]+IN NS|app IN A)' /tmp/lab-dns-failover/new/lab.example.zone > /tmp/lab-dns-failover/backup/new-rest.txt
timeout 20 diff -u /tmp/lab-dns-failover/backup/old-rest.txt /tmp/lab-dns-failover/backup/new-rest.txt
timeout 5 sh -c 'date -u +"%FT%TZ stage=import_ok" >> /tmp/lab-dns-failover/backup/timeline.log'

成功判据:新权威直接查询返回 .21、新序列号,MX/TXT/CAA/CNAME/通配符与旧权威一致,差异文件为空。失败判据:导入缺记录、TTL 被改写、CNAME 与其它记录冲突、通配符覆盖异常、SOA 未更新。回滚:新权威尚未被委派,直接停止 new-auth 并保留文件即可,不影响旧权威。

3. 检查 DNSSEC/DS 状态

在改父区委派前先记录 DS 和 DNSKEY。本 Lab 的 zone 未签名,DS 应为空;如果真实域名有 DS,必须先确认每个仍被解析器使用的 DS 都能在当前对外权威区找到匹配的 DNSKEY,否则验证型解析器会返回 SERVFAIL:

timeout 20 dig @127.0.0.1 -p 5353 +dnssec +norecurse DS lab.example | tee /tmp/lab-dns-failover/backup/ds-imported.txt
timeout 20 dig @127.0.0.1 -p 5355 +dnssec DNSKEY lab.example | tee /tmp/lab-dns-failover/backup/dnskey-old-2.txt
timeout 20 dig @127.0.0.1 -p 5356 +dnssec DNSKEY lab.example | tee /tmp/lab-dns-failover/backup/dnskey-new.txt
timeout 20 dig @127.0.0.1 -p 5356 +dnssec RRSIG lab.example | tee /tmp/lab-dns-failover/backup/rrsig-new.txt
timeout 5 sh -c 'date -u +"%FT%TZ stage=dnssec_checked" >> /tmp/lab-dns-failover/backup/timeline.log'

判断规则:DS 为空且 zone 未签名时,记录“无 DNSSEC 链”并继续;DS 非空时,新提供商必须沿用原密钥,或先做成双签名/双 DS 过渡,再切 NS。不要在旧 zone 仍对外服务时把 DS 指向旧区没有发布的新密钥,也不要为了赶进度临时删除 DS 而不记录安全取舍。

4. 切换父区 NS 委派

把父区的 lab 委派从 ns1.old.example. 改为 ns1.new.example.,保留两条 glue。然后重启父区和递归缓存。真实递归器会自己跟随委派,本 Lab 用显式 upstream 模拟“解析器已按新 NS 选择权威”的状态,重启会清空本地缓存,生产环境不会这样做:

timeout 10 sed 's/^lab IN NS ns1[.]old[.]example[.]$/lab IN NS ns1.new.example./' /tmp/lab-dns-failover/parent/example.zone > /tmp/lab-dns-failover/parent/example.zone.tmp
timeout 10 mv /tmp/lab-dns-failover/parent/example.zone.tmp /tmp/lab-dns-failover/parent/example.zone
timeout 90 docker compose -f /tmp/lab-dns-failover/compose.yaml restart parent
timeout 20 dig @127.0.0.1 -p 5353 +noall +answer +authority lab.example NS | tee /tmp/lab-dns-failover/backup/parent-ns-after.txt
timeout 20 dig @127.0.0.1 -p 5353 +short ns1.new.example A
timeout 10 sed 's/forward [.] 172[.]29[.]0[.]11/forward . 172.29.0.12/' /tmp/lab-dns-failover/resolver/Corefile > /tmp/lab-dns-failover/resolver/Corefile.tmp
timeout 10 mv /tmp/lab-dns-failover/resolver/Corefile.tmp /tmp/lab-dns-failover/resolver/Corefile
timeout 90 docker compose -f /tmp/lab-dns-failover/compose.yaml restart resolver
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer app.lab.example A | tee /tmp/lab-dns-failover/backup/app-after.txt
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer lab.example SOA | tee /tmp/lab-dns-failover/backup/soa-after.txt
timeout 30 docker compose -f /tmp/lab-dns-failover/compose.yaml logs --tail=20 probe
timeout 5 sh -c 'date -u +"%FT%TZ stage=ns_switched" >> /tmp/lab-dns-failover/backup/timeline.log'

成功判据:父区 NS 只有新 NS 且 glue 指向 .12;递归缓存返回 .21 和新序列号;探针日志从 old 切到 new;旧权威仍能直接查询到旧记录。失败判据:父区仍返回旧 NS、glue 缺失、递归缓存仍返回 .20、探针持续失败、新权威返回 REFUSED/SERVFAIL。回滚:把父区 NS 改回旧值,恢复缓存 upstream,重启后验证;新权威保持运行,便于排查。

观察

阶段 1 只看到 TTL 从 30 降到 5,探针仍输出 old;对递归缓存连续查询时,TTL 会逐步递减,缓存过期后重新从权威取得 5 秒记录。阶段 2 新权威直接查询已经返回 .21 和新序列号,但父区和递归缓存仍指向旧权威,探针也仍是 old,说明“新权威可用”不等于“已经切换”。阶段 3 的 DS 查询没有答案,DNSKEY 和 RRSIG 也为空,记录的是未签名路径。阶段 4 父区 NS 变为 ns1.new.example.,递归缓存返回 .21、序列号 2026102502,探针在数秒内切到 new;旧权威仍可查询,作为回滚材料保留。

建议把实际时间线整理成下表,“目标耗时”只是演练预算,不是生产 SLO:

阶段 动作 关键观察 目标耗时
0 建立基线 父区旧 NS、缓存 .20、探针 old —
1 TTL 30→5 权威与缓存 TTL ≤ 5、TTL 递减 ≤ 1 分钟
2 导入新 zone 新权威 .21、序列号递增、记录一致 ≤ 5 分钟
3 检查 DS/DNSKEY DS 为空或与新 DNSKEY 匹配 ≤ 2 分钟
4 切父区 NS 与缓存 父区新 NS、缓存 .21、探针 new ≤ 2 分钟
5 回滚或收尾 旧 NS、旧记录、探针恢复 old ≤ 2 分钟

判断

  1. 委派层先看父区:用 dig NS 确认 NS 集合,用 glue 查询确认每个 NS 地址可达。父区已经更新但递归仍返回旧值时,优先怀疑父区 TTL 或解析器缓存,而不是新权威故障。
  2. 权威层要绕过缓存,直接查旧、新权威:比较 SOA 序列号、权威标志、Answer 和 Authority 段。新权威直接可用但递归失败,通常说明委派、glue 或缓存还没跟上。
  3. 缓存层用同一个递归缓存连续查询,观察 TTL 递减和缓存过期。dig 默认走系统解析器,必须显式写 @127.0.0.1 -p 5354,否则可能测到宿主机或公司 DNS。
  4. DNSSEC 层把 DS、DNSKEY、RRSIG 分开看:没有 DS 不代表没有风险,只代表当前没有验证链;有 DS 但 DNSKEY 不匹配时,未开启验证的 dig 可能仍返回答案,验证型解析器却返回 SERVFAIL。不要把 dig 成功当作 DNSSEC 通过。
  5. 应用层要区分新连接和旧连接。DNS 已切但探针仍输出 old,常见原因是连接池、长连接、客户端缓存或负缓存;探针重启只能作为实验手段,不能当作生产修复。
  6. 容易误判:新权威查询成功不代表父区已委派;父区 NS 已改不代表全球缓存已过期;app 记录改到 .21 是本次演练的计划内差异,不能把它当成导入错误;www 是 CNAME,验证时要沿链查到最终 A 记录。

恢复与回滚

回滚顺序与变更顺序相反:先恢复父区 NS 和 DS,再恢复递归缓存 upstream,最后验证应用。父区 NS 的 TTL 由父区控制,真实环境不能像普通记录一样提前调低,回滚后要按父区 TTL 等待缓存过期。旧权威、旧 zone 和旧密钥在观察窗口结束前不得删除:

timeout 10 sed 's/^lab IN NS ns1[.]new[.]example[.]$/lab IN NS ns1.old.example./' /tmp/lab-dns-failover/parent/example.zone > /tmp/lab-dns-failover/parent/example.zone.tmp
timeout 10 mv /tmp/lab-dns-failover/parent/example.zone.tmp /tmp/lab-dns-failover/parent/example.zone
timeout 90 docker compose -f /tmp/lab-dns-failover/compose.yaml restart parent
timeout 10 sed 's/forward [.] 172[.]29[.]0[.]12/forward . 172.29.0.11/' /tmp/lab-dns-failover/resolver/Corefile > /tmp/lab-dns-failover/resolver/Corefile.tmp
timeout 10 mv /tmp/lab-dns-failover/resolver/Corefile.tmp /tmp/lab-dns-failover/resolver/Corefile
timeout 90 docker compose -f /tmp/lab-dns-failover/compose.yaml restart resolver
timeout 20 dig @127.0.0.1 -p 5353 +noall +answer +authority lab.example NS
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer app.lab.example A
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer lab.example SOA
timeout 20 docker compose -f /tmp/lab-dns-failover/compose.yaml logs --tail=20 probe
timeout 5 sh -c 'date -u +"%FT%TZ stage=rollback_ok" >> /tmp/lab-dns-failover/backup/timeline.log'

回滚触发条件:父区 NS 无法正确更新、关键记录缺失或值不一致、DNSSEC 验证出现 SERVFAIL、递归缓存持续返回旧地址超过两个 TTL、探针连续失败超过 2 分钟、新提供商控制面不可用。触发后停止后续步骤,由负责人决定继续向前修复还是回滚;不要在两个提供商之间反复切换。

如果新提供商已经接收动态更新、签发了 ACME 证书或产生新记录,回滚前先导出并核对,避免这些数据只存在于新 zone。实验结束只清理本项目,不要对其他 Compose 项目执行 down -v:

timeout 60 docker compose -f /tmp/lab-dns-failover/compose.yaml down -v --remove-orphans
timeout 30 sh -c "docker ps -a --format '{{.Names}}' | grep '^lab-dns-' || true"
timeout 30 rm -rf /tmp/lab-dns-failover

验证

最终验证覆盖委派、权威、缓存、记录和 DNSSEC 五个层面。所有命令都带超时,并且显式指定服务器和端口:

timeout 20 dig @127.0.0.1 -p 5353 +noall +answer +authority lab.example NS
timeout 20 dig @127.0.0.1 -p 5353 +short ns1.old.example A ns1.new.example A
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer app.lab.example A
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer lab.example SOA
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer www.lab.example CNAME
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer demo.apps.lab.example A
timeout 20 dig @127.0.0.1 -p 5354 +noall +answer lab.example MX lab.example TXT lab.example CAA
timeout 20 dig @127.0.0.1 -p 5355 +noall +answer app.lab.example A
timeout 20 dig @127.0.0.1 -p 5356 +noall +answer app.lab.example A
timeout 20 dig @127.0.0.1 -p 5353 +dnssec +norecurse DS lab.example
timeout 20 curl -sS http://127.0.0.1:8080/
timeout 20 curl -sS http://127.0.0.1:8081/
timeout 30 docker compose -f /tmp/lab-dns-failover/compose.yaml logs --tail=20 probe

复盘

相关命令

参考链接

读完自测

  1. 为什么预降 TTL 后还要等待旧 TTL 过期?只改权威记录会怎样?
  2. 新权威直接查询正常,但递归缓存仍返回旧地址,应该按什么顺序排查?
  3. DS 非空时,切换权威提供商前必须确认什么?如果 DS 与 DNSKEY 不匹配会有什么表现?
答案
  1. 递归解析器、客户端和连接池可能仍缓存旧记录。降低 TTL 只影响之后的查询,已经缓存的记录要等旧 TTL 到期;如果不等就切 NS,部分请求仍会打到旧权威,回滚时同样要等缓存过期。
  2. 先查父区 NS 和 glue 是否已更新,再查递归缓存是否仍缓存旧答案、TTL 是否未过期,最后直接查新权威确认记录和权威标志。不要先重启新权威,否则会掩盖委派或缓存问题。
  3. 必须确认父区每个 DS 都能在当前对外权威区找到算法和 key tag 匹配的 DNSKEY,RRSIG 有效;常用做法是沿用原密钥,或先做双签名/双 DS 过渡。不匹配时验证型解析器会返回 SERVFAIL,而未开启验证的 dig 可能仍显示正常答案。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令