이미 다른 앱, 마이크로서비스를 K8s 에서 돌리고 있다면, Kafka 만 K8s 밖에 두려다 수많은 승인 절차로 조직적 마찰이 생긴다.
이럴 때 “끝까지 싸울 가치는 없다”. K8s 에서 돌릴 수 있으니 그렇게 하면 환경을 빨리 할당받고, 인프라 팀에게 익숙한 환경이라 장애 시 지원도 낫다.
VM 으로 표준화한 조직이 Kafka 용 로컬 디스크 물리 머신을 받기 어려워하는 것과 같은 맥락이다.
결국 다수의 클러스터를 운영하게 됨
Use case 가 늘면 프로덕션 클러스터가 여러 개가 되고, dev/stg/blue-green 환경까지 붙는다.
K8s(특히 Operator)는 새 클러스터 배포, 관리를 쉽게 한다. 브로커 추가가 명령 한 줄 또는 설정 한 줄, 설정 변경, 업그레이드, 재시작도 전체 브로커, 클러스터에 일괄로 쉬워진다.
Ansible 같은 도구로도 배포는 되지만 스케일 아웃, 모니터링, 재시작, 업그레이드 내장 기능이 없다.
까다로운 점
Kafka 는 stateful 서비스라 stateless 마이크로서비스보다 K8s 설정이 복잡하다. 가장 큰 난관은 스토리지와 네트워크이며, 둘 다 일관되게 낮은 지연(low latency)을 보장해야 한다.
당시 Kafka 는 ZooKeeper 에 의존해, K8s 위에 Kafka 와 더불어 또 다른 stateful 시스템인 ZooKeeper 까지 운영해야 했다. 이후 KRaft(KIP-500)가 Kafka 3.3 에서 프로덕션 준비(KIP-833), 4.0 에서 ZooKeeper 가 제거되어 이 부담은 사라졌다.
스토리지
stateful 서비스는 공유 스토리지(shared storage)를 요구한다.
많은 조직이 공유 스토리지에서 일관된 low latency 를 내지 못한다. 스토리지 팀이 요구사항을 이해하고 충족 여부를 함께 검증해야 한다.
K8s 로컬 Persistent Volume 은 당시 베타라 프로덕션 비권장이었으나, 1.14(2019)에서 GA 되어 지금은 프로덕션에서 쓸 수 있다.
네트워크 - Kafka 만의 난제
브로커는 서로 교체 불가(not interchangeable)다. 클라이언트는 자신이 produce/consume 하는 파티션의 리드 레플리카를 가진 브로커와 직접 통신해야 한다.
따라서 모든 브로커를 단일 LB 주소 뒤에 둘 수 없고, 특정 브로커로 라우팅할 방법(headless service 등)이 필요하다. 네트워크 팀의 협조가 필요하다.
StatefulSet
persistent volume + headless service 설정을 “임시방편(workaround)“으로 보는 시각도 있으나, Kafka 의 요구사항을 K8s 가 메커니즘으로 지원하는 것이다.
StatefulSet 은 K8s 1.9 부터 일급 시민(first-class citizen)이다.
권장사항
숙련된 스토리지, 네트워크 팀의 협조가 있을 때에만(if and only if) Kafka on K8s 가 성공한다. 없으면 문제에 부딪힌다. 사실 이는 늘 그랬다. Kafka 는 좋은 인프라에 의존한다.
Kafka 를 K8s 에 올리는 첫 서비스로 고르지 말 것. 인프라 팀이 먼저 stateless 서비스(예: Kafka Streams 앱)로 배포, 모니터링, 업그레이드, 트러블슈팅 경험을 쌓게 한다.
앱 하나만 K8s 에 올리는 것은 이점이 적다. 비즈니스 앱들도 K8s 에서 돌 때 Kafka 를 K8s 에 올리는 이득이 가장 크다.
프로비저닝에 Helm Charts, 운영에 Operator 를 쓰고 스토리지, 네트워크 지원이 있으면 Kafka on K8s 는 꽤 쉽다. 당시 Confluent Operator 얼리액세스 기준이며, 현재는 Strimzi(CNCF), Confluent for Kubernetes(CFK) 등이 쓰인다.