Cloud Wave 3기의 1조(5명) 조장을 맡아 올리브영 온라인 쇼핑몰 AWS 인프라 구축 프로젝트를 진행했습니다.
(2024.08.08 ~ 2024.08.30)
R&R: PM, 클라우드 아키텍트 및 엔지니어
- 아키텍처 설계 및 구축
- 무중단 배포
1. 프로젝트 소개
프로젝트 시나리오:
인기 브랜드가 올리브영 온라인 쇼핑몰에 입점할 때, 트래픽 급증에도 안정적인 서비스를 제공할 수 있는 인프라 구축이 필요
- 타겟 유저: 고객, 관리자, 개발자
- 기대 효과:
1. 갑작스러운 트래픽 폭증에도 견딜 수 있는 안정적인 서비스 제공
2. 백오피스를 이용해 신속한 신규 상품 등록과 Node, Pod 스케일링 가능
3. 백업&DR 을 통한 장애 발생 시 데이터 손실과 서비스 중단 시간 최소화
2. 아키텍처 설계
2-1. 메인 아키텍처
프로젝트 기간이 짧다는 점을 고려하여 러닝커브가 낮아 빠르게 활용할 수 있는 서비스들과 AWS 완전 관리형 서비스를 최대한 활용하여 아키텍처를 설계하였습니다.
2-2. Service Flow
사용자는 Route 53에 등록된 도메인 네임을 이용해 WAF가 설정된 CloudFront와 ALB를 거쳐 프론트와 백엔드 서비스에 접근합니다. 이때 ALB의 경로 기반 라우팅을 통해 다양한 서비스에 적절히 API 호출이 이루어지도록 했습니다.
1. 프론트엔드 배포에 CloudFront와 S3를 이용하여 정적 콘텐츠 캐싱을 통해 지연 시간을 최소화하고 OAC를 통해 보안 강화
2. 백엔드 배포에 비용 효율성과 이식성을 고려하여 EKS를 사용
3. ALB와 CloudFront에 ACM과 WAF 적용하여 보안 강화
4. User, Product, Back-office로 서비스를 나누어 Pod 단위로 배포 -> 각 서비스에 다른 Scaling 기준 적용 가능
2-3. DB
DB 계층에는 AWS 완전 관리형 서비스인 Aurora DB와 ElastiCache를 활용하여 트래픽 증가에도 부하 분산을 통해 서비스의 안정성을 유지할 수 있게 하였습니다.
1. Product 서비스는 데이터를 Redis에서 우선적으로 조회하고, 캐시가 존재하지 않는 경우에만 DB에 접근하도록 했습니다.
2. DB 접근 시에도 Aurora 클러스터의 읽기/쓰기 분산 처리를 활용하여, 읽기 작업은 리더 인스턴스에서, 쓰기 작업은 라이터 인스턴스에서 처리하여 부하를 효율적으로 분산하였습니다.
3. 쓰기 작업 시 Redis의 cache를 update하게 했고 redis primary와 aurora writer instance가 replication을 통해 실시간으로 자신과 replica의 상태를 동기화하여, 데이터 무결성을 보장하도록 하였습니다.
2-4. CI/CD
CI/CD에는 gutlab이나 harbor와 달리 별도의 설치나 설정이 필요없고 빠르게 활용할 수 있는 github과 ECR의 private repository와 github action을 사용하였습니다.
CI/CD Flow
개발자가 코드를 Github에 올리면 github action이 트리거되어 코드를 이미지로 빌드하여 ECR에 push한 후, IaC용 repository의 매니페스트 파일의 이미지 태그를 수정합니다. 이후 Argocd가 Github Webhook을 수신하면 클러스터와 매니페스트 파일의 차이를 확인하고, 싱크를 맞춰주면 CI CD 과정이 마무리 됩니다.
argo rollout을 이용한 배포 전략 구현
평소에는 롤아웃과 롤백이 빠르고 간단한 blue/green으로 배포하고 대규모 업데이트 시에는 일정 기간동안 신규 버전을 테스트해볼 수 있는 canary로 배포할 수 있도록 두가지 배포를 모두 구현하여, 상황별로 다른 배포 전략을 사용할 수 있게 하였습니다.
Ex) rollout.yaml
---
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: service-user
namespace: service
spec:
replicas: 1
selector:
matchLabels:
type: service
for: user
template:
metadata:
labels:
type: service
for: user
spec:
containers:
- name: user
image: {imageURL}
ports:
- containerPort: 8080
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 200m
memory: 512Mi
strategy:
blueGreen:
activeService: service-user-active
previewService: service-user-preview
autoPromotionEnabled: false
# strategy:
# canary:
# maxSurge: '25%' # canary 배포로 생성할 pod의 비율
# maxUnavailable: 0 # 업데이트 될 때 사용할 수 없는 pod의 최대 수
# steps:
# - setWeight: 25 # 카나리로 배포된 서버로 전송해야될 트래픽 비율
# - pause: { duration: 1h } # AutoPromotion Time
---
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: service-products
namespace: service
spec:
replicas: 5
selector:
matchLabels:
type: service
for: products
template:
metadata:
labels:
type: service
for: products
spec:
containers:
- name: products
image: {imageURL}
ports:
- containerPort: 8080
resources:
requests:
cpu: 1
memory: 1024Mi
limits:
cpu: 1
memory: 1024Mi
strategy:
blueGreen:
activeService: service-products-active
previewService: service-products-preview
autoPromotionEnabled: false
# strategy:
# canary:
# maxSurge: '25%' # canary 배포로 생성할 pod의 비율
# maxUnavailable: 0 # 업데이트 될 때 사용할 수 없는 pod의 최대 수
# steps:
# - setWeight: 25 # 카나리로 배포된 서버로 전송해야될 트래픽 비율
# - pause: { duration: 1h } # AutoPromotion Time
2-5. Logging System
1. cloudtrail과 vpc flow logs, waf의 로그를 Amazon CloudWatch Logs와 S3 버킷에 수집하여 다방면에서 보안 위협을 확인할 수 있도록 하였습니다.
2. 실시간 모니터링을 위한 CloudWatch log의 보존기한은 3일로 설정하고, S3 버킷에서는 라이프 사이클 정책을 적용해 비용 효율적으로 3개월동안 저장하도록하였습니다.
3. CloudWatch 로그에 대한 경보가 발생되면, SNS와 Lambda를 통해 Slack으로 전송되어, 문제에 빠르게 대처할 수 있게 하였습니다.
2-6. Backup & DR
주문 정보가 자주 업데이트될 것을 예상하여, 최소한의 데이터 손실을 목표로 2시간의 RTO(복구 목표 시간)와 0초에 가까운 RPO(복구 목표 시점)을 설정하였습니다. 또한 신뢰성과 비용 효율성 모두 고려한 파일럿 라이트 전략을 사용하였습니다.
데이터 복제 및 백업 과정
1. 거의 0초에 가까운 RPO 요구사항을 충족하기 위해 글로벌 데이터베이스를 사용해 리전 간 데이터 복제 시 별도의 스토리지 계층을 사용하게 하여 더 낮은 지연 시간이 소요되도록 하였습니다.
2. 주문 정보가 업데이트되면, 쓰기 인스턴스가 데이터를 저장하고, 인스턴스와 리전 간 데이터가 빠르게 복제됩니다. 이로 인해 데이터가 안정적으로 다른 리전에 동기화됩니다.
3. AWS backup을 사용해 주기적으로 데이터베이스를 백업하여 데이터에 문제가 생겼을 경우 데이터를 롤백할 수 있도록 하였습니다.
DR 리전을 사용한 복구 과정
product 리전에 장애가 발생하면 운영자에게 알림 메일이 전송됩니다. 이를 통해 문제를 인식하면, 테라폼을 사용하여 클러스터와 cli 서버를 생성한 후 Route53의 경로를 수정해 서비스를 정상화 할 수 있습니다.
Terraform module
자주 사용하는 리소스를 테라폼 코드에서 모듈화하여 코드 재사용성을 높였으며, 여러 사람이 협업해도 쉽게 리소스를 관리 할 수 있도록 하였습니다.
실제 테스트 결과
기존 백엔드 서버에 연결되었던 도메인 주소를 DR 리전의 백엔드 서버로 연결하였을 때, 정상적으로 api호출이 되고, 그 결과가 Product 리전의 데이터베이스에 저장된 값과 같은 것을 확인할 수 있었습니다.
2-7. Security
1. EKS에 kubectl을 할 CLI server를 private subnet에 두고 Session Manager를 통해서만 접근 가능하게하여 보안을 강화하면서도 IAM 계정만 있으면 언제든, 어떤 기기를 통해서든 cli server에 접근 할 수 있도록 하였습니다.
2. WAF를 활용해 SQL 인젝션과 XSS 공격, DDos 공격으로부터 애플리케이션을 보호할 수 있도록 하였습니다.
3. RDS Proxy를 통해 데이터베이스 연결을 보다 효율적으로 관리하도록 하였습니다.
RDS Proxy는 커넥션 풀의 관리 뿐만 아니라 Secrets Manager와 IAM에 대한 통합 관리를 통해 데이터베이스 인증 정보를 안전하게 관리하고 접근 제어를 강화합니다.
4. github actions 를 이용하므로 github에서 제공하는 Secrets 기능을 사용하여 애플리케이션의 중요한 설정 정보나 인증 정보를 안전하게 관리 할 수 있도록 하였습니다.
3. Scailing
시나리오 상 인기 브랜드가 입점하는 시점에 트래픽 폭증할 것이 예상되기 때문에,
사전에 스케일 아웃을 해두고 시간이 지난 후 스케일 인을 하는 방식으로 운영할 것입니다.
그러나 사전에 인지하지 못하는 경우나 예상보다 트래픽이 더 많이 증가하는 경우가 발생할 수 있기 때문에, 스케일 인/아웃이 빠르고 비용 효율적인 운영을 위해 오토스케일링 전략이 필요합니다.
Pod Auto scaling: HPA를 이용해 metric server가 수집한 metric 기반으로 스케일 인, 아웃되도록 구성
1. cluster autoscaler는 오토스케일링 그룹을 거쳐 노드를 스케일링하므로 노드 생성 속도가 느리다는 단점이 있고 이를 ASG의 Warm pool을 통해 보완하더라도 노드의 타입이 하나로 제한되고 동기화 문제가 발생할 수 있습니다.
2. 카펜터는 직접 노드를 스케일링 하므로 노드 스케일링 속도가 빠르면서도 파드의 요구사항에 맞는 최적의 노드를 프로비저닝하기 때문에 비용을 절감할 수 있다는 장점이 있습니다.
-> Node Auto scaling: 카펜터를 사용하였고 테스트한 결과 30초 이내에 노드가 완전히 준비되는 것을 확인할 수 있었습니다.
Overprovisioning
카펜터의 경우 Pod가 스케줄링 되지 않아야 새로운 노드를 띄우기 때문에 pod Anti Affinity를 가지는 overprovision용 pod를 생성하는 방법으로 pod 수만큼 노드를 확장하여 파드가 스케줄링될 여유 공간을 확보할 수 있었고 이러한 스케일링 전략을 활용하여 백오피스를 이용한 노드와 파드 스케일링 기능을 구현하였습니다.
4. 차별화 전략
4-1. Monitoring
EKS 모니터링 시스템으로 Datadog을 선택했습니다.
Prometheus와 Grafana가 모니터링 도구로 많이 사용되지만, datadog은 인프라, 애플리케이션, 로그 등을 하나의 플랫폼에서 관리할 수 있고, SaaS로 제공되어 인프라 관리 부담이 없다는 장점이 있기 때문에 짧은 기간 진행되는 프로젝트의 특성 상 빠른 활용와 운영 효율성이 중요하다고 생각하여 Datadog이 Prometheus와 Grafana보다 더 나은 선택이라고 판단했습니다.
DataDog Architecture
데이터독이 클러스터의 데이터를 수집하는 과정은 다음과 같습니다.
1. 데이터독 에이전트가 파드와 함께 생성되어 데이터를 수집해 클러스터 에이전트에 전달합니다.
2. 클러스터의 정보를 수집한 클러스터 에이전트가 데이터독 서버에 수집한 정보를 전달합니다.
3. 사용자가 대시보드를 통해 클러스터를 모니터링할 수 있습니다.
DataDog APM
Datadog의 APM 기능을 추가로 도입하여, 애플리케이션의 성능을 상세히 모니터링하고 분석할 수 있었습니다.
특히, APM을 통해 오류가 언제 어떤 서비스에서 발생했는지를 파악할 수 있어, 문제를 신속하게 복구하고 애플리케이션의 안정성을 유지하는 데 큰 도움이 되었습니다.
DataDog RUM
실제 사용자가 웹 애플리케이션과 상호작용하는 방식을 추적하고 모니터링하는 기능인 RUM을 활용하여 사용자들이 애플리케이션을 어떤 흐름으로 사용하는지, 어떤 상황에서 문제가 발생하는지 모니터링할 수 있었습니다.
이처럼 Datadog을 통해 저희 프로젝트의 인프라와 애플리케이션을 효율적으로 관리할 수 있었고, 오류 발생 시에도 신속하게 문제의 원인을 파악하고 대처하여 안정적인 서비스 운영과 사용자 경험 향상이 가능했습니다.
4-2. Back Office
백오피스의 서비스 플로우는 사용자의 서비스 플로우와 비슷하지만 백오피스의 특성 상 보안이 중요하기 때문에 cognito와 WAF를 통해 보안을 강화하였습니다.
관리자는 WAF의 Web ACL에 허용된 단 하나의 IP 주소에서만 관리자 페이지에 접근할 수 있습니다. 이때, AWS Cognito의 사용자 풀에 등록된 계정만 로그인할 수 있으며, MFA를 이용한 인증까지 추가로 거쳐야 최종적으로 백오피스 대시보드에 접근할 수 있습니다
실행 화면
대시보드에서는 상품에 대한 CRUD 작업을 수행할 수 있으며, db에 쓰기 작업을 한 후 작업이 이루어질 때마다 Redis 캐시를 자동으로 update하여 최신 데이터를 반영할 수 있도록 하였습니다.
관리자 페이지의 스케일링 버튼을 이용한 노드와 파드 스케일링
관리자가 Pod와 Node의 스케일링을 실시간으로 조정하거나 예약된 시간에 맞춰 자동으로 스케일링할 수 있는 기능도 구현하였습니다.
관리자가 pod와 node의 개수를 설정한뒤 스케일링 버튼을 누르면, Backoffice server가 argocd와 연결된 레포지토리를 clone한 뒤, 현재 pod와 노드의 replica 수를 설정된 수로 변경한 후 커밋 및 푸시합니다. 이후 저희가 설계한 CD과정에 따라 변경된 스케일링 설정이 즉시 반영됩니다.
이 기능을 통해 인기 브랜드가 입점하기 전에 예상되는 트래픽에 대비해 즉시 혹은 미리 스케줄링을 통해 스케일아웃 할수 있게 하여, 트래픽 증가에 신속하게 대응할 수 있습니다
오토 스케일링 화면
스케일 아웃이 동작하는 과정의 예시입니다. 관리자가 스케일링 버튼을 누르면, IaC code가 저장되어 있는 Repository에 관리자가 입력한 갯수만큼 자동으로 커밋 및 푸시가 되고, 처음에는 8개였던 pod가 12개의 pod로 스케일 아웃 되었습니다.
5. 테스트
nGrinder를 사용한 부하테스트
nGrinder를 사용해 부하테스트를 진행하였고, vUser를 1500으로 설정한 후 5분간 부하테스트한 결과 다음과 같이 불안정한 결과가 나왔습니다.
1. Cache와 DB Memory type 수정
datadog의 APM 서비스를 통해 지연이 Redis와 Aurora에서 발생한다는 것을 발견할 수 있었고,
확인 결과 Redis와 Aurora의 인스턴스 타입을 모두 t타입으로 설정했기 때문에 credit을 모두 소진했을 경우 성능 저하가 발생했을 것이라고 예상할 수 있었습니다.
DB와 Redis의 인스턴스 타입을 r타입으로 변경한 후 테스트한 결과, 다음과 같이 비교적 안정적인 그래프를 확인할 수 있었습니다.
그러나 여전히 갑작스럽게 TPS가 떨어지는 구간이 존재함을 발견할 수 있었습니다.
2. Pod Scaling 시점 수정
K9s를 이용해 Pod에 대한 부하를 모니터링하여 Pod가 생성되기 시작할 때 이러한 구간이 발생함을 확인하였고,
HPA가 scale out을 시작해야할 cpu 사용률 기준을 너무 높게 잡아 pod가 너무 느린 시점에 늘어난다는 것을 알아낼 수 있었습니다.
이를 수정한 후 부하테스트를 진행한 결과,
CPU와 Memory 사용률이 과도하게 높아지기 전에 pod와 node가 생성되는 것을 확인할 수 있었고,
다음과 같이 더 안정적인 TPS 그래프를 얻을 수 있었습니다.
6. 회고
1. PM으로서 프로젝트를 계획하고 협업하며 실제로 인프라 환경을 구축하고 서비스를 배포해보니,예상치 못한 변수들이 많았고 실무 경험이 왜 중요한지 조금이나마 알게되는 계기가 되었습니다.
2. 올리브영은 Fargate를 사용하기 때문에 EKS Managed Node Group이 아닌 EKS Fargate로 구축하려 하였으나, argocd와 같은 오픈소스 툴의 적용에 대한 정보가 상대적으로 많이 부족하여 시간 관계 상 중간에 포기해야만 했던 것이 아쉬웠습니다. 기회가 된다면 이번 프로젝트로 얻은 지식과 경험을 살려 EKS Fargate로 인프라를 구축하는 것에 다시 도전해보고 싶습니다.
3. 프로젝트 기간이 짧아 istio를 사용해보지 못한 것이 아쉽습니다. 기회가 된다면 argo rollout과 istio를 연계해 자동 롤백 기능을 구현해보고 싶습니다.
4. 프로젝트를 통해 얻은 성과를 정리하고 표현하는 것이 매우 어렵고 중요한 일이라는 것을 깨달았습니다. 특히 연습을 통해 발표와 PT 능력을 길러야겠다고 느꼈습니다.
5. 클라우드 엔지니어로서의 역할은 충실히 하였지만, 프로젝트 계획이나 팀원들의 역할 분배, 팀원들이 구현한 기능 파악 등 PM으로서 신경쓰지 못한 부분이 있어 아쉽습니다. 다음 기회가 있다면 PM으로서의 역할에 더 중점을 두고 프로젝트를 진행해보고 싶습니다
'Cloud engineering' 카테고리의 다른 글
ArgoCD 설치 및 설정 (1) | 2024.09.01 |
---|---|
Terraform 이용해 EKS Cluster 구축하기 (0) | 2024.09.01 |
부하 테스트 및 조회 성능 개선(Ehcache, DB index) (0) | 2024.06.11 |
[ Jenkins ] Jenkins를 이용해 빌드/배포 자동화하기 (0) | 2024.02.15 |
[ AWS ] Spring Boot 서버 AWS EC2에 배포하기 (0) | 2024.02.12 |