무거운 서비스 어떻게 배포하는 것이 좋을까요?

사업의 목적으로 어떤 엔진을 개발했습니다.

그런데, 계산량이 많아서, 무난한 조건하에서는 10분, 악조건 하에서는 몇시간씩 걸리는데, 한번 시작되면 CPU/메모리 점유율이 거의 100% 가까이 차오릅니다.
이 수치는 Ryzen 7, 8GB 에서 나온 것이고, 실제 서비스는 좀 더 좋은 머신을 쓸 예정이긴 해도, 원래 몇일 이상 걸리던 것을 최적화한 것이라, 머신의 성능외에는 더 개선할 만한 여지가 없는 것으로 판단하고 있습니다.

원래 계획은 엔진을 웹API 서버에 올려 서비스하려고 했는데, 시스템 점유율이 이렇게 높다 보니, 엔진이 구동되는 동안 웹서버가 먹통이 될 수도 있겠다는 생각이 들더군요.

이렇게 무거운 모듈도 돌리면서, 웹서비스는 뻗지 않도록 하려면, 전체 서비스를 어떻게 구성하는 게 좋은지 현업에 계신 분들의 고견을 여쭙습니다.

참고로, 저는 현업 개발 경험이 전무하고, 프로그래밍은 독학했습니다.
닷넷의 프레임워크들은 공부 목적으로 거의 다 한번 씩 다뤄봤고, 자마린으로 소소한 앱 몇개 만든 게 전부입니다.

그리고, 데스크탑 앱으로 만들면 이러한 고민을 덜 해도 되겠지만, 웹서비스가 목표인 점 고려해주시면 좋겠습니다.

추가=======================
엔진 모듈이 처리하는 데이터는 분할하지 못 하는 특성이 있습니다.
즉, 부분합의 총합이 전체합과 다릅니다. 그래서, 데이터를 분산 처리하는 것은 의미가 없습니다.

3개의 좋아요

비슷한 시스템으로 트랜스코딩 서버가 있겠네요.
무조건 별도 시스템 둡니다.
웹서버의 WebAPI를 이용해서 일거리를 받아오고 진행률등을 업데이트하고요.

이런류의 무거운 작업을 웹서버랑 같이 돌려야 한다면 시간이 더 오래 걸리더라도 시스템 자원을 덜 쓰도록 만드는것이 맞고요.

7개의 좋아요

이런 아키텍쳐 로 이벤트 소싱 하는 방법도 있습니다. 러닝커브가 높지만 가장 세련된 방식으로 알고있습니다.
아니면 MessageQue를 써서 분산 배치 처리를 아키텍쳐를 구성해보심이 어떨까요?
이런 고가용성 서비스는 MessageQue를 많이 쓸것 같아요
웹서비스를 통해서 해당 mq로 event 를 발생시키고 backgroud worker로작업을 수행하는 아키텍쳐는 어떨까요?

6개의 좋아요

Orleans 의 소개 페이지를 잠깐 봤는데, 이건 또 하세월 감이네요 ㅠㅠ

3개의 좋아요

집행하실 수 있는 정확한 가용 예산을 알 수 없는 상황에서 추정을 근거로 몇 가지 옵션을 이야기해볼 수 있을 것 같은데요,

매월 발생하는 클라우드 인프라 비용이 부담이 되신다면, 서버 구입 비용과 네트워크 비용 + 상면비를 매달 지불하면서 장기적으로 서버 소유권을 이전받는 상품이 있는지 검토해보시면 좋겠습니다.

그리고 기술적인 측면에서는

a. 서비스를 빨리 출시하는 것이 목표이고, 확장성에 대한 고려는 지금 당장하지 않고 기술 부채로 전환해서 관리를 하실 예정이라면, 프로세스 간 통신 (IPC) 설계 방식을 사용하면서 계산 작업에 투입할 CPU 코어, 웹 서비스에 투입할 CPU 코어를 선택적으로 지정할 수 있습니다.

이렇게 사용하는 것을 Processor Affinity (프로세서 선호도)를 지정한다고 말하며, 닷넷 웹 서비스를 쓰신다고하니 IIS 사용이 추정되어 관련 자료 하나를 링크해드립니다. 계산 작업용 프로세스의 경우에는 일반적인 애플리케이션 대신 NT 서비스로 만들어서 띄우시면 사용자가 로그인할 때까지 기다리지 않고, 모든 로그는 이벤트 로그에 데이터베이스 형태로 기록해서 남길 수 있으니 이렇게 만드시는 것을 추천합니다.

b. 기술 부채를 최소화하는 것이 목표에 있고, 추후 사업이나 기술적 확장성을 염두에 두고 계시다면, 윈도우나 리눅스 컨테이너로 애플리케이션을 개발해서 운영하시는 것을 추천합니다. 컨테이너로 개발할 경우, 단일 컴퓨터에서도 실행이 가능하고, 추후 분산 시스템 구축을 위해 쿠버네티스로 이동하더라도 어렵지 않게 이전이 가능합니다.

다만 이 경우 컨테이너에 맞춰서 애플리케이션을 설계하고 최적화해야하기 때문에, 지금 개발하신 애플리케이션 아키텍처와 호환되지 않는 부분을 조정하거나 다시 개발해야 할 수 있습니다.

이 때에도, 아래 자료를 참고하셔서 단일 머신에서 각 컨테이너 별로 CPU Affinity를 지정하는 옵션을 앞의 경우와 마찬가지로 동일하게 사용할 수 있습니다.

c. 여기에 더해, 사용자의 요청 - 처리 - 서비스 응답 까지의 소요 시간이 실시간이어야 하는지, 아니면 예약 성격으로 나중에 응답을 되돌려주어도 괜찮은 것인지에 따라 설계 방식이 아주 상이해질 수 있고, 시스템 자원 배분 구성도 달라질 수 있습니다. 개발해서 출시하시려는 서비스에 대한 디테일한 정보가 있다면 더 생산적인 논의가 가능할 것 같아 이 부분에 대한 설명도 추가해주실 것을 제안드립니다.

6개의 좋아요

개념이 어려우시다면 @파란매 님이 말씀하신거처럼 MQ를 통해 쪼개는 개념을 생각하시면 될 거 같습니다.

말씀하신걸 ‘모놀리식’ 이라는 방식인데 하나의 프로세스에 모든 기능을 다 때려넣는건데요.

단점은 하나 뻗으면 다 뻗고 처리가 오래걸린다.
하지만 디버깅이 좀 유리하다 정도가 있습니다.

이제 그걸 나름의 추상화로 기능을 조금씩 MessageQueue(MQ) 로 쪼개셔서 step by step으로 다음 처리메세지 전달 → 처리 메세지 전달 → … 이런식으로 처리하시면 분산처리라서 디버깅은 조금 어려울 수 있겠으나, 하나가 뻗는다고 다 뻗지도 않고 중간 과정을 체크하실 수도 있습니다.

쪼갠다

이거 하나만 생각하시면 좋을 거 같아요.

혹 MQ에 대한 경험이 적으시다면, 가장 생각 없이 쉽게 쓰실 수 있는 것은 Redis의 List 자료구조입니다.

  1. 일단 현재의 프로그램을 여러 개의 프로젝트로 쪼갠다. (기준은 무거운 기능 또는 도메인 단위)
  2. 처리된거를 redis에 list형태로 넣는다. (여러 메세지 브로커처럼 이런 저런 설정이 없어서 쉽습니다)
  3. 다른 프로세스가 redis에서 가져간다.
  4. 처리

이렇게하면되는데 redis가 접근이 쉽다는 이유는

  1. 도커데스크탑설치 (설치하면서 시키는대로 하면 가상화 옵션을 모두 켜게 됩니다.)
  2. 도커 데스크탑의 메인 대시보드에 있는 레디스 이미지 다운로드 & 컨테이너 화
  3. 레디스 DB 0번로 그냥 고정해서 쭉 사용 List Left Push → Right List Pop

무슨 말인지 감이 안오시겠지만 해보시면 오실거라 생각합니다.

7개의 좋아요

자세한 설명 감사드립니다.
그런데, 용어의 수준이 제수준을 조금 벗어나 있네요.
그래도, 이해가능한 부분을 답변을 드리면,

a. 현재 제 수준으로 봤을 때는, 가장 가용성이 높아 보이는 방식인 것 같습니다. 좋은 자료 링크 감사드립니다.

b. 현재는 a 답변과 같이 윈도우+닷넷 환경 내에서 가용한 방법이 가장 효율적인 것 같습니다. 모듈 자체는 닷넷 6 클래스 라이브러리로 작성했기 때문에 시스템에 대한 의존성은 크게 없다고 판단하고 있어, 향 후에 꼭 필요하다면, 도커를 고려해보겠습니다

c. 원래는 6분 이내 완료를 목표로 했으나, 완성된 모듈의 성능이 저러 하니, 실시간 성은 포기했습니다.
지금은 유료 사용자(있을려나 싶기도 하고요 :blush:)는 가급적 빠른 시간(10분 이내) 이내에 답변을 주어, 계산의 결과를 보고 데이터를 수정할 수 있는 자유를 누리게 하고, 무료 사용자는 엔진 서버가 주는 대로, 그렇지만, 하루를 넘기지 않는 선에서 제공하는 것으로 생각 중입니다.

5개의 좋아요

엔진 모듈은 전체 시스템에서 계산을 담당하는 요소만을 닷넷 6 라이브러리로 작성한 것이라, 함수 호출 한번이 저만큼의 시스템 자원을 소진키시고 있는 것입니다.
아직 monolithic 하다거나 그렇지 않다거나 할 만한 시스템은 구축하지 않았습니다.

디버깅을 어느정도 마친 후에는 비동기 메서드로 작성할 것이라, 시스템을 다운 시키는 일을 최소화할 생각입니다.

3개의 좋아요

아 그랬군요. 그럼 제가 상황을 오해한 것 같습니다.

그러면 그냥 엔드포인트는 받아서 넘기는 행위만하고 따로 계산 작업은 다른 프로세스에서 하는 형태로 해야겠군요…!

3개의 좋아요

예. 제가 걱정하는 부분은 함수실행 때문에, 운영체제도 흔들릴 정도이니, 운영체제 안에서 실행되는 호스트도 무사하지는 못할 것 같다는 점입니다.
질문의 요지는 어떻게 하면 호스트가 영향을 받지 않게 만들 것인지, 그리고, 한 3일 이내의 기간에 수백에서 수천개의 엔진 인스턴스가 구동되는데, 이건 또 어떻게 구성해야하는지 입니다.

3개의 좋아요

그럼 @rkttu 님께서 말씀하신 docker 방식을 쓰셔야할 거 같고, 그런 대용량 연산을 단 시간내에 처리하기 위해서는 docker image에 대한 scale out이 필요한 부분 같습니다.

호스트가 흔들리는 것이 예상되신다면 container(가상화 격리) 개념이 있는 docker는 필수 이실 거 같습니다.

클라우드 환경이 답이라는 생각이 들긴 합니다…ㅠ (아 물론 수천개의 엔진 인스턴스라면 가격 부담도 함께…)

3개의 좋아요

맞습니다. 클라우드 가격이 가장 부담스럽습니다. ㅠㅠ

2개의 좋아요

시간이 오래 걸리는 작업들을 많이 다루고 있는데 메시지큐 기반의 비동기로 처리하셔야 합니다. 예를 들면 어떤 웹 API를 만들었는데 그 코드 안에서 1GB 파일의 압축을 한다고 가정했을 때, 100명이 동접하면 100개의 1GB 압축이 동시에 실행되는 것입니다. 이런 경우 메시지큐 기반으로 처리하면 아무리 많이 요청해도 등록된 순서에 따라서 처리되기 때문에 서버가 절대 안죽습니다. 단지 대기 시간이 늘어나는 것이지요.

명절 기차표 예매 시스템도 이런 식으로 비동기 큐 처리를 합니다. 대기 순번이 표시되는 서비스는 다 이런 식입니다.

4개의 좋아요

답변 감사합니다. 메세지 큐 기반의 처리가 어떻게 돌아가는지 이해하는데 많은 도움이 되었습니다.
그런데, 제 고민은 엔진을 console 앱에서 호출해도, 시스템 반응이 없어질 정도로 CPU 메모리 자원을 거의 잡아 먹습니다.

이런 상황에서, 엔진이 구동되는 동안, 웹 API 호스트가 제대로 동작할지 의문이라는 것입니다. 호스트가 동작은 할 수 있어야 요청을 수신하거나, 메시지 큐를 운영하거나 할 수 있지않겠습니까?

2개의 좋아요

메시지 큐를 수신하는 부분은 원격지에서도 가능합니다.
꼭 같은 시스템에 있을필요는 없어요 아니 오히려 원격지 분산처리를 위해 나온 아키텍처예요
구현은 어렵지 않습니다 . 아니 오히려 많이 simple 해지고 확장하는데 유리합니다.
RabbitMQ, NSQ 아니면 Redis 도 괜찮고요
아니면 심지어 격리된 VM에서도 운영하거나 도커 이미지로 실행하면 리소스 독립적으로
운영이 가능합니다.
그리고 성격이 맞는지 모르겠지만 AWS ramda,Azure Function 같은 서비스도 있긴 합니다.

3개의 좋아요

자세한 답변감사합니다.
RabbitMQ 찾아보니, MQTT 비슷한 거 같네요.
엔진 구동 시스템이 메세지 대기 => 메시지 수신 => 계산 => 계산된 데이터를 DB에 업로드 => 완료 메시지 게시 => 메세지 대기 순으로 하면 되겠군요.

2개의 좋아요

음 근데 지금 말씀하시는거보니 메서드 자체가 오래 걸리시는 거 같아서 저도 메세지큐를 제안드렸다가 철회한 것인데…

메서드 자체를 여러 프로세스로 쪼개시는게 아닌 이상 메세지큐 적용이 어려우실 거 같습니다.

쪼개시는게 가능하신지부터 체크해보시면 좋을거 같습니다.

뭐 물론 못쪼개실것 같지는 않은데…혹시 모르니까…;;

그리고 쪼개기만 가능하다면 위에 나온 제 답변도 포함해서 모든 답변을 다 받으실 수 있으실 것 같습니다.

3개의 좋아요

지속적 관심 감사합니다.
불행하게도 메서드도 데이터도 못 쪼개는 케이스입니다.

3개의 좋아요