Published on

다른 팀의 서비스 인수하기

Authors

최근에 다른 팀이 운영하던 서비스를 넘겨받았어요. 몇 년 동안 큰 장애 없이 돌아가던 서비스였어요.

받을 건 다 받았어요. 저장소 권한이 넘어왔고, 빌드도 되고, 배포 파이프라인도 그대로 살아 있었어요.

그런데 이것만으로는 알 수 없는 게 있었어요. 이 서비스를 앞으로 어떻게 운영해야 하는지, 문제가 생기면 언제 알게 되는지, 알고 나서 뭘 해야 하는지요.

비어 있던 것을 어떤 순서로 채웠는지 적었어요. 비슷한 상황에 놓였다면 이 순서를 그대로 따라가도 돼요.

운영 방법은 들었지만 기준이 없었어요

인수인계 미팅에서 얻은 건 많았어요. 지금까지 이 서비스를 어떻게 운영해왔는지, 정기적으로 손이 가는 일은 무엇이고 어떤 순서로 처리하는지를 자세히 들었어요. 스크립트로 돌리는 일회성 작업이나 요청이 들어오면 수동으로 처리하는 절차 같은 것들이요. 코드만 봐서는 알 수 없는 내용이라 미팅이 아니면 얻을 수 없었어요.

비어 있는 건 다른 쪽이었어요. 하나는 문제가 생겼을 때 알아차릴 수단이었어요. 대시보드도 알럿도 없어서, 무언가 잘못되면 소비 팀이 알려주기 전까지는 알 방법이 없었어요.

다른 하나가 더 근본적이었어요. 무엇을 문제로 볼지에 대한 기준이 없었어요. 응답이 얼마나 느려지면 문제인지, 실패율이 몇 퍼센트를 넘으면 대응해야 하는지 정해진 게 없었어요. 그리고 기준이 없으면 알럿도 만들 수 없어요. 어떤 조건에서 울릴지를 모르니까요.

그래서 이 글은 인수인계를 잘 받는 요령이 아니에요. 넘겨받고 나서 비어 있던 자리를 어떤 순서로 채워갔는지, 그러면서 공부하고 정한 것들을 남긴 기록이에요.

경계와 소비자부터 그렸어요

가장 먼저 한 일은 서비스가 무엇을 하고 무엇을 하지 않는지, 그리고 누가 이걸 쓰는지 그리는 거였어요. C4 모델(시스템을 네 단계 축척으로 나눠 그리는 다이어그램 표기법)에서 두 번째 단계인 컨테이너 다이어그램 정도로 한 장이면 충분했어요.

소비자별 경로와 크리티컬 패스

이 서비스는 하나가 아니었어요. 한 저장소가 성격이 다른 배포 단위 둘을 갖고 있었어요. 수집 요청을 받는 gRPC 서버와, 조회와 관리 화면을 담당하는 HTTP 서버였어요. 요청량은 비교가 안 되게 차이 났고요. 둘을 같은 기준으로 보면 어느 한쪽이 반드시 틀려요. 낮은 쪽에 맞추면 gRPC 서버가 무방비가 되고, 높은 쪽에 맞추면 관리 화면에 과한 요구가 가요. 그래서 이후의 모든 기준을 배포 단위별로 따로 잡았어요.

소비자를 코드 검색으로만 세면 안 됐어요. 호출하는 코드가 남아 있어도 실제로는 트래픽이 0인 경로가 있었고, 반대로 문서 어디에도 없는데 꾸준히 부르는 팀이 있었어요. 관측 도구에서 호출자별 볼륨을 실제로 재고 나서야 소비자 목록이 정확해졌어요.

가장 값진 답은 최대 소비자에게 물어서 얻었어요. 우리가 5분 죽으면 그쪽은 어떻게 되나요? 재시도가 있어서 데이터가 사라지지는 않는다고 했어요. 대신 그동안 데이터가 쌓이지 않고 밀려요. 그리고 이 경로가 전체 요청의 대부분이었어요.

복구가 되니까 심각하지 않다고 볼 수도 있어요. 그런데 밀리는 양이 곧 전체 요청의 대부분이라면 이야기가 달라져요. 그래서 이 경로를 크리티컬 패스로 잡았어요. 여기까지가 손대기 전에 파악한 전부예요.

지표를 정하고 기준선부터 쟀어요

이 서비스가 정상이라는 걸 무엇으로 말할지 정해야 했어요. 처음에는 일반적인 것부터 골랐어요. 요청 성공률, 응답 지연, 그리고 파드 수예요. 앞의 둘은 소비자가 겪는 상태를 말해주고, 파드 수는 서비스가 버티고 있는지를 말해줘요. 이렇게 서비스 상태를 재는 지표를 SLI(서비스 수준 지표)라고 불러요.

그다음이 기준선 실측이에요. 최근 값을 재서 지금 실제로 어느 수준인지 확인했어요. 이 단계를 건너뛰고 목표를 정하면 근거 없는 숫자가 되고, 그런 숫자는 첫 위반에서 "그건 우리가 합의한 적 없다"로 무너져요.

실측값을 놓고 최소로 지켜야 하는 수준을 정했어요. 흔히 SLO(서비스 수준 목표)라고 부르는 것이에요. 기준선보다 조금 낮게 잡았어요. 실측값을 그대로 목표로 쓰면 평소와 조금만 달라져도 위반이 되고, 그러면 아무도 그 숫자를 보지 않게 되거든요. 그리고 서버마다 다르게 잡았어요. 요청을 대부분 받는 gRPC 서버와 가끔 열리는 HTTP 서버에 같은 기준을 걸 이유가 없으니까요.

대상지표기준선최소로 지켜야 하는 수준
gRPC 서버요청 성공률99.9%99.5%
gRPC 서버응답 지연 p9940ms100ms 이내
HTTP 서버요청 성공률99.5%99%
HTTP 서버응답 지연 p99120ms500ms 이내
공통파드 수6~103 이상

예시로 채운 표예요. 실제 값은 서비스마다 다르게 나와요.

측정 창은 길게 잡지 않았어요. 거의 실시간으로 보면서 어긋나면 바로 알 수 있게 했어요.

목표치를 정했으면 못 지켰을 때 무엇을 할지도 정해야 해요. 저희는 이 수준을 못 지키는 상황이 이어지면 다음 사이클에 기능 개발보다 신뢰성 작업을 먼저 하기로 했어요. 이런 약속이 없으면 위반해도 아무 일이 일어나지 않아서 같은 숫자가 그대로 남아요.

크리티컬 패스는 따로 챙겼어요

여기까지는 서비스 전체를 하나로 보고 잰 숫자예요. 그런데 이것만 보면 놓치는 게 있어요.

전체 성공률이 목표 안에 있어도 크리티컬 패스만 골라서 실패하고 있을 수 있어요. 요청은 하나하나 같은 무게로 세어지는데, 앞에서 봤듯이 이 서비스에서 막히면 곤란한 경로는 따로 있었으니까요.

그래서 크리티컬 패스를 따로 떼서 봤어요. gRPC 서버 경로의 성공률과 지연을 전체 지표와 나란히 두고, 대시보드에도 패널을 따로 뒀어요. 대시보드는 기준선을 실측할 때 쓴 쿼리를 그대로 옮기면 돼서 금방 만들었어요. 중요한 건 패널이 실제로 SLI를 그리는지였어요. CPU와 메모리만 있고 SLI 패널이 없는 대시보드는 장애 때 아무 도움이 안 돼요.

알럿도 같은 기준으로 나눴어요. 크리티컬 패스는 짧게 보고 바로 호출하고, 나머지는 조금 더 지켜본 다음 채널로 알리게 했어요. 조건은 앞에서 정한 최소 수준을 그대로 가져다 썼어요.

알럿조건긴급도
gRPC 요청 성공률 하락5분간 99.5% 미만즉시 호출
gRPC 응답 지연 상승5분간 p99 100ms 초과즉시 호출
gRPC 요청량 급감평소 대비 50% 이하즉시 호출
파드 수 부족3개 미만즉시 호출
HTTP 요청 성공률 하락15분간 99% 미만채널 알림
HTTP 응답 지연 상승15분간 p99 500ms 초과채널 알림

요청량 급감을 넣은 건 성공률로는 안 잡히는 실패가 있어서예요. 소비자가 아예 호출을 못 하고 있으면 우리 쪽 성공률은 100%로 보여요. 들어온 요청은 다 성공했으니까요.

SLA를 소비 팀과 합의했어요

SLO는 우리 팀 안에서 정한 목표예요. 소비 팀과 맺는 약속은 따로 만들었고, 이걸 SLA(서비스 수준 협약)라고 해요. 목표치는 SLO보다 한 단계 낮췄어요. 두 숫자를 같게 두면 목표를 아슬아슬하게 지킨 달에 약속까지 같이 위태로워지거든요.

지표약속위반 시
gRPC 요청 성공률99%24시간 안에 원인과 재발 방지책 공유
HTTP 요청 성공률98.5%24시간 안에 원인과 재발 방지책 공유

문서로 남기고 온콜에 편입했어요

여기까지 정한 걸 팀 문서 저장소에 모았어요. SLO와 SLA, 대시보드와 알럿 구성, 그리고 인수인계 때 들었던 수동 운영 업무를 FAQ처럼 정리했어요. 마지막 건 코드에는 안 보이는데 첫날부터 우리 일이 되는 것들이라, 받은 그대로 적어두지 않으면 나중에 다시 물어볼 곳이 없어요.

그 문서를 놓고 팀에 공유 세션을 열었어요. 그리고 온콜 로테이션에 이 서비스를 편입했어요. 다른 팀이 운영하던 서비스를 우리 팀으로 옮기는 일은 여기서 끝났어요.

정리하면

인수인계 미팅에서 운영 방법은 들었어요. 없던 건 문제를 알아차릴 수단과, 무엇을 문제로 볼지에 대한 기준이었어요. 코드는 이미 저장소에 있었으니 이관에서 실제로 할 일은 그 둘을 새로 만드는 것이었어요.

순서를 다시 적으면 이래요.

  1. 서비스 경계와 소비자를 그려요. 어떤 유스케이스가 깨지면 곤란한지 보고 크리티컬 패스를 정해요.
  2. SLI를 정하고 기준선을 실측한 다음, 그 값을 놓고 SLO와 못 지켰을 때의 대응을 정해요.
  3. 크리티컬 패스는 전체 지표와 따로 떼서 대시보드와 알럿을 만들어요.
  4. SLO를 기준으로 SLA를 만들어 소비 팀과 합의해요.
  5. 정한 것과 넘겨받은 운영 업무를 팀 문서 저장소에 모으고, 공유 세션을 열어요.
  6. 온콜에 편입해요.

새 서비스를 만든다면 앞부분이 조금 달라요. 기준선을 뽑을 이력이 아직 없으니까요. 그럴 땐 확정된 목표치를 출시 조건으로 걸기보다, 잴 수 있는 상태와 잠정 목표까지만 갖추고 출시한 다음 실측으로 확정하는 편이 나아요. 순서는 지표를 정하는 것부터 시작해서 똑같고, 목표치만 나중에 채워지는 셈이에요.

돌아보면 이관에서 가장 오래 걸린 건 코드를 이해하는 일이 아니었어요. 무엇을 정상이라고 부를지 정하는 일이었어요.