Simple DNS-based service discovery for multi-cluster Kubernetes
DNSMesh watches pods and services across multiple Kubernetes clusters and serves DNS A records for resources with specific annotations. It enables seamless cross-cluster service discovery using a single DNS query.
helm install dnsmesh oci://ghcr.io/tommyskeff/charts/dnsmesh -n dnsmesh --create-namespacedocker pull ghcr.io/tommyskeff/dnsmesh:latest<service>.<realm>.<domain>
- service - The service name (from
dnsmesh.tommyjs.dev/serviceannotation) - realm - The realm/environment (from
dnsmesh.tommyjs.dev/realmannotation) - domain - Base domain (from Helm
dns.domain, default:clusterset.local)
Example: nginx-test.prod-na.clusterset.local
- Multi-cluster support - Watch resources across multiple Kubernetes clusters
- Annotation-based discovery - Opt-in via
dnsmesh.tommyjs.dev/expose: "true" - Dynamic placeholders - Use
{name},{ordinal},{namespace}, etc. in annotations - IPv4 and IPv6 support - Serves both A and AAAA records
- Random load balancing - Multiple IPs for the same name are returned randomly
- Configurable TTL - Control DNS caching behavior
- Health endpoints -
/healthzand/readyzfor Kubernetes probes - Exponential backoff - Graceful handling of cluster connectivity issues
DNSMesh reads cluster configuration from a Kubernetes secret. The secret must contain a config key with JSON in this format:
{
"clusters": [
{
"name": "na-prod",
"apiServer": "https://10.20.1.1:6443",
"caCert": "<CA_CERTIFICATE_PEM>",
"token": "<SERVICE_ACCOUNT_TOKEN>"
}
]
}| Field | Description |
|---|---|
clusters[].name |
Unique identifier for the cluster |
clusters[].apiServer |
Kubernetes API server URL |
clusters[].caCert |
CA certificate for TLS verification |
clusters[].token |
ServiceAccount bearer token |
Add these annotations to resources you want to expose via DNS:
annotations:
dnsmesh.tommyjs.dev/expose: "true"
dnsmesh.tommyjs.dev/service: "my-app"
dnsmesh.tommyjs.dev/realm: "prod-na"This creates DNS records like my-app.prod-na.clusterset.local.
The service and realm annotations support dynamic placeholders that are resolved at runtime:
| Placeholder | Description | Example Value |
|---|---|---|
{name} |
Name of the pod or service | redis-cluster-0 |
{ip} |
IP address in dash form | 10-0-0-1 |
{ordinal} |
StatefulSet pod ordinal (empty for non-StatefulSet pods/services) | 0, 1, 2 |
{namespace} |
Kubernetes namespace | production |
{kind} |
Resource type | pod or service |
{node} |
Node name (empty for services) | node-1 |
StatefulSet with per-pod DNS names:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
template:
metadata:
annotations:
dnsmesh.tommyjs.dev/expose: "true"
dnsmesh.tommyjs.dev/service: "redis-{ordinal}"
dnsmesh.tommyjs.dev/realm: "prod"This creates DNS records like redis-0.prod.clusterset.local, redis-1.prod.clusterset.local, etc.
Namespace-based realm:
annotations:
dnsmesh.tommyjs.dev/expose: "true"
dnsmesh.tommyjs.dev/service: "my-app"
dnsmesh.tommyjs.dev/realm: "{namespace}"This creates DNS records using the Kubernetes namespace as the realm, e.g., my-app.production.clusterset.local.
Combined placeholders:
annotations:
dnsmesh.tommyjs.dev/expose: "true"
dnsmesh.tommyjs.dev/service: "{namespace}-{name}"
dnsmesh.tommyjs.dev/realm: "{kind}"This creates DNS records like production-my-app-0.pod.clusterset.local.
To forward DNS queries for your domain to DNSMesh, add a server block to your CoreDNS ConfigMap:
clusterset.local:53 {
errors
cache 30
forward . <DNSMESH_SERVICE_IP>
}
See charts/dnsmesh/values.yaml for all options. Key settings:
| Value | Default | Description |
|---|---|---|
dns.domain |
clusterset.local |
Base domain for all DNS records |
dns.ttl |
30 |
DNS record TTL in seconds |
logLevel |
info |
Log level: debug, info, warn, error |
service.clusterIP |
"" |
Static ClusterIP for stable CoreDNS config |
go build -o dnsmesh ./cmd/dnsmesh
go test ./...
docker build -t dnsmesh:local .
helm upgrade --install dnsmesh ./charts/dnsmesh -n dnsmesh \
--set image.repository=dnsmesh --set image.tag=localThis project uses release-please to manage releases.
- App releases: Tagged as
v<version>(e.g.v0.1.0) - Chart releases: Tagged as
dnsmesh-chart-v<version>(e.g.dnsmesh-chart-v0.1.0)
Apache License 2.0