PostgreSQL 데이터베이스의 커넥션이 차며 서비스가 멈추던 문제를 EBS IOPS 부족으로 진단하고 스토리지를 교체한 기록이다. EBS 선택지를 비용과 성격으로 비교하고, 마이그레이션 방법 세 가지를 검토해 복제 방식으로 진행한다. 작업 막바지에 터진 성능 문제를 ANALYZE로 해결한 대목이 핵심이다.
핵심 포인트- 코드와 쿼리를 뒤져도 원인이 안 나왔고 피크 타임에 반복 재현되면서 EBS IOPS 소진으로 결론 났다.
- gp2는 GB당 3 IOPS를 주고 1TB 미만은 I/O Credit 제한까지 걸려 있어 고부하 환경에서 장애를 부를 수 있다.
- io1은 같은 IOPS 기준 gp2의 두 배 이상 비싸고 처리량도 두 배인데, 이 서비스는 처리량이 아니라 IOPS가 부족한 상태였다.
- RAID로 묶는 방안은 IOPS가 오르는 것은 RAID 0뿐이고 무결성 때문에 RAID 10으로 가면 io1보다 비싸진다.
- 스냅샷으로 만든 EBS는 블록에 최소 한 번 접근해야 성능이 나와 사전 워밍에만 6~8시간이 걸린다.
- 복제로 옮긴 직후 단순 SELECT가 극도로 느렸는데 테이블별 ANALYZE 한 번으로 원상 복구됐다.
상세 정리- 증상: 간헐적으로 커넥션이 꽉 차며 서비스가 짧게 느려지거나 멈췄고, 코드와 쿼리를 검토해도 복잡한 쿼리가 없어 원인을 짚기 어려웠다.
- 확정: 이용자가 몰리는 시간대에 같은 장애가 반복되면서 EBS IOPS 문제로 가닥을 잡았다.
- EBS의 장점: 스냅샷으로 백업하고 그 이미지로 복제 인스턴스를 몇 번의 클릭으로 만들 수 있으며 데이터 내결함성이 일반 스토리지보다 훨씬 강하다.
- EBS의 주의점 1: 네트워크로 인스턴스와 데이터를 주고받으므로 트래픽이 높으면 최적화 옵션을 켜야 성능이 나온다.
- EBS의 주의점 2: gp2는 용량에 비례해 IOPS가 정해지고 소용량은 크레딧 제한까지 걸린다.
- 과거 사례: 500GB를 쓰던 시절 트랜잭션 급증으로 I/O 크레딧을 다 써 주기적으로 프리징이 났고, 2TB로 늘려 크레딧 제한을 없애고 6,000 IOPS를 확보해 해결했다.
- 이번 판단: 쿼리 튜닝이나 캐시 활용도 가능하지만 개선 지점을 하드웨어 업그레이드에 두기로 했다.
- 선택지 1 io1: 원하는 성능과 용량을 고를 수 있는 가장 깔끔한 해법이지만 가격이 걸림돌이다. 도입 전에 IOPS뿐 아니라 초당 처리량이 얼마나 필요한지 따져야 한다.
- 선택지 1 기각: 이 서비스는 IOPS가 더 필요하고 처리량은 부족하지 않아 후보에서 뺐다.
- 선택지 2 RAID: RAID 0에서만 IOPS가 오르고 RAID 1은 읽기 처리량만 확보된다. RAID 0 단독은 무결성에 취약해 RAID 10으로 가야 하는데 그러면 io1보다 비싸진다.
- 최종 선택: gp2에서 IOPS를 최대로 확보할 수 있는 용량을 할당해 비용을 억제하고, 그래도 부족하면 io1으로 옮기기로 했다.
- 마이그레이션 방법 1 스냅샷: 스냅샷을 떠서 큰 EBS를 만들고 마운트하는 방식은 간단해 보이지만, 스냅샷 기반 볼륨은 블록에 최소 한 번 접근해야 성능이 확보된다.
- 방법 1 기각: 모든 블록을 미리 읽는 작업이 6~8시간 이상 걸리는데 메인 데이터베이스에서 그 작업을 돌릴 수는 없다.
- 마이그레이션 방법 2 DMS: 중계 서버가 데이터를 복제하고 로그까지 반영해줘 거의 무중단 시나리오를 짤 수 있었다.
- 방법 2 기각: 테스트 중 PostgreSQL에만 있는 JSON과 ARRAY 타입을 지원하지 않는다는 것을 발견해 포기했다.
- 마이그레이션 방법 3 복제: 대상 서버를 스탠드바이로 준비하고 마스터에서 복제 준비를 마친 뒤, 서비스를 중단하고 pg_basebackup으로 데이터를 받아 무결성을 확인하고 재개하는 시나리오를 세웠다.
- 사고: 계획대로 진행해 시간 안에 끝냈는데 오픈 전 내부 테스트에서 평범한 SELECT가 극도로 느렸다.
- 대응 순서: REINDEX나 FULL VACUUM은 시간을 크게 잡아먹으므로 가장 짧은 ANALYZE부터 시도하고 안 되면 순차로 올라가기로 했다.
- 두 명령의 역할: VACUUM은 참조되지 않는 데이터를 정리해 재사용 공간으로 돌리는 작업이고, ANALYZE는 통계 데이터를 수집해 최적의 쿼리 계획을 세우게 하는 작업이다.
- 해결: 테이블별로 ANALYZE를 한 번씩 수동 실행하자 느리던 쿼리가 곧바로 원상 복구됐고, 예정보다 약 2시간 늦게 서비스를 재개했다.
- 결론으로 남긴 것: 클라우드 모니터링으로 IOPS와 대역폭을 살펴 적합한 볼륨을 고를 것, 복제가 끝나면 테이블별 ANALYZE를 반드시 수행할 것, DMS는 기능 제약을 미리 확인할 것을 든다.
왜 읽나AWS에서 데이터베이스가 이유 없이 멈춘다면 스토리지 계층부터 의심하게 해주고, 복제 후 통계가 비어 있어 생기는 함정까지 미리 알 수 있다.