Skip to main content

Instalación

Strimzi Operators

Toda la configuración se realiza en un operator (controller) que se encarga de gestionar y hacer el deploy a través de los custom resources que él proporciona.

Existe más de un operator que iremos instalando poco a poco.

Vamos a instalar el operator pero lo instalaremos vía helm.

helm repo add strimzi https://strimzi.io/charts/
helm repo update
## Si quieres ver el values.yaml completo
helm show values strimzi/strimzi-kafka-operator

Vamos a definir dos réplicas en este values, siendo que una siempre necesita estar activa para garantizar una alta disponibilidad. Crea el values.yaml abajo.

#values.yaml
replicas: 2

# La versión por defecto de las imágenes que utilizará.
# Es decir, es la versión de strimzi
defaultImageTag: 0.45.0

leaderElection:
enable: true

podDisruptionBudget:
enabled: true
minAvailable: 1
maxUnavailable:

Importante mencionar que no definimos qué namespace este operator observará. Por defecto observará el namespace kafka, en realidad el mismo en que sea instalado. Si necesita que observe recursos creados en otros namespaces debe definirse en el values.

Vamos a aplicar.

helm install strimzi strimzi/strimzi-kafka-operator --values values.yaml --namespace kafka --create-namespace

k get deploy -n kafka
NAME READY UP-TO-DATE AVAILABLE AGE
strimzi-cluster-operator 2/2 2 2 3m30s

kubectl get pods -n kafka

NAME READY STATUS RESTARTS AGE
strimzi-cluster-operator-6bf566db79-kwbk6 1/1 Running 0 108s
strimzi-cluster-operator-6bf566db79-qft2r 1/1 Running 0 108s

# Si quieres revisar los logs
kubectl logs deployment/strimzi-cluster-operator -n kafka -f

El Operator pondrá a disposición para nosotros varios custom resources que utilizaremos para configurar nuestro cluster y gestionarlo a través de manifiestos en kubernetes.

Esto ya evita que crossplane necesite extender la API y tampoco necesita un provider, lo que facilitaría mucho la integración con IDPs.

Un overview sobre los custom resources existentes hasta el momento.

kubectl api-resources | grep strimzi
strimzipodsets sps core.strimzi.io/v1beta2 true StrimziPodSet
kafkabridges kb kafka.strimzi.io/v1beta2 true KafkaBridge
kafkaconnectors kctr kafka.strimzi.io/v1beta2 true KafkaConnector
kafkaconnects kc kafka.strimzi.io/v1beta2 true KafkaConnect
kafkamirrormaker2s kmm2 kafka.strimzi.io/v1beta2 true KafkaMirrorMaker2
kafkamirrormakers kmm kafka.strimzi.io/v1beta2 true KafkaMirrorMaker
kafkanodepools knp kafka.strimzi.io/v1beta2 true KafkaNodePool
kafkarebalances kr kafka.strimzi.io/v1beta2 true KafkaRebalance
kafkas k kafka.strimzi.io/v1beta2 true Kafka
kafkatopics kt kafka.strimzi.io/v1beta2 true KafkaTopic
kafkausers ku kafka.strimzi.io/v1beta2 true KafkaUser

Estudia los ejemplos de Strimzi que tiene muchas cosas buenas y podemos ir configurando el cluster poco a poco.

Existen varios operators diferentes que son controllers diferentes para propósitos diferentes en el cluster.

Creé un proyecto en github con todos los manifiestos que iremos aplicando a lo largo del camino.

Como es un cluster de prueba voy a dimensionarlo con un volumen pequeño, solamente de 5GB. Si vas a crear en producción ajusta.

Primero vamos a hablar sobre los tipos de almacenamiento pues es muy importante esclarecer esto.

En el contexto de Strimzi Kafka Operator y Kubernetes, las diferencias entre los tipos de almacenamiento Ephemeral y JBOD (Just a Bunch Of Disks) son bastante importantes.

  • Ephemeral Storage (Almacenamiento Efímero): se refiere a un tipo de almacenamiento temporal que está asociado a los pods mientras están en ejecución. Cuando el pod es destruido o reiniciado, todos los datos almacenados en ese volumen se pierden.

    • Los datos no son persistentes entre reinicios del pod.
    • Usa la infraestructura de almacenamiento local de los nodos, lo que puede ser más rápido y barato, pero no ofrece durabilidad.
    • Ideal para datos temporales, como caches, logs, o datos intermedios que no necesitan persistir entre reinicios.
    • Puede ser útil en ambientes de desarrollo o pruebas donde la durabilidad de los datos no es una prioridad.
  • JBOD (Just a Bunch of Disks): Se refiere a un tipo de configuración de almacenamiento donde puedes agregar múltiples discos físicos (o volúmenes) al pod Kafka. Cada disco puede usarse para almacenar una parte del log de mensajes de Kafka.

    • Usa volúmenes persistentes (Persistent Volumes - PVs), lo que significa que los datos permanecen almacenados incluso si el pod Kafka es reiniciado.
    • Proporciona mayor seguridad y garantía de durabilidad de los datos almacenados.
    • Puedes agregar más volúmenes conforme la necesidad para distribuir la carga de almacenamiento de Kafka. Es posible tener múltiples volúmenes conectados a Kafka, lo que aumenta la capacidad de almacenamiento.
    • Es la opción recomendada cuando necesitas garantizar la durabilidad de los datos, pues los volúmenes persisten incluso después de fallos o reinicios del pod.

Un nodo puede ser del tipo broker, control o ambos. Ya que vamos a utilizar Kraft será ambos.

Observa que estamos utilizando custom resources creados por Strimzi. Pero esto es solo una definición para lo que esperamos de los nodes.

apiVersion: kafka.strimzi.io/v1beta2 ## Custom Resource!
kind: KafkaNodePool
metadata:
name: broker
labels:
strimzi.io/cluster: kafka-cluster # A qué cluster pertenecerá (Vamos a definir ese nombre en el manifiesto del cluster aún)
namespace: kafka # El operator actúa sobre este namespace
spec:
replicas: 3 # Número de nodes que vamos a usar.
roles: # Tendrá las dos funciones
- controller
- broker
storage:
type: jbod # tipo del storage
volumes:
- id: 0
type: persistent-claim
size: 5Gi # Como es un cluster simple solo de estudio local no necesitamos crear con storages muy grandes, pero en producción no es suficiente.
kraftMetadata: shared
deleteClaim: false
# Este recurso es muy bajo para un cluster kafka, esto es solamente para jugar localmente crear algunos tópicos
resources:
requests:
memory: 1Gi
cpu: "0.3"
limits:
memory: 1Gi
cpu: "0.3"
# Vamos a distribuir los nodos entre diferentes nodes físicos de kubernetes
template:
pod:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: strimzi.io/cluster
operator: In
values:
- kafka-cluster
topologyKey: kubernetes.io/hostname

Aplica el manifiesto arriba en el cluster y nada ocurrirá, pues solo será creado realmente después que definamos el cluster abajo.

Voy a dejar los comentarios para entender mejor las partes de este custom resource.

# Basic configuration (required)
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
# Nombre de nuestro cluster.
# Todos los recursos de este cluster deben tener una label para avisar al operator que pertenece a este cluster
# La label que será usada será strimzi.io/cluster: kafka-cluster
name: kafka-cluster
# Deployment specifications
annotations:
# A través de esta annotation estamos avisando que queremos con raft y las configuraciones de los nodes están definidas en un node-pool que hicimos antes.
strimzi.io/node-pools: enabled
strimzi.io/kraft: enabled
spec:
kafka:
# Aquí tenemos algunos puertos que vamos a utilizar.
listeners:
# Name to identify the listener. Must be unique within the Kafka cluster.
- name: plain
# Port number used by the listener inside Kafka. The port number has to be unique within a given Kafka cluster. Allowed port numbers are 9092 and higher with the exception of ports 9404 and 9999, which are already used for Prometheus and JMX. Depending on the listener type, the port number might not be the same as the port number that connects Kafka clients.
port: 9092
# Listener type specified as internal or cluster-ip (to expose Kafka using per-broker ClusterIP services), or for external listeners, as route (OpenShift only), loadbalancer, nodeport or ingress (Kubernetes only).
type: internal
# Enables or disables TLS encryption for each listener. For route and ingress type listeners, TLS encryption must always be enabled by setting it to true.
tls: false
# Defines whether the fully-qualified DNS names including the cluster service suffix (usually .cluster.local) are assigned.
configuration:
useServiceDnsDomain: true
- name: tls
port: 9093
type: internal
tls: true
# - name: external
# port: 9094
# type: ingress
# tls: true
# authentication:
# type: scram-sha-512
# configuration:
# bootstrap:
# annotations:
# nginx.ingress.kubernetes.io/rewrite-target: "/"
# brokers:
# - broker: 0
# annotations:
# nginx.ingress.kubernetes.io/service-upstream: "true"
# - broker: 1
# annotations:
# nginx.ingress.kubernetes.io/service-upstream: "true"
# - broker: 2
# annotations:
# nginx.ingress.kubernetes.io/service-upstream: "true"
# ingress:
# # Agregando la anotación para el controlador NGINX
# annotations:
# kubernetes.io/ingress.class: "nginx" # Define que NGINX será el controlador de ingress
# hosts:
# - kafka.localhost
# # tls:
# # - secretName: kafka-tls # Certificado TLS, si lo tienes
# # hosts:
# # - kafka.localhost
# rules:
# - host: kafka.localhost
# http:
# paths:
# - path: /
# pathType: Prefix
# backend:
# service:
# name: kafka-cluster-kafka-bootstrap # El servicio Kafka a ser accedido externamente
# port:
# number: 9092 # Puerto de Kafka
# Listener authentication mechanism specified as mTLS, SCRAM-SHA-512, or token-based OAuth 2.0.
# authentication:
# type: tls
# - name: external1
# port: 9094
# type: route
# tls: true
# configuration:
# brokerCertChainAndKey: # (9)
# secretName: my-secret
# certificate: my-certificate.crt
# key: my-key.key

# Kafka version (recommended)
version: "3.9.0"
# KRaft metadata version (recommended)
metadataVersion: "3.9"
# Kafka configuration (recommended)
config: # (12)
auto.create.topics.enable: "false"
offsets.topic.replication.factor: 3
transaction.state.log.replication.factor: 3
transaction.state.log.min.isr: 2
default.replication.factor: 3
min.insync.replicas: 2
# Resources requests and limits (recommended)
# Removido porque estamos usando nodepool
resources:
requests:
memory: 3Gi
cpu: "3"
limits:
memory: 3Gi
cpu: "3"
# Logging configuration (optional)
# Kafka loggers and log levels added directly (inline) or indirectly (external) through a ConfigMap. A custom Log4j configuration must be placed under the log4j.properties key in the ConfigMap. For the Kafka kafka.root.logger.level logger, you can set the log level to INFO, ERROR, WARN, TRACE, DEBUG, FATAL or OFF.
logging: # (14)
type: inline
loggers:
kafka.root.logger.level: INFO
log4j.logger.io.strimzi: "DEBUG"
log4j.logger.kafka: "DEBUG"
log4j.logger.org.apache.kafka: "DEBUG"
# Readiness probe (optional)
readinessProbe: #
initialDelaySeconds: 15
timeoutSeconds: 5
# Liveness probe (optional)
livenessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5

# JVM options (optional)
# JVM configuration options to optimize performance for the Virtual Machine (VM) running Kafka.
# jvmOptions:
# -Xms: 8192m
# -Xmx: 8192m

# Authorization (optional)
# Authorization enables simple, OAUTH 2.0, or OPA authorization on the Kafka broker. Simple authorization uses the AclAuthorizer and StandardAuthorizer Kafka plugins.
# authorization:
# type: simple
# type: opa

# Metrics configuration (optional)
# Prometheus metrics enabled. In this example, metrics are configured for the Prometheus JMX Exporter (the default metrics exporter).
# metricsConfig:
# type: jmxPrometheusExporter
# valueFrom:
# # Rules for exporting metrics in Prometheus format to a Grafana dashboard through the Prometheus JMX Exporter, which are enabled by referencing a ConfigMap containing configuration for the Prometheus JMX exporter. You can enable metrics without further configuration using a reference to a ConfigMap containing an empty file under metricsConfig.valueFrom.configMapKeyRef.key.
# configMapKeyRef:
# name: kafka-metrics
# key: kafka-kraft-metrics-config.yml
# Entity Operator (recommended)
# Entity Operator configuration, which specifies the configuration for the Topic Operator and User Operator.
entityOperator:
topicOperator:
# ¿Tener un namespace solo para ítems de kafka o utilizar el mismo?
watchedNamespace: kafka
reconciliationIntervalMs: 60000
# Resources requests and limits (recommended)
resources:
requests:
memory: 512Mi
cpu: "1"
limits:
memory: 512Mi
cpu: "1"
# Logging configuration (optional)
# Specified Topic Operator loggers and log levels. This example uses inline logging.
logging: # (23)
type: inline
loggers:
rootLogger.level: INFO
userOperator:
watchedNamespace: kafka
reconciliationIntervalMs: 60000
# Resources requests and limits (recommended)
resources:
requests:
memory: 512Mi
cpu: "1"
limits:
memory: 512Mi
cpu: "1"
logging: # (24)
type: inline
loggers:
rootLogger.level: INFO
# Kafka Exporter (optional)
kafkaExporter: {} # (25)
# ...
# Cruise Control (optional)
cruiseControl: {}
# ...

Crea el manifiesto arriba y aplícalo en el cluster. Ya está definido el namespace kafka creado anteriormente. El operator entenderá lo que debe hacer y aplicará todo.

También vamos a explorar el cruiseControl que fue definido en el manifiesto pero no tiene sus reglas aún definidas para el rebalance. Varios ejemplos de cómo utilizar cruise control están disponibles, vamos a utilizar el más genérico por ahora que funciona bien. Las cosas deben adaptarse de acuerdo con la propuesta y tamaño de tu cluster. No es obligatorio, pero es muy bienvenido.

apiVersion: kafka.strimzi.io/v1beta2
kind: KafkaRebalance
metadata:
name: rebalance
namespace: kafka
labels:
strimzi.io/cluster: kafka-cluster
annotations:
strimzi.io/rebalance-auto-approval: "true"
spec:
goals:
- CpuCapacityGoal
- NetworkInboundCapacityGoal
- DiskCapacityGoal
- RackAwareGoal
- MinTopicLeadersPerBrokerGoal
- NetworkOutboundCapacityGoal
- ReplicaCapacityGoal

Aplica también el manifiesto arriba si quieres y vamos a ver qué tenemos.

❯ kubectl get kafkanodepools.kafka.strimzi.io -n kafka
NAME DESIRED REPLICAS ROLES NODEIDS
broker 3 ["controller","broker"] [0,1,2]


❯ kubectl get kafka -n kafka
NAME DESIRED KAFKA REPLICAS DESIRED ZK REPLICAS READY METADATA STATE WARNINGS
kafka-cluster True KRaft


❯ kubectl get deploy -n kafka
NAME READY UP-TO-DATE AVAILABLE AGE
kafka-cluster-cruise-control 1/1 1 1 5d1h
kafka-cluster-entity-operator 1/1 1 1 5d22h
kafka-cluster-kafka-exporter 1/1 1 1 5d
strimzi-cluster-operator 1/1 1 1 7d19h

❯ kubectl get pods -n kafka
NAME READY STATUS RESTARTS AGE
kafka-cluster-broker-0 1/1 Running 0 4h10m
kafka-cluster-broker-1 1/1 Running 0 4h10m
kafka-cluster-broker-2 1/1 Running 0 4h10m
kafka-cluster-cruise-control-5f87855476-jvf9c 1/1 Running 0 4h6m
kafka-cluster-entity-operator-794c776cb7-dd44n 2/2 Running 0 4h6m
kafka-cluster-kafka-exporter-6dcfc78f98-tlhfj 1/1 Running 0 4h6m
strimzi-cluster-operator-6bf566db79-rtwrj 1/1 Running 0 4h8m