처음 웹앱을 만들 때 대부분은 하나의 코드베이스로 시작합니다. 이것이 바로 모놀리스(Monolith) 구조입니다.
처음엔 쉽고 빠르지만, 시간이 지나고 규모가 커지면 유지보수가 어려워집니다.
그래서 등장한 것이 마이크로서비스(Microservices)와 GKE(Google Kubernetes Engine)입니다.
이 글에서는 이 개념들을 초보자 눈높이에서 설명하고, 간단한 예제로 GKE를 이용한 전환 과정을 보여드릴게요.
🏷 Monolith
모놀리스 구조는 모든 기능(프론트, 백엔드, DB 등)이 한 서버, 하나의 코드베이스에 모여 있는 구조입니다.
즉, 단일 프레임워크 기반으로 제작된 형태로 보면 됩니다.
- 백엔드: Spring Boot(Java), Django/Flask(Python), Express/Nest.js(Node.js), Rails(Ruby) 등의 단일 프레임워크
- 프론트엔드: 같은 프로젝트 내에 함께 포함되거나 별도로 분리되어도 단일 앱으로 배포
Monolith Architecture ├── src/ │ ├── controllers/ (또는 routes/) │ ├── models/ │ ├── services/ │ ├── views/ (또는 templates/) │ ├── utils/ │ └── app.js (또는 main 파일) ├── public/ (또는 static/) │ ├── css/ │ ├── js/ │ └── images/ └── package.json (또는 해당 언어의 종속성 관리 파일)
처음에는 간단하지만, 기능이 많아질수록 관리가 힘들어집니다.
🏷 Microservices
마이크로서비스는 기능을 작은 단위의 서비스로 나눈 구조입니다. 각각 독립적으로 개발, 배포, 스케일링이 가능합니다.
- 프론트엔드 서비스 – 사용자 화면 처리
- 주문 서비스 – 주문 처리
- 상품 서비스 – 상품 데이터 처리
Microservices Architecture
__ Frontend Service
|__ 사용자 화면 UI 처리
|__ API Gateway 또는 BFF와 통신
__ Order Service
|__ 주문 생성, 조회, 취소
|__ 주문 상태 관리
|__ 메시지 큐 또는 이벤트 기반 처리
__ Product Service
|__ 상품 목록, 상세 정보 제공
|__ 상품 등록/수정/삭제
|__ 재고 관리
이처럼 마이크로서비스한 서비스는 문제가 생겨도 전체 서비스가 멈추지 않습니다.
그럼 많이들 사용하는 API 와의 비슷해 보이는데.. 맞습니다. 마이크로서비스는 보통 API를 사용하여 서로 통신을 합니다.
마이크로서비스가 상위 개념(설계 철학?)으로 보면 되며, 그 하위에 API가 있다면 보시면 됩니다.
(반드시 마이크로 서비스에 API가 탑재되어야 하는 것은 아닙니다.)
🏷 Google Kubernetes Engine (GKE)
GKE는 Google Cloud의 관리형 쿠버네티스 서비스입니다. 컨테이너화된 애플리케이션의 오케스트레이션을 도와주며, 배포, 확장 및 관리의 복잡한 부분을 처리합니다.
이 부분은 앞으로 좀 더 다루어 보면, 감이 잡힐거라 생각이 됩니다.
🏷 Migration 을 하는 이유
그럼 왜 잘 작동하던 Monolith를 Microservices 로 이전을 하느냐는 근본적인 질문을 던질 수 밖에 없습니다.
✅ 모놀리스의 문제점:
- 비효율적인 확장 (특정 기능만 트래픽이 많아도 전체 애플리케이션을 확장해야 해서 리소스 낭비)
- 긴 배포 주기와 다운타임 (작은 수정도 전체 시스템을 재배포해야 해서 시간 소요)
- 단일 장애 지점 (한 부분의 오류가 전체 시스템을 다운시킬 수 있음)
- 복잡성이 증가할수록 이해하기 어려움 (코드베이스가 커지면서 개발자가 모든 부분을 파악하기 어려워짐)
- 전체 애플리케이션에 기술 스택이 고정됨 (새로운 기술 도입 시 전체 시스템에 영향을 줌)
✅ 마이크로서비스의 이점:
- 수요에 따라 서비스를 독립적으로 확장 (인기 있는 결제 서비스만 추가 서버로 확장 가능)
- 전체 시스템에 영향을 주지 않고 업데이트 배포 (사용자 서비스만 업데이트해도 다른 기능 정상 작동)
- 더 나은 장애 격리 (상품 서비스가 다운되어도 장바구니와 결제는 계속 작동)
- 명확한 경계를 가진 팀의 독립적인 작업 (상품팀, 결제팀이 서로 간섭 없이 개발 가능)
- 다양한 서비스에 다른 기술을 사용할 자유 (데이터 처리에 Python, 실시간 기능에 Node.js 등 최적 기술 선택)
이 내용을 보면 왜 microservices로 전환을 해야 하는지에 대한 이유가 명확해 질겁니다.
🏷 구현 과정
✅ 서비스 경계 구분하기
모놀리스를 분석하고 독립적인 서비스가 될 수 있는 논리적 구성 요소를 식별해서 다음과 같은 특성을 가진 부분을 찾아서 구분해 줘야 합니다.
- 특정 비즈니스 기능을 제공
- 다른 부분에 대한 의존성이 최소화됨
- 독립적으로 수정 가능
✅ 각 서비스 컨테이너화하기
# 프론트엔드 (Frontend UI) 도커 빌드
docker build -t frontend-service:1.0 ./frontend
# 상품 API (Products Service) 도커 빌드
docker build -t products-service:1.0 ./products
# 주문 API (Orders Service) 도커 빌드
docker build -t orders-service:1.0 ./orders
✅ GKE로 배포
gcloud container clusters create shop-cluster --num-nodes=3
GKE 클러스터 생성하고 난뒤 각각 서비스를 빌드하면 됩니다.
Products Service
# Docker 이미지 빌드 + 푸시
cd ~/monolith-to-microservices/microservices/src/products
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/products:1.0.0 .
# GKE 배포
kubectl create deployment products --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/products:1.0.0
# 외부 노출(외부 접근을 허용)
kubectl expose deployment products --type=LoadBalancer --port=80 --target-port=8082
Orders Service
# Docker 이미지 빌드 + 푸시
cd ~/monolith-to-microservices/microservices/src/orders
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/orders:1.0.0 .
# GKE 배포
kubectl create deployment orders --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/orders:1.0.0
# 외부 노출(외부 접근을 허용)
kubectl expose deployment orders --type=LoadBalancer --port=80 --target-port=8081
Frontend Service
# Docker 이미지 빌드 + 푸시
cd ~/monolith-to-microservices/microservices/src/frontend
gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/frontend:1.0.0 .
# GKE 배포
kubectl create deployment frontend --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/frontend:1.0.0
# 외부 노출(외부 접근을 허용)
kubectl expose deployment frontend --type=LoadBalancer --port=80 --target-port=8080
===========================================
내용이 좀 어려워 올리까 말까 했는데.. 조금이나마 도움이 될까 싶어 올려 봅니다.
(블로그에 쓴 내용중 주요 내용을 발취해 보았습니다.)
보다 자세한 것은 블로그를 보시면 조금 더 보기는 편할거 같습니다.
2탄으로 실전편도 정리해 두었으니, 같이 보시면 좀 더 이해가 쉬울겁니다.. ^^;