Skip to content

labring-sigs/registry-dns-switcher

Repository files navigation

Registry DNS Switcher

Registry DNS Switcher reads registry proxy health metrics from VictoriaMetrics and updates a DNS record to the selected healthy IP.

It is designed for metrics emitted by sealos-state-metrics registryproxy collector:

sealos_registry_proxy_status{check_type="api",ip="10.0.0.12"} 1
sealos_registry_proxy_status{check_type="manifest",ip="10.0.0.12"} 1

An IP is healthy only when both api and manifest samples are 1. Among healthy IPs, the largest priority wins. Targets with the same priority are selected by switchPolicy.tieBreaker.

switchPolicy.unhealthyFor delays failover after the current DNS IP becomes unhealthy. switchPolicy.healthyFor delays switching to a selected healthy target while the current DNS IP is still healthy. When no configured target is healthy, the tool keeps the current DNS record unchanged and logs a warning.

Configuration

run:
  interval: 30s
  dryRun: false

switchPolicy:
  unhealthyFor: 2m
  healthyFor: 5m
  tieBreaker: order

victoriaMetrics:
  url: http://victoria-metrics.monitoring.svc:8428
  queryPath: /api/v1/query
  timeout: 10s
  bearerToken: ${VICTORIA_METRICS_BEARER_TOKEN}
  basicAuth:
    username: ${VICTORIA_METRICS_BASIC_AUTH_USERNAME}
    password: ${VICTORIA_METRICS_BASIC_AUTH_PASSWORD}
  metricName: sealos_registry_proxy_status
  latencyMetricName: sealos_registry_proxy_response_time_seconds
  registryEndpointLabel: endpoint
  latencyMatchers: {}

registry:
  endpoint: https://registry-proxy.example.com:5443
  repository: ""
  reference: ""

targets:
  - ip: 10.0.0.12
    priority: 100
  - ip: 10.0.0.13
    priority: 90

dns:
  provider: fake
  recordName: registry-proxy.example.com
  ttl: 60
  fake:
    records:
      registry-proxy.example.com/A: 10.0.0.99

The fake provider keeps records in memory and logs every update. It is useful for testing the full VictoriaMetrics query and priority-selection flow without touching external DNS.

Switching Scenarios

Assume the configured targets are:

targets:
  - ip: 10.0.0.12
    priority: 100
  - ip: 10.0.0.13
    priority: 90
  - ip: 10.0.0.14
    priority: 80

An IP is eligible only when both health checks are successful:

sealos_registry_proxy_status{check_type="api",ip="<ip>"} == 1
sealos_registry_proxy_status{check_type="manifest",ip="<ip>"} == 1

Common outcomes:

  • Current DNS points to 10.0.0.13, and 10.0.0.13 becomes unhealthy. If 10.0.0.12 is healthy, the service waits for switchPolicy.unhealthyFor, then updates DNS to 10.0.0.12 because it is the highest-priority healthy target.
  • Current DNS points to 10.0.0.13, and 10.0.0.13 stays healthy. If 10.0.0.12 is also healthy, the service waits for switchPolicy.healthyFor, then switches back to 10.0.0.12 because it has higher priority.
  • Current DNS points to 10.0.0.12, and 10.0.0.12 is healthy. The service keeps DNS unchanged because the current record already points to the selected target.
  • Current DNS points to 10.0.0.12, and all configured targets are unhealthy. The service keeps DNS unchanged and logs no healthy target found.
  • Two healthy targets have the same priority. The service uses switchPolicy.tieBreaker: order picks the first target in targets, and latency picks the lower-latency target from sealos_registry_proxy_response_time_seconds.
  • The selected target is IPv4. The service writes the A record and removes a stale AAAA record for the same dns.recordName; IPv6 selection does the symmetric cleanup.

switchPolicy.tieBreaker controls how targets with the same priority are selected:

switchPolicy:
  tieBreaker: order

order keeps the first healthy target from targets. To select the lower-latency IP for equal priority, enable latency:

switchPolicy:
  tieBreaker: latency

victoriaMetrics.registryEndpointLabel defaults to endpoint. Set it to exported_endpoint when the scrape pipeline renames the registry proxy metric label because the scrape target already uses endpoint.

victoriaMetrics.latencyMetricName defaults to sealos_registry_proxy_response_time_seconds. The latency metric from sealos-state-metric has endpoint, ip, and check_type labels. By default, latency tie-breaking uses the lowest latency sample per IP from all returned check_type values. Use victoriaMetrics.latencyMatchers to narrow the latency query:

victoriaMetrics:
  latencyMetricName: sealos_registry_proxy_response_time_seconds
  latencyMatchers:
    check_type: manifest

VictoriaMetrics authentication can use either a bearer token or basic auth. Values support environment variable expansion:

victoriaMetrics:
  bearerToken: ${VICTORIA_METRICS_BEARER_TOKEN}
victoriaMetrics:
  basicAuth:
    username: ${VICTORIA_METRICS_BASIC_AUTH_USERNAME}
    password: ${VICTORIA_METRICS_BASIC_AUTH_PASSWORD}

For Cloudflare:

dns:
  provider: cloudflare
  recordName: registry-proxy.example.com
  ttl: 60
  cloudflare:
    apiToken: your-token
    zoneId: your-zone-id
    proxied: false

For AliDNS:

dns:
  provider: alidns
  recordName: registry-proxy.example.com
  ttl: 60
  alidns:
    regionId: cn-hangzhou
    accessKeyId: your-access-key-id
    accessKeySecret: your-access-key-secret
    domainName: example.com
    rr: registry-proxy

dns.recordName is the FQDN to maintain. For AliDNS, domainName is the base zone and rr is optional; when empty, the tool derives it from recordName.

Run

go run . --config config.example.yaml --once --dry-run

Long-running mode:

go run . --config config.yaml

Flags override config:

--once      run one reconciliation and exit
--dry-run   select target IP without changing DNS

VictoriaMetrics Query

The tool queries:

sealos_registry_proxy_status{<registryEndpointLabel>="<registry.endpoint>",check_type="api"}
sealos_registry_proxy_status{<registryEndpointLabel>="<registry.endpoint>",check_type="manifest"}

registry.info, registry.repository, and registry.reference are optional. When set, they are added as label matchers. Additional static label matchers can be set under victoriaMetrics.matchers.

TDD Coverage

Tests cover the behaviors that decide correctness:

  • Both api and manifest must be healthy for an IP to be eligible.
  • Highest priority wins among healthy enabled targets.
  • Same-priority targets can use the configured tie-breaker.
  • Failover waits for switchPolicy.unhealthyFor.
  • Switchback waits for switchPolicy.healthyFor.
  • All targets unhealthy keeps DNS unchanged.
  • DNS update is skipped when the record already points to the selected IP.
  • PromQL matcher construction is deterministic.

Run tests:

go test ./...

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages