12P by xguru 20일전 | favorite | 댓글 7개
  • Wafris는 Rails 미들웨어 클라이언트를 제공하는 오픈 소스 웹 애플리케이션 방화벽 회사임
  • v1 클라이언트는 로컬 Redis 데이터 저장소가 필요했으나, v2에서는 SQLite를 사용함
  • Redis에서 SQLite로 마이그레이션하기로 결정한 배경과 성능 고려사항, 아키텍처 변경 사항을 설명

TL;DR

  • SQLite는 잘하는 것과 못하는 것이 있음
  • Redis는 잘하는 것과 못하는 것이 있음
  • 전통적인 RDBMS(Postgres/MySQL)는 잘하는 것과 못하는 것이 있음
  • 이러한 데이터 저장소는 직접 교체할 수 없으며, 그렇게 하려고 하면 곤란해짐
  • 이 글은 Redis 기반 v1 클라이언트를 SQLite 기반 v2 클라이언트로 리아키텍팅하면서 거친 테스트와 의사 결정 과정을 설명함

변경을 강제한 요인

  • Wafris의 목표는 개발자들이 쉽게 사이트를 보호할 수 있게 만드는 것임
  • Redis 배포 이슈로 인해 v1은 이 목표를 완전히 달성하지 못함
  • Heroku 등 Redis를 쉽게 사용할 수 있는 환경에서 작업했기에 Redis를 선택했으나, 많은 사용자가 Redis 배포 이슈를 겪음
  • Redis 같은 별도 DB를 사용하게 하는 건 사용자를 위한 게 아님

"속도"란 무엇인가?

  • Redis는 전통적 RDBMS보다 "빠르지만", 여전히 연결, 메모리, 프로세스 등을 관리해야 함
  • 클라우드 환경에서는 네트워크 지연이 큰 문제가 될 수 있음
  • 들어오는 HTTP 요청마다 Wafris 규칙을 평가해야 하므로 네트워크 지연이 애플리케이션 속도를 늦출 수 있음

단일체(Monolith-ish) 가정

  • 완전 분산 애플리케이션도 있지만, 대부분 Rails 앱은 "장엄한 모놀리스(Majestic Monoliths,단일체)"임
  • 여러 영역에 배포되고, 기능이 겹치는 서버로 분할되거나, 부분적으로만 Rails인 앱은 Redis 사용 시 더 많은 문제가 발생함

아키텍처에 대한 재고

  • Wafris는 Rails 미들웨어로 설치되는 웹 애플리케이션 방화벽임
  • 간단히 2단계로 나누면 1) HTTP 요청을 규칙과 비교하고, 2) 처리 결과를 보고함
  • 규칙 "읽기"(1단계)가 "쓰기"(2단계)보다 훨씬 중요함
  • 읽기는 순차적으로 처리되어야 하고, 실패하면 안되며, 사용자 체감 성능에 영향을 줌
  • 쓰기는 느리게, 일괄 처리하거나 비동기로 할 수 있음

Enter SQLite

  • SQLite가 적합한 용도에 대해서는 다른 이들이 잘 설명하고 있음
  • SQLite는 클라이언트/서버 DB와 경쟁하는 게 아니라 fopen()과 경쟁함
  • 네트워크 왕복을 제거하면 Redis보다 훨씬 빨라질 것으로 예상됨
  • SQLite와 Redis의 벤치마크 평가를 결정함

SQLite와 Redis 벤치마킹

  • 벤치마킹은 정밀한 숫자로 자신을 속이는 어두운 기술임
  • 데이터 저장소 벤치마킹은 더욱 까다로움
  • 절대적 속도를 구하려는 게 아니라 우리 데이터와 사용 사례에 특화된 벤치마크를 만듦
  • 최적화 트윅은 무시함. Wafris를 앱에 넣으면 바로 작동하길 원함
  • 이론적 벤치마크가 아닌 우리 앱의 주요 경로와 최악의 쿼리를 테스트함
  • IP 범위(IPv4, IPv6)를 범주에 매핑하는 복잡한 "lexical decimal" 자료구조 요청이 최악의 쿼리임
  • 범위 조회를 미리 계산해 SQLite 테이블과 Redis 정렬 집합에 씀
  • 들어오는 HTTP 요청마다 요청 IP를 허용/차단 사용자 지정 범위, GeoIP 범위, IP 평판 범위와 비교해야 함

테스트 프로토콜

  • M2 맥북 에어에서 홈브루로 설치한 Redis와 로컬 SQLite DB로 테스트함
  • 기존 범위 데이터 세트(120만 개 항목)에 대해 테스트함
  • 여러 IP 세트를 SQLite와 Redis에 동일한 순서로 실행함
  • 각 배수마다 테스트를 5번 실행하고 평균을 냄

테스트 결과

  • SQLite가 Redis를 압도적으로 이김(우리의 특수한 사용 사례에서)
  • SQLite는 로컬 Redis 인스턴스보다 약 3배 빨랐음
  • 네트워크 지연을 고려하기 전의 결과임
  • SQLite가 Redis와 동등하기만 해도 네트워크 시간을 없앨 수 있어 이득임

차트에서 빠진 것

  • SQLite 성능이 벤치마크에서 2배 나빠도 실제로는 네트워크 지연 때문에 더 빠를 수 있음
  • Redis 서버를 아무리 강력하게 해도 네트워크 대역폭, 연결 등의 한계가 있고 지역 간 지연이 있음
  • SQLite는 "무료로" 거의 무한한 수평 확장이 가능함
  • SQLite로 온보딩이 훨씬 좋아짐. 사용자는 사용되는지도 모를 것임
  • Redis에서 더 많은 성능을 뽑아낼 수 있지만, 사용자가 Redis 설정을 변경하도록 설득하기 어려웠음

결과는 시작에 불과함

  • SQLite가 Redis보다 빠르다고 입증했지만 실제 절충이 있음
  • 위 테스트에서는 쓰기를 고려하지 않음
  • 읽기와 쓰기가 경쟁하는 것을 관리하기 위해 DB에 연결, 연결 풀, 트랜잭션 등이 필요함
  • 마치 전기 슈퍼카가 콘크리트 블록을 싣고 가기 어려운 것처럼, SQLite를 적합하지 않은 역할에 쓰면 안 됨

동기화 아키텍처 구축

  • v1(Redis)에서는 사용자가 Wafris Hub에서 규칙을 업데이트하면 Redis 데이터 저장소의 규칙이 업데이트됨
  • SQLite에서는 웹 서버로 "푸시"할 수 없으므로 작동하지 않음
  • v2(SQLite)에서는 1) 사용자가 Wafris Hub에서 규칙 업데이트 2) 일정 간격으로 클라이언트가 업데이트된 규칙 확인 3) 규칙이 업데이트되면 완전히 새로운 SQLite DB 다운로드
  • 이는 사용자의 설치 및 구성 책임을 크게 줄여줌
  • v2 클라이언트의 설치 성공률이 3배 증가함

SQLite 분산 아키텍처

  • 자동 확장이 활성화된 클라우드 제공업체에 배포된 Rails 앱을 고려해보자
  • 요청이 100req/s에서 10,000req/s로 증가하면 컴퓨팅 인스턴스는 확장되지만 DB는 그렇지 않음
  • 실제로 Rails 앱이 과부하로 인해 중단되는 주된 이유임
  • SQLite DB를 각 컴퓨팅 인스턴스에 동기화하면 모든 호출을 로컬로 유지할 수 있어 이 문제를 해결함

쓰기는 어떻게 하나?

  • 앱을 읽기(규칙 평가)와 쓰기(보고) 경로로 분할한 다음 쓰기 경로는 무시했음
  • 쓰기 경로는 1) Wafris Hub에 비동기로 연결해 보고 2) 보고서 일괄 전송 3) 클라이언트에서 DB 쓰기 완전 제거로 재설계함
  • 다른 사람들에게는 작동하지 않겠지만, 우리는 배포가 쉽고 빠른 Wafris 클라이언트를 원하는 사용자만 신경 씀

결론

  • SQLite를 사용하는 v2 아키텍처에 매우 만족함
  • 이미 많은 사이트가 공격을 견디고 온라인 상태를 유지하는 데 도움이 됨
  • 시작하기가 훨씬 쉬워져 우리의 지원 작업과 사용자의 번거로움이 줄어듦
  • 이는 더 안전하고 보안성 높은 인터넷을 위한 승리라고 생각함

Sqlite가 충분히 좋긴 한데, 이 경우는 어쩐지 그냥 redis에 적합하지 않은 use case였던 것이 아닌지... 싶네요.

벤치마크를 M2에서 했다는건 좀..

m2에서 한것이 어떤점이 문제가 될 수 있을까요? (실제 서비스 환경이 m2프로세서가 아니라는 점 외에)

그게 문제죠. 실험실에서 실험하고, 이건 상업용으로 완벽하다! 라고 주장하는것.

그러면 aws 인스턴스마다 다 측정해봐야하나요? 오픈소스에 바라는게 너무 많으시네요

같은 서버 환경에서 진행을 한건데 문제가 되나요?
벤치마크에는 특정 CPU를 써야하는 건지...?

자기만족에 가깝죠.