Kafka on Kubernetes
Production deployment guide for Apache Kafka on Kubernetes clusters.
Architecture Overview
Section titled “Architecture Overview”StatefulSet Deployment
Section titled “StatefulSet Deployment”Namespace and ConfigMap
Section titled “Namespace and ConfigMap”apiVersion: v1kind: Namespacemetadata: name: kafka---# configmap.yamlapiVersion: v1kind: ConfigMapmetadata: name: kafka-config namespace: kafkadata: server.properties: | # KRaft mode process.roles=broker,controller node.id=${KAFKA_NODE_ID} controller.quorum.voters=0@kafka-0.kafka-headless:9093,1@kafka-1.kafka-headless:9093,2@kafka-2.kafka-headless:9093
# Listeners listeners=PLAINTEXT://:9092,CONTROLLER://:9093 advertised.listeners=PLAINTEXT://${HOSTNAME}.kafka-headless.kafka.svc.cluster.local:9092 controller.listener.names=CONTROLLER inter.broker.listener.name=PLAINTEXT
# Log configuration log.dirs=/var/kafka/data num.partitions=3 default.replication.factor=3 min.insync.replicas=2
# Performance num.network.threads=8 num.io.threads=16 socket.send.buffer.bytes=102400 socket.receive.buffer.bytes=102400 socket.request.max.bytes=104857600Headless Service
Section titled “Headless Service”apiVersion: v1kind: Servicemetadata: name: kafka-headless namespace: kafka labels: app: kafkaspec: type: ClusterIP clusterIP: None publishNotReadyAddresses: true ports: - name: client port: 9092 targetPort: 9092 - name: controller port: 9093 targetPort: 9093 selector: app: kafkaBootstrap Service
Section titled “Bootstrap Service”apiVersion: v1kind: Servicemetadata: name: kafka-bootstrap namespace: kafka labels: app: kafkaspec: type: ClusterIP ports: - name: client port: 9092 targetPort: 9092 selector: app: kafkaStatefulSet
Section titled “StatefulSet”apiVersion: apps/v1kind: StatefulSetmetadata: name: kafka namespace: kafkaspec: serviceName: kafka-headless replicas: 3 podManagementPolicy: Parallel selector: matchLabels: app: kafka template: metadata: labels: app: kafka spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: kafka topologyKey: kubernetes.io/hostname nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: topology.kubernetes.io/zone operator: In values: - us-central1-a - us-central1-b - us-central1-c terminationGracePeriodSeconds: 300 securityContext: fsGroup: 1000 runAsUser: 1000 runAsNonRoot: true containers: - name: kafka image: apache/kafka:4.3.0 ports: - name: client containerPort: 9092 - name: controller containerPort: 9093 env: - name: KAFKA_NODE_ID valueFrom: fieldRef: fieldPath: metadata.name - name: KAFKA_HEAP_OPTS value: "-Xms4g -Xmx4g" command: - bash - -c - | export KAFKA_NODE_ID=${HOSTNAME##*-} /opt/kafka/bin/kafka-storage.sh format -t $(cat /var/kafka/data/cluster-id 2>/dev/null || kafka-storage.sh random-uuid | tee /var/kafka/data/cluster-id) -c /etc/kafka/server.properties --ignore-formatted exec /opt/kafka/bin/kafka-server-start.sh /etc/kafka/server.properties resources: requests: memory: "6Gi" cpu: "2" limits: memory: "8Gi" cpu: "4" volumeMounts: - name: data mountPath: /var/kafka/data - name: config mountPath: /etc/kafka readinessProbe: tcpSocket: port: 9092 initialDelaySeconds: 30 periodSeconds: 10 livenessProbe: tcpSocket: port: 9092 initialDelaySeconds: 60 periodSeconds: 20 volumes: - name: config configMap: name: kafka-config volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] storageClassName: ssd resources: requests: storage: 100GiStorage Classes
Section titled “Storage Classes”AWS EBS
Section titled “AWS EBS”apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: ssdprovisioner: ebs.csi.aws.comparameters: type: gp3 iops: "6000" throughput: "500" encrypted: "true"volumeBindingMode: WaitForFirstConsumerallowVolumeExpansion: trueAzure Disk
Section titled “Azure Disk”apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: ssdprovisioner: disk.csi.azure.comparameters: skuName: PremiumV2_LRS diskIOPSReadWrite: "6000" diskMBpsReadWrite: "500"volumeBindingMode: WaitForFirstConsumerallowVolumeExpansion: trueGCP Persistent Disk
Section titled “GCP Persistent Disk”apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: ssdprovisioner: pd.csi.storage.gke.ioparameters: type: pd-ssdvolumeBindingMode: WaitForFirstConsumerallowVolumeExpansion: truePod Disruption Budget
Section titled “Pod Disruption Budget”apiVersion: policy/v1kind: PodDisruptionBudgetmetadata: name: kafka-pdb namespace: kafkaspec: minAvailable: 2 selector: matchLabels: app: kafkaResource Recommendations
Section titled “Resource Recommendations”Pod Resources
Section titled “Pod Resources”| Component | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|---|---|---|---|
| Development | 500m | 1 | 2Gi | 4Gi |
| Production | 2 | 4 | 6Gi | 8Gi |
| High Throughput | 4 | 8 | 12Gi | 16Gi |
JVM Heap Settings
Section titled “JVM Heap Settings”| Memory Limit | Heap Size | Recommended |
|---|---|---|
| 4Gi | 2g | Development |
| 8Gi | 4g | Production |
| 16Gi | 8g | High throughput |
Network Policies
Section titled “Network Policies”apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: kafka-network-policy namespace: kafkaspec: podSelector: matchLabels: app: kafka policyTypes: - Ingress - Egress ingress: # Inter-broker communication - from: - podSelector: matchLabels: app: kafka ports: - port: 9092 - port: 9093 # Client access from application namespaces - from: - namespaceSelector: matchLabels: kafka-client: "true" ports: - port: 9092 egress: # Inter-broker communication - to: - podSelector: matchLabels: app: kafka ports: - port: 9092 - port: 9093 # DNS resolution - to: - namespaceSelector: {} podSelector: matchLabels: k8s-app: kube-dns ports: - port: 53 protocol: UDPExternal Access
Section titled “External Access”NodePort Service
Section titled “NodePort Service”apiVersion: v1kind: Servicemetadata: name: kafka-external namespace: kafkaspec: type: NodePort ports: - name: client port: 9092 nodePort: 30092 selector: app: kafkaLoadBalancer (Cloud)
Section titled “LoadBalancer (Cloud)”apiVersion: v1kind: Servicemetadata: name: kafka-lb namespace: kafka annotations: service.beta.kubernetes.io/aws-load-balancer-type: nlb service.beta.kubernetes.io/aws-load-balancer-internal: "true"spec: type: LoadBalancer ports: - name: client port: 9092 targetPort: 9092 selector: app: kafkaIngress (with SNI)
Section titled “Ingress (with SNI)”apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: kafka-ingress namespace: kafka annotations: nginx.ingress.kubernetes.io/ssl-passthrough: "true"spec: ingressClassName: nginx rules: - host: kafka.example.com http: paths: - path: / pathType: Prefix backend: service: name: kafka-bootstrap port: number: 9092Helm Chart
Section titled “Helm Chart”values.yaml
Section titled “values.yaml”replicaCount: 3
image: repository: apache/kafka tag: "3.7.0" pullPolicy: IfNotPresent
resources: requests: memory: "6Gi" cpu: "2" limits: memory: "8Gi" cpu: "4"
jvmOptions: heapSize: "4g"
persistence: enabled: true storageClass: ssd size: 100Gi
config: numPartitions: 3 defaultReplicationFactor: 3 minInsyncReplicas: 2
affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchLabels: app: kafka topologyKey: kubernetes.io/hostname
podDisruptionBudget: minAvailable: 2
metrics: enabled: true port: 9404Monitoring
Section titled “Monitoring”ServiceMonitor (Prometheus)
Section titled “ServiceMonitor (Prometheus)”apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: kafka namespace: kafkaspec: selector: matchLabels: app: kafka endpoints: - port: metrics interval: 30s path: /metricsJMX Exporter Sidecar
Section titled “JMX Exporter Sidecar”# Add to StatefulSet containers- name: jmx-exporter image: bitnami/jmx-exporter:latest ports: - name: metrics containerPort: 9404 args: - "9404" - "/etc/jmx-exporter/config.yaml" volumeMounts: - name: jmx-config mountPath: /etc/jmx-exporterRolling Updates
Section titled “Rolling Updates”Update Strategy
Section titled “Update Strategy”spec: updateStrategy: type: RollingUpdate podManagementPolicy: OrderedReadyPerforming Updates
Section titled “Performing Updates”# Update imagekubectl set image statefulset/kafka kafka=apache/kafka:3.8.0 -n kafka
# Monitor rolloutkubectl rollout status statefulset/kafka -n kafka
# Rollback if neededkubectl rollout undo statefulset/kafka -n kafkaRolling Update Considerations
Rolling updates should be performed during low-traffic periods. Each broker restart causes partition leadership changes and potential consumer rebalancing.
Related Documentation
Section titled “Related Documentation”- Architecture Overview - Kafka architecture
- Operations - Operational procedures
- Monitoring - Monitoring guide
- AWS Deployment - AWS-specific guide