Kafka - 데이터 미러링(1): 다중 클러스터 아키텍처


대부분의 DB 에서는 DB 서버 간 지속적으로 데이터를 복사하는 것을 복제(replication)라고 한다.

비슷하게 같은 클러스터에 속한 카프카 노드 간의 데이터 교환을 복제라고 하고, 카프카 클러스터 간의 데이터 복제는 미러링(mirroring) 이라고 한다.
아파치 카프카에는 클러스터 간 데이터 복제를 수행하기 위한 툴로 미러메이커(MirrorMaker) 를 포함하고 있다.

이 포스트에서는 아래의 내용에 대해 알아본다.

  • 클러스터 간 데이터의 일부 혹은 전부를 미러링하는 방법
  • 클러스터 간 미러링의 활용 사례
  • 이런 사례를 구현하기 위해 사용되는 아키텍처와 각 아키텍처의 장단점

목차


개발 환경

  • mac os
  • openjdk 17.0.12
  • zookeeper 3.9.2
  • apache kafka: 3.8.0 (스칼라 2.13.0 에서 실행되는 3.8.0 버전)

1. 클러스터 간 미러링 사례

  • 지역 및 중앙 클러스터
    • 하나의 기업이 지리적으로 분산된 지역, 대륙 간에 하나 이상의 데이터센터를 가지고 있으며, 각 데이터센터에 카프카 클러스터가 설치되어 있는 경우
    • 어떤 애플리케이션은 로컬 클러스터만 사용하는 것으로도 충분하지만, 여러 데이터센터의 데이터를 필요로 하는 애플리케이션도 있음 (그게 아니라면 데이터센터 간 미러링 솔루션이 필요가 없음)
    • 고전적인 사례로는 수요와 공급에 따라 가격을 조정해야 하는 경우가 있음
      • 사무실을 운영 중인 각 도시에 데이터센터를 갖고 있고, 각 지역의 수요와 공급에 대한 정보를 수집하여 그에 따라 가격을 조정함
      • 이 모든 데이터는 나중에 중앙 클러스터로 미러링되어 회사 단위의 수익 보고를 할 때 사용할 수 있음
  • 고가용성(HA, High Availability) 과 재해 복구(DR, Disaster Recovery)
  • 규제
    • 여러 나라에서 사업을 운영하는 경우 국가별로 다른 법적, 규제적 요구 조건을 따르기 위해 나라마라 있는 카프카 클러스터별로 서로 다른 설정과 정책을 시행함
    • 예를 들어 어떤 데이터는 엄격한 접근 설정과 함께 서로 분리된 클러스터에 저장한 후 그 중 일부는 보다 개방적인 접근 설정이 되어있는 클러스터로 복사
    • 규제 정책이 지역별로 데이터의 보존 기한을 결정하기 때문에 이를 준수하기 위해 데이터를 서로 다른 설정을 가진 클러스터에 나눠서 저장해야 함
  • 클라우드 마이그레이션
    • 많은 회사들은 사내 데이터센터와 클라우드 공급자 모두를 사용하여 비즈니스를 운영함
    • 이에 따라 애플리케이션이 클라우드 공급자의 여러 리전에서 실행되기도 함
    • 이런 경우 각 온프레미스 데이터센터와 각 클라우드 리전에 최소 1개의 카프카 클러스터가 존재하는 것이 보통임
    • 이런 카프카 클러스터들은 데이터센터 간 데이터를 효율적으로 전송하기 위해 각 데이터센터나 리전에 위치한 애플리케이션에 의해 사용됨
    • 예를 들어 카프카 커넥트를 사용하여 DB 의 변경 내역을 로컬 카프카 클러스터로 보낸 후 이를 다시 클라우드 환경에서 실행되는 카프카 클러스터로 미러링함으로써 새로운 애플리케이션이 이 데이터를 사용
    • 이런 방식은 데이터센터 간 트래픽을 관리하고, 보안 유지, 비용 절감을 가능하게 해 줌

2. 데이터센터 간 통신의 현실적 문제

데이터센터 간 통신에서 현실적으로 고려해야 할 사항들에 대해 간략히 살펴본다.

  • 높은 지연(High Latency)
    • 두 카프카 클러스터 간의 거리나 네트워크 hop 개수가 증가함에 따라 통신 지연 역시 증가함
  • 제한된 대역폭(Limited Bandwidth)
    • 광역 통신망(WAN, Wide Area Network)은 일반적으로 단일 데이터센터 내부보다 훨씬 낮은 대역폭을 가지며, 사용 가능한 대역폭 역시 시시각각 변하는 특성이 있음
    • 지연이 높아지는 만큼 사용 가능한 대역폭을 최대한 활용하는 것 역시 더 어려워짐
  • 더 높은 비용(Higher Cost)
    • 대역폭이 제한되어 있을 뿐 아니라 이를 확장하는데 엄청난 비용이 듦
    • 공급자가 서로 다른 데이터센터, 리전, 클라우드와의 데이터전송에 과금을 함
    • 위와 같은 이유로 클러스터 간 통신에는 더 많은 비용이 들어감

아파치 카프카의 브로커와 클라이언트는 하나의 데이터센터 안에서 실행되도록 설계되었기 때문에 브로커와 클라이언트 사이에 낮은 지연, 높은 대역폭을 가진 상황을 상정하여 각종 타임아웃 기본값이나 다양한 버퍼의 크기 등이 여기에 맞춰서 설정되어 있다.
이러한 이유로 카프카 브로커를 서로 다른 데이터센터에 나눠서 설치하는 것은 권장하지 않는다.

대부분의 경우 원격 데이터센터에 데이터를 쓰는 것은 피하는 것이 좋다.
원격 데이터센터에 데이터를 쓰게 되면 더 높은 통신 지연과 네트워크 에러 발생 가능성을 감수해야 한다.
물론 프로듀서 전송 재시도 횟수를 늘림으로써 네트워크 에러를 처리할 수 있고, 전송을 시도하는 사이 레코드를 저장해두는 버퍼의 크기를 증가시킴으로써 높은 통신 지연에 대응은 가능하다.

원격 클러스터 간 복제가 필요할 경우 원격 브로커 간의 통신, 원격 프로듀서-브로커 통신을 제외하면 원격 브로커-컨슈머 통신이 남는데 이 방식은 가장 안전한 형식의 클러스터 간 통신이다.
즉, 클러스터 간 데이터를 복제할 때 브로커-컨슈머 통신이 다른 통신 방식보다 더 안전하다.

  • 원격 브로커-브로커 통신
    • 원 클러스터의 브로커가 다른 클러스터의 브로커와 직접 통신하여 데이터 동기화
    • 브로커 간 직접 연결은 양쪽 네크워크 간 높은 신뢰가 필요하고, 보안 설정이 느슨하면 클러스터 자체가 외부 공격에 취약해질 수 있음
  • 원격 프로듀서-브로커 통신
    • 한 클러스터의 프로듀서(데이터 생성자)가 원격 클러스터의 브로커로 데이터 직접 전송
    • 프로듀서-브로커 통신은 네트워크 지연, 인증 문제, 트래픽 관리 등에서 취약할 수 있음
  • 원격 브로커-컨슈머 통신
    • 한 클러스터의 브로커에서 다른 클러스터의 컨슈머(데이터 소비자)가 데이터를 가져가는 방식
    • 최소한의 권한 요구
      • 데이터 소비 측에서 작업을 수행하므로 원격 클러스터 브로커에 대한 직접적인 쓰기 권한을 요구하지 않으므로 클러스터 간 상호 작용이 줄어들어 보안 측면에서 상대적으로 안전함
      • 즉, 원 클러스터의 브로커에 대한 읽기 권한만 요구하고, 쓰기 권한은 필요하지 않음
    • 네트워크 구성 간소화
      • 컨슈머가 데이터를 가져오는 방식으로 동작하므로, 브로커 간 직접 연결(브로커-브로커 통신)에서 발생할 수 있는 복잡한 네트워크 설정 문제가 줄어듦
    • 보안 강화
      • 원격 브로커에 대한 외부 노출을 최소화하여 잠재적인 보안 위험을 줄임
    • 미러메이커와 같은 도구는 이 방식을 사용하여 원격 데이터를 안전하게 복제함

원격 브로커-컨슈머 통신을 사용하게 되면 네트워크 단절이 일어났을 때 컨슈머가 데이터를 읽을 수는 없지만, 레코드 자체는 연결이 복구되어 컨슈머가 읽을 수 있게 될 때까지 카프카 브로커 안에 안전하게 저장되어 있으므로 네트워크 단절로 인한 데이터 유실의 위험성이 없다.
물론 대역폭이 제한되어 있으므로 하나의 데이터센터 안에 위치한 여러 애플리케이션이 다른 데이터센터에 위치한 카프카 브로커들로부터 데이터를 읽어와야 하는 경우에는 각 데이터센터에 카프카 클러스터를 설치한 후 필요한 데이터를 미러링하는 방식이 같은 데이터를 광역 통신망을 통해 여러 번 읽어오는 것보다는 낫다.


3. 다중 클러스터 아키텍처

데이터센터 간 통신을 위해 카프카를 조정하는 방법에 대해 이야기한다.
여기서 논의되는 아키텍처 대부분 해당하는 원칙은 아래와 같다.

  • 하나의 데이터센터당 한 개 이상의 클러스터 설치
  • 각 데이터센터 간에 각각의 이벤트(에러로 인한 재시도는 제외)를 정확히 한 번씩 복제
  • 가능하면, 원격 데이터센터에 쓰는 것보다 원격 데이터센터에서 읽어오는 것이 더 나음

이제 1. 클러스터 간 미러링 사례 에서 본 활용 사례를 구현할 때 성공적으로 사용되어 온 공통적인 아키텍처 패턴에 대해 알아본다.


3.1. 허브-앤-스포크 아키텍처(hub-and-spokes)

허브-앤-스포크 아키텍처는 여러 개의 로컬 카프카 클러스터와 한 개의 중앙 카프카 클러스터가 있는 상황이다.

허브-앤-스포크 아키텍처

허브-앤-스포크 아키텍처는 데이터가 여러 개의 데이터센터에서 생성되는 반면, 일부 컨슈머만 전체 데이터를 사용해야 하는 경우에 사용된다.
허브-앤-스포크 아키텍처는 각 데이터센터에서 실행되는 애플리케이션에 대해서는 해당 데이터센터의 로컬 데이터만 사용할 수 있게 해줄 뿐 모든 데이터센터에서 생성된 전체 데이터를 사용할 수 있도록 하지는 않는다.

허브-앤-스포크 아키텍처의 장점은 항상 로컬 데이터센터에서 데이터가 생성되고, 각 데이터센터에 저장된 이벤트가 중앙 데이터센터로 단 한번만 미러링된다는 점이다.
단일 데이터센터의 데이터를 처리하는 애플리케이션은 해당 데이터센터에 배치하고, 다수의 데이터센터에서 생성된 데이터를 처리하는 애플리케이션은 모든 데이터가 미러링되는 중앙 데이터센터에 배치한다.
미러링은 한 방향으로만 진행되고, 각 컨슈머는 언제나 같은 클러스터에서 데이터를 읽으므로 이 아키텍처는 배포, 설정, 모니터링이 간편하다.

이런 장점은 곧 단점도 될 수 있다.
아래의 예를 보자.
여러 도시에 지점이 있는 은행이 있고, 각 도시마다 카프카 클러스터에 고객 정보다 거래 내역을 저장하고 있다고 하자.
모든 데이터는 은행의 중앙 클러스터에 미러링된다.
고객이 만일 다른 도시에 있는 지점을 방문하게 되면 해당 지점에는 그 고객의 정보가 없으므로 해당 지점은 원격 클러스터의 데이터를 사용해야 하거나(권장되지 않는 상황) 고객 정보를 사용할 방법이 아예 없게 된다.

이러한 이유로 허브-앤-스포크 아키텍처를 사용하는 것은 다른 지역 데이터센터에서 완전히 분리된 일부 데이터를 사용하는 것으로 충분한 경우로 국한된다.

허브-앤-스포크 아키텍처를 구현할 때는 중앙 데이터센터에 각 지역 데이터센터별로 미러링 프로세스(원격 지역 클러스터의 데이터를 읽어서 중앙 클러스터에 쓰는)를 최소 한 개 이상 설정해야 한다.


3.2. 액티브-액티브 아키텍처(active-active)

액티브-액티브 아키텍처는 2개 이상의 데이터센터가 전체 데이터의 일부 혹은 전체를 공유하면서, 각 데이터센터가 모두 읽기와 쓰기를 수행할 수 있어야 하는 경우 사용된다.

액티브-액티브 아키텍처

<액티브-액티브 아키텍처 장점>

  • 인근 데이터센터에서 사용자들의 요청 처리 가능
  • 허브-앤-스포크 아키텍처와 달리 사용할 수 있는 데이터가 제한되어 발생하는 기능 제한이 없으며, 성능상의 이점도 있음
  • 데이터 중복(redundancy) 과 회복 탄력성(resilience) 이 좋음
    • 모든 데이터센터가 모든 기능을 동일하게 가지므로 한 데이터센터에서 장애가 발생하더라고 다른 데이터센터가 처리 가능
    • 이런 장애 복구는 네트워크 리다이렉션만을 필요로하며, 가장 쉽고 명료한 형태의 장애 복구임

<액티브-액티브 아키텍처 단점>

  • 데이터를 여러 위치에서 비동기적으로 읽거나 변경할 경우 발생하는 충돌을 피하는 것이 어려움
    • 이는 이벤트를 미러링하는 데 있어서의 기술적인 어려움을 포함함
      • 예를 들어 같은 이벤트가 클러스터 사이를 무한히 오가면서 반복적으로 미러링되지 않을 것이라고 확신할 수 있는지?
    • 두 데이터센터 간의 데이터 일관성을 유지하는 것이 어려움

두 데이터센터 간의 데이터 일관성에 대한 예를 보자.

예1) 사용자가 A 데이터센터에 이벤트를 쓰고, B 데이터센터로부터 이벤트를 읽어오는 상황임
A 데이터센터에 쓴 데이터가 B 데이터센터에 도착하지 않은 경우를 보자.
사용자가 장바구니에 물건을 담고(A 데이터센터) 확인을 해보니 장바구니에 해당 물건이 안 담겨있을 수 있음(B 데이터센터)
이러한 이유로 액티브-액티브 아키텍처는 각 사용자를 특정 데이터센터에 고정시킴응로써 사용자가 다른 위치에서 접속하거나 해당 데이터센터에 장애가 발생하지 않는 한 같은 사용자가 동일한 클러스터를 사용하도록 하는 방법을 마련해야 함

예2) A 데이터센터에서 발생한 이벤트에는 사용자가 물건 a 를 주문했다는 내용이 들어있고, 거의 동시에 B 데이터센터에서 발생한 이벤트에는 같은 사용자가 물건 b 를 주문했다는 내용이 들어있는 상황임
이 이벤트가 미러링되면 각 데이터센터는 2개의 이벤트를 가지게 됨
두 데이터센터에서 실행되는 애플리케이션은 이러한 상황을 처리해야 함
만일 둘 중에 올바른 것 하나를 고르기로 한다면 양쪽에서 실행되고 있는 애플리케이션들이 모두 같은 결론을 내릴 수 있도록 일관적인 규칙이 필요함
만일 두 이벤트가 모두 맞다고 판정하여 물건을 모두 배송한 후 반품 처리를 할 수도 있음 (아마존은 이런 방식으로 충돌 문제를 해결하고 있음)
하지만 주식 거래와 같은 경우엔 그렇게 할 수 없음
즉, 충돌을 최소화하고, 실제 발생했을 때 이를 어떻게 다룰 것인지에 대한 대책이 필요함

동일한 데이터셋을 여러 위치에서 비동기적응로 읽고 쓸 때 발생하는 문제를 해결할 방법이 있다면 액티브-액티브 아키텍처는 확장성, 회복성, 유연성, 가성비 모든 면에서 가장 좋은 선택이므로 매우 권장된다.
따라서 순환 복제를 방지하고, 사용자들을 대체로 같은 데이터센터 안에 유지하며, 충돌이 발생했을 때 처리하는 방법에 대해 신경쓸 필요가 있다.

<액티브-액티브 아키텍처 미러링 수행 시 주의할 점>

  • 각 쌍의 데이터센터의 양 방향에 대해 미러링 작업에 대한 복잡성
    • 데이터센터가 3개일 경우 각 데이터센터 간 양방향 미러링이 매우 복잡해짐 (A ↔ B, A ↔ C, B ↔ C)
      • 데이터센터 수가 증가할수록 미러링 작업의 복잡성은 기하급수적으로 증가함
    • 최근의 미러링 도구들은 프로세스 공유를 통해 이런 복잡성을 줄임
      • 목적지 클러스터에 대한 모든 미러링 작업을 하나의 프로세스에서 처리함
      • 예) 목적지 데이터센터가 A, B, C 일 경우 A ↔ B, A ↔ C, B ↔ C 와 같은 작업을 각각 따로 처리하는 것이 아니라 A 에서 하나의 프로세스가 모든 목적지(B, C) 에 대한 미러링 작업을 통합적으로 수행함
      • 각 쌍의 데이터센터 간 양방향 미러링을 개별적으로 관리할 필요없이 중앙 프로세스에서 통합적으로 관리하므로 복잡성이 감소
      • 동일한 프로세스에서 다수의 작업을 처리하므로 리소스 사용이 최적화되어 효율성이 향상
      • 데이터센터가 늘어나도 별도 프로세스를 추가하지 않고 기존 구조를 확장할 수 있으므로 확장성이 좋음
  • 같은 데이터가 클러스터 사이를 오가면서 끝없이 순환 미러링되는 것을 막아야 함
    • 각각의 ‘논리적 토픽’에 대해 데이터센터 별로 별도의 토픽을 두고, 원격 데이터센터에서 생성된 토픽의 복제를 막음으로써 해결 가능
      • 예) 논리적 토픽 users 는 한 데이터센터에서는 AA.users 토픽에 저장하고, 다른 데이터센터에서는 BB.users 토픽에 저장함
        미러링 프로세스는 AA 데이터센터의 AA.users 토픽을 BB 데이터센터로, BB 데이터센터의 BB.users 토픽을 AA 데이터센터로 미러링하게 됨
      • 결과적으로 각 이벤트는 한 번씩만 미러링되며, 각 데이터센터는 AA.users, BB.users 토픽 모두를 갖게 됨
      • 컨슈머가 모든 사용자 이벤트를 읽어오고 싶다면 *.users 패턴을 가진 토픽을 읽으면 됨
      • 서로 다른 네임스페이스를 가진 데이터센터들이 있다고 생각할 수도 있는데 미러메이커와 같은 미러링 툴은 이와 유사하게 네이밍 컨벤션을 사용하여 순환 미러링을 방지하고 있음

카프카 0.11.0 에 추가된 레코드 헤더(record header) 기능은 각 이벤트에 데이터가 생성된 데이터센터를 tag 할 수 있도록 한다.
헤더에 심어진 정보는 이벤트가 무한히 순환 미러링되는 것을 방지하거나, 다른 데이터센터에서 미러링된 이벤트를 별도로 처리하는 등의 용도로 사용될 수 있다.


3.3. 액티브-스탠바이 아키텍처(active-standby)

액티브-스탠바이 아키텍처는 다중 클러스터에 대한 요구 조건이 특정한 상황의 재해 대비 뿐일 때 사용한다.

액티브-스탠바이 아키텍처

장애 복구용 데이터 센터는 보통 모든 애플리케이션의 비실행 상태 복사본을 평소에 갖고 있다가 비상 시 운영자가 실제로 이 애플리케이션을 시작시키면 작동하는 식으로 동작한다.

<액티브-스탠바이 아키텍처 장점>

  • 설치가 간단함
    • 그냥 두 번째 클러스터를 설치한 뒤 첫 번째 클러스터의 모든 이벤트를 미러링하는 프로세스를 설치하기만 하면 됨
    • 데이터 접근, 충돌 처리 등의 문제에 대해 신경쓸 필요 없음

<액티브-스탠바이 아키텍처 단점>

  • 멀쩡한 클러스터를 놀려야 함
  • 카프카 클러스터 간의 장애 복구 시 일체의 데이터 유실이나 중복 없이 카프카 클러스터를 완벽하게 복구하는 것은 불가능함

재해 상황을 제외하면 대기 상태에 있는 클러스터는 분명히 자원의 낭비로 보인다.
어떤 조직에서는 프로덕션 클러스터보다 DR(Disaster Recovery) 클러스터를 훨씬 작은 크기로 생성함으로써 이 문제를 해결하려 하지만 이렇게 최소한의 크기로 설정된 클러스터가 비상시에 제대로 작동할 수 있을지 확신할 수 없기 때문에 이것은 매우 위험한 생각이다.
어떤 조직에서는 재해가 발생하지 않는 동안 읽기 전용 작업의 일부를 DR 클러스터에 옮겨서 처리하곤 하는데 이것은 사실 스포크가 하나인, 허브-앤-스포크 아키텍처의 소규모 버전을 운용하는 것과 같다.

중요한 것은 아파치 카프카에서 DR 클러스터로 어떻게 장애 복구를 하는가인데 이를 위해 SRE(Site Reliability Engineering) 팀은 정기적응로 장애 복구 훈련을 해야 한다. (보통 분기당 한 번)

<장애 복구에 필요한 것들>

  • 재해 복구 계획
  • 계획에 없던 장애 복구에서의 데이터 유실과 불일치
  • 장애 복구 이후 애플리케이션의 시작 오프셋
  • 장애 복구가 끝난 후
  • 클러스터 디스커버리

3.3.1. 재해 복구 계획

재해 복구 계획 시엔 2개의 지표를 생각해야 한다.

  • 복구 시간 목표(RTO, Recovery Time Objective)
    • 모든 서비스가 장애가 발생한 뒤 다시 작동을 재개할 때까지의 최대 시간
    • 극단적으로 낮은 수준의 RTO 는 자동화된 장애 복구(failover)에서는 가능하므로, 더 낮은 RTO 를 목표로 할 수록 수동 작업과 애플리케이션 재시작을 최소화해야함
  • 복구 지점 목표(RPO, Recovery Point Objective)
    • 장애 결과로 인해 데이터가 유실될 수 있는 최대 시간
    • 낮은 RPO 는 지연 시간이 짧은 실시간 미러링을 필요로 함
    • 아예 0 으로 하려면 동기적인 방식으로 미러링을 수행해야 함

3.3.2. 계획에 없던 장애 복구에서의 데이터 유실과 불일치

여러 카프카 미러링 솔루션들은 모두 비동기적으로 작동하기 때문에 DR 클러스터는 주 클러스터의 가장 최신 메시지를 가지고 있지 못한다.

동기적으로 동작하는 솔루션에 대해서는 3.4. 스트레치 클러스터(Stretch Cluster) 를 참고하세요.

따라서 DR 클러스터가 주 클러스터에서 얼마나 뒤처져 있는지를 항상 모니터링하고, 너무 많이 뒤처지지 않도록 해야 한다.
예) 카프카 클러스터당 초당 1,000,000 개의 메시지를 처리하고, 주 클러스터와 DR 클러스터 사이의 lag 이 5ms 이면, DR 클러스터는 주 클러스터에 비해 적어도 5,000개(1,000,000*0.005)의 메시지가 뒤처져있는 셈

따라서 예상치 못한 장애 복구는 어느 정도의 데이터 유실을 감수해야 한다.

사전에 계획된 장애 복구의 경우 주 클러스터는 먼저 멈추고, 애플리케이션을 DR 클러스터로 마이그레이션 하기 전에 미러링 프로세스가 남은 메시지를 모두 미러링할 때까지 기다림으로써 유실을 방지할 수 있다.

만일 예상치 못한 장애가 발생하여 메시지를 유실했을 경우, 미러링 솔루션들은 현재는 트랜잭션을 지원하지 않는다.
예) 여러 토픽들이 서로 연관되어 있을 경우(매출과 상품명) 일부 이벤트는 DR 클러스터에 도착하고, 나머지는 아닐 수도 있음

애플리케이션은 DR 클러스터로 마이그레이션 한 뒤 해당하는 매출 기록이 있는 상품명을 처리할 수 있어야 한다.


3.3.3. 장애 복구 이후 애플리케이션의 시작 오프셋

다른 클러스터로 장애 복구 시 어려운 점 중 하나는 다른 클러스터로 옮겨간 애플리케이션이 데이터를 읽어오기 시작해야 하는 위치를 결정하는 것이다.

이에 관해 흔히 사용되는 몇 가지 방법에 대해 알아본다.


3.3.3.1. 자동 오프셋 재설정(auto offset reset)

아파치 카프카 컨슈머는 사전에 커밋된 오프셋이 없을 경우 어떻게 작동해야 하는지를 결정하는 auto.offset.reset 설정값을 갖는다.
장애 복구 계획에 이 오프셋을 미러링하는 부분이 빠져 있다면 아래 2가지 중 하나를 선택해야 한다.

  • 데이터의 맨 처음부터 읽기 시작해서 상당한 수의 많은 데이터를 처리할 것인지?
  • 데이터의 맨 끝에서부터 읽기 시작해서 알려지지 않은 개수의 이벤트를 건너뛸 것인지?

만일 애플리케이션이 아무런 이슈없이 중복을 처리할 수 있다면 전자를 선택하는 것이 좋고, 약간의 유실이 별 문제가 안된다면 후자를 선택하는 것이 좋다.
장애 복구 중인 토픽의 맨 끝에서부터 읽기 시작하는 것은 단순한만큼 많이 사용되는 방법이다.


3.3.3.2. 오프셋 토픽 복제

컨슈머는 자신의 오프셋을 __consumer_offsets 라는 특별한 토픽에 커밋한다.(카프카 0.9 버전 이후)
이 토픽을 DR 클러스터에 미러링해 준다면, DR 클러스터에서 읽기 작업을 시작하는 컨슈머는 이전에 주 클러스터에 마지막으로 커밋한 오프셋부터 작업을 재개하게 된다.

하지만 이 방식에는 3가지 주의 사항이 따른다.

  • 주 클러스터의 오프셋이 DR 클러스터의 오프셋과 일치할 것이라는 보장이 없음
    • 주 클러스터에 데이터를 3일까지만 보관하고, 토픽이 생성된 뒤 1주일 뒤부터 미러링을 시작했다고 하면 첫 4일 동안 발생한 이벤트들은 이미 삭제되었을 것이므로 주 클러스터의 첫 번째 오프셋은 2000 과 같은 값이 될 수 있음
    • 하지만 DR 클러스터의 첫 번째 오프셋 값은 0임
    • 따라서 DR 클러스터에서 다음으로 읽어야 할 이벤트 오프셋인 2003부터 읽기를 시도해야 하는 컨슈머는 읽기에 실패할 것임
  • 토픽이 처음 생성된 시점부터 미러링을 시작해서 주 클러스터와 DR 클러스의 토픽이 모두 0부터 시작할지라도, 프로듀서 재시도로 인해 오프셋이 서로 달라질 수 있음
    • 주 클러스터와 DR 클러스터 간의 오프셋을 보존하는 대안적인 미러링 솔루션에 대해서는 추후 다룰 예정입니다. (p. 270)

  • 오프셋이 완벽히 보존되었다 할지라도, 주 클러스터와 DR 클러스터 사이에 lag 이 존재하고, 현재 미러링 솔루션은 트랜잭션 기능을 지원하지 않음
    • 따라서 카프카 컨슈머가 커밋한 오프셋이 해당 오프셋에 해당하는 레코드보다 먼저 혹은 늦게 도착할 수도 있음
    • 장애 복구 중인 컨슈머 입장에서는 커밋된 오프셋은 있는데 해당하는 레코드는 없을 수도 있는 상황이 벌어짐
    • 혹은, DR 클러스터 쪽에 마지막으로 커밋된 오프셋이 주 클러스터에 마지막으로 커밋된 오프셋보다 더 오래되었을 수도 있음
    • DR 클러스터에 커밋된 오프셋이 주 클러스터에 커밋된 오프셋보다 오래된 것이거나 재시도로 인해 DR 클러스터의 레코드 오프셋이 주 클러스터 오프셋보다 낮은 경우 어느 정도의 중복 처리는 감수해야 함
    • 또한, DR 클러스터의 마지막으로 커밋된 오프셋에 해당하는 레코드가 실제로는 존재하지 않을 때 어떻게 처리해야 할지도 결정해야 함
      (토픽의 처음부터 처리할지, 마지막부터 처리할 지)

오프셋 토픽 복제는 위와 같은 제약이 따르지만, 여전히 이 방식은 다른 방식에 비해 단순하면서도 상대적으로 적은 이벤트 중복이나 유실로 생긴 장애를 복구할 수 있도록 해준다.


3.3.3.3. 시간 기반 장애 복구: kafka-consumer-group

카프카 0.10.0 버전부터 메시지는 카프카로 전송된 시각을 가리키는 타임스탬프 값을 갖는다.
그리고 0.10.1.0 버전부터 브로커는 타임스탬프를 기준으로 오프셋을 검색할 수 있는 인덱스와 API 를 포함한다.
따라서 DR 클러스터로 장애 복구를 할 때 문제가 발생한 시점이 오전 1:10 이면 컨슈머가 오전 1:08분에 해당하는 데이터부터 처리를 재개하도록 할 수 있다.

‘오전 1:08분 시점으로 데이터가 복구되었습니다.’ 가 ‘마지막으로 커밋된 오프셋일 수도 있고 아닐 수도 있는 시점으로 복구했습니다.’ 보다 낫다.

유일한 문제는 컨슈머가 어떻게 오전 1:08분 시점의 데이터를 읽게 하는 것인가? 이다.

한 가지 방법은 애플리케이션에 해당 로직을 직접 추가하는 것이다.
즉, 애플리케이션의 시작 시간을 지정할 수 있는 사용자 설정을 추가하면 애플리케이션은 새로운 API 를 이용하여 주어진 시각에 해당하는 오프셋을 가져오고, 해당 오프셋으로 이동한 뒤 바로 그 시점부터 읽기 작업을 시작하면 된다.

카프카 0.11.0 버전부터 타임스탬프 기준 초기화를 포함하는 다양한 오프셋 초기화를 지원하는 kafka-consumer-groups 툴을 제공한다.
이 툴이 실행되는 도중에 컨슈머 그룹은 잠시 작업을 중단하였다고 실행이 완료되는 즉시 다시 시작된다.

특정 컨슈머 그룹의 모든 토픽에 대한 컨슈머 오프셋을 특정 시점으로 초기화하는 예시

$ bin/kafka-cosumer-groups.sh --bootstrap-server localhost:9092 --reset-offsets --all-topics \
 --group my-group --to-datetime 2024-01-01T01:08:00.000 --execute

시간 기반 장애 복구 방식은 장애 복구에 있어서 어느 정도의 확실성이 보장되어야 하는 경우에 권장된다.


3.3.3.4. 오프셋 변환

오프셋 토픽을 미러링할 때 가장 어려운 것은 주 클러스터와 DR 클러스터의 오프셋이 어긋날 수 있다는 점이다.

미러메이커를 포함한 미러링 솔루션들은 오프셋 변환 메타 데이터를 저장하기 위해 카프카 토픽을 사용한다.
예) 주 클러스터의 오프셋 495가 DR 클러스터의 오프셋 500에 매핑된다고 했을 때 오프셋 변환 토픽에는 (495,500) 이 저장됨
이후 중복 등의 이유로 두 오프셋 값 차이가 변해서 오프셋 596이 600 에 해당하게 되면 새로운 매핑값인 (596,600) 이 저장됨

위와 같을 때 495와 596 사이의 모든 오프셋 매핑을 저장할 필요는 없다.
즉, 이 사이에 오프셋 값 차이가 그대로 유지된다고 보고 주 클러스터의 오프셋 550 은 DR 클러스터의 오프셋 555로 매핑하는 것이다.

실제 장애 복구 작업을 수행하게 되면 타임스탬프 기준으로 오프셋을 변환하는 대신, 주 클러스터 오프셋에 매핑되는 DR 클러스터 오프셋을 찾아서 그 지점부터 작업을 재개하게 된다.
컨슈머가 저장된 매핑으로부터 가져온 새로운 오프셋을 사용하여 작업을 시작하도록 할 때 타임스탬프 혹은 오프셋 변환 데이터를 선택하여 작업할 수 있다.

오프셋 변환 방식은 커밋된 오프셋이 레코드보다 먼저 미러링되거나, 커밋된 오프셋이 제 시간에 맞춰 DR 클러스터로 미러링되지 않을 경우엔 여전히 문제가 있지만 어떠한 경우에는 적절한 방식이 될 수 있다.


3.3.4. 장애 복구가 끝난 후

장애 복구가 끝나면 이제 장애가 생겼던 주 클러스터를 DR 클러스터로 역할 변경을 해주어야 한다.

이 때 주의해야 할 점 2가지가 있다.

  • 어디서부터 미러링을 시작해야 하는가?
    • 어떠한 방법을 하던 간에 중복, 유실 등의 문제가 발생할 수 있음
  • 이전의 주 클러스터는 DR 클러스터가 가지고 있지 않은 이벤트를 가지고 있을 가능성이 높음
    • 만일 새로운 데이터를 반대 방향으로 미러링하기 시작한다면 이 여분의 이벤트들은 여전히 이전 주 클러스터 안에 있을 것이고 결국 두 클러스터의 내용이 서로 달라지게 됨

일관성과 순서 보장이 극도로 중요할 때 가장 간단한 방법은 원래 주 클러스터에 저장된 데이터와 커밋된 오프셋을 완전히 삭제한 뒤 새로운 주 클러스터에서 완전히 새것이 된 DR 클러스터로 미러링을 시작하는 것이다.


3.3.5. 클러스터 디스커버리

스탠바이 클러스터를 준비할 때 고려해야 할 것 중 하나는 장애가 발생한 상황에서 애플리케이션이 장애 복구용 클러스터와 통신을 시작하는 방법을 알 수 있게 하는 것이다.

만일 프로듀서나 컨슈머 설정에 주 클러스터 브로커들의 호스트 이름을 하드코딩했다면 이것은 어려운 일이 될 것이다.

대부분 호스트 이름을 최대한 단순하게 정한 뒤 DNS 를 써서 주 클러스터 브로커와 연결한다.
이 후 장애 상황이 오면 DNS 이름을 스탠바이 클러스터로 돌린다.

카프카 클라이언트는 클러스터 메타데이터를 얻어와서 다른 브로커의 위치를 찾기 위해 하나의 브로커에만 성공적으로 접근할 수 있으면 되기 때문에 디스커버리 서비스가 모든 브로커에 대한 IP 주소를 가질 필요는 없다. 대체로 3개의 브로커에 대한 정보만 있으면 된다.

대부분의 장애 복구 상황에서는 장애 발생 후 컨슈머 애플리케이션이 작업을 재개할 새 오프셋을 잡아주어야 한다.
복구 시간 목표(RTO, Recovery Time Objective)를 극도로 낮추기 위해 애플리케이션 재시작이 없는 완전히 자동화된 장애 복구를 하고자 한다면, 장애 복구 로직이 클라이언트 애플리케이션에 내장되어 있어야 한다.


3.4. 스트레치 클러스터(Stretch Cluster)

액티브-스탠바이 아키텍처는 카프카 클러스터에 장애가 발생했을 때 애플리케이션이 다른 클러스터와 통신하도록 함으로써 전체 업무가 마비되는 것을 방지하기 위해 사용된다.

스트레치 클러스터는 하나의 카프카 클러스터를 여러 데이터센터에 설치함으로써 데이터센터 전체에 문제가 발생했을 때 카프카 클러스터에 장애가 발생하는 것을 방지하기 위해 사용한다.

스트레치 클러스터는 다중 클러스터가 아닌 단지 하나의 클러스터이다.
결과적으로 두 클러스터를 동기화시켜주는 미러링 프로세스가 필요없다.
카프카 복제 메커니즘이 평소대로 클러스터 안의 브로커들을 동기화된 상태로 유지시켜주는데 이 방식은 동기적인 복제를 포함한다.

프로듀서는 보통 메시지가 카프카에 성공적으로 쓰여진 다음에 브로커로부터 응답을 받는데 스트레치 클러스터는 아래와 같은 방법을 통해 메시지가 두 데이터센터에 위치한 카프카 브로커 각각에게 성공적으로 쓰여진 뒤 응답이 가도록 설정할 수 있다.

  • 각각의 파티션이 하나 이상의 데이터센터에 분산해서 레플리카를 저장하도록 rack 설정
  • min.insync.replicas 설정
  • acks=all 설정

위와 같이 설정하면 각 쓰기 작업이 최소 2개의 데이터센터에서 성공한 후 응답이 간다.

컨슈머가 정의된 rack 기준으로 가장 가까운 레플리카에서 읽어올 수 있도록 client.rack 을 설정할 수 있다.
브로커는 컨슈머의 rack 설정과 자신의 rack 설정을 맞춰본 후 로컬 레플리카가 최신 상태를 갖고 있는지 확인 후 그게 아니라면 리더에게 처리를 넘긴다.
컨슈머는 로컬 데이터센터에 위치한 팔로워로부터 데이터를 읽어옴으로써 높은 처리율, 낮은 지연, 데이터센터 간 통신을 줄임으로써 비용도 낮을 수 잇다.

<스트레치 클러스터 장점>

  • 동기적인 복제
    • DR 클러스터가 주 클러스터와 언제나 100% 동기화되어 있어야 하는 경우
  • 양 데이터센터와 클러스터 안의 모든 브로커가 사용됨
    • 액티브-스탠바이 아키텍처에서 본 자원 낭비가 전혀 없음

<스트레치 클러스터가 대응할 수 없는 장애>

  • 데이터센터 전체에 장애가 난 경우는 대응할 수 있지만, 애플리케이션이나 카프카에 장애가 발생하는 경우는 커버하지 않음
  • 복잡한 운영 설정도 제한됨

스트레치 클러스터 아키텍처는 카프카(+ 주키퍼)를 높은 대역폭과 낮은 지연을 가진 회선으로 서로 연결된 최소 3개의 데이터센터에 설치할 수 있을 경우에 적합하다.
예) 한 리전 안에 3개의 가용 영역 사용, 인접한 주소에 3개의 건물

3개의 데이터센터 조건은 주키퍼 클러스터 때문이다.
주키퍼 클러스터는 홀수 개의 노드로 이루어지며, 노드 중에서 과반 이상만 작동한다면 전체 클러스터 역시 작동에 이상이 없다.
2개의 데이터센터에 홀수 개의 주키퍼 노드를 배치하면 하나의 데이터센터에 과반 이상의 노드가 배치될 수 밖에 없고, 따라서 해당 데이터센터에 문제가 생기면 주키퍼오 카프카도 연쇄적으로 장애가 발생한다.
따라서 데이터센터를 3개로 두어 어느 데이터센터에도 과반 이상의 노드가 배치되지 않도록 하는 것이다.

2.5 데이터센터 아키텍처

스트레치 클러스터 아키텍처에서 자주 사용되는 모델 중 하나는 2.5 데이터센터 아키텍처임

2.5 데이터센터 아키텍처는 2개의 데이터센터에 카프카와 주키퍼를 설치하고, 0.5 클러스터에 하나의 주키퍼 노드를 설치함으로써 데이터센터 중 하나에 장애가 발생한 경우에도 쿼럼 역할을 할 수 있도록 함


참고 사이트 & 함께 보면 좋은 사이트

본 포스트는 김병부 저자의 O’REILLY 카프카 핵심 가이드 2판를 기반으로 스터디하며 정리한 내용들입니다.






© 2020.08. by assu10

Powered by assu10