CloudflareInfrastructureOutagePost-mortem

Cloudflare 왜 터졌는가.

·6 min read

어제 FastAPI 공식문서 보고있었는데 갑자기 서버가 터져버리는 경험을 했다.

처음에는 아니 공식문서 관리 안함? 이 생각이었는데,

원인은 바로 Cloudflare 이놈이 터져서였다.

전 세계 인터넷 트래픽 관문 역할을 하는 서비스인데,

웹사이트 5개 중 1곳은 이용할 정도로 점유율이 높은 편이다.

전세계 적으로 서비스하고 규모도 크다보니 DDoS 공격의 최전선을 차지하고 있는 터라,

처음에는 DDoS로 밀렸다 싶었는데,

생각보다 어이없는 실수라서 가져와 봤다.

결론부터 말하자면...

코딩 실수했다.

게다가 하지 말라는 것을 해버렸다.

시작은 DB 부터

당시에 시스템 개선한다고 데이터베이스에 대한 접근 제어를 강화하기 위한 권한 변경 작업을 진행했다.

보안을 높이자! -> 쿼리를 시스템 계정이 아닌 초기 사용자 계정 권한으로 실행하자!

여기까지는 좋았다.

하지만 사용자 계정에 명시적 접근 권한을 부여하는 변경 사항을 적용하면서 문제가 생겼다.

SELECT name, type FROM system.columns WHERE table = 'http_requests_features' ORDER BY name;

자 문제의 쿼리다.

기존 시스템 계정에서는 default DB를 기준으로 table을 조회했다.

아마도 암묵적인 규칙에 의해 기본 DB를 참조한 것으로 보이는데,

유저 계정으로 변경되고 나서부터는 r0 DB라는 DB 까지 타겟팅이 된 것이다.

결국 DB가 반환하는 데이터가 중복 되면서 양이 폭증 해버렸다.

쿼리를 짠다면 지켜야할 몇가지 수칙 중에, 명확하게 스키마(또는 DB) 명을 지정해서 가져오라는 규칙을 지키지 않은 결과인 것이다.

데이터 양 늘었다고 왜 뒤짐?

http_requests_features 요놈을 어디서 가져다 쓰냐면,

Cloudflare에 코어 프록시 내부에 봇 관리 모듈이 존재한다.

file_Lf4iPxflAaSAgWs70l

모든 요청에 대해서 봇 점수를 매기는 역할인데,

이때 봇인지 아닌지 예측하는 개별 특성을 가진 '기능 설정 파일'을 입력으로 받는다.

그럼 이 '파일'에 내부 데이터는 어디서 가져오겠는가

아까 본 http_requests_features 여기다.

보통 60개 데이터 가진 파일 데이터가 폭증하면서 기존 200개 제한을 뚫어버리고 예외가 발생해 버렸다.

"예외 났으면 잘 처리해주면 되는거 아닌가요?"

근데 잘 처리 안해서 이 사단이 났다.

당시에 Rust 언어에서 unwrap 이라는 함수를 썼는데,

이름 그대로 wrap 된걸 뜯어서 내용 보는 함수다.

Rust에서는 값이 있을 수도, 없을 수도(또는 에러) 있는 상황을 처리하기 위해 Option이나 Result라는 상자(Wrapper)를 사용한다.

상자(Wrapper): 성공하면 선물, 실패하면 폭탄

unwrap는 "야 상자 안에 무조건 선물이지? 믿고 찢는다?"라고 슈뢰딩거의 상자깡을 시전하며 스스로 폭사하는 것이다.

아무튼 오류에 대해 '우아하게 실패'하세요 라는 설계 원칙을 정면으로 부정하고,

"에러야? 응 뒤질게"를 선택한것이다.

리스크가 리스크다 보니 Rust에서도 unwrap 쓰지 말라고 권고까지 한다.

아무튼 모듈이 죽으면 뭐다? 5xx 에러다,

연관된 모든 시스템에게 나 뒤졌어요를 알린다는 것이다.

어째서 unwrap를 썼는가

상식적으로 위험한 함수에 대해서는 기피하는게 맞을텐데,

이번에는 예외의 대상이 바로 설정이라는 점도 원인이다.

파일을 읽어서 해당 설정 파일이 네트워크 전체에 전파 되는 구조다 보니,

설정 변경은 코드 배포보다 안전하다는 암묵적인 믿음이 실수를 초래한 것이라고 판단 된다.

우리의 교훈

  1. 하지 말라는 거 하지 말고, 무슨 일이든 터질 수 있으니 방어적 프로그래밍을 해야 한다.

  2. 설정도 코드니깐 검증 과정 잘 거치자

← Previous
Ruff
Next →
멘토링 내용 공유해드림, 기획자 내용도 있음