问题背景
公司去年收购了一家子公司,今年上半年把子公司的邮箱整体迁移到集团的新域名,旧域名保留转发一段时间后,上个月正式下掉了 MX 记录。迁移项目由 IT 基础架构组负责,邮箱、通讯录、日历都迁完了,验收时重点看的是“员工能不能正常收发邮件”。
总部 ERP 有一个运行了两年的日报任务:每晚 8 点给子公司三千名员工各发一份 PDF 日报,单个附件大约 3MB。这个任务的收件人列表是实施时从旧通讯录导出的,全部是 @oldco.example 地址。邮箱迁移时没有人想到去检查应用侧的发件配置,下 MX 之前也没有做邮件流量审计。
故障现象
- 邮箱迁移后的前两周,子公司员工陆续反馈“收不到日报”;ERP 日志显示发送成功,SMTP 返回 250,应用侧没有任何报错。
- 一个月后,监控告警:邮件服务器
/var分区使用率冲到 96%,Postfix 队列积压超过一万五千封。 - 总部其他外发邮件开始延迟,日志里偶尔出现
queue file write error,部分新邮件被直接拒绝。 qshape deferred显示队列里 90% 以上的邮件指向oldco.example,正是那个已经停用的旧域名。- 队列里的邮件不是退信,而是完整的日报:每封都带着 3MB 的 PDF 附件。
- 磁盘使用率在每天晚八点后明显抬升,白天几乎不下降。
排查过程
第一步看队列整体。postqueue -p 的尾部汇总显示队列里有一万多封邮件、占用十几 GB;qshape deferred 按域名分布排序,oldco.example 遥遥领先,其他域名只有零星几封。问题高度集中,不是全网邮件故障。
第二步抽样看内容。从 postqueue -p 里取一个队列 ID,用 postcat -q <QID> 打印,发现发件人是 [email protected],收件人是 @oldco.example 的员工地址,主题是日报,大小约 3MB。队列里堆积的不是退信,而是根本没有投递出去的原邮件。
第三步查投递日志。maillog 里对应的记录是 Host or domain name not found. Name service error for name=oldco.example type=MX: Host not found, try again,状态 deferred。这解释了为什么应用看到 250:SMTP 的 250 只表示“服务器已接收并负责投递”,不代表对方已经收到。邮件进入队列后,DNS 解析失败,Postfix 按临时故障处理,开始退避重试。
第四步确认域名状态。dig MX oldco.example 返回 NXDOMAIN,dig MX newco.example 正常。旧域名的 MX 确实已经删除,新域名的邮件系统工作正常,说明迁移本身没问题,问题在于还有应用往旧地址发信。
第五步算容量。每晚三千封、每封 3MB,一天就是 9GB;Postfix 默认 maximal_queue_lifetime 是 5 天,重试期间邮件一直留在队列里,最坏情况会累积到 40GB 以上。服务器的 /var 分区总共 60GB,原本使用率就有七成,几天之内被队列填满是必然结果。
第六步查配置。ERP 的收件人列表仍然是旧域名,没有做有效性校验;Postfix 侧没有队列深度监控,也没有在域名下线前做过发件人审计。整个链路里没有任何一个环节会发现“这些邮件永远送不到”。
解决方案
- 先止血:暂停 ERP 的日报任务,避免继续往队列里灌邮件;用
postqueue -p配合grep oldco.example找出目标域名的队列 ID,分批postsuper -d <QID>删除。不要直接postsuper -d ALL,队列里可能还有正常的延迟邮件。 - 修正收件人:从新域名通讯录重新导出收件人列表,更新 ERP 配置;发送前做地址格式和域名有效性校验,失效地址直接剔除并记录,而不是交给 MTA 慢慢重试。
- 快速失败:如果短期内还有历史数据要发往旧域名,可以在
transport_maps里给oldco.example配error:5.1.1,让 Postfix 在接收阶段就拒绝,避免再次进入队列。治本仍然是改配置。 - 压缩队列生命周期:把
maximal_queue_lifetime从默认 5 天调整为 2 天,bounce_queue_lifetime调整为 1 天;对确定不可达的域名,重试五天没有意义,只会放大磁盘压力。 - 降低单封体积:日报 PDF 改为上传到文件服务、邮件里只发下载链接,或者按人按需生成。3MB 的附件乘以三千人,放大的不只是带宽,还有队列和备份的磁盘占用。
- 补监控:采集队列总数、
qshape deferred的 Top 域名、最老 deferred 邮件年龄、/var使用率;队列超过阈值或最老邮件超过 24 小时就告警。这样在磁盘被填满之前就能发现异常。
根因分析
直接根因是 ERP 仍向已停用的旧域名群发带大附件的日报,旧域名 MX 删除后邮件无法投递,Postfix 按临时故障在队列里重试五天,三千封 × 3MB 的日增量迅速把 /var 分区填满。
更深一层,是把域名迁移当成了“邮箱搬家”,而没有当成“应用依赖迁移”。邮箱、通讯录迁完了,但所有引用旧域名的系统配置、收件人列表、发件地址都没有被盘点;下 MX 之前也没有审计还有多少流量指向旧域名。迁移项目以“员工能收信”为验收标准,恰好漏掉了“系统还能不能发信”。
还有一层是对 SMTP 的误解。应用看到 250 就认为发送成功,实际上那只是“已入队”;真正的投递结果要等队列重试结束才知道,而应用既不处理退信,也没人看队列。邮件系统在替应用默默重试了五天,直到磁盘告警才把问题暴露出来。
预防措施
- 域名下线或邮箱迁移必须走“应用依赖清单”:全量扫描配置、数据库、脚本里的旧域名,逐项确认发件人和收件人,迁移完成后再保留至少一个月的 MX 过渡期。
- 应用发送邮件前做收件人有效性校验,失效地址进入抑制列表,不再重复投递;批量任务要有发送量上限和失败率熔断。
- 邮件队列纳入基础监控:队列长度、Top 目标域名、最老邮件年龄、队列目录磁盘使用率,四项都要有阈值告警。
- 大附件改为链接下载,控制单封邮件体积;批量报表按需发送,避免“三千人 × 3MB”这种线性放大。
- 迁移验收增加“金丝雀收件人”:在新旧域名各放一个受控邮箱,确认收发链路都正常,而不是只看迁移当天的一封测试邮件。
总结
SMTP 的 250 不是“对方收到了”,而是“我替你收下了”。邮件队列是一个磁盘支撑的等待室,正常情况下它安静地重试几分钟;一旦目标域名永远不可达,它就会安静地吃掉几天、几十 GB 的磁盘。这次故障里没有攻击、没有硬件损坏,只有一个被遗忘的旧域名和一串没人核对的收件人地址。域名迁移的验收标准,不该只是“员工能收信”,还应该包括“所有系统还能不能把信发出去”。