Repository navigation
Closed
Labels
Description
platform-template/k8s/providers/docker/infrastructure/controllers/coredns/deployment.yaml
Line 1 in 39ac525
| # TODO: Rework CoreDNS deployment into a helm-release |
# TODO: Rework CoreDNS deployment into a helm-release
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
annotations:
deployment.kubernetes.io/revision: "1"
configmap.reloader.stakater.com/reload: "coredns"
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kube-dns
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
k8s-app: kube-dns
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: k8s-app
operator: In
values:
- kube-dns
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- args:
- -conf
- /etc/coredns/Corefile
image: registry.k8s.io/coredns/coredns:v1.14.3@sha256:884b72dd6d2f7d367902af420605e0288dffedb0516ce29330423ae3f8f5c6fa
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 5
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: 8181
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/coredns
name: config-volume
readOnly: true
dnsPolicy: Default
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: coredns
serviceAccountName: coredns
terminationGracePeriodSeconds: 30
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
name: coredns
name: config-volume
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
None yet
Activity
devantler commentedon Jun 8, 2026
Design analysis & decomposition — CoreDNS raw manifests → HelmRelease
I scoped this against the live tree and the upstream chart so the rework lands as a faithful, reviewable swap rather than a behaviour change to system-critical DNS. Summary: expressible with the official chart, but two ownership decisions need your call before implementation — they're not mechanical.
Current state (what we actually manage)
The platform deliberately replaces only the CoreDNS workload + config, leaving the rest distro-managed:
k8s/bases/.../coredns/corefile-config-map.yaml— theCorefileConfigMapk8s/providers/docker/.../coredns/deployment.yaml— the Deployment (image pinnedregistry.k8s.io/coredns/coredns:v1.14.3@sha256:884b…, 2 replicas, anti-affinity,priorityClassName: system-cluster-critical,CriticalAddonsOnly/control-plane tolerations, RO-rootfs +NET_BIND_SERVICE, reloader annotationconfigmap.reloader.stakater.com/reload: coredns)k8s/providers/docker/.../coredns/pod-disruption-budget.yaml—minAvailable: 1There is no Service, ServiceAccount, ClusterRole/Binding in our tree — the k3d/distro bootstrap owns the
kube-dnsService (with its fixed clusterIP that kubelet hands every pod via--cluster-dns), thecorednsServiceAccount, and CoreDNS RBAC.The crux — the upstream chart's default ownership ≠ ours
The official
coredns/corednschart defaults toisClusterService: true(renders the Service askube-dnsinkube-system,k8s-app: kube-dns), which is the right mode. But its defaults differ from our scope:serviceAccount.create: false✅ matches us (uses existingcorednsSA).rbac.create: true✋ the chart would create ClusterRole/Binding we don't currently own.kube-dns, Flux/Helm must adopt that pre-existing Service (it needs themeta.helm.sh/release-name+-namespaceannotations andapp.kubernetes.io/managed-by: Helmlabel, or Helm refuses to take it over), and we must pinservice.clusterIPto the distro's kube-dns IP (k3d default10.43.0.10) so the address kubelet points pods at never changes.These are runtime/live-migration facts that a
helm templatediff cannot prove safe — hence framing them here rather than guessing them in a PR.Proposed values mapping (concrete)
Following the house HelmRelease+HelmRepository pattern (cf.
keda/),coredns/becomes:helm-repository.yaml→https://coredns.github.io/helmhelm-release.yamlvalues:image.repository/tag+image.sha→ keep the exactv1.14.3@sha256:884b…pinreplicaCount: 2,podDisruptionBudget.minAvailable: 1resources(limitsmemory: 170Mi; requestscpu: 100m/memory: 70Mi), thepreferredDuringSchedulinganti-affinity, the two tolerations,priorityClassName: system-cluster-criticalpodAnnotations.configmap.reloader.stakater.com/reload: coredns(note: with the chart owning the ConfigMap, the reloader target name may change — verify, or drop reloader since Helm rolls the Deployment onCorefilechange anyway)servers:→ map the current Corefile block-for-block (health{lameduck 5s},ready,kubernetes … ttl 30,prometheus :9153,forward . /etc/resolv.conf {max_concurrent 1000},cache 30 {disable success/denial cluster.local},loop,reload,loadbalance). This is the fiddliest part — needs a careful diff so semantics are byte-equivalent.service.clusterIP: ${coredns_cluster_ip:=10.43.0.10}(Flux substitution),serviceAccount.create: falseTwo decisions for you (the ownership calls)
kube-dnsService for Helm adoption + pinservice.clusterIP; (b) configure the distro to skip its CoreDNS so the chart owns everything from a clean slate; (c) keep the Service distro-managed and only Helm-manage Deployment+ConfigMap (not cleanly supported by the chart — likely rules the chart out). I lean (a) for templates targeting fresh bootstraps.rbac.create: true(chart takes over ClusterRole/Binding) vsfalse(keep distro-managed). (a) is cleaner long-term but means adopting/colliding with the distro's existing RBAC.Validation plan
helm template coredns coredns/coredns -f values.yamland diff the rendered Deployment/ConfigMap/Service/PDB against the current manifests — confirm image pin, Corefile semantics, resources, probes, anti-affinity, tolerations, clusterIP all match.kube-dns), andkubectl -n kube-system get svc kube-dnskeeps the original clusterIP.Acceptance criteria
coredns/mirrors thekeda/HelmRelease+HelmRepository layout; the rawdeployment.yaml/pod-disruption-budget.yaml/corefile-config-map.yamlare removed.# TODO: Rework CoreDNS deployment into a helm-releaseis gone.Once you pick the Service-adoption + RBAC ownership path, the implementation is mechanical and I can ship it as a
helm template-diff-validated draft. Flagging the chart-cruft cleanup as a separate small win regardless: the rawdeployment.yamlcarrieskubectl get -o yamlexport noise (deployment.kubernetes.io/revision,creationTimestamp: null,schedulerName: default-scheduler, defaultterminationMessage*) that should drop out in the conversion.devantler commentedon Jun 14, 2026
Design pass: CoreDNS → Flux
HelmReleaseScoped this
nextitem with a read-only investigation. Summary, chart mapping, and the gotchas that make this a design-first / incremental change rather than a one-shot rewrite.Why this is worth doing (the real motivation = consistency)
CoreDNS is the only controller under
k8s/.../infrastructure/controllers/still shipped as a hand-rolledDeployment+CorefileConfigMap. Every one of the other ~20 controllers (cert-manager, cilium, metrics-server, kube-prometheus-stack, …) is a FluxHelmReleasewith the repo's standard remediation/PDB/topology conventions. So this is a drift/consistency fix: bring the lone outlier onto the established pattern, and inherit the chart's upgrade path instead of pinning a raw image + Corefile by hand.The live
devantler-tech/platformrepo carries the identical raw-Deployment CoreDNS (k8s/providers/docker/infrastructure/controllers/coredns/deployment.yaml, basecorefile-config-map.yaml, plus a Hetzner Corefile patch). If we convert only the template, the template diverges from platform. The correct order is: prototype + validate onplatform(it has a real cluster + CD), then propagate the provenHelmReleasehere. This template change should land after the platform one so the scaffold mirrors a battle-tested config.Proposed chart mapping (
coredns/coredns)Add
k8s/bases/infrastructure/controllers/coredns/helm-release.yaml(+ aHelmRepository/OCIRepositorysource, per the repo convention), mirroringmetrics-server/helm-release.yaml. Values must reproduce today's behavior exactly:replicaCount: 2;podDisruptionBudget: { minAvailable: 1 }(replaces the standalone PDB).k8sAppLabel: kube-dnsandservice.name: kube-dnsso the Service keeps its identity.service.clusterIPmust be pinned to the cluster DNS IP (10.96.0.10) — the chart otherwise allocates a fresh ClusterIP, which breaks every pod's/etc/resolv.conf. This is the single biggest migration hazard.priorityClassName: system-cluster-critical, theCriticalAddonsOnly+ control-plane tolerations, thek8s-app=kube-dnspodAntiAffinity, resources (req 100m/70Mi, lim 170Mi), and the hardenedsecurityContext(drop ALL, add NET_BIND_SERVICE, readOnlyRootFilesystem, allowPrivilegeEscalation:false) — all expressible via chart values.image/chartversionto the digest already in use (coredns v1.14.3).servers:block — the chart default is close but NOT identical. Our Corefile has a non-default cache block that must be carried verbatim:forward . /etc/resolv.conf { max_concurrent 1000 },kubernetes … { pods insecure; fallthrough …; ttl 30 },health { lameduck 5s },prometheus :9153,ready,loop,reload,loadbalance. The Hetzner provider's Corefile patch must map to a provider-level values override.Validation plan (behavioral DNS correctness is NOT CI-catchable here)
kubeconform/Validate-Scaffold only checks theHelmReleaseCR schema, not the rendered chart — so CI will not catch a wrong Corefile or ClusterIP. Required steps:helm template coredns coredns/coredns --version <v> -f values.yamland diff the renderedCorefilebyte-for-byte against the current ConfigMap.clusterIP: 10.96.0.10.kubernetes.default, an external name) and confirm no pod DNS disruption during the cutover (the chart's Deployment replaces the managed one — watch for adoption vs. recreate).Acceptance criteria
platformCoreDNS runs as aHelmReleasewith the Corefile + ClusterIP + criticality preserved; live DNS verified.helm-release.yaml+ source; docker/hetzner providers via values overrides); rawdeployment.yaml+ standalone PDB removed;corefile-config-map.yamlfolded into chart values.helm template | kubeconform -strictclean; Validate Scaffold green.Recommend tackling
platformfirst; happy to draft that PR once prioritized.refactor: strip kubectl-export cruft from CoreDNS Deployment manifest (…
devantler commentedon Jul 7, 2026
Investigated the migration to scope it safely — two non-obvious couplings make the naive "swap the Deployment for a HelmRelease" break cluster DNS, so recording them here to make this actionable.
Current shape (docker/Talos provider)
k8s/bases/…/coredns/corefile-config-map.yaml— ConfigMapcoredns(the Corefile), shared with the hetzner provider (hetzner only patches it, keeps Talos's own Deployment).k8s/providers/docker/…/coredns/{deployment.yaml,pod-disruption-budget.yaml}— the Deploymentcoredns(+ PDB) that overrides Talos's bootstrapped CoreDNS Deployment (same name/namespacekube-system).kube-dnsService, thecorednsServiceAccount, and CoreDNS RBAC.Sharp edges (each one is a DNS outage if missed)
coredns/corednschart cannot render "Deployment only." It always emits a Service and a ConfigMap (there is noservice.enabled/configmap.enabledtoggle — confirmed against the chart'svalues.yaml). So the HelmRelease must own the whole CoreDNS stack for docker (Deployment + Service + ConfigMap + PDB), which means dropping the shared base ConfigMap from the docker kustomization (base stays hetzner-only) and moving the Corefile into the chart'sservers:/config values.10.96.0.0/12(no override intalos-local/), so kube-dns is deterministically10.96.0.10→ setservice.clusterIP: 10.96.0.10andservice.name: kube-dns. Setk8sAppLabelOverride: kube-dnsso thek8s-app: kube-dnsselector (used by the PDB and kube-proxy) is preserved.cluster.coreDNS.disabledManifests/cluster.coreDNS.disabled: trueto the Talos machine config (talos-local/cluster/…) — a coupled machine-config change in a different layer from the k8s manifests.Proposed first increment
A single draft PR that: adds a
corednsHelmRepository + HelmRelease (values reproducing the current Deployment:replicas: 2, resources, securityContext, anti-affinity, tolerations,priorityClassName: system-cluster-critical, the Corefile,serviceAccount.create: false+namecoredns,rbac.create: false,service.clusterIP: 10.96.0.10,k8sAppLabelOverride: kube-dns), disables Talos's built-in CoreDNS, removes the base ConfigMap from the docker kustomization, and keeps the PDB. Static render (ksail workload validate) proves the manifests; the ClusterIP + Talos-ownership swap need a livetalos-localbring-up to confirm DNS — so this lands as a draft and its promotion gate is a live DNS check.Leaving the hetzner provider untouched (it correctly keeps Talos-managed CoreDNS + a Corefile patch).
feat(coredns): migrate docker-provider CoreDNS from Deployment to Hel…
github-actions commentedon Jul 9, 2026
CoreDNS Deployment -> HelmRelease. Docker-provider only — Talos's own
CoreDNS is disabled via talos-local/cluster/disable-coredns.yaml (cluster.coreDNS.disabled:
true) so this chart owns the whole stack (ServiceAccount + RBAC + Deployment + Service +
ConfigMap + PDB). `coreDNS.disabled` skips Talos's CoreDNS bootstrap entirely on a fresh
cluster (not just the Deployment) — verified against CodeRabbit's upstream research on
#62 — so serviceAccount.create/rbac.create must stay true; the chart owns its own identity
rather than assuming a preexisting Talos-created one. The Service is pinned to the kube-dns
name + Talos's default kube-dns ClusterIP (10.96.0.10, from the 10.96.0.0/12 service
subnet) so it adopts the address kubelet/kube-proxy already expect, instead of Talos's
bootstrapped kube-dns Service. The hetzner provider is untouched (stays on Talos-managed
CoreDNS + a Corefile patch).
platform-template/k8s/providers/docker/infrastructure/controllers/coredns/helm-release.yaml
Lines 1 to 11 in ccfdc01