2025年8月15日凌晨3点07分,我被电话吵醒。电话那头是值班同事焦急的声音:“全站挂了,用户完全访问不了。”
穿上衣服的同时,我已经开始在脑子里过可能的原因。全站挂意味着不是单点问题,要么是网络层出问题,要么是DNS出问题,要么是核心网关全部挂掉。登上VPN查看监控,所有服务的健康检查都是绿的,但外部探测全红。
那一刻我意识到:八成是DNS。
这篇文章是那次故障的完整复盘,从发现问题到定位根因,从紧急恢复到后续改进。写下来是希望其他团队能从中吸取教训,不要踩同样的坑。
故障影响
- 持续时间:2小时17分钟
- 影响范围:全部对外服务无法访问
- 用户影响:约50万用户受影响
- 直接损失:预估约80万(根据时段GMV计算)
- 间接损失:品牌声誉、用户投诉
时间线
03:07 - 收到告警,值班同事发现异常
03:12 - 开始排查,确认服务本身正常
03:18 - 定位到DNS解析问题
03:25 - 尝试切换DNS提供商
03:35 - 发现是域名到期导致
03:40 - 联系域名注册商紧急续费
04:15 - 域名续费成功
04:50 - DNS记录恢复
05:24 - 全部服务恢复正常
05:30 - 故障关闭,开始写复盘报告
二、故障发现与初步排查
告警触发
凌晨3点07分,多个告警同时触发:
[CRITICAL] HTTP探测失败 - https://www.example.com - Connection timeout
[CRITICAL] HTTP探测失败 - https://api.example.com - Connection timeout
[CRITICAL] HTTP探测失败 - https://m.example.com - Connection timeout
[WARNING] 外部可用性下降至0%
值班同事第一时间检查了服务状态:
# 检查网关服务状态
kubectl get pods -n gateway
# NAME READY STATUS RESTARTS AGE
# nginx-ingress-xxx-abc 1/1 Running 0 5d
# nginx-ingress-xxx-def 1/1 Running 0 5d
# 检查后端服务
kubectl get pods -n production | grep -v Running
# 没有异常Pod
所有服务都是正常的,但用户就是访问不了。
网络层排查
# 从外部网络测试连接
curl -v https://www.example.com
# * Could not resolve host: www.example.com
# 嗯?DNS解析失败
# 换一个DNS服务器试试
nslookup www.example.com 8.8.8.8
# Server: 8.8.8.8
# Address: 8.8.8.8#53
#
# ** server can‘t find www.example.com: SERVFAIL
# 再试试其他DNS
nslookup www.example.com 1.1.1.1
# Server: 1.1.1.1
# Address: 1.1.1.1#53
#
# ** server can‘t find www.example.com: SERVFAIL
多个公共DNS都无法解析,问题指向域名本身。在排查复杂的DNS问题时,这种系统性故障往往意味着根源不在本地。
DNS深入诊断
# 使用dig进行详细诊断
dig www.example.com +trace
# 输出(简化):
# . 518400 IN NS a.root-servers.net.
# com. 172800 IN NS a.gtld-servers.net.
# example.com. 172800 IN NS ns1.dnsprovider.com.
# ;; <<>> DiG 9.11.4 <<>> www.example.com +trace
# ;; Query time: 45 msec
# ;; connection timed out; no servers could be reached
# 权威DNS服务器无法响应
问题出在权威DNS服务器上。进一步检查:
# 检查域名的NS记录
dig example.com NS
# 尝试直接查询权威DNS
dig @ns1.dnsprovider.com www.example.com
# ;; connection timed out; no servers could be reached
权威DNS服务器完全无法访问。
三、根因定位
第一个怀疑:DNS提供商故障
# 检查DNS提供商状态页面
curl -s https://status.dnsprovider.com | grep -i "operational"
# All Systems Operational
# DNS提供商状态页显示正常,排除
第二个怀疑:DNS配置被篡改
登录DNS管理后台检查:
- 所有DNS记录存在且正确
- 没有异常的修改记录
- 域名状态显示... serverHold
看到serverHold的那一刻,我的心凉了半截。
查明真相
# 使用whois查询域名状态
whois example.com
# Domain Name: EXAMPLE.COM
# Registry Domain ID: 123456789
# Registrar: Example Registrar Inc.
# Domain Status: serverHold https://icann.org/epp#serverHold
# Updated Date: 2022-08-15T03:00:00Z
# Creation Date: 2017-08-15T00:00:00Z
# Registry Expiry Date: 2022-08-15T00:00:00Z
Registry Expiry Date: 2022-08-15
域名刚刚过期。
更讽刺的是,域名是5年前注册的,今天凌晨刚好到期。而且是主域名,不是什么边缘业务的域名。
为什么没有续费
事后调查发现:
- 续费邮件被忽略:域名注册商提前60天、30天、7天、1天都发送了续费提醒邮件,但这些邮件发到了已离职员工的邮箱
- 没有备用联系人:域名注册时只填了一个联系人
- 财务流程问题:域名续费需要走采购流程,但没有人发起
- 监控缺失:没有对域名到期时间进行监控
四、紧急恢复
续费操作
凌晨3点40分,我们开始联系域名注册商:
1. 登录域名注册商后台 - 成功
2. 尝试在线续费 - 失败,提示“域名已进入赎回期”
3. 联系注册商客服 - 非工作时间,无人接听
4. 提交紧急工单 - 等待响应
5. 查找注册商24小时紧急联系方式 - 找到海外电话
6. 国际长途联系 - 成功
凌晨4点15分,经过复杂的身份验证后,注册商完成了域名续费。
DNS恢复过程
域名续费成功后,还需要等待DNS记录恢复:
# 持续监控DNS解析状态
while true; do
echo "=== $(date) ==="
dig www.example.com +short
sleep 60
done
# 输出:
# === Mon Aug 15 04:20:00 CST 2022 ===
# (空)
# === Mon Aug 15 04:30:00 CST 2022 ===
# (空)
# === Mon Aug 15 04:40:00 CST 2022 ===
# (空)
# === Mon Aug 15 04:50:00 CST 2022 ===
# 10.0.0.100
# 10.0.0.101
DNS解析恢复需要等待全球DNS缓存更新,即使域名续费成功,也需要时间传播。
加速恢复
为了加速恢复,我们做了以下操作:
# 1. 清除CDN缓存(如果有)
# 2. 通知云厂商刷新DNS
# 3. 主动访问各地DNS服务器触发更新
# 批量测试全球DNS解析情况
for dns in 8.8.8.8 8.8.4.4 1.1.1.1 9.9.9.9 208.67.222.222; do
echo "Testing $dns:"
dig @$dns www.example.com +short
done
凌晨5点24分,全部外部探测恢复绿色,故障解除。
五、事后分析
直接原因
域名到期未续费,导致域名状态变为serverHold,DNS解析失败。
根本原因
- 流程缺失:域名续费没有纳入IT资产管理流程
- 监控盲区:没有对域名到期时间进行监控
- 单点故障:联系人只有一个且已离职
- 知识断层:域名管理相关的知识没有传承
影响分析
业务影响:
├── 官网无法访问 - 2小时17分钟
├── APP无法使用 - 2小时17分钟
├── API服务中断 - 2小时17分钟
├── 用户投诉 - 约200起
└── 预估损失 - 约80万
技术影响:
├── DNS解析完全失败
├── CDN缓存失效
├── 部分SSL证书无法验证
└── 内部服务间调用失败(使用了域名)
六、改进措施
立即行动(1周内完成)
1. 域名续费
# 所有域名续费至少5年
域名列表:
- example.com - 续费至 2027-08-15
- example.cn - 续费至 2027-08-15
- example-api.com - 续费至 2027-08-15
2. 添加多个联系人
域名联系人配置:
主联系人:
姓名: 运维负责人
邮箱: ops-lead@company.com
电话: 138xxxx
备用联系人1:
姓名: CTO
邮箱: cto@company.com
电话: 139xxxx
备用联系人2:
姓名: IT部门邮件组
邮箱: it-all@company.com
3. 开启自动续费
所有域名开启自动续费,绑定公司信用卡。
短期改进(1个月内完成)
1. 域名监控系统
#!/usr/bin/env python3
# domain_monitor.py - 域名到期监控
import whois
import smtplib
from datetime import datetime, timedelta
DOMAINS = [
'example.com',
'example.cn',
'example-api.com',
]
ALERT_DAYS = [90, 60, 30, 14, 7, 3, 1]
def check_domain_expiry(domain):
"""检查域名到期时间"""
try:
w = whois.whois(domain)
expiry_date = w.expiration_date
if isinstance(expiry_date, list):
expiry_date = expiry_date[0]
return expiry_date
except Exception as e:
print(f"Error checking {domain}: {e}")
return None
def days_until_expiry(expiry_date):
"""计算距离到期的天数"""
if expiry_date is None:
return -1
return (expiry_date - datetime.now()).days
def send_alert(domain, days_left, expiry_date):
"""发送告警"""
# 发送邮件、短信、钉钉等
print(f"ALERT: {domain} expires in {days_left} days ({expiry_date})")
def main():
for domain in DOMAINS:
expiry = check_domain_expiry(domain)
days_left = days_until_expiry(expiry)
if days_left <= 0:
send_alert(domain, days_left, expiry)
elif days_left in ALERT_DAYS:
send_alert(domain, days_left, expiry)
print(f"{domain}: expires in {days_left} days ({expiry})")
if __name__ == '__main__':
main()
2. 资产管理流程
域名资产管理流程:
1. 录入阶段:
- 所有域名必须录入IT资产管理系统
- 记录:域名、注册商、到期时间、联系人、用途
2. 监控阶段:
- 每日自动检查域名状态
- 到期前90/60/30/7天告警
- 异常状态立即告警
3. 续费阶段:
- 到期前90天发起续费流程
- 财务预算提前规划
- 续费完成后更新资产记录
4. 审计阶段:
- 每季度审计域名资产
- 清理不再使用的域名
- 检查联系人有效性
3. 添加Prometheus监控
# prometheus告警规则
groups:
- name: domain_expiry
rules:
- alert: DomainExpiryWarning
expr: domain_expiry_days < 30
for: 1h
labels:
severity: warning
annotations:
summary: "域名即将到期"
description: "域名 {{ $labels.domain }} 将在 {{ $value }} 天后到期"
- alert: DomainExpiryCritical
expr: domain_expiry_days < 7
for: 1h
labels:
severity: critical
annotations:
summary: "域名即将到期(紧急)"
description: "域名 {{ $labels.domain }} 将在 {{ $value }} 天后到期,请立即续费"
长期改进
1. 多DNS提供商
DNS架构改进:
主DNS提供商: CloudFlare
备用DNS提供商: AWS Route53
切换策略:
- 主备NS记录同时配置
- 监控主DNS可用性
- 自动或手动切换
2. 灾备演练
DNS灾备演练计划:
频率: 每季度一次
内容:
- 模拟DNS提供商故障
- 模拟域名解析失败
- 测试切换流程
- 验证恢复时间
3. 内部服务不依赖外部DNS
架构改进:
问题: 内部服务间调用使用了公网域名
改进:
- 内部服务使用内部DNS或服务发现
- Kubernetes服务使用ClusterIP
- 必须使用域名时用内部域名
七、经验教训
技术层面
- DNS是单点:即使做了多活架构,DNS故障会导致全部不可用
- TTL设置:合理设置DNS TTL,太长会影响切换速度
- 健康检查:外部探测和DNS解析要分开监控
- 内外网隔离:内部服务不要依赖外部DNS
流程层面
- 资产管理:域名、证书等资产必须有专人管理
- 联系人冗余:关键资产至少3个联系人
- 自动续费:能自动的就不要手动
- 知识传承:关键信息要有文档,不能只在某人脑子里
应急响应
- 7x24联系方式:关键供应商的紧急联系方式要提前准备
- 权限准备:紧急情况下谁有权限操作?能不能快速拿到权限?
- 演练:没有演练过的应急预案等于没有预案
八、DNS故障排查清单
基于这次故障,我整理了一个DNS故障排查清单,供参考:
# DNS故障排查清单
## 1. 确认DNS是否正常
dig www.example.com +short
nslookup www.example.com 8.8.8.8
## 2. 检查域名状态
whois example.com | grep -E "Status|Expiry"
## 3. 追踪DNS解析路径
dig www.example.com +trace
## 4. 检查权威DNS
dig example.com NS
dig @ns1.provider.com www.example.com
## 5. 检查DNS记录
dig www.example.com A
dig www.example.com CNAME
dig example.com MX
dig example.com TXT
## 6. 检查DNS提供商状态
# 访问DNS提供商状态页面
## 7. 检查网络连通性
ping ns1.provider.com
traceroute ns1.provider.com
## 8. 常见状态码解释
# NOERROR: 正常
# NXDOMAIN: 域名不存在
# SERVFAIL: 服务器错误
# REFUSED: 拒绝服务
## 9. 域名状态解释
# ok/active: 正常
# serverHold: 被注册局暂停(通常是欠费或违规)
# clientHold: 被注册商暂停
# pendingDelete: 等待删除
# redemptionPeriod: 赎回期
九、总结
关键教训
- 域名是基础设施的基础:再完善的架构,域名出问题都白搭
- 自动化一切能自动化的:续费、监控、告警
- 冗余是应对单点故障的唯一方法:联系人冗余、DNS提供商冗余
- 演练发现问题:很多问题只有真出事才能发现
后续效果
改进措施实施后:
- 所有域名纳入监控,到期前90天开始告警
- 域名续费流程自动化,无需人工干预
- 每季度进行DNS灾备演练
- 至今(2025年)未再发生域名相关故障
最后的话
这次故障我被记了一个事故,年终绩效受影响。但我觉得值得——因为这次故障让整个团队都意识到了基础设施管理的重要性,也让我学到了很多。这类运维领域的“低级错误”恰恰是最容易被忽略的致命弱点。
写这篇复盘不是为了甩锅或者诉苦,而是希望其他团队能从中吸取教训。域名过期这种低级错误,任何一个团队都可能犯。但只要做好监控和流程,完全可以避免。
如果这篇文章能让你检查一下自己公司的域名到期时间,那它的目的就达到了。也欢迎在云栈社区这样的技术平台交流更多基础设施管理的实战经验与教训。
参考资料