尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

ExternalDNS 集成 Skipper RouteGroup 源:从 CRD 部署到 DNS 记录生成的完整指南

发布时间:2026/9/25 3:01:08

资讯中心
01
ARTICLE

ExternalDNS 集成 Skipper RouteGroup 源:从 CRD 部署到 DNS 记录生成的完整指南

ExternalDNS 集成 Skipper RouteGroup 源:从 CRD 部署到 DNS 记录生成的完整指南
云原生【免费下载链接】external-dnsConfigure external DNS servers dynamically from Kubernetes resources项目地址https://gitcode.com/gh_mirrors/ex/external-dns点击查看免费下载导读本文围绕 ExternalDNS 的skipper-routegroup源Source展开讲解如何让 ExternalDNS 监听 Zalando Skipper 的 RouteGroup 自定义资源自动将其hosts与负载均衡状态转换为 DNS 记录并同步到 AWS Route 53 等 DNS 提供商。读完本文你将掌握 RouteGroup CRD 的安装与 RBAC 授权、两种部署清单的编写、FQDN 模板的使用以及源码层面的记录生成原理与注解过滤机制。背景什么是 Skipper RouteGroup 源Skipper 是 Zalando 开源的高性能 HTTP 路由器其 RouteGroup CRDCustom Resource Definition用于描述一组路由规则包括hosts对外域名与routes后端路由定义。ExternalDNS 将其作为一类 DNS 源从 RouteGroup 资源中提取域名与负载均衡目标进而调用 DNS 提供商 API 完成记录的增删改。在 ExternalDNS 的源分类中skipper-routegroup被标记为 Ingress Controllers 类别说明它解决的是「Ingress 控制器场景下的域名自动解析」问题与 docs/sources/about.md 中列出的其他源ingress、service、traefik-proxy 等属于同一套插件体系。其源标识定义在 source/types/types.goSkipperRouteGroup Type skipper-routegroup并通过 source/skipper_routegroup.go 中的声明式注解注册到源码列表// externaldns:source:nameskipper-routegroup // externaldns:source:categoryIngress Controllers // externaldns:source:descriptionCreates DNS entries from Skipper RouteGroup resources // externaldns:source:resourcesRouteGroup.zalando.org // externaldns:source:filtersannotation,label // externaldns:source:namespaceall,single // externaldns:source:fqdn-templatetrue从这些注解可以确认该源支持注解与标签过滤、支持全命名空间或单命名空间监听、支持 FQDN 模板并且其操作的资源类型是RouteGroup.zalando.orgAPI 组zalando.org。本文是对 provider 特定部署教程如 AWS、Azure 等参见 docs/sources/index.md的补充聚焦 RouteGroup 源本身。前置要求RouteGroup CRD 与最小权限安装 RouteGroup CRD--sourceskipper-routegroup要求 ExternalDNS 启动前就已完成 RouteGroup CRD 的安装。原因是 NewRouteGroupSource 在初始化阶段会先对routegroups.zalando.org执行一次探测性的List调用Limit: 1if _, err : client.ZalandoV1().RouteGroups(cfg.Namespace).List(ctx, metav1.ListOptions{Limit: 1}); err ! nil { if apierrors.IsNotFound(err) { return nil, fmt.Errorf(--sourceskipper-routegroup requires the RouteGroup CRD (routegroups.zalando.org) to be installed: %w, err) } return nil, fmt.Errorf(failed to list RouteGroups: %w, err) }也就是说CRD 缺失时 ExternalDNS 会在启动阶段直接报错退出而不是延迟到同步周期才失败。安装 CRD 的命令为kubectl apply -f https://raw.githubusercontent.com/zalando/skipper/master/dataclients/kubernetes/deploy/apply/routegroups_crd.yaml说明该 URL 指向 Skipper 上游仓库中的 CRD 清单实际部署时应固定到与你的 Skipper 版本匹配的 tag 或 commit避免上游变更导致 CRD schema 漂移。对应地source/skipper_routegroup_test.go 中的TestNewRouteGroupSource明确验证了两种失败路径CRD 缺失时返回包含requires the RouteGroup CRD的错误初始 List 被拒绝如 RBAC 无权限时返回failed to list RouteGroups。这解释了文档中「Without it, ExternalDNS exits at startup」的底层原因。ServiceAccount 所需权限该源依赖 informer 机制对routegroups.zalando.org进行监听因此服务账户需要以下权限资源权限routegroups.zalando.orgget、list、watch代码层面源在 NewRouteGroupSource 中创建了routegroup-client提供的 SharedInformerFactoryrginformers.NewSharedInformerFactoryWithOptions并等待本地缓存同步informers.WaitForCacheSync这进一步印证了 list/watch 是硬性依赖。此外如果你同时启用了其他源如 service、ingress还需要对应资源services、pods、nodes、endpointslices的只读权限详见下文 RBAC 清单。FQDN 模板基于 RouteGroup 顶层字段生成域名与部分源如 ingress不同--fqdn-template在 RouteGroup 源中是直接对 RouteGroup 对象本身求值因此模板字段是在顶层寻址的而非嵌在spec内部。文档给出的标准示例--fqdn-template{{.Name}}.{{.Namespace}}.example.com该模板会对每个名为my-rg、位于prod命名空间的 RouteGroup 生成my-rg.prod.example.com这一 DNS 记录。.Metadata前缀的兼容与弃用从代码看模板引擎接收的是一个特殊的routeGroupWrappersource/skipper_routegroup.go它通过Metadata()方法把ObjectMeta暴露给模板// routeGroupWrapper adds a Metadata() accessor to *rgv1.RouteGroup so that // fqdn templates using {{.Metadata.Name}} continue to work alongside the // canonical {{.Name}} form. // // Deprecated: use top-level fields directly (e.g. {{.Name}} instead of {{.Metadata.Name}}). type routeGroupWrapper struct { *rgv1.RouteGroup }因此{{.Metadata.Name}}、{{.Metadata.Namespace}}这类写法当前仍然可以解析但已标记为弃用未来版本将移除。文档明确建议新模板一律使用顶层字段{{.Name}}、{{.Namespace}}。测试用例 source/skipper_routegroup_test.go 同时覆盖了两种写法并验证二者生成完全相同的 endpoint如rg1.namespace1.example方便你平滑迁移模板。与注解的协同combine 语义fqdn-templatetrue意味着该源支持将模板结果与注解域名合并。模板生成的域名通过sc.templateEngine.CombineWithEndpoints与 RouteGroup 自身的 hosts/注解域名进行合并见 source/skipper_routegroup.go即默认情况下配置--fqdn-template后 ExternalDNS 会忽略external-dns.kubernetes.io/hostname注解若同时配置--combine-fqdn-annotation则模板生成的域名与注解域名会同时生成记录。关于模板可用字段、自定义函数toLower、replace、contains、hasKey、isIPv4等与多模板写法的完整参考见 docs/advanced/fqdn-templating.md。一个结合 RouteGroup 的实际用法示例--fqdn-template{{ .Name }}.{{ .Namespace }}.example.com \ --fqdn-template{{ if hasKey .Labels env }}{{ .Name }}.{{ index .Labels env }}.internal.example.com{{ end }}第二条模板只有 RouteGroup 带env标签时才生成记录避免为无标签资源生成形如my-rg..internal.example.com的非法域名。部署清单无 RBAC 集群对于未启用 RBAC 的集群直接部署 Deployment 即可无需 ServiceAccount 与 ClusterRoleapiVersion: 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 # update this to the desired external-dns version image: registry.k8s.io/external-dns/external-dns:v0.22.0 args: - --sourceskipper-routegroup - --policyupsert-only # prevents ExternalDNS from deleting any records, set --policysync to enable full synchronization (including deletions) - --provideraws - --registrytxt - --txt-owner-idmy-identifier参数说明--sourceskipper-routegroup启用 RouteGroup 源。可与其他源并用如--sourceservice --sourceskipper-routegroup此时多个源产出的 endpoint 会合并处理。--policyupsert-only只创建/更新记录不删除任何记录适合首次上线或不想让 ExternalDNS 拥有删除权的场景改为--policysync则启用包含删除在内的完整同步。--provideraws目标 DNS 提供商。本文以 AWS 为例实际可替换为任意 docs/providers.md 中列出的提供商azure、google、cloudflare、rfc2136 等。--registrytxt使用 TXT registry 记录所有权配合--txt-owner-id实现多实例安全协同详见 docs/registry/txt.md。--txt-owner-idmy-identifierTXT 记录中的所有者标识建议改为你自己的唯一字符串。部署清单启用 RBAC 的集群启用 RBAC 的集群需要补齐 ServiceAccount、ClusterRole 与 ClusterRoleBinding。若你同时启用了多个源请在 ClusterRole 的rules中按需增补资源例如增加ingresses、ingressclasses等文档中的注释「Could be change if you have multiple sources」正是此意apiVersion: v1 kind: ServiceAccount metadata: name: external-dns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-dns rules: - apiGroups: [] resources: [services,pods,nodes] verbs: [get,list,watch] - apiGroups: [discovery.k8s.io] resources: [endpointslices] verbs: [get,list,watch] - apiGroups: [zalando.org] resources: [routegroups] verbs: [get,list,watch] --- 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 # update this to the desired external-dns version image: registry.k8s.io/external-dns/external-dns:v0.22.0 args: - --sourceskipper-routegroup - --policyupsert-only # prevents ExternalDNS from deleting any records, set --policysync to enable full synchronization (including deletions) - --provideraws - --registrytxt - --txt-owner-idmy-identifier注意 ClusterRole 中对核心资源的授权仅涉及只读操作get/list/watch不需要对 DNS 提供商凭据做任何集群内授权——provider 凭据通过环境变量或 AWS IAM 角色单独配置。Helm 用户可在 charts/external-dns/templates/clusterrole.yaml 中查看官方 Chart 对routegroups.zalando.org的内置授权作为手工清单的参考。记录生成原理从 RouteGroup 到 EndpointEndpoint 的两种来源源的核心逻辑在 Endpoints 方法中遍历 informer 索引器中的全部 RouteGroup为每个对象生成 endpoint然后合并去重endpoint.MergeEndpoints。单个 RouteGroup 的 endpoint 来自两个渠道endpointsFromRouteGroupsource/skipper_routegroup.go遍历rg.Spec.Hosts中的每个域名结合 targets 生成记录若配置了--ignore-hostname-annotation则跳过external-dns.kubernetes.io/hostname注解中的域名。endpointsFromTemplatesource/skipper_routegroup.go执行 FQDN 模板把渲染出的每个 hostname 也转换为记录。两条渠道的结果通过CombineWithEndpoints合并对应文档中的 combine 语义最终通过endpoint.AttachRefObject挂上事件引用用于 pkg/events 中的变更事件追踪。Targets 的取值优先级targets即 DNS 记录的值的解析顺序在 endpointsFromRouteGroup 中清晰可见若 RouteGroup 带external-dns.kubernetes.io/target注解则优先使用注解指定的目标否则回落到rg.Status.LoadBalancer.RouteGroup由 Skipper 写入的负载均衡器状态依次收集其中的IP与Hostname字段。因此只有当 RouteGroup 的 status 已由 Skipper 控制器填充、或显式配置 target 注解时才会产生有实际解析值的记录。测试用例 source/skipper_routegroup_test.go 专门验证了「无 hosts 或无 destinations 时都不产生 endpoint」的边界行为。记录类型A / AAAA / CNAME的自动判定Endpoint 的记录类型由endpoint.EndpointsForHostname依据 target 的形态自动决定target 为 IPv4 地址 →A记录target 为 IPv6 地址 →AAAA记录target 为域名hostname→CNAME记录同一域名同时存在 IP 与 hostname 时会生成多条不同类型的记录。这一点由 source/skipper_routegroup_test.go 中的混合 targets 用例验证一个rg1.k8s.example同时生成A指向1.5.1.4与CNAME指向lb.example.org两条记录IPv6 目标则生成AAAA。因此你无需在 RouteGroup 中声明记录类型只需保证 status 或 target 注解中的目标合法。注解与过滤精确控制哪些 RouteGroup 生效核心注解该源复用了 ExternalDNS 的标准注解体系前缀默认external-dns.kubernetes.io/定义于 source/annotations/annotations.go注解作用external-dns.kubernetes.io/hostname在 hosts 之外补充指定域名--ignore-hostname-annotation可关闭external-dns.kubernetes.io/target显式指定记录目标优先级高于 status 中的负载均衡器信息external-dns.kubernetes.io/ttl指定 DNS 记录 TTL秒未设置时使用 provider 默认值external-dns.kubernetes.io/controller值为dns-controller时确认由 ExternalDNS 接管external-dns.kubernetes.io/aws-*等提供商特定属性如aws/weight权重会透传到 provider-specific 属性中TTL 的处理可见 endpointsFromRouteGroupannotations.TTLFromAnnotations读取 ttl 注解并写入endpoint.RecordTTLsource/skipper_routegroup_test.go 验证了external-dns.kubernetes.io/ttl: 2189会原样出现在生成的 endpoint 上。provider-specific 注解如external-dns.kubernetes.io/aws-weight: 10同样会被透传测试用例见 source/skipper_routegroup_test.go。完整注解语义可参考 docs/annotations/annotations.md。注解过滤与标签过滤初始化时源通过 informer 索引器挂载了三类过滤器source/skipper_routegroup.go--annotation-filter如kubernetes.io/ingress.classskipper仅处理带指定注解的 RouteGroup。测试 source/skipper_routegroup_test.go 验证了等值过滤与集合过滤in (nginx, skipper)两种写法。--label-filter如apptest仅处理带指定标签的 RouteGroup见 source/skipper_routegroup_test.go。控制器注解匹配仅处理external-dns.kubernetes.io/controllerdns-controller的对象见 source/skipper_routegroup_test.go。这意味着你可以用「按 ingress class 分流」「按环境标签分流」等策略让同一集群中的多套 ExternalDNS 各管一部分 RouteGroup互不干扰。与 Kubernetes 事件的联动skipper-routegroup源实现了事件驱动的变更传播AddEventHandlersource/skipper_routegroup.go把 RouteGroup 的 informer 事件处理器注册到控制器RouteGroup 发生增删改后ExternalDNS 会立即重新生成 endpoint 并触发同步而非仅依赖周期轮询。测试TestEndpointsFromRouteGroupssource/skipper_routegroup_test.go 起覆盖了多 RouteGroup、注解/标签过滤、TTL、模板组合等端到端场景可作为理解该源行为的权威样例。常见问题与排查建议启动即退出日志报requires the RouteGroup CRDCRD 未安装或安装顺序晚于 ExternalDNS 启动。先执行 CRD 安装命令再重启 ExternalDNS。启动报failed to list RouteGroupsServiceAccount 缺少routegroups.zalando.org的 list 权限检查 ClusterRole。RouteGroup 有 hosts 但没有任何记录确认status.loadBalancer.routeGroup已被 Skipper 填充或为 RouteGroup 添加external-dns.kubernetes.io/target注解参见上文「Targets 的取值优先级」。模板生成的域名不符合预期优先使用顶层字段{{.Name}}/{{.Namespace}}.Metadata写法虽仍可用但已弃用。模板语法细节与函数清单见 docs/advanced/fqdn-templating.md。想控制删除行为生产环境首次接入建议--policyupsert-only确认记录符合预期后再切换为--policysync。Helm 部署可参考 charts/external-dns/README.md 与 charts/external-dns/templates/clusterrole.yaml通过extraArgs传入--sourceskipper-routegroup等参数。总结Skipper RouteGroup 源为基于 Skipper 的 Kubernetes 集群提供了一条「声明式域名管理」路径以 RouteGroup CRD 为事实来源以 status/注解为目标解析以 FQDN 模板为域名扩展手段配合 TXT registry 与事件驱动机制将域名记录全生命周期托管给 ExternalDNS。本文涉及的核心代码均位于 source/skipper_routegroup.go测试证据集中在 source/skipper_routegroup_test.go进一步阅读可结合 docs/sources/index.md源总览、docs/advanced/fqdn-templating.md模板进阶与 docs/registry/txt.mdTXT registry 原理。赞分享云原生【免费下载链接】external-dnsConfigure external DNS servers dynamically from Kubernetes resources项目地址https://gitcode.com/gh_mirrors/ex/external-dns点击查看免费下载相关推荐7种场景下的ComfyUI-Custom-Scripts深度解析掌握AI绘画工作流增强的3层架构方案7种场景下的ComfyUI Custom Scripts深度解析掌握AI绘画工作流增强的3层架构方案 ComfyUI Custom Scripts是一个专注于云原生终极界面字体解决方案Source Sans 3 专业使用指南终极界面字体解决方案Source Sans 3 专业使用指南 还在为现代用户界面字体选择而烦恼吗面对琳琅满目的字体库你是否曾因字体渲染不清晰、字重选择有限云原生ExternalDNS 支持的 Source 全解析从 Kubernetes 资源发现到 DNS 记录的生成ExternalDNS 支持的 Source 全解析从 Kubernetes 资源发现到 DNS 记录的生成 本文围绕 docs/sources/index.云原生上一篇开源数据库工具DBeaver画不出桑基图一条第三方图表库集成路径救场下一篇OpenProject 容器化部署实战从一条命令试跑到稳定支撑团队上线创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。