ADR-07: 공유 인프라 전략
| 날짜 | 작성자 | 영향 리포지토리 |
|---|---|---|
| 2024-12-17 | @KubrickCode | 전체 |
Context
문제 상황
여러 서비스로 구성된 분산 시스템에서, 데이터베이스와 캐시 같은 공유 인프라를 어떻게 관리할 것인가는 핵심적인 아키텍처 결정. 이 결정은 다음에 영향을 미침:
- 데이터 일관성: 서비스 간 데이터 무결성 유지 방법
- 운영 복잡도: 관리해야 할 인프라 구성요소 수
- 비용 효율: 리소스 활용률과 인프라 비용
- 서비스 독립성: 서비스별 독립 배포 및 스케일링 가능 여부
- 장애 격리: 인프라 장애 시 영향 범위
데이터 인프라 스펙트럼
| 접근 방식 | 데이터 일관성 | 운영 오버헤드 | 서비스 독립성 | 비용 |
|---|---|---|---|---|
| 공유 데이터베이스 | 강함 (ACID) | 낮음 | 낮음 | 낮음 |
| 서비스별 스키마 | 강함 (ACID) | 낮음~중간 | 중간 | 낮음 |
| 서비스별 데이터베이스 | 최종 일관성 | 높음 | 높음 | 높음 |
| 하이브리드 (읽기 복제) | 혼합 | 중간 | 중간 | 중간 |
핵심 결정 요소
- 팀 구조: 단일 팀 vs 서비스별 독립 팀
- 데이터 관계: 서비스 간 데이터 연산 빈도
- 일관성 요구사항: 강한 일관성 필요 vs 최종 일관성 허용
- 운영 역량: DevOps/DBA 전문성 보유 여부
- 스케일 요구사항: 현재 및 예상 트래픽 패턴
- 컴플라이언스: 데이터 격리 또는 멀티테넌시 요구사항
핵심 질문
공유 상태에 대한 접근이 필요한 여러 서비스가 있는 시스템에서, 운영 단순성과 아키텍처 유연성의 균형을 맞추기 위해 데이터베이스와 캐시 인프라를 어떻게 구성해야 하는가?
Decision
모든 서비스가 공통 데이터베이스와 캐시 인스턴스에 접근하는 공유 인프라 전략을 채택한다.
핵심 원칙:
- 운영 단순화: 데이터 인프라의 단일 관리 지점
- 데이터 일관성: 서비스 경계를 넘는 ACID 트랜잭션 활용
- 비용 효율: 공유를 통한 리소스 활용 극대화
- 실용적 트레이드오프: 복잡도 감소를 위해 결합도 수용
- 진화 경로: 필요시 분해를 위한 설계 고려
Options Considered
Option A: 공유 인프라 (선택됨)
모든 서비스가 단일 데이터베이스 인스턴스와 공유 캐시 계층에 연결함.
장점:
- 운영 단순화: 단일 데이터베이스 모니터링, 백업, 유지보수
- 강한 일관성: 서비스 간 데이터에 대한 ACID 트랜잭션, 조율 불필요
- 비용 효율: 인프라 중복 없음, 연결 풀 공유
- 쿼리 단순화: API 오케스트레이션 없이 서비스 간 데이터 조인
- 통합 스키마: 단일 진실 공급원, 이해 용이
- 지연 시간 감소: 서비스 간 데이터 접근에 네트워크 홉 없음
단점:
- 강한 결합: 스키마 변경 시 모든 서비스 간 조율 필요
- 공유 운명: 데이터베이스 장애가 모든 서비스에 동시 영향
- 스케일링 제약: 서비스별 독립 DB 스케일링 불가
- 리소스 경쟁: 한 서비스의 무거운 쿼리가 다른 서비스에 영향
- 기술 선택 제한: 모든 서비스가 호환 가능한 데이터 모델 사용 필수
- 마이그레이션 복잡도: 나중에 서비스 분리 시 어려움
Option B: 서비스별 데이터베이스
각 서비스가 전용 데이터베이스 인스턴스를 소유하고 관리함.
장점:
- 서비스 독립성: 각 팀이 데이터 모델과 스키마 진화 제어
- 장애 격리: 데이터베이스 문제가 개별 서비스에 한정
- 독립 스케일링: 서비스 요구에 따른 데이터베이스 리소스 스케일링
- 기술 자유도: 각 서비스가 최적의 데이터베이스 기술 선택 가능
- 명확한 소유권: 데이터 책임에 대한 모호함 없음
- 팀 자율성: 독립적 배포 및 마이그레이션 일정
단점:
- 운영 오버헤드: 여러 데이터베이스 관리, 모니터링, 백업 필요
- 데이터 일관성: 분산 트랜잭션 또는 최종 일관성 필요
- 높은 비용: 더 많은 인스턴스, 연결, 인프라
- 쿼리 복잡도: 서비스 간 데이터는 API 호출 또는 이벤트 동기화 필요
- 데이터 중복: 비정규화 필요한 경우 많음
- 조율 오버헤드: 서비스 간 통신 패턴 필요
Option C: 서비스별 스키마
단일 데이터베이스 인스턴스에서 각 서비스별로 격리된 스키마 사용함.
장점:
- 논리적 분리: 인프라를 공유하면서 명확한 경계
- 마이그레이션 경로: 나중에 완전 분리로의 전환 용이
- 비용 효율: 논리적 격리를 가진 단일 인스턴스
- 일부 독립성: 스키마 변경이 서비스 스키마 내에 한정
- 균형 잡힌 트레이드오프: 공유와 격리 사이의 중간 지점
단점:
- 여전히 공유 운명: 인스턴스 수준 장애가 모든 서비스에 영향
- 리소스 경쟁: 공유된 컴퓨팅 및 I/O 리소스
- 경계 침범 유혹: 스키마 간 의존성 생성 용이
- 부분적 이점: 완전한 격리나 완전한 공유의 이점 모두 부분적
Option D: 읽기 복제본을 활용한 하이브리드
공유 주 데이터베이스와 서비스별 읽기 복제본 또는 캐시 사용함.
장점:
- 읽기 확장성: 읽기 부하를 복제본에 분산
- 쓰기 일관성: 쓰기에 대한 단일 진실 공급원
- 점진적 진화: 추가 분해를 위한 경로
- 성능 최적화: 서비스별 읽기 최적화
단점:
- 복제 지연: 읽기에 대한 최종 일관성
- 복잡도: 관리할 여러 데이터 소스
- 비용: 추가 복제본 인스턴스
- 부분적 해결: 쓰기 스케일링 미해결
Consequences
Positive
운영 단순화
- 단일 데이터베이스 인스턴스 모니터링 및 유지보수
- 통합된 백업 및 재해 복구 절차
- 중앙화된 성능 튜닝 및 최적화
- 인프라 알림 피로도 감소
데이터 무결성
- ACID 트랜잭션이 서비스 데이터 간 일관성 보장
- 분산 트랜잭션 조율 불필요
- 참조 무결성이 데이터베이스 수준에서 강제됨
- 단일 진실 공급원으로 동기화 문제 제거
비용 효율
- 리소스 공유를 통한 낮은 인프라 비용
- 공유 연결 풀로 오버헤드 감소
- 단일 관리형 데이터베이스 서비스 구독
- 더 큰 인스턴스 크기에 대한 규모의 경제
개발 속도
- API 오케스트레이션 없이 서비스 간 쿼리
- 복잡한 데이터 관계에 대한 익숙한 관계형 패턴
- 단일 데이터베이스로 단순화된 로컬 개발
- 데이터 흐름 이해를 위한 인지 부하 감소
관찰 가능성
- 통합된 쿼리 로그와 성능 메트릭
- 느린 쿼리 분석을 위한 단일 지점
- 데이터 접근 패턴의 전체적 뷰
- 서비스 경계를 넘는 단순화된 디버깅
Negative
서비스 결합
- 스키마 변경에 팀 간 조율 필요
- 서비스 간 배포 의존성
- 완화 방안: 명확한 스키마 소유권 설정, 하위 호환 변경 사용, 스키마 변경 리뷰 프로세스 구현
영향 범위
- 데이터베이스 장애가 모든 의존 서비스에 영향
- 유지보수 윈도우가 전체 시스템에 영향
- 완화 방안: 고가용성 구성, 자동 장애 조치, 포괄적 백업 전략, 서비스의 서킷 브레이커
스케일링 제한
- 서비스 워크로드별 독립적 데이터베이스 스케일링 불가
- 리소스 집약적 작업이 모든 서비스에 영향
- 완화 방안: 쿼리 최적화, 캐싱 전략, 읽기 부하가 높은 워크로드용 읽기 복제본, 연결 풀링
기술 제약
- 모든 서비스가 동일한 데이터베이스 기술로 작업 필요
- 다른 데이터 패턴에 대한 스토리지 최적화 불가
- 완화 방안: 데이터베이스 기능 유연하게 사용 (JSON 컬럼 등), 특화된 요구를 위한 마이그레이션 경로 계획
장기 유연성
- 독립 스케일링을 위한 서비스 추출 어려움
- 공유 데이터 모델로 팀 자율성 제한
- 완화 방안: 깔끔한 데이터 접근 계층 추상화, 스키마에서 서비스 경계 문서화, 분해 트리거 계획
Technical Implications
| 측면 | 함의 |
|---|---|
| 스키마 관리 | 단일 마이그레이션 경로, 파괴적 변경에 조율 필요 |
| 연결 풀링 | 서비스 간 공유 풀, 적절한 크기 조정 필요 |
| 쿼리 성능 | 서비스 간 영향, 쿼리 최적화 규율 필요 |
| 백업/복구 | 단일 백업 전략, 통합된 특정 시점 복구 |
| 보안 | 공유 접근 제어, 앱 계층에서 서비스 수준 권한 |
| 모니터링 | 통합 메트릭, 쉬운 상관관계 분석, 공유 알림 |
| 로컬 개발 | 더 간단한 설정, 단일 데이터베이스 컨테이너/인스턴스 |
| 테스팅 | 공유 테스트 데이터베이스, 테스트 격리 전략 필요 |
캐시 인프라 고려사항
공유 캐싱(Redis/Memcached)도 유사한 트레이드오프를 가짐:
| 고려사항 | 공유 캐시 | 서비스별 캐시 |
|---|---|---|
| 키 네임스페이스 | 충돌 방지를 위한 프리픽스 필요 | 자연스러운 격리 |
| 메모리 | 효율적 활용, 잠재적 제거 | 전용 리소스, 예측 가능 |
| 장애 | 캐시 미스가 모든 서비스에 영향 | 격리된 장애 |
| 무효화 | 서비스 간 캐시 조율 용이 | 분산 무효화 필요 |
When to Reconsider
- 성능 병목: 데이터베이스가 특정 서비스의 제한 요소가 됨
- 팀 확장: 독립 팀이 자율적 데이터 소유권 필요
- 컴플라이언스 요구사항: 규정에 의해 데이터 격리 의무화
- 기술 요구: 서비스에 특화된 데이터베이스 필요 (그래프, 시계열 등)
- 스케일 분기: 서비스 간 극적으로 다른 스케일링 요구사항
- 장애 격리: 영향 범위 축소가 중요한 요구사항이 됨
- 스키마 충돌: 스키마 변경에 대한 빈번한 조율 오버헤드
