apiVersion: apps/v1 kind: StatefulSet metadata: name: fancy-dns namespace: dns labels: app.kubernetes.io/name: fancy-dns spec: serviceName: fancy-dns replicas: 2 podManagementPolicy: Parallel selector: matchLabels: app.kubernetes.io/name: fancy-dns template: metadata: labels: app.kubernetes.io/name: fancy-dns annotations: # A config change is a SIGHUP, not a restart: the server re-reads the # file and hot-swaps every component. reloader.stakater.com/auto: "true" spec: # DNS on a non-privileged port needs NET_BIND_SERVICE on Linux, or # hostNetwork. hostNetwork is used here so the node's port 53 is served # directly and the load balancer does not add a hop. hostNetwork: true dnsPolicy: Default securityContext: runAsNonRoot: true runAsUser: 65532 runAsGroup: 65532 fsGroup: 65532 seccompProfile: type: RuntimeDefault topologySpreadConstraints: - maxSkew: 1 topologyKey: kubernetes.io/hostname whenUnsatisfiable: ScheduleAnyway labelSelector: matchLabels: app.kubernetes.io/name: fancy-dns containers: - name: fancy-dns image: fancydns/fancy-dns:latest imagePullPolicy: IfNotPresent args: - -config - /etc/fancy-dns/config.yaml ports: - name: dns-udp containerPort: 53 protocol: UDP - name: dns-tcp containerPort: 53 protocol: TCP - name: dot containerPort: 853 protocol: TCP - name: doh containerPort: 443 protocol: TCP - name: admin containerPort: 8080 protocol: TCP securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] # Liveness: the process is up. /api/healthz is unauthenticated on # purpose, because a probe cannot carry an API key. livenessProbe: httpGet: path: /api/healthz port: admin initialDelaySeconds: 5 periodSeconds: 20 # Readiness: the listener is actually bound, so the Service does not # send traffic to a pod that is still binding. readinessProbe: httpGet: path: /api/healthz port: admin initialDelaySeconds: 3 periodSeconds: 5 failureThreshold: 2 # Startup: binding :53 and fetching the first blocklist can take a # while on a cold node, so give it room before the liveness probe # starts killing it. startupProbe: httpGet: path: /api/healthz port: admin periodSeconds: 3 failureThreshold: 30 resources: requests: cpu: 250m memory: 256Mi limits: # No CPU limit on purpose: a DNS server that gets throttled # turns into dropped queries, which is worse than a noisy # neighbour. The memory limit is the one that matters, because # the cache is sized in entries. memory: 1Gi volumeMounts: - name: config mountPath: /etc/fancy-dns/config.yaml subPath: config.yaml readOnly: true - name: zones mountPath: /etc/fancy-dns/zones readOnly: true - name: tls mountPath: /etc/fancy-dns/tls readOnly: true - name: data mountPath: /var/lib/fancy-dns - name: tmp mountPath: /tmp volumes: - name: config configMap: name: fancy-dns-config - name: zones configMap: name: fancy-dns-zones optional: true - name: tls secret: secretName: fancy-dns-tls - name: data emptyDir: {} - name: tmp emptyDir: {} --- apiVersion: v1 kind: Service metadata: name: fancy-dns namespace: dns labels: app.kubernetes.io/name: fancy-dns spec: clusterIP: None selector: app.kubernetes.io/name: fancy-dns ports: - name: dns-udp port: 53 targetPort: dns-udp protocol: UDP - name: dns-tcp port: 53 targetPort: dns-tcp protocol: TCP - name: dot port: 853 targetPort: dot - name: doh port: 443 targetPort: doh - name: admin port: 8080 targetPort: admin --- # The admin console and the DoH endpoint share a port only if the ingress # terminates TLS; when it does, this routes /api and /dns-query to the same # backend. Kept separate from the Service above because a headless Service # cannot front an Ingress. apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: fancy-dns namespace: dns annotations: nginx.ingress.kubernetes.io/backend-protocol: "HTTP" nginx.ingress.kubernetes.io/proxy-buffering: "off" nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" spec: rules: - host: dns.example.com http: paths: - path: /api pathType: Prefix backend: service: name: fancy-dns port: name: admin - path: /dns-query pathType: Prefix backend: service: name: fancy-dns port: name: doh - path: / pathType: Prefix backend: service: name: fancy-dns port: name: admin tls: - hosts: [dns.example.com] secretName: fancy-dns-tls --- apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: fancy-dns namespace: dns spec: minAvailable: 1 selector: matchLabels: app.kubernetes.io/name: fancy-dns --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: fancy-dns namespace: dns spec: scaleTargetRef: apiVersion: apps/v1 kind: StatefulSet name: fancy-dns minReplicas: 2 maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 behavior: scaleDown: # A DNS server that flaps is worse than one that is briefly oversized: # every scale-down drops the local cache and the blocklist lease. stabilizationWindowSeconds: 600