다중 테넌트 Rails 애플리케이션의 용량 계획 및 메모리 관리 전략

Capacity Planning for Multi-Tenant SQLite Applications

작성자
Ruby Weekly
발행일
2025년 10월 12일

핵심 요약

  • 1 다중 사용자 및 SQLite 환경에서 엄격한 메모리 제한으로 인한 용량 계획의 어려움을 분석하고 해결책을 모색합니다.
  • 2 Fly.io 환경에서 cgroup v1을 활용하여 Rails 애플리케이션의 테넌트별 메모리 제한을 성공적으로 구현하여 안정성을 확보합니다.
  • 3 jemalloc을 통한 메모리 단편화 감소 및 Fly.io의 유휴 머신 서스펜션 기능을 활용하여 자원 효율성을 개선합니다.

도입

본 문서는 SQLite 데이터베이스를 사용하는 다중 사용자 환경에서 고정된 머신과 엄격한 메모리 제한으로 인해 발생하는 복잡한 용량 계획 문제에 대한 심층적인 분석을 제공합니다. 각 댄스 스튜디오 사용자에게 여러 이벤트(테넌트)가 할당되고, 각 이벤트가 별도의 Rails 애플리케이션 인스턴스와 데이터베이스를 사용하는 토폴로지에서 메모리 부족 문제가 서비스 안정성에 미치는 영향을 배경으로, 효율적인 메모리 관리 전략의 필요성을 강조합니다.

이 글은 다중 테넌트 Rails 애플리케이션 환경에서 직면하는 용량 계획 및 메모리 관리 문제를 다루며, 현재 직면한 문제점과 탐색 중인 해결책, 그리고 최종적으로 구현된 개선 사항을 상세히 설명합니다. 현재 시스템은 8대의 2GB RAM 머신에 70개 이상의 사용자가 분산되어 있으며, 메모리 부족 시 OOM 킬 및 성능 저하가 발생하고 있습니다. 이 문제를 해결하기 위한 다양한 접근 방식이 고려되었습니다.

현재 시스템 구성 및 메모리 관리 노력

  • 아키텍처: 각 사용자(댄스 스튜디오)는 여러 이벤트(테넌트)를 가지며, 각 이벤트는 자체 SQLite 데이터베이스와 Rails 애플리케이션 인스턴스를 사용합니다. 모든 테넌트는 특정 머신에 고정되며, 여러 사용자가 동일한 머신을 공유합니다.

  • Puma 설정: 테넌트당 3개의 스레드로 구성되어 있으며, 유휴 상태인 테넌트는 5분 후, 머신은 30분 후 자동으로 서스펜드됩니다.

  • jemalloc 활용: 멀티스레드 Puma 환경에서 메모리 단편화를 줄이기 위해 jemalloc을 메모리 할당자로 사용합니다.

  • Web Socket: Web Socket 연결은 메모리 사용량이 100KB 미만으로, 주요 메모리 우려 사항이 아닙니다.

  • Fly.io 서스펜션: Fly.io의 머신 서스펜드 기능이 잘 작동하여 자원 효율성을 높이고 있습니다.

탐색 중인 해결책

  • 머신 증설 및 메모리 축소: 사용자당 더 많은 머신을 할당하고 개별 머신의 메모리를 1GB로 줄이는 방안이 고려되었습니다. 이는 특정 사용자 문제 발생 시 다른 사용자에게 영향을 주지 않는다는 장점이 있지만, 배포 및 관리 오버헤드 증가와 라우팅 복잡성 증가라는 단점이 있습니다.

  • 테넌트별 메모리 제한 (cgroups): 초기에는 Fly.io에서 Linux cgroups v2를 통한 테넌트별 메모리 제한 구현에 어려움이 있었습니다. 이 방법은 특정 테넌트가 메모리 한계를 초과했을 때 해당 테넌트만 OOM 킬하고 재시작하여 다른 테넌트에 영향을 주지 않도록 하는 이상적인 해결책입니다.

업데이트: cgroup v1을 통한 테넌트별 메모리 제한 구현 성공

  • 문제 진단: Fly.io가 cgroup v2 파일이 존재하지만 실제로는 cgroup v1이 활성화된 하이브리드 구성을 사용하고 있음을 파악했습니다.

  • 해결책: Navigator는 이제 cgroup v2에서 메모리가 활성화되지 않은 경우 /sys/fs/cgroup/memory/ 경로의 cgroup v1으로 자동 폴백하도록 구현되었습니다.

  • 결과: 이를 통해 memory.limit_in_bytes를 설정하여 테넌트당 512MiB와 같은 개별 메모리 제한을 성공적으로 적용할 수 있게 되었습니다. 이제 특정 테넌트가 메모리 한계를 초과하면 해당 Rails 프로세스만 OOM 킬되고 자동으로 재시작되며, 동일 머신의 다른 테넌트들은 정상적으로 계속 실행됩니다. 이는 privileged 컨테이너나 호스트 cgroup 네임스페이스 접근 없이 테넌트별 진정한 메모리 격리를 제공합니다.

결론

이 포스트는 다중 테넌트 Rails 애플리케이션 환경에서 직면하는 복잡한 용량 계획 및 메모리 관리 문제에 대한 심층적인 탐색과 실질적인 해결책을 제시합니다. 특히 Fly.io 환경에서 cgroups v1을 활용한 테넌트별 메모리 제한 구현 성공은 중요한 진전입니다. 이는 개별 테넌트의 메모리 사용량을 효과적으로 격리하여 전체 시스템의 안정성을 향상시키고, 자원 효율성을 극대화하는 데 기여할 것입니다. 이러한 접근 방식은 유사한 아키텍처를 가진 다른 Ruby 개발자들에게도 실용적인 해결책을 제시하며, 향후 시스템 확장에 중요한 기반이 될 것입니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!