IoT 상태 정보 실시간 동기화 구현 시 발생한 문제 해결을 위한 EDA 도입 사례

jj J·2023년 3월 17일
1

IoT 상태 정보 실시간 동기화 로직에서 발생한 문제를
기존의 모놀리식 아키텍처에서 EDA, MSA로 변경해
장애 격리 및 비용 효율화를 진행한 건이 있어 기록하고자 한다.

우선 어떤 문제를 풀었는지 얘기하기 전에, 기존 시스템 구조에 대해 간략히 설명하겠다.
솔루션은 아래와 같은 요소로 구성되어 있다.

  1. 펌웨어(판매되는 IoT 기기에 설치됨)
  2. 서버(AWS IoT 서비스와 통신하며, 각종 비즈니스 로직 처리)
  3. 모바일 앱(펌웨어와 서버와 통신하며, 기기 제어 및 모니터링 인터페이스를 제공)

펌웨어는 부팅 후 부터 종료까지 AWS IoT 서비스에 직접 연결되어 있으며, 전원 On/Off 시 또는 상태 변화 후 AWS IoT 서비스에 상태를 동기화해준다.
서버는 이 동기화 이벤트를 구독하고 있다가, 메시지가 수신되면 이를 DB에 반영한다.

그리고 앱에서는 상태 표시를 위해, 5초에 1번씩 서버로 펌웨어 상태 조회를 폴링 방식으로 요청한다. 서버는 상태를 DB에서 읽어와 반환해준다.


그림으로 표시하면 위와 같다.
빨간색은 펌웨어 상태가 반영되는 과정, 초록색은 앱에서 펌웨어 전원 상태를 동기화 하는 과정이다.

문제

  1. 펌웨어에서 무한 재부팅 이슈가 발생했고, 부팅마다 상태 정보를 업데이트하기 때문에 서버로 동시에 엄청난 양의 업데이트 요청이 들어왔다.
  2. 모놀리식 아키텍처 서버였기 때문에, 해당 이슈는 전체 서버의 장애로 전파되어 모든 API 응답 시간이 느려지고, Request Timeout도 발생했다.
  3. 문제가 발생한 기기 ID를 찾아, 원격으로 조치해 해당 기기를 차단시키고, 무한 재부팅 이슈를 수정했다.

외부에서 발생하는 장애는 언제든 다시 발생할 수 있다고 느꼈고, 가용성 확보를 위해 재발 시 장애 격리할 방법 마련이 필요했다.

해결 과정

  1. 해당 비즈니스 로직은 IoT 기기의 상태 변화라는 이벤트에 기반해 동작하기 때문에, 이벤트가 발생할 때만 즉시 동작하면, 응답 속도 개선과 불필요한 호출로 인한 비용 효율화 두마리 토끼를 모두 잡을 수 있을 것이라 판단했다.

  2. IoT 상태 변화 이벤트 흐름에 따라 이벤트 스토밍을 통해 아키텍처를 구성하였고, 해당 이벤트 흐름 바운더리와 기존 모놀리식 서버 바운더리는 서로 통신할 필요도 없고, 로직 상 독립적으로 나뉘어져있음을 확인하였다.

  3. 따라서, 해당 IoT 상태 변화 비즈니스 로직을 별도 마이크로 서비스로 분리하였을 때 발생할 수 있는 통신 비용/복잡도 증가 등의 문제가 발생하지 않을 것이라 생각했다.

  4. 장점으로는 향후 또다른 외부 장애가 발생해도 해당 서비스 내 격리되어, 기존 전체 모놀리식 서버에는 장애가 전파되지 않아 가용성이 향상될 수 있고, 불필요한 폴링을 하지 않아 성능/비용 효율화가 있을 것이라 보았다.

  5. 단점 대비 장점이 명확했기 때문에 EDA 도입 및 마이크로 서비스 분리를 결정했다.

  6. 남은 한가지 문제는, DB에 저장된 이벤트를 앱에 실시간으로 동기화 해주는 것이었다.

  7. 구현 가능한 방법으로는 별도 웹소켓 서버를 두어, 클라이언트(앱)와 연결된 채로 이벤트 수신 시 앱에 이벤트를 전달해주는 방법이 있었다.

  8. 하지만, 현재 AWS Lambda로 서버를 서빙하고 있었고, 스케일 아웃에 따른 분산 환경을 고려하면 별도 DB에서 세션 클러스터링해주는 로직까지 구현이 필요했다.

  9. 여러 고려점 중 우선 순위는 최대한 빠른 핫픽스 대응, 최소한의 금전적 비용이었기 때문에 구현 복잡도가 높아지고, 추가 통신 비용이 발생하는 해당 방법은 적용하기 부적합했다.

  10. 다른 방안을 찾던 중 Google Firbase Realtime Database을 찾았다. 이벤트 브로커 개념이 들어간 DB로 클라이언트에서 DB 필드를 구독할 수 있고, 업데이트 시 해당 필드를 구독한 클라이언트에게 Broadcast해주는 방식이었다.

  11. AWS Lambda 서버에서는 상태 변경 이벤트를 받으면 해당 이벤트를 전처리해, Realtime Database에 REST API로 업데이트하고, 해당 DB 업데이트 이벤트는 구독한 앱으로 전달되어 실시간으로 반영할 수 있었다.

  12. 가장 좋은 점은 무료 사용가능한 트래픽 규모가 현재 운영 규모의 10배에 달했기 때문에, 공짜로 쓸 수 있었던 것이었다. 비용 최소화가 우선순위였던 현 상황에 적합한 방법이었다.

  13. 적용 전 기존 MongoDB를 사용할 때 대비 비용 측면 장단점이 어떻게 되는지 아래와 같이 비교해보았다.

결과 요약

요약하면 아래와 같다.

  • 상태 동기화 로직에 어떠한 장애가 발생해도 전체 서버로 장애가 전파되지 않고 격리된다.
  • 현재 운영 규모의 10배까지 무료 로 사용할 수 있게 됐다.

향후 개선할 점

  • 향후 서비스가 빠르게 성장해 현재 운영 규모의 10배에 도달하면 Realtime database를 사용하지 않고, 다른 DB로 변경할 수 있다.
  • 하지만, 현재 구조는 앱에서 Realtime database SDK 의존성을 설치해 로직을 구현하였으므로, DB가 바뀌면 앱까지 의존성 변경 및 코드 수정이 필요하다.
  • 또한, Realtime database 서비스 자체 장애 발생이나, 실시간 동기화 로직에 문제가 생기는 경우 앱에서 정보를 표시해줄 수 없다.
  • 따라서, 앱과 Realtime database 사이를 중계하는 웹소켓 서버를 두어 앱은 웹소켓 서버에 의존해 Realtime database같은 DB 종류와 세부 로직은 모르게 한채로 이벤트 정보만 수신해 향후 DB가 변경되어도 앱은 변경없이 계속 사용할 수 있도록 한다.
  • 그리고, 실시간 동기화 로직에 문제가 생겨도 앱에서 정보를 표시할 수 있게, DB 이벤트 값 수동 조회 API를 제공해 실시간 동기화가 되지 않으면 직접 값을 읽어올 수 있도록 fallback 처리가 필요할 것이다.
profile
매일 발전

0개의 댓글