PowerDNS lua-records (`ifurlup`, `pickclosest`, `ifportup`) cover everything k8gb was doing — geo-aware response selection, health-checked failover, weighted round-robin — at the authoritative DNS layer. Eliminates a separate K8s controller, CRD set, and CoreDNS plugin from every Sovereign. Changes: - platform/k8gb/ deleted (Chart.yaml, values.yaml, blueprint.yaml never authored — only README existed) - products/catalyst/bootstrap/ui/public/component-logos/k8gb.svg deleted - componentGroups.ts: remove k8gb component (PowerDNS already there) - componentLogos.tsx: drop logo_k8gb + k8gb map entry - model.ts DEFAULT_COMPONENT_GROUPS spine: replace k8gb with powerdns - StepInfrastructure.tsx: copy refers to PowerDNS lua-records, not k8gb - provision.html: replace k8gb tile and edges with powerdns - catalog.generated.ts regenerated (now includes bp-powerdns) - docs sweep — every k8gb reference in PLATFORM-TECH-STACK, NAMING- CONVENTION, SOVEREIGN-PROVISIONING, SRE, ARCHITECTURE, GLOSSARY, COMPONENT-LOGOS, IMPLEMENTATION-STATUS, BUSINESS-STRATEGY, TECHNOLOGY-FORECAST, README, infra/hetzner/README, platform READMEs (cilium, external-dns, failover-controller, litmus, flux, opentofu) rewritten to point at PowerDNS lua-records / MULTI-REGION-DNS.md. Historical entries in VALIDATION-LOG.md preserved as audit trail. - New docs/MULTI-REGION-DNS.md — canonical reference for the lua-record patterns (ifurlup all/pickclosest/pickfirst, ifportup, pickwhashed), Application Placement → lua-record selector mapping, when to add a second Sovereign region, operational checks. Closes #171. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
141 lines
3.7 KiB
Markdown
141 lines
3.7 KiB
Markdown
# ExternalDNS
|
|
|
|
DNS synchronization (registers/deletes records via the PowerDNS REST API and external cloud DNS APIs where applicable). Per-host-cluster infrastructure (see [`docs/PLATFORM-TECH-STACK.md`](../../docs/PLATFORM-TECH-STACK.md) §3.1) — runs on every host cluster, primarily on the DMZ block. PowerDNS (see [`docs/PLATFORM-POWERDNS.md`](../../docs/PLATFORM-POWERDNS.md)) is the authoritative server for every Sovereign zone; ExternalDNS uses the `webhook` provider (`external-dns-pdns`) to write A/AAAA/CNAME records into PowerDNS. Health-checked geo-failover lives in PowerDNS lua-records — see [`docs/MULTI-REGION-DNS.md`](../../docs/MULTI-REGION-DNS.md).
|
|
|
|
**Status:** Accepted | **Updated:** 2026-04-27
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
ExternalDNS synchronizes Kubernetes resources (Gateway, Service, Ingress) with external DNS providers, enabling automatic DNS record management.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
subgraph K8s["Kubernetes"]
|
|
GW[Gateway API]
|
|
Svc[Services]
|
|
ExtDNS[ExternalDNS]
|
|
end
|
|
|
|
subgraph DNS["DNS Providers"]
|
|
PDNS[PowerDNS<br>(authoritative — every Sovereign zone)]
|
|
CF[Cloudflare]
|
|
R53[Route53]
|
|
HDNS[Hetzner DNS]
|
|
end
|
|
|
|
GW --> ExtDNS
|
|
Svc --> ExtDNS
|
|
ExtDNS --> PDNS
|
|
ExtDNS --> CF
|
|
ExtDNS --> R53
|
|
ExtDNS --> HDNS
|
|
```
|
|
|
|
---
|
|
|
|
## Supported DNS Providers
|
|
|
|
| Provider | Availability |
|
|
|----------|--------------|
|
|
| Cloudflare | Always |
|
|
| Hetzner DNS | If Hetzner chosen |
|
|
| AWS Route53 | If AWS chosen |
|
|
| GCP Cloud DNS | If GCP chosen |
|
|
| Azure DNS | If Azure chosen |
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### ExternalDNS Deployment
|
|
|
|
```yaml
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: external-dns
|
|
namespace: external-dns
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: external-dns
|
|
image: registry.k8s.io/external-dns/external-dns:v0.14.0
|
|
args:
|
|
- --source=gateway-httproute
|
|
- --source=gateway-grpcroute
|
|
- --source=service
|
|
- --provider=cloudflare
|
|
- --cloudflare-proxied
|
|
- --txt-owner-id=openova
|
|
- --txt-prefix=_externaldns.
|
|
env:
|
|
- name: CF_API_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: cloudflare-credentials
|
|
key: api-token
|
|
```
|
|
|
|
---
|
|
|
|
## PowerDNS Integration (geo + health-checked failover)
|
|
|
|
ExternalDNS writes plain A/AAAA/CNAME records into PowerDNS via the REST API. Geo-aware and health-checked failover responses are owned by PowerDNS lua-records, written by the `catalyst-dns` controller — ExternalDNS does NOT manage lua-record content.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph Region1["Region 1"]
|
|
App1[Application]
|
|
ExtDNS1[ExternalDNS]
|
|
end
|
|
|
|
subgraph Region2["Region 2"]
|
|
App2[Application]
|
|
ExtDNS2[ExternalDNS]
|
|
end
|
|
|
|
subgraph PDNS["PowerDNS Authoritative"]
|
|
ZoneAPI[REST API]
|
|
Lua[lua-records (ifurlup, pickclosest)]
|
|
end
|
|
|
|
ExtDNS1 -->|"plain A/AAAA"| ZoneAPI
|
|
ExtDNS2 -->|"plain A/AAAA"| ZoneAPI
|
|
ZoneAPI --- Lua
|
|
```
|
|
|
|
See [`docs/MULTI-REGION-DNS.md`](../../docs/MULTI-REGION-DNS.md) for the lua-record patterns.
|
|
|
|
---
|
|
|
|
## Record Types
|
|
|
|
| Source | Record Type | Example |
|
|
|--------|-------------|---------|
|
|
| Gateway | A/CNAME | `api.<domain>` |
|
|
| Service (LoadBalancer) | A | `svc.<domain>` |
|
|
| catalyst-dns (lua-record author) | LUA A | `app.<domain>` (geo + health-checked) |
|
|
|
|
---
|
|
|
|
## TXT Registry
|
|
|
|
ExternalDNS uses TXT records to track ownership:
|
|
|
|
```
|
|
_externaldns.api.<domain> TXT "heritage=external-dns,external-dns/owner=openova"
|
|
```
|
|
|
|
This prevents ExternalDNS from modifying records it doesn't own.
|
|
|
|
---
|
|
|
|
*Part of [OpenOva](https://openova.io)*
|