요약

  • Kinesis 프런트엔드는 서버가 늘면 기존 서버도 새 동료와 통신할 스레드를 만들어야 했다. 작은 증설이 전체 프런트엔드를 운영체제의 스레드 한도 너머로 밀었다.
  • 재시작 중 경로 정보를 만드는 작업이 고객 요청 처리와 자원을 다퉜다. 수천 대의 서버를 되살리는 속도는 시간당 수백 대로 제한됐다.

수천 대가 있는데 시간당 수백 대씩만 들어올 수 있었다. 창고의 입고 이야기가 아니다. 2020년 11월 25일, 미국 북버지니아에서 Kinesis 프런트엔드 서버들이 서비스를 재개하던 속도다.

AWS의 공식 사고 보고서에 따르면 첫 복귀 그룹은 태평양 표준시 오전 10시 7분에 트래픽을 받았다. Kinesis가 정상으로 돌아온 시각은 오후 10시 23분이었다. 그 사이 오류율은 내려갔다. 원인을 없애는 일과 모든 처리 능력을 되찾는 일이 서로 다른 일정표를 썼다.

사건의 발단은 과감한 도박처럼 보이지 않는다. AWS가 프런트엔드 용량을 조금 늘렸다. 늘어난 서버가 기존 서버의 일을 늘렸고, 그 부담이 운영체제 설정의 최대 스레드 수를 넘겼다. 회사는 증설을 촉발 요인, 스레드 한도를 근본 원인으로 구분했다.

클라우드에서 서버 한 대를 더 갖는다는 말은 간단하다. 그 한 대를 다른 서버들이 얼마나 돌봐야 하느냐는 훨씬 덜 간단하다.

새 동료를 알리는 데에도 비용이 든다

Kinesis의 백엔드는 분할된 데이터 스트림을 셀 단위의 클러스터에서 처리했다. 프런트엔드는 인증과 요청 제한을 맡고, 요청을 맞는 백엔드 샤드로 보냈다. 그러려면 구성원과 샤드 소유 관계를 담은 지도가 필요했다.

프런트엔드는 여러 출처에서 정보를 받아 이 지도를 유지했다. 다른 프런트엔드와 메시지를 주고받는 일도 포함됐고, 각 서버는 다른 구성원들과 통신할 운영체제 스레드를 만들었다. 서버가 추가되면 이미 일하던 서버도 새 구성원을 알아보고 통신 준비를 해야 했다. 발견에는 최대 한 시간이 걸릴 수 있었다.

스레드가 한도에 닿자 지도 구성이 끝나지 않았다. 데이터 처리부가 있어도 요청을 제대로 안내할 수 없었다. 백엔드를 셀로 나눈 구조와 프런트엔드 전체의 조정 부담은 다른 문제였다. 격리됐다는 설명을 들을 때는 정확히 어느 부분까지 격리됐는지 물어야 한다.

장비를 더 넣으면 쓸 수 있는 용량도 그만큼 늘어난다는 계산은 이 지점에서 틀어진다. 새 장비의 처리 능력에서 전체 구성원이 새 장비 때문에 치르는 비용을 빼야 한다. 그 비용이 어느 한도를 넘으면 계산 결과는 덧셈이 아니다.

서두르면 다시 퇴장한다

AWS는 추가한 용량을 제거했다. 테스트를 더 하지 않고 운영체제 한도부터 올리는 방법은 선택하지 않았다. 문제는 이후의 재시작이었다.

서버가 켜지면 샤드 지도를 다시 구성해야 했다. 그 작업과 고객 요청 처리가 같은 자원을 놓고 경쟁했다. 너무 빨리 많은 서버를 복귀시키면 응답이 느려지고, 느린 서버가 비정상으로 판정돼 다시 제외될 수 있었다. 복구를 재촉한 결과가 복구 후퇴일 수도 있었다.

팀은 시작할 때 이웃 프런트엔드에서 정보를 가져오는 대신 기준이 되는 메타데이터 저장소를 직접 읽도록 바꿨다. 그래도 수천 대 규모의 복귀에는 속도 조절이 필요했다. 이미 산 장비를 안전하게 다시 쓰기 위한 작업에 시간이 들었다.

시장을 움직이는 지표는 평상시의 처리량만이 아니다. 고장 후 자원을 다시 사용할 수 있게 만드는 처리량도 납품 능력이다. 여기에 필요한 시간을 생략한 용량 표는 고객에게 가장 바쁜 날의 일정을 숨긴다.

기록을 남기다가 입구가 막혔다

Cognito는 API 접근 패턴을 분석하려고 Kinesis에 정보를 보냈다. 전송은 최선 노력 방식이었고 로컬 버퍼도 있었다. 오래 지속된 장애가 버퍼 코드의 잠재적 결함을 드러냈다. 쌓인 데이터 때문에 웹서버가 막히면서 일부 사용자는 인증하거나 임시 자격 증명을 얻을 수 없었다.

기록을 위한 작업이 본업의 입구를 막은 셈이다. 버퍼가 있다는 사실보다 버퍼가 찬 뒤의 동작이 중요해졌다. 처음 몇 분을 버티는 설계가 긴 중단까지 버티는 설계인지, 두 질문을 나눠야 했다.

CloudWatch의 지표와 로그 처리도 흔들렸고 일부 경보는 데이터 부족 상태로 바뀌었다. 그 지표를 쓰는 반응형 자동 확장도 늦어졌다. Lambda에서는 지표 버퍼가 실행 호스트의 메모리 경합을 일으켰다. Kinesis의 스레드 한도와 Lambda의 메모리 문제를 같은 자원 부족으로 뭉뚱그리면 고칠 곳이 흐려진다.

평소 서비스 상태를 게시하는 도구마저 Cognito를 사용했다. 의존성이 적은 예비 도구는 작동했지만 수작업이 많고 익숙하지 않아 공지가 지연됐다. 시스템을 관찰하고 설명하는 도구도 시스템과 함께 숨이 찼다.

더 큰 서버로 더 작은 명단 만들기

AWS가 발표한 단기 대책 가운데 큰 CPU와 메모리를 가진 서버로 옮기는 방안이 있었다. 서버 수를 줄여 각 서버가 상대해야 할 동료와 스레드를 줄이려는 조치였다. 더 세밀한 스레드 감시, 한도 상향 테스트, 시작 시간 단축도 포함됐다. 캐시 분리, 일부 대형 서비스의 프런트엔드 분할, 프런트엔드 셀 구조 확대는 공유 범위를 바꾸려는 계획이었다.

장비 구매와 관계 정리를 한 목록에 올린 이유가 있다. 유용한 처리 능력은 장비 사양에서 내부 조정 비용을 공제한 뒤에 남는다. 서버 숫자를 늘리는 일과 실패 범위를 줄이는 일이 같은 방향으로 움직인다는 보장은 없다.

AWS의 정적 안정성 설명은 의존 서비스가 어려워져도 기존 기능이 계속 작동하도록 준비하라고 말한다. 장애 허용과 격리 지침은 미리 마련한 여유 자원의 비용도 함께 다룬다. 기다림을 줄이는 준비는 공짜가 아니다. 기다리는 사람의 시간이 공짜도 아니다.

이 역사 자료에는 현재 프런트엔드 한도나 최근 전체 재시작 시험이 없다. 조건을 공개한 최신 시험이 현재 복구 능력의 판단을 바꿀 자료다. 2020년 기록만으로도 구매 질문 하나는 선명해진다. 서버는 몇 대인가, 그 서버들을 다시 쓸 수 있게 되는 속도는 얼마인가.