ExternalDNS 接入 DNSimple 实战指南:从 API Token 到 A/TXT 记录全自动同步

发布时间:2026/9/25 11:45:28
ExternalDNS 接入 DNSimple 实战指南:从 API Token 到 A/TXT 记录全自动同步 云原生【免费下载链接】external-dnsConfigure external DNS servers dynamically from Kubernetes resources项目地址https://gitcode.com/gh_mirrors/ex/external-dns点击查看免费下载本教程面向希望让 Kubernetes 集群中的 Service 与 DNSimple 托管域名自动同步的运维与开发人员。文章围绕 docs/tutorials/dnsimple.md 的完整部署流程展开结合仓库中 provider/dnsimple/dnsimple.go 的源码实现讲清楚三个核心问题如何准备 DNSimple 凭证区分 User Token 与 Account Token、如何在有/无 RBAC 的集群中部署 ExternalDNS以及如何验证 A 与 TXT 记录确实被自动创建。读完本文你将获得一套可复现、可排障的 DNSimple ExternalDNS 部署方案。前置条件与版本要求使用本教程前请确认环境满足以下条件一个可用的 Kubernetes 集群并且kubectl已连接到该集群。一个 DNSimple 账户且该账户下已有需要被 ExternalDNS 管理的 DNS zone例如example.com。ExternalDNS 版本不低于 v0.4.6。本文中的部署清单使用的镜像标签为registry.k8s.io/external-dns/external-dns:v0.23.0该版本在 DNSimple Provider 的凭证解析与 zone 过滤逻辑上与本文描述一致。如果你要使用其他版本请以 docs/faq.md 中关于镜像来源的说明为准替换镜像标签即可。第一步创建 DNSimple API Access TokenExternalDNS 需要通过 DNSimple API 操作 DNS 记录因此第一步是在 DNSimple 账户中生成一个 API access token按 DNSimple 官方文档中关于 API access token 的说明操作。生成后将 token 保存为环境变量供后续部署使用。ExternalDNS 运行时会读取以下环境变量对应源码见 provider/dnsimple/dnsimple.go环境变量是否必需说明DNSIMPLE_OAUTH必需生成的 DNSimple API access token。源码中newProvider会先读取该变量若为空直接返回错误no dnsimple oauth token provided。DNSIMPLE_ACCOUNT_IDUser Token 时必需需要被管理的域名所属的 DNSimple 账户 ID例如1001234。DNSIMPLE_ZONESUser Token 时必需需要被管理的 DNS zone 列表逗号分隔例如mydomain.com,example.com。这里的关键区别在于 token 的类型Account tokentoken 绑定某个具体账户可以直接列出并操作该账户下的 zones。此时DNSIMPLE_ACCOUNT_ID与DNSIMPLE_ZONES可不设置。User tokentoken 属于某个用户用户可能对多个账户有访问权限但 User token 本身没有列出 zones 的权限。因此必须显式提供DNSIMPLE_ACCOUNT_ID指定目标账户并通过DNSIMPLE_ZONES手工声明要管理的 zone 列表。从源码看provider/dnsimple/dnsimple.go 的账户解析逻辑为优先使用DNSIMPLE_ACCOUNT_ID环境变量若未设置则调用 DNSimple 的whoami接口自动获取账户 ID。而 Zones 方法的实现也印证了DNSIMPLE_ZONES的设计初衷——源码注释明确指出当DNSIMPLE_OAUTH是 User API token 而非 Account API token 时User token 没有权限列出另一个账户下的 zones所以需要用DNSIMPLE_ZONES显式声明 zone 列表。相关行为在 provider/dnsimple/dnsimple_test.go 中有测试覆盖设置了DNSIMPLE_ZONES时Zones()直接根据环境变量构造 zone 列表不再调用ListZonesAPI。第二步部署 ExternalDNS将kubectl连接到目标集群后选择以下两个清单之一进行部署。集群未启用 RBAC最小化部署清单将下面的内容保存为externaldns.yamlapiVersion: apps/v1 kind: Deployment metadata: name: external-dns spec: strategy: type: Recreate selector: matchLabels: app: external-dns template: metadata: labels: app: external-dns spec: containers: - name: external-dns image: registry.k8s.io/external-dns/external-dns:v0.23.0 args: - --sourceservice - --policyupsert-only # prevents ExternalDNS from deleting any records, set --policysync to enable full synchronization (including deletions) - --domain-filterexample.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple. - --providerdnsimple - --registrytxt env: - name: DNSIMPLE_OAUTH value: YOUR_DNSIMPLE_API_KEY - name: DNSIMPLE_ACCOUNT_ID value: SET THIS IF USING A DNSIMPLE USER ACCESS TOKEN - name: DNSIMPLE_ZONES value: SET THIS IF USING A DNSIMPLE USER ACCESS TOKEN各启动参数的含义如下参数定义见 pkg/apis/externaldns/types.go--sourceservice监听 Service 资源作为 DNS 记录来源。想同时监听 Ingress 等资源可追加多个--source。--policyupsert-only只创建和更新记录不删除任何记录是保守的默认推荐值。若需要全量同步包含删除被移除资源的记录改为--policysync。该参数没有默认值必须在sync、upsert-only、create-only三者中显式指定。--domain-filterexample.com可选。仅管理与该域名后缀匹配的 zone需与你在 DNSimple 中创建的 zone 保持一致。--providerdnsimple指定使用 DNSimple Provider。该值必须在--provider参数允许的枚举列表中Provider 名在 pkg/apis/externaldns/types.go 中定义工厂函数映射见 provider/factory/provider.go。--registrytxt使用 TXT registry 记录记录所有权默认值即为txt可选aws-sd、crd、dynamodb、noop、txt。关于DNSIMPLE_ACCOUNT_ID与DNSIMPLE_ZONES如果使用的是 Account token这两个变量可以留空或省略如果使用的是 User token则必须填入对应的账户 ID 与 zone 列表见第一步的说明。集群启用 RBAC完整部署清单在 RBAC 启用的集群中ExternalDNS 需要 ServiceAccount、ClusterRole 和 ClusterRoleBinding 才能读取集群资源。将下面的内容保存为externaldns.yamlapiVersion: v1 kind: ServiceAccount metadata: name: external-dns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-dns rules: - apiGroups: [] resources: [services,pods] verbs: [get,watch,list] - apiGroups: [discovery.k8s.io] resources: [endpointslices] verbs: [get,watch,list] - apiGroups: [extensions,networking.k8s.io] resources: [ingresses] verbs: [get,watch,list] - apiGroups: [] resources: [nodes] verbs: [list] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: external-dns-viewer roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-dns subjects: - kind: ServiceAccount name: external-dns namespace: default --- apiVersion: apps/v1 kind: Deployment metadata: name: external-dns spec: strategy: type: Recreate selector: matchLabels: app: external-dns template: metadata: labels: app: external-dns spec: serviceAccountName: external-dns containers: - name: external-dns image: registry.k8s.io/external-dns/external-dns:v0.23.0 args: - --sourceservice - --policyupsert-only # prevents ExternalDNS from deleting any records, set --policysync to enable full synchronization (including deletions) - --domain-filterexample.com # (optional) limit to only example.com domains; change to match the zone you create in DNSimple. - --providerdnsimple - --registrytxt env: - name: DNSIMPLE_OAUTH value: YOUR_DNSIMPLE_API_KEY - name: DNSIMPLE_ACCOUNT_ID value: SET THIS IF USING A DNSIMPLE USER ACCESS TOKEN - name: DNSIMPLE_ZONES value: SET THIS IF USING A DNSIMPLE USER ACCESS TOKEN与无 RBAC 清单的差异仅在于新增了 RBAC 对象并将 Deployment 的serviceAccountName设置为external-dns。ClusterRole 授予的权限覆盖了 Service 源所需的services、pods、endpointslices、ingresses与nodes资源的读取权限与 source/service.go 等 source 实现的读取范围一致。执行部署kubectl create -f externaldns.yaml第三步部署一个带注解的 Nginx Service为了验证 ExternalDNS 的同步能力创建一个名为nginx.yaml的示例应用它由一个 Nginx Deployment 和一个 LoadBalancer 类型的 Service 组成apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx name: nginx ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx annotations: external-dns.kubernetes.io/hostname: validate-external-dns.example.com spec: selector: app: nginx type: LoadBalancer ports: - protocol: TCP port: 80 targetPort: 80注意 Service 上的注解external-dns.kubernetes.io/hostname其值必须与你创建的 DNSimple zone如example.com保持一致也可以是该 zone 的子域名例如www.example.com。ExternalDNS 正是通过这条注解判断哪些 Service 需要被注册到 DNS移除注解会导致 ExternalDNS 删除对应的 DNS 记录。注解机制的整体设计可参考 docs/annotations/annotations.md。创建资源kubectl create -f nginx.yaml云服务商为 LoadBalancer 分配外部 IP 可能需要一些时间。执行kubectl get services nginx查看状态当EXTERNAL-IP字段出现地址时说明 Service 已经可以从外部访问。一旦拿到外部 IPExternalDNS 就会感知到新的 Service 地址并开始同步 DNSimple 的 DNS 记录。从源码看同步链路是这样的ExternalDNS 从 Service 源收集 Endpoint对应源码source/service.go在计划阶段与 DNSimple 中已有的记录对比生成 Create/Update/Delete 变更对应 plan/plan.go 的计划逻辑最后由 provider/dnsimple/dnsimple.go 的ApplyChanges方法把这些变更拆分为CREATE、UPDATE、DELETE三类操作并批量提交。submitChanges内部会先调用Zones()获取 zone 列表为每条记录调用dnsimpleSuitableZone选择最合适的 zone取最长后缀匹配Apex 记录记录名与 zone 名相同会被转换为空 name 提交见 provider/dnsimple/dnsimple.go。第四步验证 DNSimple DNS 记录同步完成后可以通过三种方式验证记录是否创建成功。A 记录与 TXT 记录都会被创建——TXT 记录是--registrytxt机制下的所有权标记记录。方式一通过 whoami 获取账户 ID如果不知道 DNSimple 账户 ID可以调用 DNSimple Identity API 的whoami端点获取curl -H Authorization: Bearer $DNSIMPLE_ACCOUNT_TOKEN \ -H Accept: application/json \ https://api.dnsimple.com/v2/whoami响应示例{ data: { user: null, account: { id: 1, email: example-accountexample.com, plan_identifier: dnsimple-professional, created_at: 2015-09-18T23:04:37Z, updated_at: 2016-06-09T20:03:39Z } } }响应中data.account.id即为账户 ID。这也对应源码中未设置DNSIMPLE_ACCOUNT_ID时的自动获取逻辑——newProvider会调用identity.Whoami取回账户 ID见 provider/dnsimple/dnsimple.go。方式二查看 DNSimple 控制台登录 DNSimple打开 Record Editor 页面地址格式为https://dnsimple.com/a/YOUR_ACCOUNT_ID/domains/example.com/records。将YOUR_ACCOUNT_ID替换为你的账户 ID将example.com替换为验证时使用的真实域名即可在页面上看到 ExternalDNS 创建的 A 记录与 TXT 记录。方式三调用 DNSimple Zone Records API使用 DNSimple 的「List records for a zone」接口可以脚本化地验证 A 与 TXT 记录是否创建成功curl -H Authorization: Bearer $DNSIMPLE_ACCOUNT_TOKEN \ -H Accept: application/json \ https://api.dnsimple.com/v2/YOUR_ACCOUNT_ID/zones/example.com/recordsnamevalidate-external-dns同样需要替换YOUR_ACCOUNT_ID与example.com为你自己的值。注意原文档中该 URL 以name...结尾实际请求时需要将改为?作为查询参数分隔符即.../records?namevalidate-external-dns否则过滤参数不会被正确解析。查询结果中应能看到validate-external-dns.example.com的 A 记录以及配套的 TXT 记录。源码层面Records()方法provider/dnsimple/dnsimple.go会把 DNSimple 中的每条 zone record 转换为 ExternalDNS Endpoint记录类型会先经过provider.SupportedRecordType过滤A、AAAA、CNAME、TXT、SRV、NS 等受支持类型Apex 记录name 为空会被映射为 zone 域名本身并携带记录的 TTL。该逻辑在 provider/dnsimple/dnsimple_test.go 中有完整表格化测试覆盖了多种记录类型、双栈AAAAA同名记录以及 Apex 空 name 等场景。清理环境验证完成后删除示例资源与 ExternalDNS 本身kubectl delete -f nginx.yaml kubectl delete -f externaldns.yaml删除已创建的 DNS 记录删除集群资源并不会自动清除 DNSimple 中已创建的 DNS 记录需要手动清理。从前面的验证步骤中拿到记录的 ID然后调用 DNSimple 的「Delete a zone record」接口删除对应记录。进阶理解Provider 内部的变更执行细节如果你希望进一步了解 ExternalDNS 与 DNSimple 交互的底层机制可以从以下几点入手阅读源码全部位于 provider/dnsimple/dnsimple.go凭证与账户解析newProviderL106-L135读取DNSIMPLE_OAUTH缺失时报错账户 ID 优先取DNSIMPLE_ACCOUNT_ID否则回退到whoami。Zone 列表的来源Zones()L158-L196优先使用DNSIMPLE_ZONES环境变量User token 场景否则分页调用ListZones并对每个 zone 应用domainFilter与zoneIDFilter过滤。TTL 的处理newDnsimpleChangeL236-L252中若 Endpoint 未配置 TTL则使用默认值defaultTTL 36001 小时与 DNSimple 默认一致Endpoint 配置了 TTL 时使用其值。测试用例 provider/dnsimple/dnsimple_test.go 验证了自定义 TTL 会被原样传入CreateRecord。Update/Delete 前查找记录 IDGetRecordIDL330-L352通过「name type」双重条件在 zone 内查找记录 ID以区分双栈场景下同名但不同类型的 A/AAAA 记录。Zone 匹配dnsimpleSuitableZoneL355-L366对主机名做最长后缀匹配选择最具体的 zone。测试 provider/dnsimple/dnsimple_test.go 验证了嵌套 zone 场景下会选中更长的 zone 名。dry-run 支持submitChangesL264-L324在dryRun为真时只打印日志不实际调用 API便于演练变更。这些细节共同保证了 ExternalDNS 在 DNSimple 上的行为是幂等且可预期的同一条记录重复同步不会产生重复创建删除操作会精确命中目标记录未知 zone 中的变更会被安全跳过对应测试 provider/dnsimple/dnsimple_test.go。总结本教程完整走通了 DNSimple ExternalDNS 的部署链路创建 API token 并区分 User/Account 两种凭证的配置方式、在有/无 RBAC 集群中分别部署 ExternalDNS、部署带external-dns.kubernetes.io/hostname注解的 LoadBalancer Service 触发同步以及通过 DNSimple 控制台或 REST API 验证 A 与 TXT 记录。在此基础上结合 provider/dnsimple/dnsimple.go 的源码你可以进一步掌握凭证解析、zone 过滤、TTL 默认值与记录 ID 查找等底层实现为生产环境排障和二次定制提供依据。关于更多 Provider 的接入方式与通用配置项可参考 docs/providers.md 与 docs/faq.md。赞分享云原生【免费下载链接】external-dnsConfigure external DNS servers dynamically from Kubernetes resources项目地址https://gitcode.com/gh_mirrors/ex/external-dns点击查看免费下载相关推荐Bebas Neue 免费商用标题字体安装、字重、排版一次讲透Bebas Neue 免费商用标题字体安装、字重、排版一次讲透 找标题字体翻遍免费库是不是总差点意思Bebas Neue 字体免费商用是专为标题而生的无云原生text-to-cad CAD 修复闭环STEP 建模失败的诊断分类与最小修复方法text to cad CAD 修复闭环STEP 建模失败的诊断分类与最小修复方法 在 text to cad 仓库的 $cad 技能中从 build123云原生终极指南如何在macOS上使用eqMac专业音频均衡器提升音质体验终极指南如何在macOS上使用eqMac专业音频均衡器提升音质体验 你是否厌倦了macOS系统单调的音频效果想要为你的音乐、电影和游戏带来专业级的音质提升云原生上一篇3步掌握Path of Building PoE2流放之路2角色规划终极指南下一篇终极指南如何用C重制经典武侠游戏《金庸群侠传》并加入现代战斗系统 创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考