Add NodeLocal DNSCache platform component

This commit is contained in:
juvdiaz 2026-05-26 23:05:21 -06:00
parent 7b0b060a1c
commit 40662b2b74
3 changed files with 368 additions and 0 deletions

View File

@ -43,6 +43,7 @@ accidentally modify the cluster.
3. `bootstrap/platform` 3. `bootstrap/platform`
- installs a minimal Calico deployment through the Tigera operator - installs a minimal Calico deployment through the Tigera operator
- installs NodeLocal DNSCache for node-local DNS query caching
- installs OpenEBS - installs OpenEBS
- creates `openebs-hostpath-retain` - creates `openebs-hostpath-retain`
- installs Argo CD - installs Argo CD
@ -230,6 +231,15 @@ settings without blocking existing pods during the first rollout. After reports
are clean, individual policies can be promoted to `Enforce` in are clean, individual policies can be promoted to `Enforce` in
`bootstrap/platform/main.tf`. `bootstrap/platform/main.tf`.
## DNS Cache
`bootstrap/platform` installs NodeLocal DNSCache in `kube-system` with
`registry.k8s.io/dns/k8s-dns-node-cache`. The default listens on
`169.254.20.10` and the kube-dns service IP `10.96.0.10`, which keeps the
rollout compatible with the current kube-proxy iptables path without rewriting
kubelet DNS settings across the nodes. Override `nodelocal_dns` if the service
CIDR or upstream DNS servers change.
## Secrets ## Secrets
Use SOPS with age for secrets that need to live in Git. Start from Use SOPS with age for secrets that need to live in Git. Start from

View File

@ -26,6 +26,57 @@ provider "helm" {
} }
} }
locals {
nodelocal_dns_corefile = <<EOT
${var.nodelocal_dns.cluster_domain}:53 {
errors
cache {
success 9984 30
denial 9984 5
}
reload
loop
bind ${var.nodelocal_dns.local_ip} ${var.nodelocal_dns.cluster_dns_ip}
forward . ${var.nodelocal_dns.cluster_dns_ip} {
force_tcp
}
prometheus :9253
health ${var.nodelocal_dns.local_ip}:8080
}
in-addr.arpa:53 {
errors
cache 30
reload
loop
bind ${var.nodelocal_dns.local_ip} ${var.nodelocal_dns.cluster_dns_ip}
forward . ${var.nodelocal_dns.cluster_dns_ip} {
force_tcp
}
prometheus :9253
}
ip6.arpa:53 {
errors
cache 30
reload
loop
bind ${var.nodelocal_dns.local_ip} ${var.nodelocal_dns.cluster_dns_ip}
forward . ${var.nodelocal_dns.cluster_dns_ip} {
force_tcp
}
prometheus :9253
}
.:53 {
errors
cache 30
reload
loop
bind ${var.nodelocal_dns.local_ip} ${var.nodelocal_dns.cluster_dns_ip}
forward . ${join(" ", var.nodelocal_dns.upstream_dns_servers)}
prometheus :9253
}
EOT
}
resource "helm_release" "calico_crds" { resource "helm_release" "calico_crds" {
name = "calico-crds" name = "calico-crds"
repository = var.calico.repository repository = var.calico.repository
@ -176,6 +227,293 @@ EOT
} }
} }
resource "kubernetes_manifest" "nodelocal_dns_service_account" {
for_each = var.nodelocal_dns.enabled ? { enabled = true } : {}
depends_on = [null_resource.calico_ready]
manifest = {
apiVersion = "v1"
kind = "ServiceAccount"
metadata = {
name = "node-local-dns"
namespace = "kube-system"
labels = {
"kubernetes.io/cluster-service" = "true"
"addonmanager.kubernetes.io/mode" = "Reconcile"
"app.kubernetes.io/managed-by" = "opentofu"
"app.kubernetes.io/part-of" = "nodelocal-dns"
"app.kubernetes.io/name" = "node-local-dns"
"homelab.dev/platform-component" = "nodelocal-dns"
"homelab.dev/platform-component-id" = "dns-cache"
}
}
}
}
resource "kubernetes_manifest" "nodelocal_dns_upstream_service" {
for_each = var.nodelocal_dns.enabled ? { enabled = true } : {}
depends_on = [null_resource.calico_ready]
manifest = {
apiVersion = "v1"
kind = "Service"
metadata = {
name = "kube-dns-upstream"
namespace = "kube-system"
labels = {
"k8s-app" = "kube-dns"
"kubernetes.io/cluster-service" = "true"
"addonmanager.kubernetes.io/mode" = "Reconcile"
"kubernetes.io/name" = "KubeDNSUpstream"
}
}
spec = {
ports = [
{
name = "dns"
port = 53
protocol = "UDP"
targetPort = 53
},
{
name = "dns-tcp"
port = 53
protocol = "TCP"
targetPort = 53
},
]
selector = {
"k8s-app" = "kube-dns"
}
}
}
}
resource "kubernetes_manifest" "nodelocal_dns_config_map" {
for_each = var.nodelocal_dns.enabled ? { enabled = true } : {}
depends_on = [null_resource.calico_ready]
manifest = {
apiVersion = "v1"
kind = "ConfigMap"
metadata = {
name = "node-local-dns"
namespace = "kube-system"
labels = {
"addonmanager.kubernetes.io/mode" = "Reconcile"
"app.kubernetes.io/managed-by" = "opentofu"
"app.kubernetes.io/name" = "node-local-dns"
"app.kubernetes.io/part-of" = "nodelocal-dns"
}
}
data = {
Corefile = local.nodelocal_dns_corefile
}
}
}
resource "kubernetes_manifest" "nodelocal_dns_daemonset" {
for_each = var.nodelocal_dns.enabled ? { enabled = true } : {}
depends_on = [
kubernetes_manifest.nodelocal_dns_service_account,
kubernetes_manifest.nodelocal_dns_upstream_service,
kubernetes_manifest.nodelocal_dns_config_map,
]
manifest = {
apiVersion = "apps/v1"
kind = "DaemonSet"
metadata = {
name = "node-local-dns"
namespace = "kube-system"
labels = {
"k8s-app" = "node-local-dns"
"kubernetes.io/cluster-service" = "true"
"addonmanager.kubernetes.io/mode" = "Reconcile"
}
}
spec = {
updateStrategy = {
rollingUpdate = {
maxUnavailable = "10%"
}
}
selector = {
matchLabels = {
"k8s-app" = "node-local-dns"
}
}
template = {
metadata = {
labels = {
"k8s-app" = "node-local-dns"
}
annotations = {
"prometheus.io/port" = "9253"
"prometheus.io/scrape" = "true"
}
}
spec = {
priorityClassName = "system-node-critical"
serviceAccountName = "node-local-dns"
hostNetwork = true
dnsPolicy = "Default"
nodeSelector = {
"kubernetes.io/os" = "linux"
}
tolerations = [
{
key = "CriticalAddonsOnly"
operator = "Exists"
},
{
effect = "NoExecute"
operator = "Exists"
},
{
effect = "NoSchedule"
operator = "Exists"
},
]
containers = [
{
name = "node-cache"
image = var.nodelocal_dns.image
resources = {
requests = {
cpu = "25m"
memory = "5Mi"
}
}
args = [
"-localip",
"${var.nodelocal_dns.local_ip},${var.nodelocal_dns.cluster_dns_ip}",
"-conf",
"/etc/Corefile",
"-upstreamsvc",
"kube-dns-upstream",
]
securityContext = {
capabilities = {
add = ["NET_ADMIN"]
}
}
ports = [
{
containerPort = 53
name = "dns"
protocol = "UDP"
},
{
containerPort = 53
name = "dns-tcp"
protocol = "TCP"
},
{
containerPort = 9253
name = "metrics"
protocol = "TCP"
},
]
livenessProbe = {
httpGet = {
host = var.nodelocal_dns.local_ip
path = "/health"
port = 8080
}
initialDelaySeconds = 60
timeoutSeconds = 5
}
volumeMounts = [
{
mountPath = "/run/xtables.lock"
name = "xtables-lock"
readOnly = false
},
{
mountPath = "/etc/coredns"
name = "config-volume"
},
{
mountPath = "/etc/kube-dns"
name = "kube-dns-config"
},
]
},
]
volumes = [
{
name = "xtables-lock"
hostPath = {
path = "/run/xtables.lock"
type = "FileOrCreate"
}
},
{
name = "kube-dns-config"
configMap = {
name = "kube-dns"
optional = true
}
},
{
name = "config-volume"
configMap = {
name = "node-local-dns"
items = [
{
key = "Corefile"
path = "Corefile.base"
},
]
}
},
]
}
}
}
}
}
resource "kubernetes_manifest" "nodelocal_dns_metrics_service" {
for_each = var.nodelocal_dns.enabled ? { enabled = true } : {}
depends_on = [kubernetes_manifest.nodelocal_dns_daemonset]
manifest = {
apiVersion = "v1"
kind = "Service"
metadata = {
name = "node-local-dns"
namespace = "kube-system"
annotations = {
"prometheus.io/port" = "9253"
"prometheus.io/scrape" = "true"
}
labels = {
"k8s-app" = "node-local-dns"
}
}
spec = {
clusterIP = "None"
ports = [
{
name = "metrics"
port = 9253
targetPort = 9253
},
]
selector = {
"k8s-app" = "node-local-dns"
}
}
}
}
resource "helm_release" "openebs" { resource "helm_release" "openebs" {
depends_on = [null_resource.calico_ready] depends_on = [null_resource.calico_ready]
name = "openebs" name = "openebs"

View File

@ -92,6 +92,26 @@ variable "kyverno" {
} }
} }
variable "nodelocal_dns" {
type = object({
enabled = bool
image = string
local_ip = string
cluster_dns_ip = string
cluster_domain = string
upstream_dns_servers = list(string)
})
default = {
enabled = true
image = "registry.k8s.io/dns/k8s-dns-node-cache:1.26.8"
local_ip = "169.254.20.10"
cluster_dns_ip = "10.96.0.10"
cluster_domain = "cluster.local"
upstream_dns_servers = ["1.1.1.1", "8.8.8.8"]
}
}
variable "observability" { variable "observability" {
type = object({ type = object({
namespace = string namespace = string