Apache Flink en K8s y Kafka: PyFlink, Go, operaciones y precios gestionados

Streaming con estado, puntos de control, K8s, PyFlink, Go.

Índice

Apache Flink es un marco para computaciones con estado sobre flujos de datos acotados y no acotados.

Los equipos lo adoptan para transmisión (streaming) correcta y de baja latencia con semántica de tiempo de evento (marcadores de tiempo), tolerancia a fallos (puntos de control), actualizaciones controladas (puntos de guardado) y superficies operativas (métricas y REST).

Procesamiento de flujos de Apache Flink

Esta guía está dirigida a equipos de DevOps y desarrolladores de Go/Python. Compara los modelos de implementación (autoadministrado vs. gestionado), explica la arquitectura central, cubre configuraciones de Kubernetes (Helm y Operador) e implementaciones independientes, contrasta Flink con Spark, Kafka Streams, Beam y bases de datos de streaming, y muestra patrones de integración de PyFlink y Go, incluidas las tuberías orientadas a LLM e IA.

Para obtener un contexto más amplio sobre patrones de infraestructura de datos, incluido el almacenamiento de objetos, bases de datos y mensajería, consulte Infraestructura de datos para sistemas de IA: Almacenamiento de objetos, bases de datos, búsqueda y arquitectura de datos de IA.

Apache Flink se posiciona explícitamente como un motor de procesamiento de flujos con estado: modela tu lógica como una tubería de operadores y Flink la ejecuta como un flujo de datos distribuido con estado gestionado y semántica de tiempo. En la documentación moderna de Flink, el proyecto se describe como un marco y motor de procesamiento distribuido para computaciones con estado sobre flujos de datos acotados y no acotados.

Desde una perspectiva práctica de ingeniería de software/DevOps, Flink es una buena opción cuando necesitas al menos una de estas propiedades:

Si necesitas uniones/agregaciones/enriquecimiento con baja latencia y garantías de corrección, típicamente utilizas el procesamiento de tiempo de evento de Flink, donde el “tiempo” es cuándo ocurrió el evento (no cuándo llegó), y los marcadores de tiempo comunican el progreso del tiempo de evento a través de la tubería.

Si necesitas computación con estado a gran escala (contadores rodantes, sesiones, reglas de fraude, ingeniería de características), Flink trata el estado como una parte de primera clase del modelo de programación y lo hace tolerante a fallos mediante puntos de control.

Si necesitas streaming operativamente robusto (fallos, actualizaciones rodantes, reinicios), Flink realiza puntos de control del estado y las posiciones del flujo para que el trabajo pueda recuperarse y continuar con la misma semántica “como una ejecución libre de fallos”.

Casos de uso típicos para equipos de DevOps, Go, Python e IA

Flink se utiliza ampliamente para “tuberías de datos y ETL”, “análisis de streaming” y “aplicaciones impulsadas por eventos” (las categorías utilizadas por la documentación de Flink).

Para un stack de DevOps + Go/Python, los patrones típicos son los siguientes:

Un servicio Go produce eventos en Kafka; Flink consume esos eventos, realiza un procesamiento con estado (por ejemplo, deduplicación, agregación con ventana, enriquecimiento) y luego escribe los hechos derivados de nuevo en Kafka o una base de datos. Los mecanismos de operadores y puntos de control de Flink existen para hacer que estas tuberías con estado sean seguras para producción.

Para equipos de ML/LLM, PyFlink llama explícitamente a escenarios como “predicción de aprendizaje automático” y la carga de modelos de aprendizaje automático dentro de UDFs de Python como una motivación de gestión de dependencias, lo que es un respaldo directo de los patrones de “trabajo de Flink como tiempo de ejecución de inferencia en línea / ingeniería de características”.

El tiempo de ejecución de Flink consiste en dos tipos de procesos: JobManager y TaskManagers. La documentación enfatiza que los clientes envían el flujo de datos al JobManager; el cliente puede luego desconectarse (modo independiente) o mantenerse conectado (modo unido).

El JobManager coordina la ejecución distribuida: programación, reacción a la finalización/fallo de tareas, coordinación de puntos de control y coordinación de la recuperación. Internamente, incluye: ResourceManager (ranuras/recursos), Dispatcher (REST + Interfaz web + creación de JobMaster por trabajo) y JobMaster (gestiona un trabajo).

Los TaskManagers ejecutan los operadores/tareas e intercambian/amortiguan los flujos de datos. La unidad de programación más pequeña es la ranura de tarea; múltiples operadores pueden ejecutarse en una sola ranura (la concatenación de operadores y el uso compartido de ranuras afectan esto).

Concatenación de operadores y ranuras de tarea para el control de rendimiento y costos

Flink concatena subtareas de operadores en tareas, donde cada tarea se ejecuta mediante un solo hilo. Esto se describe como una optimización de rendimiento que reduce la sobrecarga de transferencia de hilos y amortiguamiento, aumentando el rendimiento y disminuyendo la latencia.

Las ranuras son importantes operativamente porque son la unidad de programación de recursos/aislamiento. Flink señala que cada TaskManager puede tener una o más ranuras de tarea; la asignación de ranuras reserva memoria gestionada por ranura, pero no aísla la CPU.

Procesamiento de tiempo de evento, marcadores de tiempo y datos tardíos

Flink admite múltiples nociones de tiempo: tiempo de evento, tiempo de ingesta, tiempo de procesamiento y utiliza marcadores de tiempo para modelar el progreso en el tiempo de evento.

Para trabajar con el tiempo de evento, Flink necesita asignar marcas de tiempo a los eventos y generar marcadores de tiempo; la documentación oficial “Generación de marcadores de tiempo” explica la asignación de marcas de tiempo y la generación de marcadores de tiempo como los bloques de construcción centrales, siendo WatermarkStrategy la forma estándar de configurar estrategias comunes.

Tolerancia a fallos: puntos de control versus puntos de guardado en sistemas reales

Los puntos de control existen porque “cada función y operador en Flink pueden tener estado”; el estado debe ser controlado para volverse tolerante a fallos. Los puntos de control permiten la recuperación tanto del estado como de las posiciones del flujo para que la ejecución pueda reanudarse con semántica libre de fallos.

Flink es muy explícito en que los puntos de guardado son “una imagen coherente del estado de ejecución de un trabajo de streaming, creada mediante el mecanismo de puntos de control de Flink”, utilizada para detener y reanudar, bifurcar o actualizar trabajos. Los puntos de guardado residen en almacenamiento estable (por ejemplo, HDFS, S3).

La página oficial “Puntos de control vs. Puntos de guardado” enmarca la diferencia como copias de seguridad versus registros de recuperación: los puntos de control son frecuentes, ligeros y gestionados por Flink para recuperación de fallos; los puntos de guardado son gestionados por el usuario y se utilizan para operaciones controladas como actualizaciones.

El tiempo de ejecución de Flink de código abierto es “gratis” en el sentido de la licencia, pero en producción pagas por la infraestructura y el esfuerzo operativo.

Flink está diseñado para integrarse con administradores de recursos comunes (por ejemplo, YARN y Kubernetes) y también puede ejecutarse como un clúster independiente o como una biblioteca.

Los costos de computación y memoria están impulsados por JobManager y TaskManagers, y por tu configuración de paralelismo/ranuras. La documentación de configuración de Flink cita explícitamente jobmanager.memory.process.size, taskmanager.memory.process.size, taskmanager.numberOfTaskSlots y parallelism.default como controles centrales para configuraciones distribuidas.

El disco local es un costo oculto frecuente para trabajos con estado. Flink señala que io.tmp.dirs almacena datos locales, incluidos archivos de RocksDB, resultados intermedios derramados y JARs en caché; si se borran estos datos, pueden forzar “una operación de recuperación pesada”, por lo que deberían residir en almacenamiento que no se purga periódicamente.

El costo de almacenamiento de objetos/archivos duraderos está impulsado por los directorios de puntos de control/guardado. En la configuración de Flink 2.x, los puntos de control y puntos de guardado se configuran mediante execution.checkpointing.dir y execution.checkpointing.savepoint-dir y aceptan URIs como s3://… o hdfs://….

Los servicios gestionados reducen el costo operativo pero añaden tarifas de plataforma y restricciones. Los detalles dependen del proveedor.

Amazon Managed Service for Apache Flink factura por KPUs (1 vCPU + 4 GB de memoria por KPU) y cobra por duración y número de KPUs en incrementos de un segundo. AWS también cobra un KPU de “orquestración” adicional por aplicación y tarifas separadas de almacenamiento/copias de seguridad.

Confluent Cloud for Apache Flink es basado en uso y sin servidor: creas un grupo de computación y se te factura por CFUs consumidos por minuto mientras se ejecutan las declaraciones. La página de facturación incluye un precio de ejemplo de CFU de $0.21 por CFU-hora (dependiente de la región) y enfatiza que puedes limitar el gasto mediante máximos del grupo de computación.

Aiven y Alibaba Cloud son proveedores gestionados de Flink notables en el mercado, pero sus detalles de precios públicos y facturación varían según el plan/región y pueden requerir calculadoras o contacto con ventas; trata los costos exactos como no especificados a menos que solicites una cotización de región+plan de su documentación actual.

Ververica ofrece opciones de implementación autoadministradas y gestionadas alrededor de Flink; las páginas públicas enfatizan las opciones de implementación y posicionamiento de servicio gestionado, mientras que los precios exactos suelen manejarse mediante flujos de “contacto/detalles de precios” (por lo que los números específicos suelen estar no especificados públicamente).

Opción de implementación Mejor para Complejidad operativa Beneficios clave Riesgos clave / compensaciones
Clúster independiente (VMs/hardware sin servidor) Equipos pequeños, capacidad fija Media–Alta Control total; modelo mental más simple HA, escalado automático, actualizaciones son DIY (más trabajo manual)
Kubernetes con Flink Kubernetes Operator La mayoría de los equipos de plataformas modernas Media Implementaciones declarativas; gestión del ciclo de vida mediante bucle de control; el operador soporta implementaciones de Aplicación/Sesión/Trabajo Se requiere experiencia en Kubernetes + operador
Kubernetes nativo (sin operador) Equipos de K8s que buscan integración directa Media–Alta Integración directa de recursos; asignación/desasignación dinámica de TaskManager descrita en la documentación de Flink-on-K8s Automatización más específica que el operador
YARN Plataformas centradas en Hadoop Media Se integra con la gestión de recursos de YARN Complejidad del stack de Hadoop
AWS Managed Service for Apache Flink Pila de datos nativa de AWS Baja–Media Orquestación gestionada + opciones de escalado; unidad de facturación predecible (KPU) Acoplamiento a la plataforma; KPU de sobrecarga adicional por aplicación + tarifas de almacenamiento
Confluent Cloud for Apache Flink Empresas centradas en Kafka, aplicaciones de streaming SQL-first Baja Facturación de uso sin servidor; contabilidad de CFU-minuto; grupos de computación para limitar el gasto Costos de CFU + costos de red de Kafka; APIs específicas del servicio
Ofrecimientos gestionados de Ververica Empresas que necesitan operaciones expertas de Flink Baja–Media Posicionamiento de servicio gestionado con “expertos en Flink” Los precios a menudo no son transparentes (no especificados)

Tabla de proveedores gestionados y costos

Los precios cambian según la región y el tiempo; si necesitas números exactos para tu región, trata esto como un punto de partida y verifica contra las páginas de precios actuales del proveedor (las regiones no citadas están no especificadas).

Proveedor Forma del “Plan” Unidad de facturación Precio de computación de ejemplo Factores de costos adicionales notables
Amazon Managed Service for Apache Flink Tiempo de ejecución gestionado KPU (1 vCPU + 4 GB) Ejemplo mostrado: $0.11 por KPU-hora (US East N. Virginia) +1 KPU de orquestación por aplicación; almacenamiento en ejecución; copias de seguridad duraderas opcionales
Confluent Cloud for Apache Flink SQL/Procesamiento sin servidor CFU-minuto/CFU-hora Ejemplo mostrado: $0.21 por CFU-hora (la región varía) Las tarifas de red de Kafka siguen aplicándose; máximo del grupo de computación para limitar el gasto
Ververica (gestionado) “Plataforma de datos de streaming unificada” gestionada No especificado (páginas públicas) No especificado Características de plataforma/SLAs; precios típicamente vía ventas (no especificado)
Aiven for Apache Flink Servicio gestionado Modelo de facturación de uso por hora (plataforma completa) No especificado sin plan/región Nivel del plan + región en la nube + complementos (no especificado)
Alibaba Cloud Realtime Compute for Apache Flink Gestionado/sin servidor Facturación híbrida (pago por uso + mezcla de suscripción) No especificado sin detalles de región/espacio de trabajo Límites basados en CU y modelo de espacio de trabajo (los detalles varían; no especificado aquí)

Flink se encuentra en un ecosistema ocupado. La “mejor” elección depende de la latencia, el estado, las preferencias operativas y el modelo de autoría.

Herramienta Qué es Modelo de ejecución de streaming Historia de estado y exactamente-una vez Dónde brilla Puntos de dolor típicos
Apache Flink Motor de procesamiento de flujos distribuido para computaciones con estado Streaming continuo + tiempo de evento mediante marcadores de tiempo Tolerancia a fallos basada en puntos de control; puntos de guardado para actualizaciones controladas Tuberías con estado de baja latencia; lógica compleja de tiempo de evento Operar el estado, puntos de control y actualizaciones correctamente requiere disciplina
Apache Spark Structured Streaming Motor de streaming de Spark construido alrededor de DataFrames/Datasets Modelo de micro-lotes por defecto (con un modo continuo discutido por separado) Fuerte para tuberías analíticas; el estado existe pero a menudo tiene mayor latencia APIs unificadas de lote+streaming; ecosistema de Spark Latencia de micro-lotes y modelo mental de “streaming como lotes incrementales”
Kafka Streams Biblioteca para crear aplicaciones de procesamiento de flujos en Kafka Procesamiento registro a registro Soporta semántica de procesamiento exactamente-una vez (EOS) Aplicaciones nativas de Kafka simples; incrustado en servicio JVM Solo JVM; menos flexible para patrones de computación distribuida a gran escala
Apache Beam Modelo de programación unificado + SDKs; ejecutado mediante runners (Flink, Spark, Dataflow, etc.) Depende del runner; las tuberías de Beam se traducen a trabajos del runner La semántica depende de la matriz de capacidades del runner (específica del runner) Portabilidad, tuberías multilingües; evitar el bloqueo del motor El ajuste operativo sigue siendo específico del runner
Materialize “Capa de datos en vivo” / BD SQL de streaming; actualiza resultados incrementalmente a medida que llegan los datos Mantenimiento continuo de vistas incrementales Reivindicaciones de consistencia fuerte en la documentación del producto (los detalles son específicos del producto) Servir vistas derivadas frescas a aplicaciones/agentes de IA Modelo operativo diferente a los trabajos de Flink; no es un tiempo de ejecución de API de operador general
RisingWave Base de datos de streaming donde el procesamiento de flujos se expresa como vistas materializadas Mantenimiento continuo de vistas materializadas Primero SQL; semántica específica del motor Aplicaciones de streaming centradas en SQL sin construir trabajos de Flink Menos flexible para tuberías arbitrarias pesadas en código

Una heurística útil: si quieres un tiempo de ejecución para trabajos de streaming con estado complejos con un control profundo sobre el tiempo de evento, la lógica de operadores y las implementaciones, Flink es un candidato principal. Si quieres vistas incrementales primero en SQL para servir, las bases de datos de streaming pueden ser alternativas. Si quieres una biblioteca incrustada en un servicio, Kafka Streams es competitivo. Si quieres una definición de tubería portable entre motores, Beam es convincente.

Para arquitecturas impulsadas por eventos nativas en la nube que utilizan AWS, Construcción de microservicios impulsados por eventos con AWS Kinesis cubre patrones de Kinesis Data Streams para procesamiento en tiempo real y desacoplamiento de servicios.

Esta sección es intencionalmente práctica: configuración, implementación y cómo tus servicios de Go/Python interactúan típicamente con Flink.

Servicios Go + Kafka + Flink + capa de servicio

Flink suele ser el “medio con estado” que convierte eventos de alto volumen en señales duraderas (contadores, sesiones, anomalías, registros enriquecidos). Los puntos de control y los backends de estado son lo que hacen que ese medio sea fiable en producción.

Nota importante de versión: a partir de Flink 2.0, el archivo de configuración admitido es conf/config.yaml; el anterior flink-conf.yaml ya no está “soportado”.

Un conf/config.yaml mínimo (ilustrativo) para un pequeño clúster autoadministrado:

# conf/config.yaml (estilo Flink 2.x)
rest:
  address: flink-jobmanager.example.internal
  port: 8081

jobmanager:
  rpc:
    address: flink-jobmanager.example.internal
    port: 6123
  memory:
    process:
      size: 2048m

taskmanager:
  memory:
    process:
      size: 4096m
  numberOfTaskSlots: 2

parallelism:
  default: 2

# Valores predeterminados de puntos de control (los trabajos aún pueden sobrescribir en el código)
state:
  backend:
    type: rocksdb
execution:
  checkpointing:
    dir: s3://my-bucket/flink/checkpoints
    savepoint-dir: s3://my-bucket/flink/savepoints
    interval: 60 s

# Evita directorios tmp que se purgen (archivos RocksDB, JARs en caché, etc.)
io:
  tmp:
    dirs: ["/var/lib/flink/tmp"]

¿Por qué estas claves: la referencia de configuración de Flink documenta explícitamente los detalles de descubrimiento de rest.* y jobmanager.rpc.*, las claves de memoria de proceso, las claves de ranura/paralelismo y la configuración predeterminada de puntos de control, incluido state.backend.type, execution.checkpointing.dir, execution.checkpointing.savepoint-dir y execution.checkpointing.interval.

La elección de io.tmp.dirs es operativamente importante porque Flink lo utiliza para archivos locales de RocksDB y artefactos en caché; borrarlo puede causar una recuperación pesada.

Si estás en Flink 1.x (aún común en algunos entornos gestionados), verás flink-conf.yaml en el mundo real. Esto es legado para usuarios de Flink 2.x.

# conf/flink-conf.yaml (estilo antiguo 1.x; NO soportado en Flink 2.x)
jobmanager.rpc.address: flink-jobmanager
rest.port: 8081
taskmanager.numberOfTaskSlots: 2
parallelism.default: 2

# Las claves de puntos de control antiguos difieren según la versión; trátalo como ilustrativo.
state.backend.type: rocksdb
state.checkpoints.dir: s3://my-bucket/flink/checkpoints
state.savepoints.dir: s3://my-bucket/flink/savepoints

Si estás migrando, Flink proporciona un script de migración (bin/migrate-config-file.sh) para convertir flink-conf.yaml a config.yaml.

El Operador de Kubernetes de Flink actúa como plano de control para la gestión del ciclo de vida de aplicaciones de Flink y se instala usando Helm.

Desde la documentación oficial de Helm del operador, puedes instalar desde el árbol de fuentes del gráfico o desde el repositorio de gráficos alojado por Apache:

# instalar desde el gráfico empaquetado en el árbol de fuentes
helm install flink-kubernetes-operator helm/flink-kubernetes-operator

# instalar desde el repositorio de Helm de descargas de Apache (reemplaza <OPERATOR-VERSION>)
helm repo add flink-operator-repo https://downloads.apache.org/flink/flink-kubernetes-operator-<OPERATOR-VERSION>/
helm install flink-kubernetes-operator flink-operator-repo/flink-kubernetes-operator

Estos comandos exactos se muestran en la documentación de instalación de Helm del operador.

Ejemplo de CR FlinkDeployment (ilustrativo)

Este es un ejemplo simplificado para mostrar los puntos de integración que típicamente personalizarás (imagen, recursos, ubicaciones de puntos de control, registro/métricas). El operador reconcilia este estado deseado mediante su bucle de control.

apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
  name: sesiones-tiempo-real
  namespace: flink
spec:
  image: my-registry.example.com/flink/sesiones-tiempo-real:2026-03-06
  flinkVersion: v2_2
  serviceAccount: flink
  flinkConfiguration:
    taskmanager.numberOfTaskSlots: "2"
    state.backend.type: "rocksdb"
    execution.checkpointing.dir: "s3://my-bucket/flink/checkpoints/sesiones-tiempo-real"
    execution.checkpointing.savepoint-dir: "s3://my-bucket/flink/savepoints/sesiones-tiempo-real"
    execution.checkpointing.interval: "60 s"
    rest.port: "8081"
  jobManager:
    resource:
      cpu: 1
      memory: "2048m"
  taskManager:
    resource:
      cpu: 2
      memory: "4096m"
  job:
    jarURI: local:///opt/flink/usrlib/sesiones-tiempo-real.jar
    parallelism: 4
    upgradeMode: savepoint
    state: running

El patrón upgradeMode: savepoint es común cuando quieres actualizaciones con estado seguras; los puntos de guardado están diseñados para flujos de trabajo de detener/reanudar/bifurcar/actualizar y apuntan a ubicaciones de almacenamiento estables.

PyFlink es la API de Python para Apache Flink y se ofrece explícitamente para cargas de trabajo de lote/streaming escalables, incluidas tuberías de ML y ETL.

Cuando utilizas conectores de JVM (Kafka, JDBC, etc.) desde PyFlink, debes asegurarte de que los JARs relevantes estén disponibles para el trabajo. La documentación de “Gestión de dependencias” de Python de Flink muestra tres mecanismos estándar:

Establecer pipeline.jars (API Table), llamar a add_jars() (API DataStream) o CLI --jarfile en el momento de la presentación.

Este ejemplo lee eventos JSON de Kafka, asigna marcas de tiempo de tiempo de evento (con desorden acotado), mantiene un conteo rodante por usuario en estado clave y escribe un evento enriquecido en un tema de salida.

Notas:

  • KafkaSource se construye mediante KafkaSource.builder() y requiere servidores bootstrap, temas y un deserializador.
  • La configuración de sumidero de Kafka exactamente-una vez en PyFlink requiere establecer la garantía de entrega y un prefijo de ID transaccional.
  • Los valores predeterminados de puntos de control se pueden configurar en la configuración de Flink (execution.checkpointing.*) y/o en el código; las claves de configuración están documentadas en la referencia de configuración de Flink.
import json
from typing import Any

from pyflink.common import Duration, Types
from pyflink.common.serialization import SimpleStringSchema
from pyflink.common.watermark_strategy import WatermarkStrategy, TimestampAssigner
from pyflink.common.configuration import Configuration

from pyflink.datastream import StreamExecutionEnvironment
from pyflink.datastream.functions import KeyedProcessFunction, RuntimeContext
from pyflink.datastream.state import ValueStateDescriptor

from pyflink.datastream.connectors import DeliveryGuarantee
from pyflink.datastream.connectors.kafka import (
    KafkaSource,
    KafkaOffsetsInitializer,
    KafkaSink,
    KafkaRecordSerializationSchema,
)


class EventTimeFromJson(TimestampAssigner):
    """
    Extraer event_time_ms de la carga útil JSON.
    Esperado: {"user_id":"u1","event_time_ms":1710000000000,"event":"click",...}
    """
    def extract_timestamp(self, value: str, record_timestamp: int) -> int:
        try:
            obj = json.loads(value)
            return int(obj["event_time_ms"])
        except Exception:
            # respaldo: usar marca de tiempo del registro (ingesta) si está malformado
            return record_timestamp


class RollingCount(KeyedProcessFunction):
    def open(self, runtime_context: RuntimeContext):
        desc = ValueStateDescriptor("rolling_count", Types.LONG())
        self.count_state = runtime_context.get_state(desc)

    def process_element(self, value: str, ctx: 'KeyedProcessFunction.Context'):
        obj = json.loads(value)
        current = self.count_state.value()
        if current is None:
            current = 0
        current += 1
        self.count_state.update(current)

        # emitir evento enriquecido
        obj["rolling_count"] = current
        obj["event_time_ms"] = int(obj.get("event_time_ms", 0))
        yield json.dumps(obj)


def build_env() -> StreamExecutionEnvironment:
    # Valores predeterminados de clúster/trabajo (también se pueden establecer en config.yaml)
    cfg = Configuration()
    cfg.set_string("state.backend.type", "rocksdb")
    cfg.set_string("execution.checkpointing.dir", "s3://my-bucket/flink/checkpoints/sesiones-tiempo-real")
    cfg.set_string("execution.checkpointing.interval", "60 s")
    env = StreamExecutionEnvironment.get_execution_environment(cfg)

    # En PyFlink, los JARs del conector deben estar disponibles; usa env.add_jars(...) si es necesario.
    # env.add_jars("file:///opt/flink/lib/flink-connector-kafka-<VERSION>.jar")

    # Habilitar puntos de control explícitamente también (los trabajos pueden sobrescribir valores predeterminados)
    env.enable_checkpointing(60_000)
    env.set_parallelism(4)
    return env


def main():
    env = build_env()

    source = (
        KafkaSource.builder()
        .set_bootstrap_servers("kafka:9092")
        .set_topics("events.raw")
        .set_group_id("sesiones-tiempo-real-v1")
        .set_value_only_deserializer(SimpleStringSchema())
        .set_starting_offsets(KafkaOffsetsInitializer.earliest())
        .build()
    )

    watermark_strategy = (
        WatermarkStrategy.for_bounded_out_of_orderness(Duration.of_seconds(10))
        .with_timestamp_assigner(EventTimeFromJson())
    )

    stream = (
        env.from_source(source, watermark_strategy=watermark_strategy, source_name="kafka-events-raw")
        .key_by(lambda s: json.loads(s)["user_id"])
        .process(RollingCount(), output_type=Types.STRING())
    )

    record_serializer = (
        KafkaRecordSerializationSchema.builder()
        .set_topic("events.enriched")
        .set_value_serialization_schema(SimpleStringSchema())
        .build()
    )

    sink = (
        KafkaSink.builder()
        .set_bootstrap_servers("kafka:9092")
        .set_record_serializer(record_serializer)
        .set_delivery_guarantee(DeliveryGuarantee.EXACTLY_ONCE)
        .set_transactional_id_prefix("sesiones-tiempo-real-txn")
        .build()
    )

    stream.sink_to(sink)

    env.execute("sesiones-tiempo-real-pyflink")


if __name__ == "__main__":
    main()

Las llamadas a API anteriores coinciden con el patrón de uso del constructor KafkaSource de PyFlink y los campos requeridos. Para garantías de entrega, la documentación de KafkaSinkBuilder de PyFlink dice explícitamente que para DeliveryGuarantee.EXACTLY_ONCE debes establecer el prefijo de ID transaccional. Para marcas de tiempo/marcadores de tiempo, la documentación de marcadores de tiempo de Flink explica la asignación de marcas de tiempo y la generación de marcadores de tiempo como el mecanismo para procesar el tiempo de evento, y PyFlink proporciona una API WatermarkStrategy que refleja este modelo.

Go no tiene una API de autoría de trabajos de Flink nativa como Java/Python, por lo que los sistemas de Go típicamente se integran con Flink a través de:

  • Kafka (u otros brokers) como ingesta/salida.
  • La API REST de Flink para acciones operativas (carga de JARs, inicio de trabajos, consulta de estado del trabajo, disparo de puntos de guardado, reescalado).

Para la configuración de Kafka y patrones de desarrollo local, consulte Inicio rápido de Apache Kafka - Instalación de Kafka 4.2 con CLI y ejemplos locales.

Ejemplo de productor/consumidor de Kafka de Go (kafka-go)

package main

import (
	"context"
	"log"
	"time"

	"github.com/segmentio/kafka-go"
)

func main() {
	ctx := context.Background()

	// Productor: escribir eventos crudos
	writer := &kafka.Writer{
		Addr:         kafka.TCP("kafka:9092"),
		Topic:        "events.raw",
		RequiredAcks: kafka.RequireAll,
	}
	defer writer.Close()

	err := writer.WriteMessages(ctx, kafka.Message{
		Key:   []byte("user:u1"),
		Value: []byte(`{"user_id":"u1","event_time_ms":1710000000000,"event":"click"}`),
		Time:  time.Now(),
	})
	if err != nil {
		log.Fatal(err)
	}

	// Consumidor: leer eventos enriquecidos
	reader := kafka.NewReader(kafka.ReaderConfig{
		Brokers:  []string{"kafka:9092"},
		Topic:    "events.enriched",
		GroupID:  "go-debug-consumer",
		MinBytes: 1e3,
		MaxBytes: 10e6,
	})
	defer reader.Close()

	msg, err := reader.ReadMessage(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("enriched key=%s value=%s\n", string(msg.Key), string(msg.Value))
}

Este es código de “plomería”, pero es la superficie de integración práctica más común: los temas de Kafka son la frontera entre Flink y servicios personalizados.

La API REST de Flink es parte del servidor web del JobManager y escucha en el puerto 8081 por defecto (configurable vía rest.port).

La especificación OpenAPI oficial para el dispatcher incluye /jars/upload y establece explícitamente:

  • La carga de JAR debe enviarse como datos multi-parte
  • asegúrate de que el encabezado Content-Type se establezca en application/x-java-archive
  • proporciona un ejemplo de curl usando -F jarfile=@path/to/flink-job.jar

Un fragmento de Go práctico para cargar un JAR:

package flink

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

func UploadJar(ctx context.Context, flinkBaseURL, jarPath string) (*http.Response, error) {
	f, err := os.Open(jarPath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var body bytes.Buffer
	w := multipart.NewWriter(&body)

	part, err := w.CreateFormFile("jarfile", "job.jar")
	if err != nil {
		return nil, err
	}
	if _, err := io.Copy(part, f); err != nil {
		return nil, err
	}
	if err := w.Close(); err != nil {
		return nil, err
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, flinkBaseURL+"/jars/upload", &body)
	if err != nil {
		return nil, err
	}

	// Importante: límite multi-parte
	req.Header.Set("Content-Type", w.FormDataContentType())

	// Algunos clientes también establecen "Expect:" similar al ejemplo de curl en la especificación.
	req.Header.Set("Expect", "")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode >= 300 {
		return resp, fmt.Errorf("upload failed: %s", resp.Status)
	}
	return resp, nil
}

Este código está guiado por la descripción OpenAPI de la API REST para /jars/upload, incluido su requisito multi-parte y referencia curl.

Para ejecutar un JAR previamente cargado, Flink expone /jars/{jarid}/run y admite pasar argumentos del programa mediante parámetros de consulta (y/o cuerpo JSON).

Endpoints operativamente valiosos que probablemente automatizarás:

  • /jobs y /jobs/{jobid} para listar e inspeccionar el estado del trabajo
  • /jobs/{jobid}/savepoints para activar puntos de guardado (disparo asíncrono + sondeo)
  • /jobs/{jobid}/rescaling para activar reescalado
Preocupación PyFlink (trabajos de Python) Go (servicios alrededor de Flink)
Autoría de lógica de Flink Autoría nativa mediante APIs DataStream/Table; soporta estado + temporizadores Sin API nativa de Flink; implementar lógica en Flink (Java/Python) e integrar externamente
Conectores/dependencias Debes enviar JARs del conector vía pipeline.jars, add_jars o --jarfile No aplicable (no estás ejecutando dentro de Flink), pero gestionas clientes de Kafka/DB
Ingesta/salida Constructores KafkaSource/KafkaSink en PyFlink Bibliotecas de productor/consumidor de Kafka; patrones estándar de microservicios
Automatización de operaciones También puede llamar a endpoints REST de Flink A menudo posee la automatización: cargar JAR, implementar, reescalar, activar punto de guardado vía REST

Flink admite la exportación de métricas configurando reportadores de métricas en el archivo de configuración de Flink; estos reportadores se instancian en JobManager y TaskManagers.

Para Prometheus, Flink expone métricas en formato Prometheus cuando se configura con metrics.reporter.prom.factory.class: org.apache.flink.metrics.prometheus.PrometheusReporterFactory en un entorno de versión de Flink admitido.

Generalmente combinas esto con ServiceMonitors de Kubernetes (Operador de Prometheus) o con tu stack de monitoreo gestionado.

Escalado: paralelismo, ranuras y escalado automático basado en operadores

El modelo de programación de Flink define recursos de ejecución mediante ranuras de tarea, y cada ranura puede ejecutar una tubería de tareas paralelas.

Para el escalado manual, la API REST proporciona un endpoint de reescalado para un trabajo (/jobs/{jobid}/rescaling) como una operación asíncrona.

Si estás en Kubernetes con el Operador de Kubernetes de Flink, el proyecto del operador anuncia un “Escalador automático de trabajos de Flink” como parte de su conjunto de características, lo cual vale la pena evaluar si tus cargas de trabajo varían sustancialmente.

Copias de seguridad y actualizaciones seguras: puntos de control y puntos de guardado

Los puntos de control son para recuperación automatizada y son gestionados por Flink; los puntos de guardado son para operaciones de ciclo de vida impulsadas por el usuario (detener/reanudar/bifurcar/actualizar).

Desde una perspectiva de SRE:

  • Utiliza puntos de control para “mantener la tubería en ejecución a través de fallos”.
  • Utiliza puntos de guardado para “desplegar una nueva versión sin perder el estado”.

La API REST de Flink también admite activar puntos de guardado de forma asíncrona, lo cual es útil para flujos de trabajo de estilo GitOps “desplegar → activar punto de guardado → actualizar”.

CI/CD: GitOps + Helm + presentación de trabajos REST

Para Kubernetes:

  • Mantén la instalación del operador y tus CRs de FlinkDeployment en Git, despliega mediante Argo CD/Flux y versiona las imágenes de contenedor por compilación. La documentación de Helm del operador discute explícitamente “Trabajar con Argo CD”.

Para clústeres independientes/sesión:

  • Utiliza los endpoints de carga y ejecución de JAR de la API REST de Flink para implementaciones de artefactos inmutables.

También nota un interruptor de seguridad/operaciones sutil pero valioso: web.submit.enable gobierna las cargas a través de la interfaz web, pero la documentación señala que incluso cuando está deshabilitado, los clústeres de sesión siguen aceptando presentaciones de trabajos a través de solicitudes REST; esto es relevante al endurecer superficies de UI mientras se retiene la automatización de CI/CD.

Los sistemas de LLM a menudo son tan buenos como su contexto en tiempo real. Flink encaja en los stacks de LLM/IA como el componente que produce características, incrustaciones y agregados conductuales “siempre frescos”.

Un patrón común es:

  • ingerir acciones/eventos de usuarios,
  • agregar sesiones y preferencias,
  • producir tareas de generación de incrustaciones,
  • escribir incrustaciones en un almacén vectorial y/o almacén de características.

La documentación de gestión de dependencias de PyFlink cita explícitamente “predicción de aprendizaje automático” y la carga de modelos de ML dentro de UDFs de Python (para ejecución en clúster remoto), lo cual mapea directamente a enfoques de “inferencia en línea dentro de operadores de Flink”.

Actualizaciones de almacén de características en línea para recomendación y clasificación

El modelo de estado clave y puntos de control de Flink está construido para mantener el estado del operador a través de eventos y recuperarlo de manera fiable. Eso es una coincidencia natural para la computación continua de características (tasas rodantes, conteos, métricas de decaimiento temporal) que los recomendadores posteriores necesitan.

Compensaciones prácticas de latencia/consistencia para tuberías de IA

Si tu arquitectura requiere semántica exactamente-una vez de extremo a extremo (por ejemplo, evitar actualizaciones de características duplicadas o eventos de facturación duplicados), estructurarás sumideros y fuentes alrededor de puntos de control y garantías transaccionales.

En stacks basados en Kafka específicamente:

  • El conector de Kafka de Flink puede ofrecer garantías exactamente-una vez cuando se habilitan los puntos de control y se configuran las opciones de garantía de entrega.
  • Kafka Streams también soporta semántica exactamente-una vez (EOS), lo cual es relevante si tu “tubería de características de IA” es lo suficientemente pequeña para vivir dentro del código de la aplicación en lugar de un clúster de Flink.

Flink como constructor de contexto de IA en tiempo real

Este diagrama se basa en los primitivos centrales de Flink: procesamiento de tiempo de evento (marcadores de tiempo), backends de estado (state.backend.type y estado local gestionado por el sistema) y mecanismos de puntos de control/guardado para tolerancia a fallos y operaciones.