안녕하세요! 올리브영의 POS 개발자, Q평E평입니다.
올리브영은 유연한 서비스의 운영을 위해 IDC의 On-Premise 운영 방식에서, 많은 부분을 Cloud로 전환해오고 있습니다.
올리브영의 POS도 마찬가지인데요! POS의 레거시 시스템 현대화(Modernization) 과정을 공유해 드리고자 합니다!
Legacy To Modern : 왜 옮겨요?
현재 올리브영 POS 시스템의 아키텍처는 다음 사진처럼 구성되어 있습니다.
굉장히 심플해 보이지만, 숨은 문제점들이 많은 애물단지입니다.
대표적인 문제점들은 다음과 같습니다.
- 매장은 점점 늘어가고 POS 클라이언트의 대수는 점점 많아지는데, IDC 서버 증설의 어려움
- IDC를 확장하기 위해 많은 네트워크 작업과 설정 변경 등 많은 작업이 필요하기 때문에, 사실상 불가합니다.
- 레거시 프레임워크로 개발자가 수기로 빌드된 파일들을 배포하고 있으며, 이에 따라 버전관리의 누락이 발생 가능성 존재
- 가장 무서운 부분입니다.
Git
을 사용하긴 하지만, 실제로 파일을 올리는 작업은 개발자가 직접하고 있기 때문에 버전관리에 언제든 문제가 발생할 수 있습니다.
- 가장 무서운 부분입니다.
- 프레임워크의 로그 시스템의 빈약함
- 프레임워크의 매우 빈약한 로그 시스템이 문제 상황에 대해 대처 능력을 저하 시킵니다.
- 자바 7 버전의 제약
- 다양한 라이브러리를 사용하기 어려우며, 제한된 문법만 사용할 수 있습니다.
- 기업 프레임워크로 인한 제약
- 원본 소스가 없는 기업 프레임워크로, 기능적 확장 및 디버깅에 매우 큰 어려움이 있습니다.
... 요즘 세상에 상상도 할 수 없는 어?
요소가 한둘이 아닙니다.
이러한 불편함들을 해소하고자, 저희는 Modernization을 실행합니다.
Legacy To Modern : 그래서, 어떻게 좋아져요?
위 단점들을 해결하기 위해, Spring Boot로 API들을 마이그레이션하고, Cloud 환경인 AWS에 올리기로 결정했습니다.
이를 통해 해결되는 장점들은 다음과 같습니다.
- Elastic Load Balancing + Elastic Container Service를 통해, 트래픽에 대한 유연한 스케일아웃
- Teamcity와 CodeDeploy, ECR을 사용해 수기 배포가 아닌, 브랜치로 잘 관리된 배포
- Spring Boot를 통해 다양한 의존성을 활용 및 기능적 확장
- 커다란 Spring Community 덕분에 충분한 Reference
- 자바 17을 활용함으로써 다양한 기능 확장 및 최신 문법의 사용
Legacy To Modern : 어떻게 옮겨요?
현재의 문제점들을 해결하기 위한 아키텍처와 기술 스택들을 결정했는데요!
이것보다 더 큰 고민거리는, 매장의 영향도가 막대한 이 시스템을 어떻게 Migration 해 나아갈지 방법을 결정하는 것이었습니다.
- 설계단계에서 우리를 힘들게 한 것들
- POS Client 배포 제약
- 매장 리스크로 인한 빅뱅 적용의 어려움
- IDC의 IP 대역 문제로 인한 스위치에서 AWS로 포워딩의 불가능
클라이언트에서 들어온 특정 도메인이나 요청에 대해, L7 스위치에서 AWS 서버로 포워딩이 가능 했다면 가장 쉬웠겠지만 그렇게 호락호락하지 않았습니다.
또한 Windows인 단말기에 설치된 POS Client의 특성상, 빠른 롤백이 사실상 불가능 했습니다.
그로 인해 POS Client를 사용한 방법보다는 서버에 의존적인 Migration을 계획하게 됩니다.
그리하여 아래 3가지 적용 방안을 생각했습니다.
- API Gateway
- Gateway 역할을 하는 WAS 혹은 Web Server를 활용
- 코드 내에서 요청을 Intercept
개발자 사이드에서 아키텍처 방안을 구성하고, PE팀과 많은 회의를 진행했습니다. 그 결과,
- 올리브영에서 사용하는 API Gateway는 현재 private로 사용이 불가능 -> 1번 탈락
- WAS를 만들려면, 개발에 대한 PoC 시간 소요가 많고, 요청 흐름의 경로 자체가 바뀌므로 당장의 전수테스트가 불가피 -> 2번 탈락
그렇게 남은 Intercept 방법을 채택했고, 현재 프레임워크의 Interceptor
기능이 없다는 걸 깨닫고 Bridge 코드를 개발하게 됩니다.
Bridge의 로직은 다음과 같습니다.
- API 별로 Sevlet에 Bridge 코드를 추가
- DB의 공통 코드를 활용하여, Bridge 여부를 저장
- 프레임워크 자체의 스케줄러를 사용하여 Bridge 여부를 1분마다 업데이트
- Servlet에서 Bridge 여부에 따라, 요청을 AWS로 분기하거나 자체 처리 결정
다음과 같은 부분에 있어서, Bridge 코드는 나쁘지 않은 선택이였다고 생각합니다.
- API의 큰 흐름이 변경되지 않아, 전수 테스트를 하지 않아도 된다.
- 문제 발생 시 빠른 전환이 가능하다.
1차 적용 - 성능 테스트
POS에서 온 요청이 IDC의 서버를 거쳐 AWS로 나가기 때문에, 네트워크 홉이 추가될 수밖에 없었습니다.
그렇기에 성능이 우려되었고, 적용 전 응답시간 및 트래픽 처리에 대한 검증이 필요했습니다. 저희는 nGrinder
를 통한 성능 테스트를 진행합니다.
성능 테스트의 목표는 서버로의 요청이 Bridge를 통해 네트워크 홉이 늘어나더라도, 올영세일 기준의 트래픽을 충분히 소화할 수 있는가를 검증하는 것이었습니다.
그래서 데이터에 남아있는 3회분의 올영세일 오프라인 트래픽을 수집하고, 평시 트래픽부터 올영세일의 최대 2배의 트래픽까지 성능 테스트를 진행합니다.
결과는...! 네트워크 홉이 늘어남으로써 응답속도가 다소 늘긴 했지만, API의 성능으로 봤을 때 유의미한 수치가 아니란 판단을 내리게 됩니다.
물론 영업 중에 돌아가는 배치, DB 환경 등 여러 환경과 같은 실제와 100% 일치한 환경으로 진행하진 못해 약간 아쉬웠지만, 이 정도의 수치라면 문제없다고 판단하여 다음 스텝으로 나아가게 됩니다.
1차 적용
그렇게 Bridge를 통한 Migration을 결정하고, Risky한 빅뱅 오픈이 아닌 다음과 같은 점진적 적용을 계획했습니다.
- 1차 : READ API 대상 이관
- 비교적 영향도가 적은 순수 READ API를 대상으로 적용합니다.
- Bridge를 활용해 아키텍처 전체 흐름을 변경하고, 이에 대한 검증을 진행합니다.
- 2차 : CUD API 대상 이관
- 1차 적용을 통해 API의 흐름이 검증이 완료된 상태에서, 데이터 변경에 대한 API 이관을 Bridge를 사용해 진행합니다.
- 3차 : POS Client의 요청을 IDC가 아닌, AWS로의 다이렉트 요청으로 변경
- 기존
POS -> IDC -> AWS
에서, Client 배포를 통해POS -> AWS
로 아키텍처 흐름을 변경합니다.
- 기존
대망의 1차 적용 단계입니다! 이목이 집중 된 만큼 철저한 준비를 했는데요!
이관된 서비스가 기존의 서비스와 동일한 품질을 제공하는 것이 가장 큰 목표였습니다.
기존 IDC에서 제한된 인스턴스로 인한 커넥션 풀과 스레드를 보완하기 위해 Scale-out
조건을 고민했으며, 사용률 모니터링을 위한 Datadog
대쉬보드를 만들어 실시간으로 수집할 수 있도록 사전작업을 진행합니다.
그리하여 배포날.. 사전 반영된 Bridge 덕분에, 손쉽게 공통코드를 바꿈으로써 AWS의 중계서버로의 전환을 진행합니다!
적용 후 일주일간 Datadog
과 로그, 매장과의 소통을 활용해 집중 모니터링을 진행했습니다.
첫술이 배부를 순 없는 법.. Hotfix
는 있었습니다.
하지만 다행히 Bridge를 다시 기존 서버로 전환할 만큼 큰 문제점은 발견되지 않았고, 1차 적용이 성공적으로 완료되었다고 판단하였습니다!
다음 발걸음
올리브영 POS 서버의 Modernization 1단계를 함께하셨는데요, 현재는 2차 적용을 위해 고군분투 중입니다!
2차.. 3차가 성공적으로 적용되어 올리브영의 고객님들에게 공개되는 그날, 더 좋은 글로 다시 돌아오겠습니다.
올리브영의 발전, 기대해 주세요!