이마트앱의 마이크로서비스 여정

- 이 글에서 다루는 내용
- 마이크로서비스는 은제총알인가?
- 기존 서비스에는 어떤 어려움이 있었는가?
- 마이크로서비스를 어떻게 적용할 것인가?
- AS-IS 아키텍처
- TO-BE 아키텍처 적용방향
- 스트랭글러 패턴이란?
- 스트랭글러 패턴을 위한 아키텍처 설계
- 마이크로서비스의 문제점
- 분산환경의 어려움
- 관리의 어려움
- 마이크로서비스의 문제점 극복방안
- IaC(Infrastructure as Code)를 통한 리소스 생성 자동화
- 모니터링 및 로그 관리
- 기타
- 글을 마치며
이 글에서 다루는 내용
마이크로서비스는 너무나 많은 사람들이 이미 개념을 정의하고 공유하고 있어, 이 블로그 글에서는 굳이 이를 설명하지 않으려고 합니다.
대신, 왜 이마트앱은 마이크로서비스를 선택했는지 그리고 어떤 설계과정과 어려움이 있었고 이를 어떻게 극복하고 있는지를 말씀드리려고 합니다.
마이크로서비스는 은제총알인가?
마이크로서비스의 성공사례가 많이 부각되면서 "마이크로서비스는 좋고, 모놀리스는 좋지 않은 것" 이라는 인식을 가진 분들이 많아진 것 같습니다.
그러면 모놀리스는 무조건 나쁘고 마이크로서비스는 우리가 겪고 있는 모든 문제를 마법처럼 다 해결해줄 은제총알 일까요?
저는 단연코 그렇지 않다고 생각합니다.
그럼에도 불구하고 왜 이마트 DT본부에서는 마이크로서비스로의 여정을 시작했을까요?
이 모든 건 비즈니스에서 출발합니다. 이마트 DT본부에서 추구하는 비즈니스의 혁신, 속도 이를 달성하기 위해서는 동시다발적으로 많은 기능들을 개발하고 시험하고 적용하는 과정이 필요합니다.
하지만, 규모가 매우 큰 모놀리스 시스템으로는 이를 달성하기 어렵기에 이마트앱을 대상으로 마이크로서비스로의 여정을 시작하였습니다.
기존 서비스에는 어떤 어려움이 있었는가?
이마트앱은 오랜기간 많은 협력업체 분들의 손을 거쳐서 개발되고 운영되었던 시스템으로 아래와 같은 문제점이 있었습니다.
- 너무 많은 기능들이 하나의 코드 베이스로 관리되는 전형적인 모놀리스 구조
- 낮은 프레임워크 버전 및 JSP 등의 오래된 기술로 인한 제약
- 데이터베이스를 여러 시스템에서 공유하는 방식으로 구성되어 이로인한 장애 및 확장의 제약 발생
- 늘어나는 트래픽을 손쉽게 확장하여 안정적으로 처리할 수 없는 구조
앞서 설명한 대로 모놀리스 자체가 나쁜 것은 아닙니다. 하지만, 이로 인한 비즈니스 확장에 문제를 겪는다면 마이크로서비스 적용이 필요하다고 할 수 있습니다.
마이크로서비스를 어떻게 적용할 것인가?
그러면 아키텍트가 "앞으로는 마이크로서비스를 적용합시다" 라고 하면, 일사분란하게 적용이 가능할까요? 당연히 진행이 안될 겁니다.
마이크로서비스는 서드파티로 제공되는 고정된 솔루션이 아니기 때문에, 사람들이 이를 이해하는 방식도 각각 다르고 마이크로서비스로 전환하려는 시스템도 제각각이기 때문입니다.
따라서, 먼저 필요한 것은 AS-IS의 아키텍처를 분석하고 문제점을 도출한 후, 보다 실질적인 TO-BE 아키텍처를 설계하는 일이었습니다.
AS-IS 아키텍처
이마트앱의 AS-IS는 AWS 클라우드를 기반으로 계속 변화하고 있었는데 제가 이마트 DT본부에 합류한 당시의 아키텍처는 아래와 같습니다.

아키텍처는 크게 아래의 4가지 부분으로 나누어 볼 수 있습니다.
- Frontend App : Vue.js를 기반으로한 SPA(Single Page Application)
- Backend App : Spring/Java를 기반으로 한 백엔드 어플리케이션 (JSP로 구성된 화면영역 포함 )
- On-Prem Systems : AWS 클라우드로 아직 마이그레이션하지 않은 인터널 시스템 및 데이터베이스, 캐시 등
- CDN 및 외부시스템 : 기존에 사용중인 CDN 및 외부연동시스템
여기에서 마이크로서비스 전환을 위한 타겟이 된 시스템은 백엔드 어플리케이션 영역입니다. 앞서 설명한 대로 모놀리스로 구성되어 있고, 다양한 이유로 확장에 제약을 겪고 있었던 부분입니다.
TO-BE 아키텍처 적용방향
하지만, 야심차게 이를 "한번에 다 마이크로서비스로 전환할거야" 라는 것은 매우 어렵고 위험도가 높을 뿐더러 시간이 많이 소요되는 작업입니다.
엔지니어의 입장에서는 깔끔하게 모든 걸 다시 만들고 싶지만, 앞서 마이크로서비스를 선택했던 근본적인 이유인 비즈니스를 생각했을 때 대규모 프로젝트를 통한 진행은 현실적이지 않은 방법입니다.
따라서, 저희는 점진적인 마이크로서비스의 적용, 흔히 얘기하는 스트랭글러 패턴을 이용하여 점차적으로 마이크로서비스를 적용하기로 했습니다.
스트랭글러 패턴이란?

스트랭글러 패턴을 위한 아키텍처 설계
그러면 스트랭글러 패턴을 위해서는 무엇이 필요할까요? 간단하게는 신규 마이크로서비스가 위치할 컴퓨팅 리소스와 그리고 이 리소스로 라우팅하기 위한 방안이 필요해 보입니다.
초기에 개념적으로 간단하게 생각했던 아키텍처는 아래와 같습니다. ECS Fargate 기반으로 새로운 서비스를 생성하고, 이를 ALB의 타겟으로 분리합니다. 그리고 ALB의 도메인을 새로 구성한 다음 Frontend App에서 새로운 기능은 새로운 도메인으로 라우팅하는 방식으로 점차적으로 기존 모놀리스의 의존도를 줄여가는 방식입니다.

하지만, 이 아키텍처를 보고 이런 질문을 해볼 수 있습니다. "각 서비스별로 공통으로 적용되는 기능들은 어디에 구현할 것인가?" 대표적으로 인증을 예로 들 수 있습니다.
따라서 이를 처리하기 위해 각 마이크로서비스 앞단에 공통처리를 위한 별도의 레이어를 추가할 필요성이 생겼고, 이를 위해 API G/W를 마이크로서비스 앞단에 위치시키는 것으로 설계를 진행합니다.

하지만, 여기에서 한번 더 고민에 빠지게 됩니다. "마이크로서비스를 다른 시스템에서 호출할 때와 일반 사용자가 호출할때는 네트워크와 인증방식이 다를텐데 이를 어떻게 처리할 것인가?"
따라서 이를 위해 내부와 외부용도의 API G/W를 분리하여 아키텍처를 설계합니다.

AWS의 네트워크에 익숙하신 분들은 위에 그림에서 이상한 점을 느끼실 수 있습니다. AWS에서 제공하는 API G/W 서비스는 VPC 내부에 위치하지 않습니다.
따라서, 인터널 네트워크 통신을 위한 추가적인 설계가 필요합니다. 그리고 아래가 최종적으로 설계된 아키텍처 입니다.

최종적인 아키텍처의 모습을 각 숫자에 맞춰서 추가적으로 설명해보겠습니다.
1. AS-IS에 구성된 모놀리스 어플리케이션으로 ALB와 ECS를 기반으로 구성된 Java 어플리케이션입니다.
2. 신규로 구성된 마이크로서비스의 엔드포인트이며, 별도의 도메인을 연결하였습니다. Frontend App을 변경하여 배포하면서 새로운 기능은
이 API G/W로 라우팅 해줍니다.
3. API G/W는 사용자 VPC 외부에 위치한 서비스입니다. 이를 위해 VPC 내부 네트워크와 통신을 하려면 Private Link를 사용하게 됩니다.
4. AWS의 Private Link의 경우 NLB와의 연동만 가능한 제약이 있어, NLB가 아키텍처에 추가되었고 NLB의 타겟은 ALB가 됩니다.
5. ALB는 URL의 path를 기반으로 특정 기능별로 각 ECS 서비스로 라우팅을 처리합니다.
6. 각 마이크로서비스는 ECS Fargate 기반으로 구성됩니다.
7. Internal ALB는 내부시스템간 연동을 위하여 만들어진 엔드포인트이며, 별도의 내부 도메인을 연결하였습니다.
8. 사용자 VPC 외부에 위치한 API G/W와의 안전한 통신을 위하여 VPC Endpoint를 이용하여 ALB → API G/W간 통신을 처리합니다.
9. API G/W는 인터널 전용으로 일반 사용자의 인증이 아닌, 인터널 시스템간의 인증 등을 처리합니다.
마이크로서비스의 문제점
마이크로서비스를 적용할 때 "서비스를 작게 나눠서 구성만 하면 충분한 것인가?", "여기에 문제점은 없을까?" 하는 생각이 들게 마련입니다.
그러면 마이크로서비스를 적용할 때 발생하는 대표적인 문제점과 이를 극복하기 위해 저희가 적용하고 있는 방법들을 살펴보겠습니다.
분산환경의 어려움
마이크로서비스는 분산환경을 기반으로 동작하기에 아래와 같은 실질적인 어려움이 있습니다.
- 모놀리스에서는 단순히 메소드 호출을 기반으로 동작하던 코드를 리모트 호출로 변경해야 하며, 리모트 호출에 따른 다양한 예외상황을 고려하여 코드를 만들어야 합니다.
- 모놀리스에서는 복잡한 비즈니스 로직을 처리할 때 필수적으로 수반되는 트랜잭션 처리를 데이터베이스의 능력에 의존하게 되므로 데이터의 정합성을 유지하기 위한 어플리케이션 레벨의 노력이 상대적으로 크지 않지만, 마이크로서비스에서는 각각의 서비스가 자신만의 데이터베이스를 소유하게 되며, 여러 서비스에 걸쳐서 수행되는 복잡한 비즈니스 로직을 처리할 때는 어플리케이션 레벨에서 트랜잭션을 처리하기 위한 방안이 필요합니다.
- 수많은 작은 서비스가 분산환경으로 구성된다는 것은 그만큼 장애포인트가 늘어난다는 것이고, 복잡한 호출관계에서 하나의 서비스에만 장애가 발생해도 전체적으로 장애가 전파될 확률이 높습니다.
- 하나의 서비스가 또 다른 서비스를 호출하고 이 서비스가 다시 다른 서비스를 호출하는 등의 복잡한 호출과정에서 발생하는 문제를 추적하기는 어렵습니다.
관리의 어려움
마이크로서비스에서는 매우 많은 작은 서비스가 지속적으로 만들어지게 되는데, 이에 대한 생성 및 관리가 상당히 어려워 집니다.
- 신규 마이크로서비스 구성 시, 반복적인 리소스 생성 및 관리작업이 따르게 되고, 많은 시간이 소요되며 휴먼에러가 발생할 가능성이 높아집니다.
- 또한, 코드 배포를 위한 CI/CD 파이프라인 구성 작업도 반복적으로 일어나게 됩니다.
마이크로서비스의 문제점 극복방안
IaC(Infrastructure as Code)를 통한 리소스 생성 자동화
위에서 기술한 것처럼 마이크로서비스는 매우 많은 작은 서비스가 지속적으로 만들어지기 때문에 이에 따른 반복적인 리소스 생성 작업을 자동화하는 것이 필요합니다. 따라서 저희는 아래와 같이 AWS CloudFormation을 통하여 AWS 리소스 구성을 자동화 합니다.

IaC를 통하여 AWS의 리소스 생성을 자동화하는 과정을 살펴보겠습니다.
1. Admin은 Service Catalog 서비스를 사용하여, AWS의 여러 서비스를 제품화하여 관리합니다. 그리고 개발팀에서 요청 시 이를 이용하여 새로운 자원들을 손쉽게 만들어내는데, 이때 내부적으로 CloudFormation이 호출됩니다.
2. AWS CloudFormation은 AWS제공하는 Native한 IaC 도구이며, Stack이라는 단위로 AWS의 리소스를 프로비저닝하게 됩니다.
3. 앞서 보았던 하나의 마이크로서비스를 구성하기 위해 새로운 ECS 서비스 및 관련된 다양한 리소스는 조직의 네이밍 및 태깅 표준 등을 적용하여 자동으로 만들어집니다.
4. 이렇게 생성된 ECS 서비스는 엔드포인트가 필요하며, ALB의 타겟그룹으로 ECS 서비스가 등록되며 URL path 기반으로 라우팅되는 과정까지 자동화됩니다.
5. 또한 배포시 안정성을 위하여 Blue/Green 방식의 배포를 진행하고 있으며, 이러한 배포를 처리해주는 CodeDeploy까지도 자동으로 만들어주게 됩니다.
이와같은 방식으로 단 몇분만에 새로운 마이크로서비스의 컴퓨팅 환경을 생성하고 있으며, 이를 통하여 AWS 리소스 관리의 어려움을 극복하고 있고, 비즈니스의 구현을 가속화하고 있습니다.
모니터링 및 로그 관리
이마트앱은 클라우드 기반으로 마이그레이션을 계속 진행하는 단계이며, 이에 따라 클라우드만이 아닌 온프레미스의 리소스도 통합해서 모니터링을 할 필요가 있습니다.
또한, 앞서 설명했던 많은 마이크로서비스의 지속적인 모니터링 및 로그를 통합관리하는 것이 필요하므로 이를 위한 솔루션으로 데이터독을 활용하고 있습니다.

전반적인 로그 및 모니터링 메트릭을 수집하고 관리하기 위한 아키텍처를 살펴보겠습니다.
1. 어플리케이션은 ECS Task내 별도 컨테이너로 구동되고, 로그는 stdout, stderr로 출력됩니다.
2. 이렇게 출력되는 로그는 사이드카로 배포된 firelens 컨테이너를 통하여 Kinesis Firehose로 전달됩니다.
3. Kinesis Firehose는 수집된 로그를 datadog-hq에 전달하며, 로그의 장기보관을 위하여 S3에 추가로 적재합니다.
4. datadog-hq는 모든 로그 및 메트릭을 통합관리하며 로그 데이터는 일정기간 보관하고 삭제합니다.
5. 로그의 장기보관을 위하여 S3에 로그가 추가적으로 적재됩니다. 또한 Lifecycle 관리 정책을 이용하여 특정기간이 경과하면, 비용효율적인 S3 스토리지 클래스로 로그 데이터를 이동하고 보관주기가 경과하면 삭제하도록 관리합니다.
6. datadog-hq에서 로그가 삭제되었더라도, S3에 로그 데이터를 장기간 보존하고 있으므로, 필요 시 Athena를 통하여 조회할 수 있습니다.
7. ECS Task에 추가적으로 구성된 datadog-agent 사이드카는 JVM에 추가적으로 적용된 java-agent를 통하여 전달받은 APM 메트릭 데이터를 datadog-hq에 전달합니다.
8. AWS의 서비스는 대부분 CloudWatch Logs 에 로그를 적재하고, CloudWatch Metrics에 메트릭을 적재하게 됩니다.
9. CloudWatch Logs에 적재된 로그는 Subscription Filter를 통하여 Lambda로 전달되고 Lambda는 datadog-hq에 다시 로그를 전달합니다.
10. CloudWatch에 적재된 메트릭의 경우, datadog-hq에서 주기적으로 데이터를 수집하며, 이 때 권한은 IAM Role을 통하여 datadog-hq에 권한이 부여됩니다.
11. 온프레미스에 존재하는 여러 시스템의 경우, 서버에 직접 datadog-agent가 설치되며 에이전트는 로그 및 메트릭을 수집하여 datadog-hq에 전달합니다.
이와 같은 구성으로 다양한 AWS서비스, 어플리케이션, 온프레미스 환경의 통합 모니터링 및 로그수집이 가능하며 이를 통하여 분산환경에서의 어려움을 개선해나가고 있습니다.
기타
이외에도 아래와 같은 다양한 방안을 고민하고 적용하며 지속적으로 마이크로서비스를 발전시켜 나가고 있습니다.
- 장애전파 방지를 위해 서킷브레이커 패턴 적용 (Reslience4j)
- 이벤트 드리븐 아키텍처 적용 (Kinesis Data Stream)
- 성능향상을 위한 데이터 저장소 분리 적용 (DynamoDB, ElastiCache, S3, ...)
- 배치 워크로드의 유연한 실행을 위한 서비스 구성 (AWS Batch, Lambda, ...)
글을 마치며
이마트 DT 본부에는 본 글에 소개한 이마트앱 이외에도 다양한 시스템과 이를 위한 아키텍처가 존재합니다.
이마트 DT 본부는 이러한 다양한 시스템을 지속적으로 혁신하고 있고, 소프트웨어 아키텍처 챕터는 이를 위해 안정적이고 확장가능한 아키텍처를 설계하고 실현하는 역할을 하고 있습니다.
이와 같은 여정에 관심 있는 분들의 피드백과 조인은 언제나 환영합니다. serviceinfra@emart.com 으로 편하게 연락 부탁 드립니다.
감사합니다.