[AWS] MSK Consumer 띄우기

다른 계정에 떠있는 MSK의 consumer를 붙여 실시간성으로 log를 수집해야 할 니즈가 생겨, consumer를 선정, 적용, 아직까지 해결하지 못한 앞으로의 잠재적인 이슈를 어떻게 할 것인지의 내용을 기록합니다.

AWS 환경, 특히 managed 환경에서 MSK Consumer를 붙이고자 하는 분들께 도움이 되길 바랍니다.


후보

후보는 EMR, MSK connect, Lambda 총 3개였습니다. spring이나 fastapi로 kafka consumer를 개발하면 어떻겠냐는 의견도 있긴 했지만, kafka도 아직 익숙하지 않은데 아예 처음 해보는 백엔드 개발로 처음부터 어플리케이션을 구축하려면 잡혀있는 일정의 3배를 써도 안될것 같아 패스했습니다.

최종적으로는 lambda를 선택했고 나머지 2가지 선택지가 탈락한 이유는 다음과 같습니다.


EMR

기술적으로 spark에 대한 두려움은 크지 않았습니다. 업무에 spark을 직접 사용해본 적은 거의 없지만 마이크로배치로 처리해야할 로그 양이 그렇게 크지 않았고, 간단한 spark struct streaming 스크립트를 작성해 s3까지 가공된 parquet를 적재하는 테스트까지는 마쳤습니다.

결정적으로 EMR이 가장 비쌌습니다. 회사 상황에서 금액에 대한 압박이 크게 느껴졌기 때문에 다른 두 후보지에 비해 압도적으로 비싼 EMR이 가장 먼저 탈락했습니다.

또한 spark에 익숙하지 않은 상황에서 다음과 같은 상황에 대한 확신이 들지 않았습니다.

  • kafka에 consumer를 붙이면 몇초 단위로 수집해야할 로그의 양이 들었다 줄었다 하는데, EC2 단위로 scale in out이 되는 EMR의 스케일 속도가 이를 잘 따라갈 수 있을까?
  • 내가 만약 spark의 구조적인 문제를 알아야 해결 가능한 에러를 맞닥뜨렸을 때, 이에 대한 정확한 원인파악과 대처를 신속하게 대응할 수 있을까?

EMR on EKS라고 해서 pod 단위의 스케일링을 지원하는 EMR이 출시된 것은 확인했으나, 이 또한 잘 모르는 아키텍처이고 eks 운영에 대한 인적 리소스를 지원받기도 어려운 상황이었기 때문에 EMR을 선택하지 못했습니다.


MSK Connect

kafka connect를 MSK에서도 사용할 수 있게 되어있습니다. jar 파일로 된 connect와 플러그인을 s3에 업로드하면, 그에 맞게 connect가 동작하는 방식입니다.

MCU라는 자체 컴퓨팅 단위를 기반으로 auto scaling이 발생하기 때문에, EMR보다 유연한 스케일 관리가 가능할거라 생각했습니다. msk connect도 s3로 데이터를 전송하는 테스트까지는 마쳤습니다.

MSK Connect를 사용하지 않은 이유는 유지보수 편의성 때문입니다. transformation에 대한 유연성이 적었습니다.

connect에서 transformation을 거치려면 플러그인에 별도로 구성한 기능들을 jar 파일로 업로드해야하는데, Java에 익숙하지 않은 상태에서 각 기능과 MSK Connect의 구조 등을 파악하는데 너무 많은 시간이 걸릴거라 생각했습니다.

팀에서 제가 아닌 누구라도 유지보수를 할 수 있게 하기 위해서는 MSK Connect도 좋은 선택이 아니라고 판단했습니다.


최종 선택 Lambda

AWS Lambda를 사용한 결정적인 2가지 이유입니다.

  • 다른 2개와 비교했을 때 압도적으로 저렴하다 (1달 최대 $60 정도로 운영 가능, 평균적으로는 $50 이하 예상)
  • 유지보수가 매우 편하다 (python 환경, 스케일링 신경 쓸 필요 없음)

일단 가격 메리트가 너무나 컸습니다. EMR은 띄워놓기만 해도 비용에 EC2 비용까지 추가로 들어가기 때문에 아무리 작게 잡아도 1달 300달러 이하로는 운영이 어렵고, MSK 또한 스케일에 따라 추가비용이 부과되는데, 1달에 50달러 정도로 운영이 가능하다는 것이 상당한 메리트였습니다.

스케일에 대한 것 또한 다른 consumer에 비해 고민할 내용이 매우 적었습니다.

  • MSK를 Lambda의 트리거로 등록한다. 이 때 배치 사이즈와 최대 대기 시간을 설정한다. 배치 사이즈를 만족하면 람다가 뜨면서 사이즈만큼의 로그를 처리하고, 사이즈가 충족되지 않더라도 최대 대기 시간이 넘어가면 람다가 떠서 가장 최신 offset만큼의 데이터를 처리힌다.
  • MSK에 갑자기 매우 많은 로그가 들어오더라도 lambda의 최대 동시 실행 갯수가 계정 당 1000개이기 때문에 현재로써는 매우 여유롭다. 또한 배치 사이즈를 적절하게 잡으면 lambda의 실행시간을 10초 미만으로 설정할 수 있기 때문에, 동시성으로 문제가 생길 일이 많지 않다.

그러나 아직 더 체크해봐야 할 내용들도 있습니다.

hard limit

msk trigger로 사용하는 lambda가 현재 분당 20~40개 정도가 실행되는데, 만약 consumer를 이렇게 몇개만 더 늘리면 문제가 될 수 있습니다. lambda는 다른 서비스들에서도 사용이 되고 있기 때문에, 서비스가 더 커지거나 들어오는 로그의 양이 늘어나 1000개 이상의 lambda가 동시에 실행되는 경우는 동시성에 대한 문제가 충분히 생길 수 있습니다.

이 뿐만 아니라 lambda는 최대 15분의 실행시간에 대한 hard limit도 있습니다. 물론 제 trigger는 10초 미만으로 끝나는 짧은 작업이기 때문에 문제되지는 않지만 혹시나 큰 작업을 돌리고 싶은 분들께는 lambda는 좋은 선택지가 아닙니다.

offset 관리

lambda가 어떻게 offset을 관리하는지에 대해서 명확하게 파악하지 못했습니다. latest offset을 전달받아 작업이 완료되면 autocommit을 하는것까지 확인했는데, 그래도 고민되는 것이 많습니다.

  • auto commit으로 두었을 때, 만약 lambda가 실패하면 lambda가 수신한 offset은 커밋되지 않는 상태로 넘어가는게 맞는지?
  • 수동 commit으로 전환한다면 그 지점은 어디로 해야할지?
  • 그렇지 않다면 offset 별도 저장이 필요한지?

현재는 로직상으로는 정합성에 어긋나는 data가 들어오거나 하더라도 별도 방어로직을 가지고 있기 때문에 문제가 되지는 않지만, 알 수 없는 이유로 lambda 자체가 내려가는 경우에는 해당 offset에 대한 대처가 없습니다.

이러한 문제상황에 대해 명확한 답을 내리지 못해서, 데이터를 제공하기 전에 이런 것들을 확실하게 정리한 뒤 실제 반영을 마무리하려고 합니다.