서버 한 대짜리 자동 복구 설계 — watchdog·서킷브레이커·승인 게이트와 DOP-C02
시리즈: AWS 자격증, 실무로 되짚기
3 / 3편
- 11편 - Gen AI 자격증과 자작 RAG
- 22편 - 아키텍트 자격증과 Multi-AZ 운영
- 33편 - DevOps 자격증과 자동 복구지금 읽는 중
목차
TL;DR
- 밤사이 풍경: 예약해둔 작업들이 밤새 돌고 몇 개는 조용히 실패합니다. 봇이 죽으면 제가 자는 사이 watchdog가 되살려두고, 아침에 로그를 열면 대부분 성공에 몇 건 실패로 밤이 정리돼 있습니다
- 제가 한 것: 서버 한 대에 자동 복구를 접어 넣었습니다. 죽으면 되살리고(watchdog), 연달아 실패하면 끊고(서킷브레이커), 위험한 명령은 사람이 승인해야 통과합니다. 지금 여덟 개 작업에 이 차단기가 걸려 있습니다
- 시험이 그리는 것: 인스턴스가 죽으면 Auto Scaling이 새로 띄웁니다. 배포는 무중 단으로 넘기고 실패하면 자동 롤백입니다. 저는 같은 일을 서버 한 대에서 손으로 만들어 쓰고 있었습니다
- 여기부터는 학습 중: 무중단 배포와 대규모 롤백은 책으로만 봤습니다. 사용자가 저 하나라 껐다 켜도 아무도 불평하지 않는 환경이라, 아직 만날 일이 없었습니다
이 시리즈에 대해
1편은 검색, 2편은 인프라 이중화였습니다. 마지막인 이번 편은 운영입니다. DevOps 자격증(DOP-C02)과, 밤새 혼자 돌아가는 개인 시스템을 놓고 봅니다.
아침에 로그를 열면 밤이 정리돼 있습니다
제가 자는 사이에도 예약해둔 작업들이 밤새 줄줄이 돕니다. 그중 몇 개는 조용히 실패하고 Discord 봇도 이따금 죽습니다. 그런데 아침에 로그를 열면, 죽었던 봇은 되살아나 있고 밤은 대부분 성공에 몇 건 실패로 정리돼 있습니다. 제가 손댄 건 없습니다. Jarvis라는 개인 AI 비서를 서버 한 대에 올려두고 매일 굴리는데, 이 시스템은 제가 없어도 스스로 죽고 스스로 살아납니다.
DevOps 자격증(DOP-C02)을 준비하며 자동 복구 파트를 보다가, 이 밤사이 풍경이 겹쳐 떠올랐습니다. 시험은 이 상황을 이렇게 묻습니다.
“서비스 인스턴스가 비정상 종료되어도 사용자 영향 없이 자동으로 대체되어야 한다. 가장 적합한 구성은?”
정답은 대개 Auto Scaling 그룹 + 헬스 체크 + 자동 교체로 좁혀집니다. 죽은 인스턴스를 감지해서 사람 손 안 대고 새 인스턴스로 갈아 끼우는 구성입니다. 시험이 객관식 한 줄로 “이 구성을 고르세요”라고 주는 그 일을, 저는 서버 한 대에서 손으로 만들어 매일 쓰고 있었습니다. 그래서 자격증을 딴 뒤 운영 로그를 다시 열어, 시험이 그린 그림과 제가 만든 구조가 어디서 만나고 어디서 갈라지는지 봤습니다.
죽으면 운영체제가 다시 띄웁니다
시험이 그리는 자동 복구의 핵심은 단순합니다. 죽은 걸 감지하고, 자동으로 되살린다. 규모가 큰 서비스는 이걸 Auto Scaling과 헬스 체크로 합니다. 인스턴스가 응답을 멈추면 그룹에서 빼고 새 걸 띄웁니다.
제 개인 시스템은 서버가 한 대뿐이라 인스턴스를 갈아 끼울 수가 없습니다. 대신 macOS의 프로세스 관리자(LaunchAgent)에게 “이 봇이 비정상으로 꺼지면 네가 알아서 다시 띄워라”라고 맡겨뒀습니다.
맡겨둔 설정은 이렇게 생겼습니다.
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/> <!-- 정상 종료가 아니면(=죽으면) 다시 살림 -->
</dict>
<key>ThrottleInterval</key>
<integer>10</integer> <!-- 죽자마자 무한 재시작하지 않게 10초 간격 -->여기에 더해, 프로세스는 살아 있는데 실제로는 응답을 못 하는 경우(이른바 좀비 상태)를 잡으려고 감시용 크론(watchdog)을 따로 뒀습니다. 주기적으로 봇의 상태를 찔러보고, 살아 있지만 응답이 없으면 강제로 재기동합니다.
graph LR
A[봇 상태 확인] --> B{응답 있나?}
B -->|예| C[정상, 대기]
B -->|아니오| D[강제 재기동]
D --> A
C --> A
style A fill:#e0e7ff,stroke:#4F46E5,stroke-width:2px,color:#1a1a1a
style D fill:#fee2e2,stroke:#dc2626,stroke-width:2px,color:#1a1a1a
style C fill:#dcfce7,stroke:#16a34a,stroke-width:2px,color:#1a1a1a운영체제의 자동 재시작이 헬스 체크의 “죽으면 교체”라면, watchdog는 “살아는 있는데 일을 안 하는” 경우까지 잡는 한 겹 더입니다. 덩치는 Auto Scaling에 비할 게 못 되지만 목표는 같습니다. 제가 자는 동안 사람 손 없이 되살아나 있을 것.
계속 죽는 작업은 아예 꺼버렸습니다
한 번 죽는 건 되살리면 됩니다. 문제는 계속 죽는 겁니다. 되살리고, 또 죽고, 또 되살리는 무한 반복은 되살림 자체가 장애가 됩니다.
시험은 이 지점에서 CloudWatch 알람과 자동 롤백을 답으로 줍니다. 실패가 임계치를 넘으면 알림을 띄우고, 배포가 계속 실패하면 직전 버전으로 되돌립니다.
제 시스템은 여기에 서킷브레이커(circuit breaker)를 뒀습니다. 이름이 거창하지만 원리는 두꺼비집과 같습니다. 특정 작업이 연달아 실패하면 그 작업을 잠시 꺼두고 그래도 계속 실패하면 아예 내려버립니다. 지금 이 서버에는 여덟 개 작업에 이 두꺼비집이 걸려 있습니다.
코드로 옮기면 이 정 도입니다.
// 연속 실패가 임계치를 넘으면 그 작업을 잠시 꺼둔다
if (task.consecutiveFailures >= THRESHOLD) {
task.state = 'OPEN'; // 회로 차단: 당분간 실행 안 함
scheduleCooldown(task, COOLDOWN_MS); // 쿨다운 뒤 한 번 더 시도
}쿨다운이 지나면 한 번 살짝 열어서 재시도해보고 또 실패하면 다시 닫습니다. 그래도 계속 실패하는 작업은 영구 실패로 분류해서 자동으로 꺼버립니다. 이렇게 해두면 실패하는 작업 하나가 되살림을 반복하며 시스템 전체를 갉아먹는 일은 없습니다.
시험의 “실패가 임계치를 넘으면 롤백”과 제 “연속 실패를 넘으면 차단”은 결이 같습니다. 둘 다 실패를 감지하는 데서 멈추지 않고 실패가 번지지 않게 끊어냅니다.
배포는 껐다 켜는 게 전부입니다
여기서부터는 갈라집니다. 그리고 정직하게 밝혀둘 부분입니다.
시험이 배포에서 가장 크게 다루는 건 무중단 배포입니다. CodeDeploy의 블루/그린, 카나리 같은 전략입니다. 새 버전을 옆에 띄워두고 트래픽을 조금씩 넘기다가, 문제가 없으면 완전히 갈아타고, 문제가 있으면 즉시 되돌리는 방식입니다. 사용자는 배포가 있었는지도 모르게 넘어갑니다.
제 개인 시스템은 이걸 안 합니다. 사용자가 저 하나라, 새 버전을 올릴 때는 그냥 잠깐 껐 다 켜면 됩니다. 몇 초 끊김을 감수할 사람이 저뿐입니다. 무중단 배포는 제가 못 푼 문제라기보다 아직 만난 적 없는 문제입니다.
블루/그린이나 카나리 배포를 개인 프로젝트에서 실제로 굴려본 적은 없습니다. EKS나 쿠버네티스 위에서 대규모 무중단 배포를 운영해본 경험도 아직 없습니다. 시험에 나오는 “사용자 영향 0으로 배포하고, 문제 생기면 자동 롤백”은 저한테 아직 문제집 안에만 있습니다. 이 부분은 학습하며 넓혀가는 중입니다.
시험의 배포 파트에서 딱 하나, 제 방식과 만나는 지점이 있었습니다. 승인 게이트(Manual Approval)입니다. 시험은 배포 파이프라인 중간에 사람이 승인해야만 다음으로 넘어가는 관문을 둡니다. 위험한 변경을 자동으로 흘려보내지 않으려는 장치입니다.
제 시스템에도 비슷한 게 있습니다. 되돌릴 수 없는 위험한 명령(예: 대량 삭제, 저장소 공개 설정 변경)은 자동으로 실행되지 않게 막아뒀습니다. 제가 명시적으로 허락해야만 실행됩니다. 시험의 승인 게이트를 보고 나서야 제가 걸어둔 이 장치에도 이름이 있다는 걸 알았습니다.
감으로 만든 것에 이름이 붙었습니다
솔직히 자동 복구도, 서킷브레이커도, 승인 게이트도 시험 보기 전부터 만들어 쓰고 있었습니다. 그래서 “시험이 나한테 뭘 줬나” 싶었습니다.
달라진 건 남한테 설명할 때였습니다. 감으로 “이러면 되겠지” 하고 만든 것들에 헬스 체크·자동 롤백·블루/그린 같은 표준 이름이 붙으니 같은 걸 말하는 데 문장이 훨씬 짧아졌습니다.
안 해본 영역도 같이 선명해졌습니다. 무중단 배포, 대규모 롤백, 오토스케일링을 실제 트래픽 위에서 굴리는 일. 지금 제 규모에서는 아직 만날 일이 없어서 시험 문제로 처음 봤습니다. 어디 있는지 알았으니 다음에 그 규모를 만나면 헤맬 시간은 줄어들 겁니다.
오늘 하나만 넣는다면
자동 복구라고 하면 거창하게 들리지만, 오늘 당장 넣을 수 있는 건 하나입니다. 프로세스가 죽었을 때 사람 손 없이 되살아나는 장치 하나를 거는 겁니다. 리눅스면 systemd, macOS면 LaunchAgent, 컨테이너면 재시작 정책 한 줄이면 됩니다. 이것만 있어도 “새벽에 죽어서 아침까지 멈춰 있는” 사고의 대부분이 사라집니다. 나머지는 그 위에 한 겹씩 올리면 됩니다.
아래는 자동 복구를 넣기 전에 스스로 물어보면 좋은 순서입니다. 위에서부터 하나씩 채워가면 됩니다.
- 프로세스가 죽었을 때 사람 손 없이 되살아나는 장치가 있는가? (KeepAlive, systemd, Auto Scaling 등)
- 살아는 있는데 응답을 못 하는 상태(좀비)까지 감지하는가? (단순 프로세스 존재 확인만으로는 부족)
- 되살림이 무한 반복되지 않게 막는 장치가 있는가? (재시작 간격, 서킷브레이커)
- 반복 실패하는 작업을 자동으로 격리하거나 끄는가? (실패 하나가 전체를 갉아먹지 않게)
- 되돌릴 수 없는 위험한 작업은 사람이 한 번 승인하게 되어 있는가? (승인 게이트)
시리즈를 닫으며
처음엔 봇이 죽으면 아침에 제가 켰습니다. 그게 몇 번 반복되고 나서야 watchdog을 짰습니다. 자동 복구를 미리 설계해 넣은 게 아니라 새벽에 멈춰 있는 걸 몇 번 보고 나서 뒤늦게 붙였습니다. 이 글에서 다룬 나머지 장치들도 대체로 이 순서로 생겼습니다.
