DaemonSets ensure that specific pods run on all (or selected) nodes in a Kubernetes cluster, making them ideal for node-level services like logging agents, monitoring exporters, network proxies, and storage daemons. However, poorly configured DaemonSets can significantly impact node performance and cluster stability. This comprehensive guide explores production-ready resource optimization strategies for DaemonSets.

Understanding DaemonSet Resource Impact

DaemonSets consume resources on every node, making resource optimization critical for:

  • Cluster Scalability: Resource-heavy DaemonSets limit available capacity for application workloads
  • Node Stability: Excessive resource consumption can cause node pressure and evictions
  • Cost Efficiency: DaemonSet resources scale linearly with cluster size
  • Performance: Poorly tuned DaemonSets can impact application performance

Resource Request and Limit Optimization

Baseline Monitoring DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
  labels:
    app: node-exporter
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9100"
    spec:
      hostNetwork: true
      hostPID: true
      hostIPC: false
      containers:
      - name: node-exporter
        image: prom/node-exporter:v1.7.0
        args:
        - --path.procfs=/host/proc
        - --path.sysfs=/host/sys
        - --path.rootfs=/host/root
        - --collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)
        - --no-collector.ipvs
        ports:
        - containerPort: 9100
          protocol: TCP
          name: metrics
        resources:
          requests:
            memory: "50Mi"
            cpu: "50m"
          limits:
            memory: "100Mi"
            cpu: "200m"
        volumeMounts:
        - name: proc
          mountPath: /host/proc
          readOnly: true
        - name: sys
          mountPath: /host/sys
          readOnly: true
        - name: root
          mountPath: /host/root
          mountPropagation: HostToContainer
          readOnly: true
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 65534
      volumes:
      - name: proc
        hostPath:
          path: /proc
      - name: sys
        hostPath:
          path: /sys
      - name: root
        hostPath:
          path: /
      tolerations:
      - effect: NoSchedule
        operator: Exists
      - effect: NoExecute
        operator: Exists
      priorityClassName: system-node-critical

Optimized Logging DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: logging
  labels:
    app: fluentd
spec:
  selector:
    matchLabels:
      app: fluentd
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: fluentd
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "24231"
    spec:
      serviceAccountName: fluentd
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:v1.16-debian-elasticsearch8-1
        env:
        - name: FLUENT_ELASTICSEARCH_HOST
          value: "elasticsearch.logging.svc.cluster.local"
        - name: FLUENT_ELASTICSEARCH_PORT
          value: "9200"
        - name: FLUENT_ELASTICSEARCH_SCHEME
          value: "http"
        - name: FLUENT_UID
          value: "0"
        # Buffer configuration for memory optimization
        - name: FLUENT_BUFFER_CHUNK_LIMIT_SIZE
          value: "2M"
        - name: FLUENT_BUFFER_QUEUE_LIMIT_LENGTH
          value: "8"
        - name: FLUENT_BUFFER_TOTAL_LIMIT_SIZE
          value: "512M"
        - name: FLUENT_BUFFER_OVERFLOW_ACTION
          value: "drop_oldest_chunk"
        resources:
          requests:
            memory: "200Mi"
            cpu: "100m"
          limits:
            memory: "500Mi"
            cpu: "500m"
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: true
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
        - name: fluentd-config
          mountPath: /fluentd/etc/fluent.conf
          subPath: fluent.conf
        - name: buffer
          mountPath: /var/log/fluentd-buffers
        livenessProbe:
          httpGet:
            path: /metrics
            port: 24231
          initialDelaySeconds: 30
          periodSeconds: 30
          timeoutSeconds: 5
        readinessProbe:
          httpGet:
            path: /metrics
            port: 24231
          initialDelaySeconds: 10
          periodSeconds: 10
          timeoutSeconds: 5
        securityContext:
          privileged: false
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
            add:
            - DAC_OVERRIDE
            - CHOWN
            - FOWNER
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: fluentd-config
        configMap:
          name: fluentd-config
      - name: buffer
        emptyDir: {}
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      - key: node-role.kubernetes.io/control-plane
        effect: NoSchedule
      priorityClassName: system-node-critical
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
  namespace: logging
data:
  fluent.conf: |
    <system>
      log_level info
      workers 2
    </system>

    <source>
      @type tail
      @id in_tail_container_logs
      path /var/log/containers/*.log
      pos_file /var/log/fluentd-containers.log.pos
      tag kubernetes.*
      read_from_head true
      <parse>
        @type json
        time_format %Y-%m-%dT%H:%M:%S.%NZ
      </parse>
      # Optimize read performance
      read_lines_limit 1000
      read_bytes_limit_per_second 8388608  # 8MB/s
      skip_refresh_on_startup true
    </source>

    <filter kubernetes.**>
      @type kubernetes_metadata
      @id filter_kube_metadata
      # Optimize API calls
      cache_size 10000
      cache_ttl 3600
      skip_labels true
      skip_container_metadata false
      skip_master_url true
      skip_namespace_metadata false
    </filter>

    <match kubernetes.**>
      @type elasticsearch
      @id out_es
      host "#{ENV['FLUENT_ELASTICSEARCH_HOST']}"
      port "#{ENV['FLUENT_ELASTICSEARCH_PORT']}"
      scheme "#{ENV['FLUENT_ELASTICSEARCH_SCHEME']}"

      # Buffer configuration
      <buffer>
        @type file
        path /var/log/fluentd-buffers/kubernetes.system.buffer
        flush_mode interval
        flush_interval 10s
        flush_at_shutdown true
        retry_type exponential_backoff
        retry_timeout 1h
        retry_max_interval 30s
        chunk_limit_size "#{ENV['FLUENT_BUFFER_CHUNK_LIMIT_SIZE']}"
        queue_limit_length "#{ENV['FLUENT_BUFFER_QUEUE_LIMIT_LENGTH']}"
        total_limit_size "#{ENV['FLUENT_BUFFER_TOTAL_LIMIT_SIZE']}"
        overflow_action "#{ENV['FLUENT_BUFFER_OVERFLOW_ACTION']}"
      </buffer>

      # Performance optimization
      bulk_message_request_threshold 1048576  # 1MB
      compress_json true
    </match>

Node Selection and Affinity

Selective Node Targeting

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: gpu-device-plugin
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: gpu-device-plugin
  template:
    metadata:
      labels:
        app: gpu-device-plugin
    spec:
      # Only run on nodes with GPUs
      nodeSelector:
        accelerator: nvidia-gpu
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node.kubernetes.io/instance-type
                operator: In
                values:
                - p3.2xlarge
                - p3.8xlarge
                - p3.16xlarge
                - p4d.24xlarge
      containers:
      - name: nvidia-device-plugin
        image: nvidia/k8s-device-plugin:v0.14.3
        resources:
          requests:
            memory: "50Mi"
            cpu: "50m"
          limits:
            memory: "100Mi"
            cpu: "100m"
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]
        volumeMounts:
        - name: device-plugin
          mountPath: /var/lib/kubelet/device-plugins
      volumes:
      - name: device-plugin
        hostPath:
          path: /var/lib/kubelet/device-plugins
      tolerations:
      - key: nvidia.com/gpu
        operator: Exists
        effect: NoSchedule

Zone-Aware DaemonSet Deployment

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: zone-aware-monitor
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: zone-aware-monitor
  template:
    metadata:
      labels:
        app: zone-aware-monitor
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values:
                - us-east-1a
                - us-east-1b
                - us-east-1c
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            preference:
              matchExpressions:
              - key: node-role.kubernetes.io/worker
                operator: Exists
      containers:
      - name: monitor
        image: monitoring/zone-aware-monitor:v1.0
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: NODE_ZONE
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['topology.kubernetes.io/zone']
        resources:
          requests:
            memory: "100Mi"
            cpu: "100m"
          limits:
            memory: "200Mi"
            cpu: "200m"

Advanced Resource Management

QoS-Aware DaemonSet Configuration

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: qos-optimized-daemonset
  namespace: system
spec:
  selector:
    matchLabels:
      app: qos-optimized
  template:
    metadata:
      labels:
        app: qos-optimized
    spec:
      # Guaranteed QoS - requests equal limits
      containers:
      - name: critical-service
        image: critical/service:v1.0
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "256Mi"
            cpu: "250m"
      # System-critical priority
      priorityClassName: system-node-critical
      tolerations:
      - effect: NoSchedule
        operator: Exists
      - effect: NoExecute
        operator: Exists
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: burstable-daemonset
  namespace: system
spec:
  selector:
    matchLabels:
      app: burstable-service
  template:
    metadata:
      labels:
        app: burstable-service
    spec:
      # Burstable QoS - limits > requests
      containers:
      - name: monitoring-agent
        image: monitoring/agent:v1.0
        resources:
          requests:
            memory: "100Mi"
            cpu: "50m"
          limits:
            memory: "500Mi"
            cpu: "1000m"
      priorityClassName: system-cluster-critical

CPU Management Policies

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: cpu-pinned-daemonset
  namespace: system
spec:
  selector:
    matchLabels:
      app: cpu-pinned
  template:
    metadata:
      labels:
        app: cpu-pinned
      annotations:
        # Request specific CPU policy
        cpu-manager.kubernetes.io/policy: "static"
    spec:
      containers:
      - name: latency-sensitive-app
        image: latency/sensitive:v1.0
        resources:
          requests:
            memory: "1Gi"
            cpu: "2000m"  # Must be whole number for CPU pinning
          limits:
            memory: "1Gi"
            cpu: "2000m"
        securityContext:
          capabilities:
            add:
            - SYS_NICE
      nodeSelector:
        cpu-manager-policy: static
      priorityClassName: system-node-critical

Memory Optimization Strategies

Memory-Efficient Caching DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: caching-proxy
  namespace: system
spec:
  selector:
    matchLabels:
      app: caching-proxy
  template:
    metadata:
      labels:
        app: caching-proxy
    spec:
      containers:
      - name: proxy
        image: haproxy:2.8-alpine
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        volumeMounts:
        - name: config
          mountPath: /usr/local/etc/haproxy
        - name: cache
          mountPath: /var/cache/haproxy
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
      # Memory limits for cache volume
      volumes:
      - name: config
        configMap:
          name: haproxy-config
      - name: cache
        emptyDir:
          sizeLimit: 1Gi  # Limit cache size
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: haproxy-config
  namespace: system
data:
  haproxy.cfg: |
    global
      maxconn 2000
      # Memory tuning
      tune.bufsize 16384
      tune.maxrewrite 1024

    defaults
      mode http
      timeout connect 5s
      timeout client 50s
      timeout server 50s

    frontend http_front
      bind *:80
      default_backend http_back

    backend http_back
      balance roundrobin
      # Enable caching
      http-request cache-use cache
      http-response cache-store cache

    cache cache
      total-max-size 512m
      max-object-size 1m
      max-age 300

OOM Prevention Configuration

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: oom-protected
  namespace: system
spec:
  selector:
    matchLabels:
      app: oom-protected
  template:
    metadata:
      labels:
        app: oom-protected
    spec:
      containers:
      - name: app
        image: app/protected:v1.0
        resources:
          requests:
            memory: "500Mi"
            cpu: "200m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        env:
        # Language-specific memory limits
        - name: GOMEMLIMIT
          value: "900MiB"  # Go memory limit (90% of container limit)
        - name: NODE_OPTIONS
          value: "--max-old-space-size=900"  # Node.js heap size in MB
        - name: JAVA_OPTS
          value: "-Xmx900m -Xms500m -XX:MaxMetaspaceSize=128m"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        # OOM score adjustment
        securityContext:
          allowPrivilegeEscalation: false
      # Use guaranteed QoS for critical DaemonSets
      priorityClassName: system-node-critical

Network Optimization

Network Policy for DaemonSets

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: daemonset-network-policy
  namespace: monitoring
spec:
  podSelector:
    matchLabels:
      app: node-exporter
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    - podSelector:
        matchLabels:
          app: prometheus
    ports:
    - protocol: TCP
      port: 9100
  egress:
  # Allow DNS
  - to:
    - namespaceSelector:
        matchLabels:
          name: kube-system
    ports:
    - protocol: UDP
      port: 53
  # Allow access to Kubernetes API
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          component: apiserver
    ports:
    - protocol: TCP
      port: 6443

Host Network Optimization

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: network-optimized
  namespace: system
spec:
  selector:
    matchLabels:
      app: network-optimized
  template:
    metadata:
      labels:
        app: network-optimized
    spec:
      # Use host network for maximum performance
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - name: network-agent
        image: network/agent:v1.0
        ports:
        - containerPort: 9999
          hostPort: 9999
          protocol: TCP
        resources:
          requests:
            memory: "100Mi"
            cpu: "100m"
          limits:
            memory: "200Mi"
            cpu: "500m"
        securityContext:
          capabilities:
            add:
            - NET_ADMIN
            - NET_RAW
          privileged: false
      tolerations:
      - effect: NoSchedule
        operator: Exists

Storage Optimization

Ephemeral Storage Management

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: storage-optimized
  namespace: system
spec:
  selector:
    matchLabels:
      app: storage-optimized
  template:
    metadata:
      labels:
        app: storage-optimized
    spec:
      containers:
      - name: app
        image: app/storage:v1.0
        resources:
          requests:
            memory: "200Mi"
            cpu: "100m"
            ephemeral-storage: "1Gi"
          limits:
            memory: "500Mi"
            cpu: "500m"
            ephemeral-storage: "5Gi"
        volumeMounts:
        - name: cache
          mountPath: /cache
        - name: tmp
          mountPath: /tmp
        - name: logs
          mountPath: /var/log/app
      volumes:
      - name: cache
        emptyDir:
          sizeLimit: 2Gi
      - name: tmp
        emptyDir:
          medium: Memory  # Use memory for temp files
          sizeLimit: 100Mi
      - name: logs
        hostPath:
          path: /var/log/daemonset/app
          type: DirectoryOrCreate

Log Rotation for DaemonSets

apiVersion: v1
kind: ConfigMap
metadata:
  name: logrotate-config
  namespace: system
data:
  logrotate.conf: |
    /var/log/app/*.log {
      daily
      rotate 7
      compress
      delaycompress
      missingok
      notifempty
      create 0640 nobody nogroup
      sharedscripts
      maxsize 100M
      postrotate
        killall -SIGUSR1 app || true
      endscript
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: app-with-logrotate
  namespace: system
spec:
  selector:
    matchLabels:
      app: app-logrotate
  template:
    metadata:
      labels:
        app: app-logrotate
    spec:
      containers:
      - name: app
        image: app:v1.0
        resources:
          requests:
            memory: "200Mi"
            cpu: "100m"
          limits:
            memory: "500Mi"
            cpu: "500m"
        volumeMounts:
        - name: logs
          mountPath: /var/log/app
      - name: logrotate
        image: blacklabelops/logrotate:1.3
        env:
        - name: LOGS_DIRECTORIES
          value: "/var/log/app"
        - name: LOGROTATE_INTERVAL
          value: "hourly"
        resources:
          requests:
            memory: "10Mi"
            cpu: "10m"
          limits:
            memory: "50Mi"
            cpu: "50m"
        volumeMounts:
        - name: logs
          mountPath: /var/log/app
        - name: logrotate-config
          mountPath: /etc/logrotate.d
      volumes:
      - name: logs
        hostPath:
          path: /var/log/app
          type: DirectoryOrCreate
      - name: logrotate-config
        configMap:
          name: logrotate-config

Update Strategies

Rolling Update with Max Unavailable

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: rolling-update-daemonset
  namespace: system
spec:
  selector:
    matchLabels:
      app: rolling-update
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 2  # Update 2 nodes at a time
      maxSurge: 0  # DaemonSets don't support surge
  template:
    metadata:
      labels:
        app: rolling-update
    spec:
      containers:
      - name: app
        image: app:v2.0
        resources:
          requests:
            memory: "200Mi"
            cpu: "100m"
          limits:
            memory: "500Mi"
            cpu: "500m"
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          failureThreshold: 3

Controlled Update with OnDelete

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ondelete-daemonset
  namespace: system
spec:
  selector:
    matchLabels:
      app: ondelete
  updateStrategy:
    type: OnDelete  # Manual control over updates
  template:
    metadata:
      labels:
        app: ondelete
    spec:
      containers:
      - name: app
        image: app:v2.0
        resources:
          requests:
            memory: "200Mi"
            cpu: "100m"
          limits:
            memory: "500Mi"
            cpu: "500m"

Update script for OnDelete strategy:

#!/bin/bash
# controlled-daemonset-update.sh

set -e

NAMESPACE="system"
DAEMONSET="ondelete-daemonset"
UPDATE_INTERVAL=30  # seconds between node updates

echo "Starting controlled DaemonSet update..."

# Get all pods
PODS=$(kubectl get pods -n $NAMESPACE -l app=ondelete -o jsonpath='{.items[*].metadata.name}')

for POD in $PODS; do
  NODE=$(kubectl get pod $POD -n $NAMESPACE -o jsonpath='{.spec.nodeName}')

  echo "Updating pod $POD on node $NODE..."

  # Delete pod to trigger update
  kubectl delete pod $POD -n $NAMESPACE

  # Wait for new pod to be ready
  echo "Waiting for new pod to be ready..."
  kubectl wait --for=condition=ready pod -l app=ondelete -n $NAMESPACE --field-selector spec.nodeName=$NODE --timeout=300s

  echo "Pod on node $NODE updated successfully"
  echo "Waiting $UPDATE_INTERVAL seconds before next update..."
  sleep $UPDATE_INTERVAL
done

echo "DaemonSet update complete!"

Monitoring and Metrics

DaemonSet Metrics Exporter

apiVersion: v1
kind: ConfigMap
metadata:
  name: daemonset-metrics
  namespace: monitoring
data:
  collect-metrics.sh: |
    #!/bin/bash
    # Collect DaemonSet resource metrics

    while true; do
      NAMESPACE=${NAMESPACE:-system}
      DAEMONSET=${DAEMONSET}

      # Get DaemonSet status
      DESIRED=$(kubectl get daemonset $DAEMONSET -n $NAMESPACE -o jsonpath='{.status.desiredNumberScheduled}')
      CURRENT=$(kubectl get daemonset $DAEMONSET -n $NAMESPACE -o jsonpath='{.status.currentNumberScheduled}')
      READY=$(kubectl get daemonset $DAEMONSET -n $NAMESPACE -o jsonpath='{.status.numberReady}')
      AVAILABLE=$(kubectl get daemonset $DAEMONSET -n $NAMESPACE -o jsonpath='{.status.numberAvailable}')

      # Calculate resource usage across all pods
      TOTAL_CPU_REQUEST=0
      TOTAL_CPU_LIMIT=0
      TOTAL_MEM_REQUEST=0
      TOTAL_MEM_LIMIT=0

      PODS=$(kubectl get pods -n $NAMESPACE -l app=$DAEMONSET -o json)

      # Extract and sum resource values
      TOTAL_CPU_REQUEST=$(echo "$PODS" | jq -r '.items[].spec.containers[].resources.requests.cpu' | grep -v null | sed 's/m$//' | awk '{sum+=$1} END {print sum}')
      TOTAL_CPU_LIMIT=$(echo "$PODS" | jq -r '.items[].spec.containers[].resources.limits.cpu' | grep -v null | sed 's/m$//' | awk '{sum+=$1} END {print sum}')

      # Write Prometheus metrics
      cat <<EOF > /metrics/daemonset_metrics.prom
# HELP daemonset_desired_pods Number of desired pods
# TYPE daemonset_desired_pods gauge
daemonset_desired_pods{namespace="$NAMESPACE",daemonset="$DAEMONSET"} $DESIRED

# HELP daemonset_current_pods Number of current pods
# TYPE daemonset_current_pods gauge
daemonset_current_pods{namespace="$NAMESPACE",daemonset="$DAEMONSET"} $CURRENT

# HELP daemonset_ready_pods Number of ready pods
# TYPE daemonset_ready_pods gauge
daemonset_ready_pods{namespace="$NAMESPACE",daemonset="$DAEMONSET"} $READY

# HELP daemonset_available_pods Number of available pods
# TYPE daemonset_available_pods gauge
daemonset_available_pods{namespace="$NAMESPACE",daemonset="$DAEMONSET"} $AVAILABLE

# HELP daemonset_cpu_requests_total Total CPU requests in millicores
# TYPE daemonset_cpu_requests_total gauge
daemonset_cpu_requests_total{namespace="$NAMESPACE",daemonset="$DAEMONSET"} ${TOTAL_CPU_REQUEST:-0}

# HELP daemonset_cpu_limits_total Total CPU limits in millicores
# TYPE daemonset_cpu_limits_total gauge
daemonset_cpu_limits_total{namespace="$NAMESPACE",daemonset="$DAEMONSET"} ${TOTAL_CPU_LIMIT:-0}
EOF

      sleep 30
    done
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: daemonset-monitor
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: daemonset-exporter
  endpoints:
  - port: metrics
    interval: 30s
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: daemonset-alerts
  namespace: monitoring
spec:
  groups:
  - name: daemonset
    interval: 30s
    rules:
    - alert: DaemonSetNotScheduled
      expr: |
        daemonset_desired_pods - daemonset_current_pods > 0
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "DaemonSet {{ $labels.daemonset }} has unscheduled pods"
        description: "DaemonSet {{ $labels.namespace }}/{{ $labels.daemonset }} has {{ $value }} pods not scheduled for more than 5 minutes."

    - alert: DaemonSetPodsNotReady
      expr: |
        (daemonset_desired_pods - daemonset_ready_pods) / daemonset_desired_pods > 0.1
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "DaemonSet {{ $labels.daemonset }} has pods not ready"
        description: "{{ $value | humanizePercentage }} of DaemonSet {{ $labels.namespace }}/{{ $labels.daemonset }} pods are not ready."

    - alert: DaemonSetHighCPUUsage
      expr: |
        rate(container_cpu_usage_seconds_total{pod=~".*daemonset.*"}[5m]) > 0.8
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "DaemonSet pod {{ $labels.pod }} has high CPU usage"
        description: "DaemonSet pod {{ $labels.namespace }}/{{ $labels.pod }} is using {{ $value | humanizePercentage }} of CPU."

    - alert: DaemonSetHighMemoryUsage
      expr: |
        container_memory_working_set_bytes{pod=~".*daemonset.*"} / container_spec_memory_limit_bytes{pod=~".*daemonset.*"} > 0.9
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "DaemonSet pod {{ $labels.pod }} has high memory usage"
        description: "DaemonSet pod {{ $labels.namespace }}/{{ $labels.pod }} is using {{ $value | humanizePercentage }} of memory limit."

Resource Profiling Script

#!/bin/bash
# profile-daemonset-resources.sh

set -e

NAMESPACE=${1:-system}
DAEMONSET=${2:-node-exporter}
DURATION=${3:-300}  # 5 minutes

echo "Profiling DaemonSet $NAMESPACE/$DAEMONSET for $DURATION seconds..."

# Create output directory
OUTPUT_DIR="/tmp/daemonset-profile-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$OUTPUT_DIR"

# Get all pods
PODS=$(kubectl get pods -n $NAMESPACE -l app=$DAEMONSET -o jsonpath='{.items[*].metadata.name}')

echo "Found pods: $PODS"

# Collect metrics for each pod
for POD in $PODS; do
  echo "Profiling pod $POD..."

  # Create pod directory
  POD_DIR="$OUTPUT_DIR/$POD"
  mkdir -p "$POD_DIR"

  # Collect resource usage over time
  (
    for i in $(seq 1 $((DURATION/10))); do
      timestamp=$(date +%s)

      # Get current usage
      cpu=$(kubectl top pod $POD -n $NAMESPACE --no-headers | awk '{print $2}')
      memory=$(kubectl top pod $POD -n $NAMESPACE --no-headers | awk '{print $3}')

      echo "$timestamp,$cpu,$memory" >> "$POD_DIR/usage.csv"

      sleep 10
    done
  ) &
done

# Wait for profiling to complete
echo "Collecting data for $DURATION seconds..."
wait

# Analyze results
echo "Generating report..."

REPORT="$OUTPUT_DIR/report.txt"
{
  echo "DaemonSet Resource Profile Report"
  echo "=================================="
  echo "Namespace: $NAMESPACE"
  echo "DaemonSet: $DAEMONSET"
  echo "Duration: $DURATION seconds"
  echo "Timestamp: $(date)"
  echo ""

  for POD in $PODS; do
    echo "Pod: $POD"
    echo "----------"

    if [ -f "$OUTPUT_DIR/$POD/usage.csv" ]; then
      # Calculate averages
      avg_cpu=$(awk -F, '{sum+=$2; count++} END {print sum/count}' "$OUTPUT_DIR/$POD/usage.csv")
      avg_mem=$(awk -F, '{sum+=$3; count++} END {print sum/count}' "$OUTPUT_DIR/$POD/usage.csv")

      # Calculate peaks
      peak_cpu=$(awk -F, '{if($2>max){max=$2}} END {print max}' "$OUTPUT_DIR/$POD/usage.csv")
      peak_mem=$(awk -F, '{if($3>max){max=$3}} END {print max}' "$OUTPUT_DIR/$POD/usage.csv")

      echo "Average CPU: $avg_cpu"
      echo "Peak CPU: $peak_cpu"
      echo "Average Memory: $avg_mem"
      echo "Peak Memory: $peak_mem"

      # Get resource requests/limits
      requests=$(kubectl get pod $POD -n $NAMESPACE -o json | jq -r '.spec.containers[0].resources.requests')
      limits=$(kubectl get pod $POD -n $NAMESPACE -o json | jq -r '.spec.containers[0].resources.limits')

      echo "Requests: $requests"
      echo "Limits: $limits"
    fi

    echo ""
  done

  # Cluster-wide impact
  echo "Cluster Impact"
  echo "=============="

  total_pods=$(echo "$PODS" | wc -w)
  echo "Total DaemonSet pods: $total_pods"

  # Calculate total resource usage
  total_cpu_request=$(kubectl get pods -n $NAMESPACE -l app=$DAEMONSET -o json | jq -r '[.items[].spec.containers[].resources.requests.cpu] | map(select(. != null)) | map(rtrimstr("m") | tonumber) | add')
  total_mem_request=$(kubectl get pods -n $NAMESPACE -l app=$DAEMONSET -o json | jq -r '[.items[].spec.containers[].resources.requests.memory] | map(select(. != null)) | map(rtrimstr("Mi") | tonumber) | add')

  echo "Total CPU requests: ${total_cpu_request}m"
  echo "Total Memory requests: ${total_mem_request}Mi"

  # Get cluster capacity
  total_cluster_cpu=$(kubectl get nodes -o json | jq '[.items[].status.capacity.cpu | tonumber] | add')
  total_cluster_mem=$(kubectl get nodes -o json | jq '[.items[].status.capacity.memory | rtrimstr("Ki") | tonumber] | add / 1024')

  echo "Cluster CPU capacity: ${total_cluster_cpu} cores"
  echo "Cluster Memory capacity: ${total_cluster_mem}Mi"

  # Calculate percentage
  cpu_percentage=$(echo "scale=2; ($total_cpu_request / 1000) / $total_cluster_cpu * 100" | bc)
  mem_percentage=$(echo "scale=2; $total_mem_request / $total_cluster_mem * 100" | bc)

  echo "DaemonSet CPU usage: ${cpu_percentage}% of cluster"
  echo "DaemonSet Memory usage: ${mem_percentage}% of cluster"

} | tee "$REPORT"

echo ""
echo "Profile complete! Results saved to: $OUTPUT_DIR"
echo "Report: $REPORT"

Best Practices

1. Right-Size Resources

#!/bin/bash
# calculate-optimal-resources.sh

NAMESPACE=$1
DAEMONSET=$2

if [ -z "$NAMESPACE" ] || [ -z "$DAEMONSET" ]; then
  echo "Usage: $0 <namespace> <daemonset>"
  exit 1
fi

echo "Analyzing resource usage for $NAMESPACE/$DAEMONSET..."

# Collect metrics from Prometheus (requires prometheus-adapter)
CPU_P95=$(kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/$NAMESPACE/pods" | \
  jq -r ".items[] | select(.metadata.labels.app==\"$DAEMONSET\") | .containers[].usage.cpu" | \
  sed 's/n$//' | sort -n | awk '{all[NR] = $0} END{print all[int(NR*0.95)]}')

MEM_P95=$(kubectl get --raw "/apis/metrics.k8s.io/v1beta1/namespaces/$NAMESPACE/pods" | \
  jq -r ".items[] | select(.metadata.labels.app==\"$DAEMONSET\") | .containers[].usage.memory" | \
  sed 's/Ki$//' | sort -n | awk '{all[NR] = $0} END{print all[int(NR*0.95)]}')

# Add 20% buffer for requests
CPU_REQUEST=$(echo "scale=0; $CPU_P95 * 1.2 / 1000000" | bc)
MEM_REQUEST=$(echo "scale=0; $MEM_P95 * 1.2 / 1024" | bc)

# Add 50% buffer for limits
CPU_LIMIT=$(echo "scale=0; $CPU_P95 * 1.5 / 1000000" | bc)
MEM_LIMIT=$(echo "scale=0; $MEM_P95 * 1.5 / 1024" | bc)

echo "Recommended resource configuration:"
echo "requests:"
echo "  cpu: ${CPU_REQUEST}m"
echo "  memory: ${MEM_REQUEST}Mi"
echo "limits:"
echo "  cpu: ${CPU_LIMIT}m"
echo "  memory: ${MEM_LIMIT}Mi"

2. Priority Classes

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: daemonset-high-priority
value: 1000000
globalDefault: false
description: "High priority for critical DaemonSets"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: daemonset-medium-priority
value: 100000
globalDefault: false
description: "Medium priority for important DaemonSets"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: daemonset-low-priority
value: 10000
globalDefault: false
description: "Low priority for non-critical DaemonSets"

3. Resource Quotas

apiVersion: v1
kind: ResourceQuota
metadata:
  name: daemonset-quota
  namespace: system
spec:
  hard:
    requests.cpu: "10"
    requests.memory: 20Gi
    limits.cpu: "20"
    limits.memory: 40Gi
  scopeSelector:
    matchExpressions:
    - operator: In
      scopeName: PriorityClass
      values:
      - daemonset-medium-priority
      - daemonset-low-priority

Conclusion

Optimizing DaemonSet resources is crucial for maintaining cluster health and efficiency. Key takeaways:

  • Right-size resources based on actual usage patterns with appropriate buffers
  • Use QoS classes strategically: Guaranteed for critical services, Burstable for others
  • Implement selective scheduling to run DaemonSets only where needed
  • Monitor continuously and adjust resource allocations as workloads evolve
  • Test updates carefully using controlled rollout strategies
  • Set appropriate priorities to ensure critical DaemonSets aren’t evicted

By following these patterns and best practices, you can ensure DaemonSets provide necessary node-level services without negatively impacting application workloads or cluster stability.