메인 콘텐츠로 건너뛰기
이 페이지에서는 W&B Sweeps가 시스템 시그널과 프로세스 종료 코드를 처리하는 방식을 자세히 설명합니다. 이 정보를 활용하면 SLURM, EC2 Spot, Google Cloud 선점형 VM과 같은 선점형 환경에서도 스윕를 안정적으로 실행할 수 있습니다. 다음 섹션에서는 키보드로 run을 깔끔하게 중단하는 방법과, run이 다시 큐에 들어가는 동작을 이해하고 예측하는 데 도움이 되는 세부 정보를 설명합니다. 이 페이지는 선점형 인프라에서 스윕를 실행하는 사용자나 run 라이프사이클과 정리를 세밀하게 제어해야 하는 사용자를 대상으로 합니다. 선점되었을 때 W&B가 run을 어떻게 다시 큐에 넣는지에 대한 자세한 내용은 선점형 스윕 run 재개를 참조하세요.

종료 상태와 시그널

W&B는 트레이닝 프로세스의 종료 상태를 사용해 run을 다시 큐에 넣을지 여부와 run 상태를 어떻게 기록할지 결정합니다. 종료 코드 규약:
  • 종료 코드 0: W&B는 run이 성공적으로 완료된 것으로 간주하며 다시 큐에 넣지 않습니다.
  • 0이 아닌 종료 코드: run은 실패했거나 선점된 것으로 처리됩니다. mark_preempting()을 사용하면 W&B가 run을 다시 큐에 넣으므로, 다른 에이전트(또는 재시작 후 동일한 에이전트)가 이를 재개할 수 있습니다.
이는 프로세스가 시그널 핸들러, 예외 또는 명시적인 sys.exit() 호출로 종료되는 경우 모두에 적용됩니다. 이 규약을 이해하고 이를 전제로 동작하도록 구현하는 것은 선점형 또는 클러스터 환경에서 중요합니다. 프로세스가 포착 가능한 시그널로 인해 종료될 때는 핸들러가 실행되어, run을 다시 큐에 넣으려는 경우 wandb.run.mark_preempting()을 호출하고, 정리 작업(예: checkpoint 저장)을 수행한 뒤 0이 아닌 코드로 종료할 수 있습니다. 시그널에 의해 종료될 때의 일반적인 관례는 sys.exit(128 + signum)입니다. W&B는 해당 종료 코드를 기록하며, 동일한 다시 큐에 넣는 규칙이 적용됩니다. 운영 체제 커널이 SIGKILL로 프로세스를 종료하면 프로세스는 종료 훅을 실행할 수 없으므로 W&B는 최종 summary를 기록하지 않으며, run이 crashed 또는 killed로 표시될 수 있습니다. 그래도 에이전트는 다음 run을 시작합니다.

정체된 run과 서버 측 시간 초과

W&B는 종료 코드와 run의 활동을 모두 기준으로 run 상태를 확인합니다. run이 종료되지도 않고 약 5분 동안 새 메트릭도 기록하지 않으면, W&B는 해당 run을 비정상 종료된 것으로 표시합니다. 이는 트레이닝 프로세스가 응답하지 않게 되거나, 로깅이 중단되거나, (SIGKILL 전송처럼) 정상적인 종료 처리 없이 종료될 때 발생할 수 있습니다. 일정한 간격으로 메트릭을 기록하거나 명시된 코드로 종료해 run 상태가 실제 발생한 상황과 일치하도록 하세요.

포착 가능한 시그널과 선점

선점형 환경에서는 대부분의 시그널을 포착할 수 있으므로, 트레이닝 스크립트가 이를 가로채 정상적으로 종료할 수 있습니다. 트레이닝 스크립트에서 맞춤형 시그널 핸들러를 등록할 수 있습니다. 포착 가능한 시그널이 전달되면 핸들러가 실행되며, 이미 W&B에 전송된 메트릭은 보존됩니다. 또한 에이전트는 프로세스 종료를 감지하고 다음 run을 시작합니다. 모범 사례:
  • 핸들러는 가능한 한 일찍 등록하세요(예: 기본 트레이닝 루프에 들어가기 전).
  • 핸들러에서는 선점 후 run이 다시 큐에 들어가도록 하려면 wandb.run.mark_preempting()을 호출하고, 정리 작업(예: checkpoint 저장)을 수행한 다음, 0이 아닌 종료 코드로 종료하세요.
다음 예제에서는 SIGUSR1(일반적인 클러스터 선점 시그널)과 SIGTERM에 대한 핸들러를 등록합니다. SIGINT는 대화형 사용(예: 터미널에서 수동 취소)을 위해 그대로 둡니다. 핸들러는 wandb.run.mark_preempting()을 호출한 뒤 128 + signum으로 종료합니다:

SIGKILL (포착 불가)

SIGKILL은 운영 체제 커널에서 발생하며 포착하거나 무시할 수 없습니다. 프로세스는 핸들러나 atexit 콜백을 실행할 기회도 없이 즉시 종료됩니다. W&B는 해당 run의 최종 summary를 기록할 수 없습니다. 에이전트는 복구 후에도 스윕을 계속 진행하지만, 해당 run의 데이터는 불완전합니다. SIGKILL은 최후의 수단으로만 사용하세요. 정상 종료가 필요할 때는 SIGTERM 또는 SIGINT를 우선 사용하세요.

에이전트에서 자식 프로세스로 시그널 전달

wandb agent CLI를 사용하면 에이전트가 트레이닝 스크립트를 자식 프로세스로 실행합니다. 에이전트를 중단하면(예: Ctrl+C를 누르거나 스케줄러가 작업에 SIGTERM을 보내는 경우) 기본적으로 자식 프로세스(트레이닝 프로세스)는 시그널을 받지 못합니다. 따라서 트레이닝 스크립트는 핸들러를 실행하거나 mark_preempting()을 호출할 수 없습니다. 자세한 내용은 wandb GitHub issue #3667을 참조하세요. 자식 프로세스가 정상적으로 종료되고 핸들러에서 wandb.run.mark_preempting()을 호출할 수 있도록 하려면 --forward-signals 옵션과 함께 CLI 에이전트를 실행하세요:
W&B는 Python API의 wandb.agent()에 대해 시그널 포워딩을 지원하지 않습니다. 이 경로에서는 트레이닝 함수가 별도의 자식 프로세스가 아니라 스레드에서 실행되므로, 동일한 포워딩 동작이 적용되지 않습니다. CLI 에이전트가 포워딩이 활성화된 상태에서 SIGINT 또는 SIGTERM을 받으면, 해당 시그널을 자식 프로세스로 전달합니다. 그러면 트레이닝 스크립트의 핸들러가 실행되어, 필요하면 wandb.run.mark_preempting()wandb.finish()를 0이 아닌 종료 코드와 함께 호출한 뒤 0이 아닌 코드로 종료할 수 있습니다. 에이전트 프로세스에서 Ctrl+C를 두 번 누르면 기본적으로 에이전트는 SIGTERM을 받습니다. --forward-signals를 사용하면 에이전트가 SIGINT를 자식 프로세스로 전달하여 핸들러가 실행되도록 할 수 있습니다. 자세한 내용은 wandb agent CLI 레퍼런스를 참조하세요.

SLURM과 같은 선점형 클러스터

이 섹션에서는 SLURM, EC2 Spot, Google Cloud 선점형 VM과 같은 클러스터에서 선점이 발생해도 run이 계속 이어지도록 스윕을 설정하는 방법을 설명합니다. 선점이 발생하면 트레이닝 프로세스가 시그널을 받아 run을 선점 예정 상태로 표시하고, W&B가 run을 다시 큐에 넣을 수 있도록 0이 아닌 코드로 종료해야 합니다. 그러면 새 에이전트(또는 작업이 다시 큐에 들어간 뒤 동일한 에이전트)가 run을 재개할 수 있습니다. 트레이닝 프로세스가 시그널을 받도록 하세요:
  • 스케줄러가 에이전트에 시그널을 보내는 경우: wandb agent --forward-signals로 에이전트를 실행하세요. 그러면 스케줄러(또는 사용자)가 에이전트에 시그널을 보낼 때 에이전트가 이를 자식 프로세스에 전달합니다. 이후 자식 프로세스의 핸들러는 wandb.run.mark_preempting(), 0이 아닌 코드로 wandb.finish(exit_code=...), 그리고 sys.exit(128 + signum)(또는 다른 0이 아닌 종료 코드)를 호출할 수 있습니다.
  • 스케줄러가 시작 스크립트에 시그널을 보내는 경우(에이전트에 직접 보내지 않는 경우): 시작 스크립트가 선점 시그널을 트레이닝 프로세스에 직접 보내도록 하세요. 예를 들어 트레이닝 스크립트가 자신의 프로세스 ID를 파일에 기록할 수 있습니다. 시작 스크립트는 클러스터 시그널(예: SIGUSR1)을 trap한 뒤 kill -SIGUSR1 $(cat $PID_FILE)를 실행해 트레이닝 프로세스의 핸들러가 실행되도록 합니다.
트레이닝 스크립트에서: 클러스터에서 사용하는 시그널(예: SIGTERM 또는 SIGUSR1)에 대한 핸들러를 등록하세요. 핸들러에서는 활성 run이 있으면 wandb.run.mark_preempting()를 호출한 다음, W&B가 run을 다시 큐에 넣을 수 있도록 0이 아닌 종료 코드와 sys.exit(128 + signum)(또는 다른 0이 아닌 코드)로 run을 종료하세요. W&B가 언제 run을 다시 큐에 넣는지와 이것이 mark_preempting()과 어떻게 상호작용하는지에 대한 자세한 내용은 선점형 Sweeps run 재개를 참조하세요. 스윕 상태: 에이전트를 시작하기 전에 wandb sweep entity/project/sweep_ID --resume를 실행해 스윕이 재개 모드가 되도록 하고, 다시 큐에 들어간 run을 할당하도록 하세요. 멀티 에이전트 조정: 많은 에이전트가 동시에 실행되면(SLURM array 작업 등) 동일한 선점된 run을 차지하려고 경쟁할 수 있습니다. 이는 알려진 제한 사항입니다. 이를 우회하려면 에이전트 시작 시점을 엇갈리게 하거나 락과 같은 외부 조정 메커니즘을 사용하세요. 멀티 GPU SLURM 작업에서 하나의 프로세스만 wandb.agent()를 호출해야 하는 경우, SLURM에서 sweeps를 어떻게 실행해야 하나요?를 참조하세요.

wandb sweep --cancel

이 section에서는 취소가 OS 시그널을 직접 보내는 것과 다르게 동작하므로, --cancel command가 시그널 및 하위 프로세스와 어떻게 상호작용하는지 설명합니다. OS 시그널이 아니라 W&B API를 사용해 스윕을 취소합니다. wandb sweep --cancel entity/project/sweep_ID와 같은 command를 실행하세요. 서버가 에이전트에 종료하라고 지시하면, 에이전트는 실행 중인 하위 프로세스를 종료한 뒤 중지합니다. 취소가 실제로 적용되기까지는 짧은 지연이 있을 수 있습니다(대략 에이전트의 API 폴링 간격 기준). 취소하면 run에 **SIGKILL**이 전달됩니다. 하위 프로세스는 사용자 정의 시그널 핸들러를 실행할 기회가 없습니다. Sweeps UI에서 Cancel 컨트롤을 사용할 때도 동일합니다. 전체 스윕을 중지하고 취소된 것으로 표시하려면 --cancel을 사용하세요. 현재 run을 정상적으로 종료하려면 run에 포착 가능한 시그널을 보내세요(또는 CLI 에이전트와 함께 --forward-signals를 사용한 후 에이전트에 시그널을 보내세요). 스윕을 정상적으로 완료하려면 --cancel 대신 wandb sweep --stop을 사용하세요. 일시 중지, 재개, 중지, 취소 옵션에 대한 자세한 내용은 Manage sweeps를 참조하세요.

에이전트에 보내는 시그널과 run에 보내는 시그널

에이전트에 시그널을 보내는 것과 트레이닝 run에 시그널을 보내는 것의 차이를 이해하면 고아 프로세스와 예기치 않은 동작을 피하는 데 도움이 됩니다. 에이전트 프로세스(자식 트레이닝 프로세스가 아니라)에 시그널을 보내면, 에이전트는 종료되지만 자식 프로세스는 고아 프로세스로 계속 실행될 수 있습니다. 이 고아 프로세스는 터미널에 계속 출력할 수 있으며, 셸은 Enter를 누를 때까지 새 프롬프트를 표시하지 않을 수 있습니다. CLI 에이전트에서 --forward-signals를 사용하지 않는 한, 에이전트를 중지해도 자식 트레이닝 프로세스까지 중지된다고 보장할 수는 없습니다. 에이전트가 종료되었는지 확인하려면 프롬프트가 나타나는지만 보지 말고 ps -p [AGENT-PID] 또는 pgrep -f "wandb agent" 같은 OS 명령어를 사용하세요.

레퍼런스: mark_preempting() 및 최종 run 상태

아래 표는 mark_preempting()을 언제 호출하는지와 프로세스가 어떻게 종료되는지에 따라 run 상태가 어떻게 달라지는지 요약한 것입니다. 여기서는 트레이닝 프로그램을 서브프로세스로 사용하는 wandb agent CLI를 사용한다고 가정합니다. 시그널 핸들러 안에서만 mark_preempting()을 호출하면, SIGKILL처럼 핸들러가 전혀 실행되지 않는 경우는 처리할 수 없습니다. wandb.init() 직후 항상 mark_preempting()을 호출하면 W&B가 모든 실패를 선점으로 처리할 수 있으므로, bug나 잘못된 설정 때문인 경우까지 포함해 run이 반복적으로 다시 큐에 들어갈 수 있습니다. 선점 시그널이 명확하게 정의된 환경에서는 일반적으로 init() 후 무조건 호출하기보다, mark_preempting()을 호출한 뒤 0이 아닌 값으로 종료하는 시그널 핸들러 방식을 사용합니다.