세션이 갑자기 수백 배로 뛰었다
어느 날부터 RUM 세션 수가 평소 대비 수백 배로 튀었다. 처음엔 트래픽이 늘었나 했는데 보니까 실유저가 아니었다.
- 대부분이 해외 데이터센터 IP에서 들어오고 있었다.
- 특정 경로만 단일 페이지로 긁고 빠졌다. 클릭 같은 상호작용은 0, 한 세션에 페이지뷰 1개.
- 게다가 이 세션들이 에러(타임아웃, fetch 실패 같은 거)를 무더기로 만들고 있었다.
해외 데이터센터 IP 라고 생각했던건, RUM에서 session의 attribute를 확인했을 때 geo.as.type 값이 isp 였기 때문이다.
근데! VPN이나 기업망 경유하는 실유저도 hosting으로 찍힐 수 있다. 실제로 데이터를 보니 hosting 세션 중에도 로그인해서 멀쩡히 쓰는 진짜 유저가 소수 있었다. 그래서 비로그인 유저라는 조건을 함께 사용하여 필터링했다.
geo.as.type — IP 기반 네트워크 분류
Datadog가 클라이언트 IP를 ASN DB로 역조회해 붙이는 “그 IP가 속한 네트워크 종류”. geo.as에 as.number/as.name/as.domain/as.type이 들어간다.
값 5종:
| 값 | 의미 | 예시 | 정체 |
|---|---|---|---|
hosting | 서버 호스팅·클라우드·VPN 사업자. 사람 회선이 아니라 머신이 도는 인프라 | AWS, GCP, Azure, OVH, DigitalOcean, M247, LeaseWeb | 봇·크롤러·스크래퍼, VPN/프록시 → 거의 다 비-실유저 |
isp | 최종 사용자에게 인터넷을 파는 통신사. 가정용 광랜·모바일 데이터 | KT, SKT, LG U+, Comcast, Verizon | 사람이 집/폰에서 접속 → 진짜 유저 |
business | 기업망 | — | 실유저(회사에서 접속) |
education | 대학망 | — | 실유저 |
government | 공공망 | — | 실유저 |
근데 프론트엔드에서 RUM 시작하는 if문에서 왜 못 잡았지?
User-Agent 기반 isBot() 필터가 있었지만 이 로직에 걸리지 않았다.
이유는 단순했다. 봇이 정상처럼 Chrome User-Agent로 위장하고 있었다.
Mozilla/5.0 (Macintosh; ...) ... Chrome/xxx.0.0.0 Safari/537.36하지만 대부분의 세션이 접근 후 클릭 0회, viewport 800×600, 중국발 요청이라는 공통점을 가지고 있었다.
그래서 과금이 많이 됨.. 그리고 데이터독 RUM 과금 정책
데이터독 RUM에는 두 개념이 있다.
- ingested: SDK가 보낸 모든 이벤트
- retained: 리텐션 필터가 “이건 보관하겠다”고 고른 세션. 검색에서 보이고, 일정 기간 저장함.
우리가 설정 리텐션 필터 중에 “에러 난 세션은 100% 보관”하는 규칙이 있었다 근데 하필 봇 세션들이 환경 특성상 fetch 에러를 잔뜩 냈고, 그 에러 때문에 에러 세션 리텐션 필터에 전부 걸려서 보관(=과금)됐다.
정상 UA로 위장한 봇이 특정 경로를 대량 크롤링
→ 샘플링으로 일부 수집됨 (ingested)
→ 봇 환경에서 fetch 에러 발생
→ "에러 세션 100% 보관" 필터가 이 세션들을 retain
→ 봇 세션이 그대로 과금 ← 비용 폭증 지점원인을 쫓다 보니 RUM 과금 방식을 제대로 파게 됐는데, 이게 비용을 이해하는 핵심이다.
먼저 RUM은 “세션” 단위로 과금된다. 세션은 웹이나 모바일 앱에서의 유저 여정이며, 15분 동안 아무 동작이 없거나 최대 지속 시간인 4시간에 도달하면 만료된다.
그리고 SDK가 보낸 세션이 전부 곧바로 돈이 되는 게 아니다. 위에서 말한 retained — 리텐션 필터가 “보관하겠다”고 고른 세션 — 만 30일 저장되고 과금된다. 리텐션 필터는 들어오는 이벤트(ingested)에 쿼리를 돌려서 이 세션을 보관할지 버릴지를 정하고, retention rate(%)로 샘플링해서 비용을 조절한다.
그래서 좀 의외인 게, 클라이언트 쪽 샘플링은 오히려 100%로 두는 걸 권장한다. 수집은 일단 다 하고, 비용은 “뭘 보관할지”를 리텐션 필터로 조절하는 게 낫다. 클라이언트 샘플링부터 필터링하면 정작 나중에 봐야 할 세션까지 같이 날아가서, 필터로 살릴 방법이 없어진다.
리텐션 필터엔 위에서 말한 함정이 있다. 세션 안의 이벤트 하나가 필터에 걸리면, 그 필터의 retention rate에 따라 세션 전체를 보관할지 결정된다. 보관으로 결정된 세션의 이후 이벤트는 필터를 다시 안 거치고 자동으로 다 저장된다. 세션 단위로 끝까지 보겠다는 거다(모니터 알림 쿼리랑은 다른 개념이다). 그래서 “에러 세션 100% 보관” 같은 필터가 있으면, 봇이 에러 한 번 내는 순간 그 세션이 통째로 보관·과금된다.
필터 적용순서가 위→아래이다. 이벤트가 한 필터에 걸리면 그 아래 필터로는 평가가 안 넘어간다. 그래서 봇을 빼려면 별도 “제외 필터”를 위에 하나 두는 게 아니라, 보관하려는 모든 리텐션 필터 쿼리 안에 제외 조건을 빠짐없이 넣어야 한다.
예를 들면 ([email protected]:hosting OR @usr.id:*)처럼 “hosting이 아니거나 로그인한 세션”만 보관하는 식이다.
아래 fallback 필터에 조건을 빼먹으면 봇이 거기로 새서 또 보관된다.
여기에 Session Replay까지 켜놨으면, 보관되는 세션은 영상까지 같이 저장·과금된다. 봇 세션이 리플레이 샘플에 잡히면 비용이 더 붙는다. 단, 강제 수집된 리플레이 세션은 permanent retention filter로 보관되며, 이 필터는 리스트 맨 위에 있고 수정·삭제·비활성화할 수 없다.
데브옵스 파트 도움받기
위 방법들은 과금을 막기 위해 필터링을 추가한 부분이라 근본적인 해결이 필요하다.
- CDN, WAF 레벨 봇 차단이 따로 필요하다.
- 이런 요청들은 애초에 프론트엔드 서버으로 오지 않도록 한다.
- 현재 Go로 작성된 SEO전용 서버를 만들어 페이지 1개씩 대응중이다. 위에서 이야기한 특정 조건 - 특이한 User-Agent & 중국발 요청 등 - 을 만족하면 이 서버로 보내버린다.
데이터독 아쉬운 부분
- 이렇게 과금이 급증하는건 알람을 받을 수 없나?
- 이런 어뷰징에 가까운 기록들을 알아서 막아줄 수 없나?