十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从F5 BIG-IP DNS批量导出CNAME记录:tmsh命令与清洗脚本实战

从F5 BIG-IP DNS批量导出CNAME记录:tmsh命令与清洗脚本实战 干运维的朋友基本都遇到过这种事F5 BIG-IP DNS 上堆了两三百条 CNAME某天要迁移到云 DNS、要交给安全团队审计、或者只是想在年底盘点一下域名资产一条条在 GUI 里点开再复制一上午就没了。这篇文章就把这件事做成一个可复制的流水线直接从 F5 BIG-IP DNS 导出全部 CNAME 记录最终落到一个干净的 txt/csv 文件里。这个需求听起来简单但真正做起来会踩不少坑CNAME 对象到底存在哪个路径、tmsh 输出怎么解析、分区不对为什么查不到记录、TTL 为什么导出来全是默认值。我会把整个流程掰开揉碎讲清楚适合正在做 DNS 迁移、域名资产梳理、或者想给 F5 配置做定期备份的人参考。整个过程只需要 SSH 登录 F5配合一条命令加一个清洗脚本就能完成不需要在 GUI 里手工点任何东西。1. 先把 F5 BIG-IP DNS 里的 CNAME 记录分清类别1.1 两种容易搞混的 CNAME 对象我在实际操作中发现很多人第一次接触 F5 BIG-IP DNS 时会被“CNAME”这个词绕晕。F5 里其实有两类 CNAME导出前必须分清楚第一类是普通权威 DNS 应答里的 CNAME 记录配置路径在Local Traffic DNS CNAMEtmsh 路径是/ltm dns cname。这类记录和你在 Bind、Cloudflare 上配置的 CNAME 一样作用是告诉递归解析器“某个域名是另一个域名的别名”。第二类是全局负载均衡里的 CNAME WideIP路径在DNS GSLB Wide IPtmsh 路径是/ltm dns wideip其中 type 为 cname 的那一批。这类记录不是简单的静态别名它会配合 GSLB 的负载均衡策略把解析结果动态分发给不同数据中心的资源。如果你这次导出是为了迁移整个 DNS 解析两类都要覆盖如果只是为了给域名资产做盘点通常先导/ltm dns cname就够了。我一般习惯先跑一条命令看看两类数据分别有多大再决定处理范围。1.2 什么时候需要导出导出前要确认哪些事抛开纯手工整理不谈实际工作中触发“导出 CNAME”这个需求的高频场景就三类一是域名从 F5 往云 DNS 或自建 Bind 迁移需要一份标准的记录清单二是安全审计合规检查需要证明当前线上解析配置和变更记录一致三是做配置备份和灾难恢复预案避免设备故障后凭记忆重建配置。导出之前有几个信息必须先确认清楚否则命令跑出来也是白跑。第一是 F5 的版本12.x 和 15.x 的 tmsh 输出格式略有差异但/ltm dns cname这条路径相对稳定后面给的命令在主流版本上都能用。第二是分区Partition很多公司会建多个 partition 做环境隔离如果你只看了默认的/Common分区其他分区的 CNAME 一条都看不到。第三是登录账号权限至少要拥有tmsh的查看权限最好直接用管理员账号否则后面跑 REST API 或者导出 wideip 时会卡权限。2. 用 tmsh 命令把 CNAME 记录批量拉出来2.1 为什么不用 GUI而用 tmshBIG-IP 的 GUI 其实也有导出功能但只支持单个对象查看没有“全选导出”这种按钮。以前我见过有同事用浏览器 F12 抓接口把每个 CNAME 的 JSON 响应一个个复制出来再拼接到 Excel 里两百条记录折腾了两个小时。这种场景下tmsh 才是真正高效的路径。tmsh 是 F5 的命令行配置工具通过 SSH 登录设备后直接输入tmsh就能进入交互模式也可以在 Linux shell 下用tmsh -c 命令一次性执行。对于批量导出这种需求用-c配合重定向最顺手因为输出直接落到本地文件不用在交互界面里翻屏复制。补充一句tmsh 的 list 命令做的是“展示配置”不是“导出配置”所以它不会触发任何配置变更放心跑。这也是为什么我强烈推荐用这种方式做日常备份安全、无副作用。2.2 核心命令与输出格式解读SSH 登录 F5 之后最基础的一条命令是tmsh -c list /ltm dns cname all如果你只想看默认分区这条够了。但考虑到 partition 隔离我建议一开始就切到根目录再递归列出tmsh -c cd /; list /ltm dns cname recursiverecursive的作用就是把所有 partition 下的 CNAME 对象全部递归拉出来。执行完会得到类似这样的输出ltm dns cname www.example.com { answer { cname edge.example.com } ttl 300 last-modified-date 2024-03-12:10:23:45 } ltm dns cname mail.example.com { answer { cname mailserver.internal.example.com } ttl 3600 }注意几个关键字段ltm dns cname后面跟的是记录 owner 域名answer.cname后面是 CNAME 的最终目标ttl是缓存时间。有些记录没有 ttl 字段说明用的默认值这两个细节后面清洗的时候都要处理。如果你在/ltm dns cname下没有找到预期的记录建议再跑一条看看 WideIP 里是否有 CNAME 类型的数据tmsh -c cd /; list /ltm dns wideip recursive这条输出会包含 A、AAAA、CNAME、MX 等多种类型人工从里面挑 CNAME 比较累后面清洗脚本里我会给一个过滤方案。2.3 顺手提一种高阶玩法REST API 导出如果你所在的环境有跳板机不方便直接 SSH 到 F5可以走 REST API。BIG-IP 的 iControl REST 接口从 TMOS 11.4 开始支持调用方式和 curl 请求差不多curl -sk -u admin:你的密码 https://F5管理IP/mgmt/tm/ltm/dns/cname?optionsrecursive返回的是 JSON 格式用 jq 处理起来甚至比 tmsh 输出更干净curl -sk -u admin:密码 https://F5管理IP/mgmt/tm/ltm/dns/cname?optionsrecursive | jq -r .items[] | [.name, .answer[0].cname, .ttl] | tsv我个人习惯是能 SSH 就用 tmsh因为 REST API 遇到密码过期、双因子认证、VLAN 隔离时会多一层排障成本。但如果你是做自动化平台开发REST 方式明显更适合被流程集成这个看场景取舍。3. 把原始输出清洗成规范的 txt 记录3.1 先定目标格式别急着写脚本从 tmsh 拿到的原始输出是面向人读的配置块不是标准 DNS zone 文件格式直接交给别人用肯定不行。清洗之前先想清楚最终要什么格式。我建议最简版就三列owner、ttl、target文件后缀用 .txt内容用逗号或 Tab 分隔方便导入 Excel也方便 diff 比较owner,ttl,target,type www.example.com,300,edge.example.com,CNAME mail.example.com,3600,mailserver.internal.example.com,CNAME加不加表头看你要干什么。如果只是临时盘点可以不要表头如果要导入自动化平台建议留表头方便程序嗅探字段类型。type 列写死为 CNAME这样后续合并 A 记录、MX 记录时文件结构不冲突。3.2 用 awk 快速完成第一版清洗清洗思路其实很简单把配置块中每个对象的 owner、answer.cname、ttl 提取出来。awk 处理这种结构化的缩进文本非常高效我在低版本 F5 上验证过下面这个版本awk /^ltm dns cname / { owner$4 gsub(/\{/, , owner) answer ttl in_obj1 next } in_obj /cname/ { answer$NF } in_obj /ttl/ { ttl$NF } in_obj /^}/ { printf %s,%s,%s,CNAME\n, owner, ttl?ttl:default, answer in_obj0 } cname_raw.txt cname_records.txt这里有个细节answer$NF取的是一行中最后一个字段因为 cname 行通常是cname edge.example.com最后一个字段就是目标域名ttl?ttl:default处理的是没有显式 ttl 的对象统一标记成 default提醒你后续要回源确认实际缓存值。awk 方案适合快速得出结果但如果记录数特别多或者对象下面嵌套结构比较复杂awk 的状态机会变得脆弱。我的经验是一次性任务用 awk如果你打算把这个导出脚本固化到备份流程里直接上 Python。3.3 用 Python 脚本处理更省心Python 处理这种半结构化文本最大的优势是正则和状态控制都清晰容易维护。下面这个脚本我已经在实际项目里跑过支持/ltm dns cname和/ltm dns wideip两类输出自动跳过非 CNAME 类型import re # 读取 tmsh 原始输出 with open(cname_raw.txt, r, encodingutf-8, errorsignore) as f: lines f.readlines() records [] current None for line in lines: line line.rstrip() m re.match(rltm dns cname\s(\S)\s*\{, line) if m: current {owner: m.group(1), answer: , ttl: } records.append(current) continue if current is not None: am re.search(rcname\s(\S), line) if am: current[answer] am.group(1) tm re.search(r\bttl\s(\d), line) if tm: current[ttl] tm.group(1) if line.strip() }: current None # 如果需要处理 CNAME WideIP额外过滤 wideip 输出中 type 为 cname 的对象 with open(cname_records.txt, w, encodingutf-8) as out: out.write(owner,ttl,target,type\n) for r in records: if not r[answer]: continue ttl r[ttl] if r[ttl] else default out.write(f{r[owner]},{ttl},{r[answer]},CNAME\n) print(f导出完成共 {len([r for r in records if r[answer]])} 条 CNAME 记录)注意正则里的(\S)不要写成(.)否则 owner 后面带空格和大括号时会把整个配置块名吞进去。这个脚本跑完之后打开 cname_records.txt 就是一份可以直接交给别人的标准清单。3.4 导出的记录先做一轮自检文件生成之后别急着交出去先用wc -l看总数有没有明显偏少再随机抽几条和 GUI 里的配置对比。更严谨的做法是抽一台 F5 的 DNS listener 做在线解析校验。比如导出的第一条是www.example.com - edge.example.com可以找一台能访问 F5 DNS 服务地址的机器执行dig F5的DNS地址 www.example.com noall answer如果返回的 CNAME 记录和文件里一致说明导出和清洗链路没问题。如果发现解析结果不对先别怀疑脚本回到 GUI 里确认这条记录是不是最近被改过、tmsh 输出的缓存是不是有延迟。4. 导出过程中的常见问题与排查实录4.1 命令没输出或权限不足这是我被问得最多的问题。执行tmsh -c cd /; list /ltm dns cname recursive后屏幕上干干净净什么都查不到。大部分情况是分区没切对或者账号不是管理员。排查思路很简单先跑tmsh -c show sys version确认账号能连通设备再跑tmsh -c list /ltm dns cname all看默认分区有没有数据最后用tmsh -c cd /; list /ltm dns cname recursive看全局。如果前两步有输出、第三步没有说明系统里确实没有更多 CNAME 对象或者你的账号被限定了 partition 访问范围需要找管理员开通。4.2 文件打开乱码、换行不对从 Linux 设备上重定向出来的文件默认是 UTF-8 编码、LF 换行。你在 Windows 上用记事本打开会变成一行用 Excel 打开可能显示乱码。这不是导出出错是平台差异。解决方案有两类一是直接在 Linux 上转码完再下载用dos2unix把换行符转成 LF再用iconv确认 UTF-8二是下载后用 VS Code 或 Notepad 打开右下角把编码和换行符统一一下。我建议在 Linux 端就把文件转成带 UTF-8 BOM 的格式这样 Windows 双击打开也不会乱码printf \xEF\xBB\xBF cname_records_bom.txt cat cname_records.txt cname_records_bom.txt4.3 CNAME 和其他记录冲突F5 配置层面一般不会让你建同名的 CNAME 和 A 记录但导入其他 DNS 平台时经常遇到冲突。比如目标平台已经存在一条www.example.com A 203.0.113.10你又想把www.example.com CNAME edge.example.com导进去平台会直接报错。处理建议是在导出文件里加一列“原记录类型”导入前用脚本按 owner 去重如果同 owner 出现多条不同类型记录标记出冲突项人工决定保留哪条。另外很多云平台对 CNAME 的 owner 有“裸域名不能配置 CNAME”的限制遇到这种情况也要在清洗阶段提前揪出来。4.4 TTL 异常和默认继承F5 的 DNS CNAME 对象如果不显式写 ttl会继承 DNS profile 或 zone 的默认 TTL。导出时你会看到很多行 ttl 字段缺失如果直接标记成默认值迁移到新平台后一定要确认目标平台的默认 TTL 是否一致。我遇到过最坑的一次是F5 上显式 TTL 是 300 秒默认 TTL 是 3600 秒迁移时脚本把所有没写 TTL 的记录都按 3600 处理结果服务商要求某些域名 TTL 必须小于 600 秒不然切流量时生效太慢造成十分钟的解析真空。所以我的习惯是宁可导出时多花点时间把 default 的记录逐条到 GUI 里确认一遍也不要在 TTL 上赌默认值。5. 导出之后怎么用审计、迁移、自动备份三件事5.1 审计盘点用脚本快速统计域名资产文件拿到手后第一件事建议做整体数量统计看看和预期是否一致。用 Excel 数据透视表自然可以但如果你更习惯命令行一行 Python 就能搞定import csv from collections import Counter with open(cname_records.txt, encodingutf-8) as f: rows list(csv.DictReader(f)) print(f总记录数: {len(rows)}) targets Counter(r[target].split(.)[-1] for r in rows if . in r[target]) print(目标域名后缀 TOP10:, targets.most_common(10))这样扫一眼就能发现有没有异常的 CNAME 目标比如指向陌生域名、指向内网 IP、指向已停用的老域名。审计场景下这个动作比跑任何合规工具都直接。5.2 迁移到其他 DNS 平台把 txt 转成目标平台可识别的格式是迁移前最后一步。不同平台要求不一样自建 Bind 需要生成标准 zone 文件www.example.com. 300 IN CNAME edge.example.com.云服务商控制台通常支持批量导入 CSV字段名一般是 “Type,Host,Routing,Target”。这时候清洗脚本导出的四列数据就有用了平台要求哪个字段名就改一下表头几秒钟就能对上。有一点必须提醒CNAME 记录迁移不像 A 记录那么无脑。CNAME 的存在意味着解析结果跟着目标域名的变化走所以迁移前要确认目标域名比如 edge.example.com是否也会一同迁移否则会出现“CNAME 迁过去了目标却解析不了”的尴尬场面。5.3 定时备份与 diff 变更追踪导出 CNAME 记录这件事做一次是工具持续做就是体系。我强烈建议把导出命令和清洗脚本做成一个定时任务每天凌晨把最新的cname_records.txt提交到 git 仓库这样每次有人改 DNS 配置你都能通过 git diff 看到具体改动。我自己的习惯是放在一台能 SSH 到 F5 的管理机上配置 crontab0 2 * * * ssh adminf5-bigip /usr/bin/tmsh -c cd /; list /ltm dns cname recursive /var/dns_backup/cname_raw_$(date \%Y\%m\%d).txt 21脚本跑完后自动 git add、git commit。这样无论哪天线上 DNS 异常都可以回溯到任意一天的文件对比确认是不是有人改过 CNAME。这个习惯帮我在一次半夜故障里十分钟内定位到是一条 CNAME 目标被误改而不是去 GUI 里翻变更记录翻到天亮。我在实际使用中还有一个体会导出的 txt 不要只存一份尽量和 A 记录、PTR 记录、监听器配置放在同一个备份目录统一打时间戳。DNS 排障最怕的不是查不到记录而是只导了 CNAME结果发现问题出在别的记录类型上还得再跑一次 SSH。把这套流程固化下来之后F5 上的域名配置对我来说就像一份可以随时翻看的账本再也不是黑盒了。
返回列表