Kafka Streams와 ksqlDB 정복 : ksqlDB 시작하기(8장)

    들어가기 전

    이 글은 Kafka Streams와 ksqlDB 정복 8장을 공부하며 작성한 글입니다.

     


    8.1 ksqlDB 시작하기

    ksqlDB는 카프카 스트림즈 위에 한층 더 추상화 된 녀석이다. ksqlDB와 카프카 스트림즈는 스트리밍 처리 도구로 이해를 하면 되는데 각 컴포넌트마다 서로 다른 특성이 있기 때문에 필요한 곳에 적절히 사용할 수 있어야 한다. ksqlDB는 SQL 문법으로 되어있어 진입장벽이 낮은 대신에 복잡한 스트리밍 로직을 처리하는데 있어서는 한계가 존재한다. 반면 카프카 스트림즈는 진입장벽이 상대적으로 높은 대신 더 많은 자유도를 제공한다. 그리고 ksqlDB는 카프카 스트림즈가 한층 더 추상회 된 도구이기 때문에 카프카 스트림즈를 이해하는 것이 ksqlDB를 운영하는데 있어 큰 도움이 된다.


    8.2 ksqlDB는 무엇인가?

    ksqlDB는 Kafka Streams와 Kafka Connector를 하나의 시스템으로 통합하고, SQL 인터페이스를 이용해서 이것들을 손쉽게 이용할 수 있도록 구현된 Component다. SQL 인터페이스를 이용한다는 것은 진입 장벽을 더 낮게 해주고(Java를 몰라도 됨) 어플리케이션 구축, 배포, 유지 보수를 단순화 시켜준다는 의미다. 자세한 특징은 아래와 같다. 

    • SQL을 사용해 카프카 토픽을 Stream / Table로 추상화한다. 
    • SQL 구문만을 이용해 스트리밍 처리를 할 수 있다. 
    • 지속적으로 실행되면서 내보내기 쿼리(Push Query)를 이용해 새 데이터가 들어올 때 마다 Stream / Table에 결과를 내보내준다. Push Query는 내부적으로 카프카 스트림즈 어플리케이션으로 컴파일된다.
    • 외부 데이터 저장소와 ksqlDB를 통합할 필요가 있을 때 (데이터를 불러오고 쓸 필요가 있을 때), Kafka Connector를 SQL 구문을 이용해 쉽게 정의해서 ETL을 할 수 있다. 

    8.3 언제 ksqlDB를 사용할까? (versus 카프카 스트림즈) 

    ksqlDB는 카프카 스트림즈에 비해 확실히 상위 추상화된 컴포넌트다. 따라서 사용하기에는 더 간편하다. 그렇다면 항상 ksqlDB가 선호되는 것일까? 그렇지는 않다. 왜냐하면 상위 추상화는 확장 범위가 하위 추상화에 비해 상대적으로 낮기 때문이다. ksqlDB를 사용했을 때의 이점을 살펴보면 아래와 같다. 

    • 대화형 작업 환경이 가능하다. ksqlDB는 CLI, REST 서비스를 제공하고, 이곳에 SQL 쿼리를 제출해 스트림 처리 어플리케이션을 삭제 / 통합하는 작업을 런타임에서 할 수 있다.
    • 코드 유지보수가 손쉽다. SQL로 스트림 처리 토폴로지를 표현하므로 유지 보수할 코드가 훨씬 적어진다. 
    • 낮은 진입장벽. SQL 기반이기 때문에 새롭게 배워야 할 것들이 적은 편이다.
    • 단순화 된 아키텍처. Kafka Connector 관리와 데이터 변환을 단일 시스템(ksqlDB)에 결합해 아키텍쳐를 단순화 했다. 
    • 향상된 개발 생산성. 적은 코드로 스트림 처리 어플리케이션을 표현할 수 있다. 

    그렇다면 ksqlDB 대신에 카프카 스트림즈를 언제 사용하는 것이 좋을까? 

    • 기본적으로는 SQL로 표현 가능한 스트림 처리 어플리케이션은 ksqlDB를 사용한다. 
    • 많은 UDF를 사용하고 있는 경우는 적절한 추상화 수준의 운영인지 점검하고, 카프카 스트림즈로 바꿀 필요가 있다. 
    • 아래 기능이 필요할 때는 카프카 스트림즈로 바꿀 필요가 있다.
      • 하위 수준에서 어플리케이션 상태 접근
      • 특정 데이터를 대상으로 주기적인 함수 실행 (Punctuator)
      • ksqlDB에서 지원하지 않는 데이터 포멧 처리
      • 유연한 어플리케이션 프로파일링 / 모니터링
      • SQL로 표현할 수 없는 비즈니스 로직 처리

    8.4 ksqlDB의 통합 (Kafka Streams / Kafka Connector)

    출처 : Mastering Kafka Streams and ksqlDB

    기본적으로 ksqlDB Server의 쿼리는 다음과 같이 되어있다. 내부적으로는 Stream / Table이 존재하고 Push 쿼리는 Stream / Table에 가능하다. 그리고 Pull 쿼리는 Aggregated Table에만 가능하다. 

    • Push Query : Stream과 Table에서 Push 쿼리를 이용해서 새로운 데이터가 발생할 때 마다 Client에 데이터를 계속 보내준다. 즉, 하나의 쿼리를 실행해두면 요청이 없어도 ksqlDB로 메세지를 계속 보내준다.
    • Pull Query : ksqlDB는 Materialied View를 구현했고, 이것은 집계 쿼리 형태의 Table이 된다. Client가 Materialized View에 요청을 할 때 마다 응답으로 현재 상태(Snapshot)에 대한 데이터를 내려준다. 내부적으로 카프카 스트림즈와 StateStore를 사용하고 Key 값으로 Value를 조회하는 형태가 되어 RDBMS와 유사한 동작을 보여준다.

     

     

     

    출처 : Mastering Kafka Streams and ksqlDB

    ksqlDB는 카프카 커넥터도 함께 통합했다. ksqlDB는 카프카 커넥터를 이용해서 카프카가 아닌 다른 곳에서 데이터를 읽어오거나 데이터를 넣어줄 수 있다. 즉, 카프카 커넥터를 이용해서 카프카가 아닌 자원을 Sink / Source 노드로 각각 사용할 수 있게 되었다. ksqlDB에서는 SQL문을 이용해서 간단하게 카프카 커넥터를 지원할 수 있다.

    CREATE SOURCE CONNECTOR 'jdbc-connector' WITH(
    	"connector.class"='io.confluent.connect.jdbc.JdbcSourceConnector',
        "connection.url"='jdbc:postgresql://localhost:5432/my.db',
        "mode"='bulk',
        "topic.prefix"='jdbc-',
        "table.whitelist"='users',
        "key"='username'
    );

    8.5 ksqlDB와 RDBMS의 공통점 / 차이점

    ksqlDB와 전통적인 SQL은 어떤 공통점이 있고, 어떤 차이점이 있을까? 이 부분을 살펴보고자 한다.

    공통점

    • SQL 인터페이스
      • ksqlDB는 SQL 문법, Parser, Execution Engine을 포함하고 있다. 이 말은 RDBMS처럼 SQL을 이용해 상호소통 할 수 있다는 것을 의미한다.
    • DDL과 DDM문
      • DDL문은 DB 객체를 생성하는 역할을 하고, DML문은 생성된 DB 객체에서 데이터를 읽고 조작할 때 사용한다. RDBMS와 ksqlDB 모두 DDL / DDM문을 제공한다. 
    • 쿼리를 제출하는 네트워크 서비스와 클라이언트
      • H2 같은 RDBMS는 Web UI를 제공하고, 그곳에서 쿼리문을 작성해서 제출하면 결과를 받아볼 수 있다. ksqlDB도 ksqlDB-cli를 제공하고 이를 이용해서 쿼리를 제출하고 결과를 받아볼 수 있다. 또한 쿼리 제출을 위한 REST API까지 제공한다.
    • 스키마
      • 스키마 정의들을 포함하는 집합으로 필드 이름과 타입으로 정의한다. 또한 Postgres와 같은 유연한 데이터베이스 시스템처럼 ksqlDB도 사용자 정의 타입을 제공한다.
    • 물리화된 뷰
      • 전통적인 DB에서 읽기 성능을 최적화하기 위해 필요에 따라 Materialized View를 생성한다. Materialized View는 열성적으로 관리(데이터가 도착하자마자 생성) 하기도 하는데, 열성적으로 관리되는 Materialized View는 ksqlDB가 데이터를 표현하는 방식과 유사하다.
    • 데이터 변환에 사용되는 내장함수와 연산자들
      • ksqlDB도  +, -, /, * 등 여러 연산자를 제공하다. 뿐만 아니라 UDF 등을 이용해서 필요한 함수를 확장할 수 있다. 
    • 데이터 복제
      • 대부분의 RDBMS는 Leader - Follower 기반의 복제를 사용한다. ksqlDB, Kafka Streams, Kafka는 동일하게 Leader - Follower 기반으로 복제를 상속한다. 대화형 모드로 배포된 ksqlDB는 Command Topic이라는 내부 토픽에 쿼리들을 내보내는 문장 기반(statement-base) 복제를 사용한다. 이를 통해 단일 ksqlDB 클러스터 내의 여러 노드가 동일 쿼리를 실행할 수 있도록 보장한다. 

     

    차이점 

    • 강화된 DDL, DML문
      • 전통적인 RDBMS는 DDL / DDM문이 테이블 데이터의 모델링과 쿼리에 맞춰져있다. 그렇지만 ksqlDB는 스트림 / 테이블 이중성을 인식하고 스트림과 테이블의 데이터를 모델링하고 쿼리하는 것을 지원한다. ksqlDB는 또한 DDL문으로 커넥터 생성까지 지원한다.
    • 내보내기 쿼리 (Push Query)
      • 전통적인 RDBMS의 쿼리 형태는 현재 데이터 스냅샷에 쿼리를 던지고 요청을 만족하는 형태다. ksqlDB는 이런 형태의 쿼리(pull Query)도 지원하지만, 무한 이벤트 스트림에서 동작하므로 새로운 데이터를 수신할 때 마다 결과를 내보내는 수 개월 이상 지속될 수 있는 쿼리도 지원한다. 
    • 단순 쿼리 기능
      • ksqlDB는 열성적으로 관리되는 물리화 된 뷰를 push Query로 지속적으로 실행하거나, pull query로 상호 동작하며 실행할 수 있도록 특화된 DB다. 
    • 좀 더 정교한 스미카 관리 전략
      • ksqlDB는 SQL을 이용해서 스키마를 정의할 수 있다. 그렇지만 Schema Registry를 이용해서 스키마를 저장할 수도 있다. 스키마 레지스트리는 스키마 진화 지원/ 호환성 보장, 데이터 크기 최적화, 자동 컬럼 이름/ 데이터 타입 추론 그리고 다른 시스템과의 쉬운 통합 같은 이점을 제공한다.
    • 고가용성, 장애복구
      • ksqlDB는 카프카 / 카프카 스트림즈와 동일한 장애 복구 기능을 가지고 있다. 따라서 장애를 쉽게 복구한다.
    • 로컬 또는 원격 저장소
      • ksqlDB로 처리한 데이터는 카프카에 존재하며, 테이블을 사용할 때는 Local StateStore에 저장된다.  연산이 필요한 데이터는 모두 Local StateStore에 저장되어있기 때문에 원격 저장소에 접근하지 않아도 괜찮아 빠르게 처리할 수 있다. 동기화 커밋, 수신 확이늨 파카가 자체로 처리하고 저장 계층(Broker)이 SQL 엔진으로부터 독립되어 있어 수평 확장이 가능하다. 
    • 일관성 모델
      • 전통적인 RDBMS는 ACID를 추구한다. 그렇지만 ksqlDB는 Eventually Consistency, Async Consistency 모델을 추구한다. 

     


    8.6 아키텍쳐

    ksqlDB는 카프카 스트림즈 위에 만들어져 있으며, ksqlDB를 위해 특화된 몇 개의 컴포넌트들이 존재한다. 크게 ksqlDB 서버, SQL 엔진, REST 서비스가 존재한다. 

     

    ksqlDB 서버 

    ksqlDB 각 서버는 카프카 스트림즈 어플리케이션의 단일 인스턴스와 개념적으로 유사하다. 각 ksqlDB서버는 ksql.service.id 설정으로 여러 ksqlDB 서버에 분산 배포될 수 있다. 서로 협업하는 ksqlDB 서버 그룹을 ksqlDB 클러스터라고 부른다. ksql.service.id가 같은 녀석들끼리 하나의 ksqlDB 클러스터로 묶여서 동작한다. 

    출처 : Mastering Kafka Streams and ksqlDB

    동일한 ksql.service.id를 가지는 녀석들은 동일한 카프카 컨슈머 그룹(ksql.service.id 자체가 카프카 컨슈머 그룹이름으로 사용되지는 않음)으로 묶인다. 따라서 확장이 필요할 때는 ksql.service.id가 같은 ksqlDB 인스턴스를 더 생성해주면 되고, 동일한 카프카 컨슈머 그룹이기 때문에 리밸런싱이 일어나게 된다.

     

    SQL 엔진

    ksqlDB에는 SQL 엔진이 존재한다. SQL 엔진은 SQL문을 파싱하고 하나 이상의 카프카 스트림즈 토폴로지로 변환한 후, 최종적으로 카프카 스트림즈 어플리케이션으로 실행할 책임을 가진다. ksqlDB는 Parse된 AST(Abstract Syntax Tree)의 각 노드에 방문해서 발견한 토큰을 사용해 카프카 스트림즈 Topology를 구축한다. 예를 들어 쿼리에 WHERE절이 포함되어 있다면, ksqlDB는 filter Processor를 이용해서 토폴로지를 구성한다. SQL 엔진이 쿼리 실행에 필요한 프로세서 토폴로지를 생성하면, 최종적으로 카프카 스트림즈 어플리케이션이 실행된다. 

     

    REST 서비스

    ksqlDB는 SQL 엔진과 상호 동작할 수 있는 REST 엔터페이스를 포함하고 있다. ksqlDB CLI, ksqlDB Ui, 쿼리를 엔진에 제출하고 기타 SQL문들을 실행하는데 주로 사용된다. 기본적으로 ksqlDB의 REST 서비스는 아래 설정을 따른다.

    listeners=http://0.0.0.0:8088
    ssl.keystore.location=/path/to/ksql.server.keystore.jks
    ssl.keystore.password=...
    sssl.key.password=...

    REST API는 대화형 모드를 사용할 때는 활성화 되지만, Headless 모드를 사용할 때는 비활성화 된다. 아래에서 볼 수 있듯이 ksqlDB CLI, ksqlDB Ui는 REST API에 쿼리를 제출하고, REST API는 쿼리를 SQL Engine에게 알려준다. 


    8.7 배포 모드

    ksqlDB는 배포 관점에서 대화형 모드(Interactive Mode), 헤드리스 모드(Headless Mode)를 지원한다. 각 배포 모드가 어떤 특성을 가지는지 알아본다.

     

    8.7.1 대화형 모드(Interactive Mode) : Default Mode

    대화형 모드는 REST API가 활성화 되어있어서 새로운 쿼리를 언제든지 제출할 수 있다. 이 말은 운영 환경에서도 새로운 쿼리가 생성, 수정, 삭제 될 수 있다는 것을 의미한다. 그리고 대화형 모드는 Default로 사용된다.

    출처 : Mastering Kafka Streams and ksqlDB

    대화형 모드의 핵심 특징 중 하나는 SQL 엔진으로 제출하는 모든 쿼리는 명령 토픽(command Topic)이라는 내부 토픽에 저장된다. ksqlDB 클러스터의 다른 ksqlDB 인스턴스는 명령 토픽으로부터 쿼리를 읽어서 자기 자신에게도 동일한 쿼리를 실행하도록 한다. 즉, command Topic을 이용한 statement-base replication을 통해서 ksqlDB 클러스터의 모든 인스턴스가 동일한 쿼리 실행을 보장한다. 

    8.7.1.1 Command Topic

    대화형 모드에서 ksqlDB는 Command Topic을 이용해서 서버들끼리 명령문을 공유한다. Command Topic은 모든 SQL 명령문을 보관한다. 이 때, 재시작 되는 ksqlDB, Upgrade 되는 ksqlDB에 모두 적용될 수 있도록 특정 메타 데이터가 함께 포함되어서 Command Topic에 저장된다. 위에서 볼 수 있듯이 각 ksqlDB 서버는 Command Topic에서 Statement를 읽어서 동일한 쿼리를 실행해준다. 

    Command Topic은 _confluent-ksql-<service id>command_topic의 이름으로 자동 생성된다. 이 때, service id는 ksql.service.id 값과 동일하다.

     

    8.7.2 헤드리스 모드

    헤드리스 모드에서는 REAT API가 비활성화 되기 때문에 쿼리를 제출할 수 없다. 즉, 쿼리의 실행, 수정, 삭제가 실행되지 않는다. 주로 상용 환경에서 많이 사용될 가능성이 높다. 헤드리스 모드를 사용하기 위해서는 ksqlDB의 queries.file을 설정해주기만 하면 된다. 

    queries.file=/apth/to/query.sql

    예를 들어 위처럼 SQL 엔진이 실행할 영구 쿼리를 정의하고, 그것을 실행하도록 설정해주면 된다. 헤드리스 모드는 statement-base replication을 하기 위해 command topic(명령 토픽)을 사용하지는 않는다. 그렇지만 내부 메타 데이터를 config topic에 설정하고, 이걸 기반으로 복제한다.

    출처 : Mastering Kafka Streams and ksqlDB

     

    8.7.2.1 Config Topic

    헤드리스 모드에서는 각 서버에 SQL 파일을 제공해서 SQL을 실행할 수 있도록 한다. 그렇지만 ksqlDB 서버가 재시작되거나 업그레이드 될 때 쿼리를 호환 가능하게 빌드할 수 있도록 특정 메타 데이터가 필요하다. ksqlDB는 이런 메타 정보를 내부 토픽인 'Config topic'에 저장해서 모든 클러스터에 쿼리가 실행될 수 있도록 한다. 

    Config Topic은 _confluent-ksql-<service id>_configs로 생성된다. 이 때, service id는 ksql.service.id 값과 동일하다.

    댓글

    Designed by JB FACTORY