마이크로서비스에서는 어떻게 통신할까?

SangHun·2021년 10월 3일
0
post-thumbnail

이 글은, 대략적인 마이크로서비스에 대한 개념과 마이크로서비스처럼 구성된 소프트웨어를 개선하다가 맞닥뜨린 문제를 해결하는 과정 위주로 작성되었습니다.

대략적인 마이크로서비스 통신

마이크로서비스Microservice는 소규모의 독립적인 서비스로 소프트웨어를 구성하는 방식입니다.

이 때, 각 서비스들 사이에는 미리 정의된 API를 통해 통신하게 되는데 동기적Synchronous 통신 방식과 비동기적Asynchronous 메시지 전달 방식으로 나뉩니다.

  • 동기적 통신은 HTTP나 RPC 같은 프로토콜을 이용하여 다른 서비스가 노출한 API를 호출하는 방식입니다. 호출자가 답변을 기다리기 때문에 동기적 메시징 패턴입니다.

  • 비동기적 메시지 전달 방식은 서비스에서 메시지를 보낸 뒤 답변을 기다리지 않는 방식입니다. 하나 혹은 다수의 서비스가 메시지를 비동기적으로 처리합니다.

비동기 메시징 방식은 여러 장점이 있는데 그중 하나가 반응성Responsiveness입니다.

반응성을 예를 들어 설명하자면,
서비스A, 서비스B, 서비스C가 있고

서비스A -> 서비스B -> 서비스C

순으로 호출한다고 가정합시다.

이 때, 동기적 호출을 하면 서비스C가 끝날 때까지 기다리고 또 서비스B가 끝날 때까지 기다려야 합니다.

동기적 호출에서 비동기적 메시징으로 바뀐다면 서비스A는 더이상 서비스B와 서비스C가 끝날 때까지 기다리지 않게 됩니다.

맞닥뜨린 문제

동기적 통신의 문제

유지보수 중인 인공지능 소프트웨어는 크게 API 서비스와 머신러닝 서비스로 나뉘어 있습니다.

머신러닝 서비스는 훈련Training과 추론Inference 작업을 실행합니다.

여기서, 머신러닝 서비스에서 추론 작업을 동기적 방식으로 처리하는 문제가 있었습니다.

추론 작업이 시간이 오래걸리지 않는다고 판단했는지, gRPC 프로토콜로 추론 작업을 호출하고 결과를 반환받게 설계되어 있었다.

머신러닝 서비스에 새로운 모델과 데이터가 추가되자 결국 문제가 터졌다.

grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:
        status = StatusCode.UNAVAILABLE
        details = "upstream request timeout"
        debug_error_string = "{"created":"....","description":"Error received from peer ipv4:xx.xx.xx.xx","file":"path/src/","file_line":0000,"g
rpc_message":"upstream request timeout","grpc_status":14}"
>

시작은 gRPC timeout 에러였다. 사실 timeout 자체를 크게 설정하면 해결할 수 있지만 이는 사용자 경험을 크게 훼손하게 된다.

추론 작업이 수십 초, 수 분이 걸리게 될 수도 있는데 그 시간동안 사용자는 주구장창 기다리고만 있을 수는 없기 때문에..!

비동기 메시징으로 migration

기존에도 훈련 작업은 GCP Pub/Sub을 사용한 비동기 메시지 전달 방식으로 진행되었기 때문에 추론 작업도 Pub/Sub을 사용하도록 결정했습니다.

작업은 크게 네 단계로 나누어 진행했습니다.

  1. 추론 작업 요청을 Pub/Sub으로 발행할 수 있도록 수정한다.
  2. 머신러닝 서비스에서 Pub/Sub의 subscriber가 추론 작업을 실행하도록 기능을 추가한다.
  3. Frontend app에서 추론 작업의 결과가 나올 때까지 polling하도록 수정한다.
  4. gRPC 코드를 제거한다.

3번 단계가 뜬금없을 것이다.

짤막하게 설명하자면...

Frontend 입장에서

원래 추론 작업은 동기적이었기 때문에 HTTP request 한 번 보내면 바로 추론 결과를 response로 받았다.

추론 작업이 비동기적으로 바뀌면서, 추론 결과가 곧장 response로 반환되지 않게 된다.

때문에 추론 작업이 끝날 때까지 polling하도록 수정하게 되었다.

일반적인 HTTP로 polling하도록 하고, response에 추론이 끝났다는 정보가 있을 때까지 반복한다.

Backend 입장에서

그럼 Backend는 추가로 무얼 해야하는가?

추론 시작하는 요청과 polling 요청을 처리하도록 API를 수정해야 한다.

그리고 추론이 끝나면 결과를 DB에 저장하도록 해야한다.

이제 머신러닝 서비스는 모든 작업을 비동기적으로 실행하게 됩니다.

혼자 모든 작업을 진행하느라 조금 오래 걸리기도 했고 특히 프론트 부분... 어려운 점도 있었지만 아키텍쳐 개선에 크게 기여했다고 느껴 뿌듯합니다 ㅎㅎ

아쉬운 점도 있습니다.

처음 말했다시피, 이 소프트웨어는 마이크로서비스처럼 구성되어있습니다.

엄밀히 말하자면 마이크로서비스라 부르기 어려운데, API gateway가 HTTP만 지원하기도 하고 여러 서비스가 하나의 DB를 공유하고 있습니다.

아직 microservice architecture에 대한 깊은 이해도 부족하다고 느껴집니다.

차근히 배워서 더 개선해나가야겠습니다..!

Reference

profile
개발괴발자

0개의 댓글