본문 바로가기

문제해결

크롤러 - 계획된 정비를 장애로 오해하지 않게 만들기

 

운영 중인 크롤러에서 장애 알림이 왔다.

보조 워커 노드의 핵심 서비스가 일정 시간 동안 정상 heartbeat를 보내지 못했고, 시스템은 이를 장애로 판단해 메인 운영 노드의 fallback worker를 올렸다. 이후 보조 노드가 다시 정상 상태로 돌아오자 fallback worker는 내려갔다.

겉으로 보면 자동 복구가 잘 동작한 사례처럼 보일 수 있었다. 실제로 failover 정책 자체는 의도한 대로 움직였다. 하지만 알림을 따라가다 보니 더 중요한 문제가 보였다.

시스템은 실제 장애와 계획된 정비를 구분하지 못하고 있었다.

이번 글은 그 문제를 어떻게 확인했고, 크롤링 작업이 누락되지 않도록 어떤 방식으로 운영 절차와 상태 확인을 개선했는지 정리한 글이다.


문제를 이해하기 위한 구조

운영 중인 크롤링 플랫폼은 크게 두 종류의 노드로 나뉘어 있었다.

 

메인 운영 노드는 API, 데이터베이스, 스케줄러, 상태 확인을 담당한다. 보조 워커 노드는 실제 크롤링 작업을 처리한다.

보조 워커 노드가 일정 시간 동안 heartbeat를 보내지 못하면, 메인 운영 노드에서 fallback worker를 띄워 작업을 이어갈 수 있도록 구성되어 있었다.

 

이 구조 자체는 자연스럽다. 크롤링 작업은 외부 사이트 상태, 네트워크, VPN, 워커 컨테이너 상태 같은 변수에 영향을 많이 받는다.

 

따라서 워커 노드 하나가 불안정해졌을 때 메인 노드에서 fallback worker를 띄워 작업을 이어가는 것은 필요한 안전장치였다.

문제는 장애가 아닌 상황에서도 같은 절차가 실행될 수 있다는 점이었다.

 

예를 들어 운영자가 보조 워커 노드를 정비하기 위해 잠시 내린 경우를 생각해볼 수 있다. 이때 시스템 입장에서는 heartbeat가 끊긴 것만 보인다. 왜 끊겼는지는 모른다. 결과적으로 계획된 정비도 실제 장애와 같은 신호로 해석된다.

 

이번 알림도 그 지점에서 출발했다.


처음부터 재시작하지 않고 상태를 먼저 봤다

알림만 보면 보조 워커 노드 장애처럼 보였다. 핵심 서비스가 일정 시간 동안 동작하지 않았고, fallback worker가 올라갔다. 하지만 곧이어 보조 노드가 다시 healthy 상태가 되었고 fallback worker가 내려갔다는 알림도 확인했다.

 

이 두 알림을 같이 보면 단순 장애라고 바로 단정하기 어려웠다. 실제로 장애가 났다가 복구되었을 수도 있지만, 계획된 정비나 수동 작업과 자동 failover가 겹쳤을 가능성도 있었다.

그래서 먼저 확인한 것은 현재 크롤러가 정말 멈춰 있는지였다.

  • 신규 작업이 계속 쌓이고 있는지
  • 실행 중인 작업이 남아 있는지
  • 실패하거나 죽은 target이 있는지
  • 오늘 처리해야 할 refresh cycle이 누락되었는지

단순히 컨테이너가 떠 있는지보다 중요한 것은 크롤링 파이프라인이 실제로 안전한 상태인지였다.

 

확인 결과 크롤러는 최종적으로 정상 상태로 돌아와 있었고, dead target이나 누락된 refresh cycle은 없었다. 즉각적인 데이터 손실이나 작업 누락 문제는 아니었다.

하지만 운영 절차의 빈틈은 분명했다.

 

계획된 정비 때문에 워커가 내려간 것인지, 실제 장애 때문에 내려간 것인지 시스템이 구분하지 못했다. 그리고 운영자는 알림만 보고 이 둘을 다시 추적해야 했다.


진짜 문제는 failover가 아니라 맥락의 부재였다

이번 상황에서 자동 failover는 실패한 기능이 아니었다. 오히려 정책대로 동작했다. 보조 워커 노드가 일정 시간 이상 unhealthy 상태였고, 메인 운영 노드는 fallback worker를 올렸다. 보조 노드가 다시 healthy 상태가 되자 fallback worker를 내렸다.

문제는 그 판단에 운영 맥락이 없었다는 것이다.

 

운영 중인 시스템에서는 의도적으로 서비스를 잠시 멈추는 일이 있다. 배포, 백업, 정비, 설정 변경, 리소스 정리 같은 작업이 그렇다. 이런 작업은 장애와 다르게 다뤄야 한다. 예상된 중단이라면 알림이나 자동 복구도 그 맥락을 알고 움직여야 한다.

그렇지 않으면 운영자는 매번 같은 질문을 반복하게 된다.

  • 지금 이 failover는 진짜 장애 때문에 발생한 것인가
  • 아니면 계획된 정비 때문에 발생한 것인가
  • fallback worker가 올라간 상태에서 보조 워커를 다시 켜도 되는가
  • 신규 작업을 계속 투입해도 되는가
  • 작업이 누락되지는 않았는가

이번 개선의 목표는 이 질문을 줄이는 것이었다.

자동 failover를 없애는 것이 아니라, 계획 정비 중에는 시스템이 그 사실을 알 수 있게 만드는 것. 그리고 정비를 시작하고 끝내는 절차를 명확히 만들어 크롤링 작업이 누락되지 않도록 하는 것이 핵심이었다.


먼저 신규 작업 투입을 멈출 수 있어야 했다

운영 중인 크롤러를 수정할 때 가장 먼저 생각해야 하는 것은 이미 실행 중인 작업과 앞으로 들어올 작업을 분리하는 것이다.

이미 실행 중인 workflow를 무리하게 끊으면 중간 상태가 꼬일 수 있다. 반대로 아무 제어 없이 신규 작업을 계속 투입하면, 워커 재시작이나 failover 전환 중에 작업 상태를 추적하기 어려워질 수 있다.

그래서 정비 절차의 첫 단계는 신규 dispatch를 멈추는 것으로 잡았다.

 

여기서 중요한 점은 전체 시스템을 무작정 멈추는 것이 아니다. 이미 실행 중인 작업은 가능한 한 자연스럽게 처리되도록 두고, 새 작업의 투입만 잠시 막는다. 그러면 작업이 어디까지 진행되었는지 확인할 수 있고, 워커를 재시작하거나 교체할 때도 누락 여부를 판단하기 쉬워진다.

이후 보조 워커 노드를 멈추고 수정하거나 재기동한 뒤, health check를 통과한 것을 확인하고 dispatch를 다시 여는 흐름으로 정리했다.

정리하면 절차는 이렇게 단순해졌다.

  1. 신규 작업 투입을 잠시 멈춘다.
  2. 실행 중인 작업과 큐 상태를 확인한다.
  3. 보조 워커 노드를 정비하거나 재기동한다.
  4. heartbeat와 worker 상태를 확인한다.
  5. 정상 상태가 확인되면 신규 작업 투입을 다시 연다.

이 절차가 있어야 운영자가 매번 머릿속으로 순서를 조합하지 않아도 된다.

 

 


maintenance window를 추가했다

다음으로 추가한 것은 maintenance window였다.

maintenance window는 계획된 정비 시간이라는 표시다. 운영자가 정비를 시작할 때 일정 시간 동안 유지되는 window를 열어두면, failover monitor는 보조 워커 노드가 일시적으로 unhealthy하더라도 곧바로 fallback action을 실행하지 않는다.

물론 아무 것도 하지 않고 조용히 무시하면 안 된다. 계획 정비 중이라는 사실도 관측 가능해야 한다. 그래서 failover action은 억제하되, 상태 확인과 audit event에는 남기도록 했다.

이렇게 하면 운영자는 나중에 상태를 봤을 때 알 수 있다.

  • 지금 failover가 억제된 이유가 무엇인지
  • 언제 시작된 정비인지
  • 아직 window가 유효한지
  • 보조 워커 노드가 다시 정상 상태로 돌아왔는지

운영 알림도 같은 방향으로 정리했다. 실제 fallback action이 발생했는지, 아니면 계획 정비 때문에 억제되었는지를 구분할 수 있어야 했다.

결국 중요한 것은 알림의 양을 늘리는 것이 아니라, 알림을 보고 바로 판단할 수 있게 만드는 것이었다.


상태 확인도 함께 보강했다

운영 절차를 바꾸면 상태 확인도 같이 바뀌어야 한다.

기존에는 worker heartbeat, scheduler 상태, revision 일치 여부, 크롤링 큐 상태 등을 따로따로 봐야 했다. 이번에는 상태 확인 endpoint에서 failover 관련 정보를 함께 볼 수 있도록 개선했다.

확인할 수 있는 정보는 크게 네 가지였다.

  • 보조 워커 노드가 필요한 heartbeat를 보내고 있는지
  • 메인 운영 노드의 fallback worker가 떠 있는지
  • 현재 maintenance window가 active 상태인지
  • 최근 failover 또는 정비 관련 이벤트가 무엇이었는지

이 정보가 한 곳에 모이면 장애 대응 속도가 달라진다. 단순히 컨테이너가 떠 있는지만 보는 것이 아니라, 시스템이 왜 현재 상태를 정상 또는 비정상으로 판단하는지 같이 볼 수 있기 때문이다.

이번 작업의 핵심도 여기에 있었다.

운영자는 시스템 상태를 해석해야 한다. 그렇다면 시스템은 해석에 필요한 근거를 충분히 보여줘야 한다.


Codex와 함께 작업하면서 신경 쓴 것

이번 작업은 Codex와 함께 진행했다. 다만 목적은 코드를 빠르게 생성하는 것이 아니었다. 운영 상태를 확인하고, 변경 범위를 좁히고, 배포 순서를 검증하는 페어 작업에 가까웠다.

특히 신경 쓴 부분은 세 가지였다.

 

첫째, 다른 세션의 변경을 섞지 않는 것. 운영 저장소에는 여러 작업이 동시에 진행되고 있었기 때문에, 내가 반영할 변경만 분리해서 다뤄야 했다.

 

둘째, 실행 중인 크롤러를 수정할 때 dispatch pause, health check, resume 순서를 지키는 것. 배포 자체보다 중요한 것은 작업 누락 없이 다시 이어갈 수 있는지였다.

 

셋째, 배포 후 상태 확인 기준을 명확히 하는 것. API, scheduler, worker가 같은 revision으로 동작하는지, failover 상태가 정상인지, dead target이나 누락된 refresh cycle이 없는지 확인했다.

Codex는 분석과 구현 속도를 높여줬지만, 운영 중인 시스템에서 무엇을 멈추고 언제 다시 열지는 명확한 기준이 필요했다. 그 기준을 체크리스트처럼 가져가는 것이 이번 작업에서 가장 중요했다.


배포 중 만난 추가 문제

배포 과정에서도 작은 함정이 있었다.

 

새 revision으로 올리기 전에 schema readiness gate가 막혔다. 운영 데이터베이스에 필요한 일부 index가 없었기 때문이다. 여기서 gate를 우회하지 않고, 별도 절차로 필요한 index를 먼저 보강했다.

 

또 하나는 worker heartbeat의 TTL이었다. 보조 워커 노드를 새 revision으로 재기동한 직후에도 이전 컨테이너의 heartbeat가 잠시 남아 있었다. 이 때문에 상태 확인에서 일시적으로 revision이 섞여 보였다. 실제 새 worker는 올라와 있었지만, 오래된 heartbeat가 만료되기 전까지는 시스템이 degraded로 볼 수 있었다.

 

이런 상태를 보고 바로 다시 재시작했다면 오히려 혼란이 커졌을 것이다. 그래서 heartbeat TTL이 지나고 새 worker heartbeat만 남는지 기다린 뒤 최종 상태를 확인했다.

 

운영에서는 이런 대기 시간이 중요하다. 모든 문제가 재시작으로 해결되는 것은 아니고, 어떤 상태는 시스템이 자연스럽게 수렴할 시간을 줘야 한다.


최종 확인

배포 후에는 메인 운영 노드와 보조 워커 노드가 같은 revision으로 동작하는지 확인했다. 상태 확인 endpoint에서도 전체 상태가 정상으로 돌아온 것을 확인했다.

최종적으로 확인한 내용은 다음과 같았다.

  • 전체 상태는 정상
  • worker와 scheduler revision은 일치
  • 보조 워커 노드 heartbeat는 정상
  • 메인 운영 노드의 fallback worker는 비활성
  • dead target 없음
  • 누락된 refresh cycle 없음
  • 중복 open refresh cycle 없음
  • 오늘 처리해야 할 작업은 모두 완료 상태

이 결과가 중요했다. 단순히 컨테이너가 다시 떴다는 것이 아니라, 크롤링 플랫폼 관점에서 작업이 누락되지 않았고 다음 작업을 받을 수 있는 상태라는 것을 확인했기 때문이다.


배운 점

이번 작업을 하면서 다시 느낀 것은, 장애 대응은 단순히 빠르게 재시작하는 일이 아니라는 점이다.

물론 빠른 복구는 중요하다. 하지만 운영 중인 시스템에서는 왜 그런 판단이 내려졌는지, 그 판단이 실제 장애에 대한 것인지 계획된 작업에 대한 것인지 구분할 수 있어야 한다.

 

이번 개선으로 자동 failover가 완벽해졌다고 말할 수는 없다. 모든 장애 가능성을 제거한 것도 아니다. 다만 계획 정비 중 불필요한 failover action을 줄였고, 정비 시작부터 재기동, health check, dispatch 재개까지의 절차를 더 명확하게 만들었다.

 

또한 운영자가 상태 확인 endpoint만 보고도 failover 상태와 maintenance window를 함께 판단할 수 있게 되었다. 다음번에 비슷한 알림이 오면, 이게 실제 장애인지 계획된 정비인지 더 빠르게 구분할 수 있다.

 

문제 해결은 코드를 고치는 일에서 끝나지 않았다. 운영자가 안전하게 판단할 수 있는 상태를 만들고, 작업을 멈추고 다시 여는 기준을 남기는 것까지가 이번 문제 해결이었다.

 

Codex와 함께 빠르게 분석하고 구현할 수 있었지만, 운영 중인 시스템에서 가장 중요했던 것은 결국 검증 가능한 절차를 만드는 일이었다.