> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

> W&B Sweeps가 UNIX 시그널, 종료 코드 및 스윕 run에서 선점을 처리하는 방법을 알아보세요.

# 시그널 처리 및 스윕 run

이 페이지는 W\&B Sweeps가 시스템 시그널과 프로세스 종료 코드를 처리하는 방법에 대한 세부 정보를 제공합니다. preemptible 환경(예: SLURM, EC2 Spot 또는 Google Cloud preemptible VM)에서 스윕을 안정적으로 실행하는 데 이 정보를 사용하세요. 다음 섹션에서는 키보드에서 run을 깔끔하게 중단하는 방법과 run이 다시 큐에 들어가는 동작을 이해하고 예측하는 데 도움이 되는 세부 정보를 설명합니다. 이 페이지는 preemptible 인프라에서 스윕을 실행하거나 run 라이프사이클 및 cleanup에 대한 세밀한 제어가 필요한 사용자를 대상으로 합니다. preempted될 때 W\&B가 run을 다시 큐에 들어가게 하는 방법에 대한 자세한 내용은 [Resume preemptible Sweeps runs](/ko/products/wandb/runs/resuming#resume-preemptible-sweeps-runs)를 참조하세요.

<h2 id="exit-status-and-signals">
  종료 상태 및 시그널
</h2>

W\&B는 트레이닝 프로세스 종료 상태를 사용하여 run이 다시 큐에 들어가는지와 run 상태가 어떻게 기록되는지를 결정합니다.

**종료 코드 계약:**

* **종료 코드 0**: W\&B는 run이 성공적으로 완료된 것으로 간주하고 다시 큐에 들어가지 않습니다.
* **0이 아닌 종료 코드**: W\&B는 run을 실패하거나 preempted된 것으로 처리합니다. [`mark_preempting()`](/ko/products/wandb/ref/python/experiments/run#mark_preempting)을 사용하면 W\&B는 run을 다시 큐에 들어가게 하여 다른 에이전트(또는 재시작 후 동일한 에이전트)가 재개할 수 있도록 합니다.

이 규칙은 프로세스가 시그널 핸들러, 예외 또는 명시적인 `sys.exit()` 호출로 종료되는 경우 모두 적용됩니다. preemptible 또는 클러스터 환경에서는 이 계약을 이해하고 따르는 것이 중요합니다.

프로세스가 [포착 가능한 시그널](#catchable-signals-and-preemption)로 인해 종료될 때 핸들러를 실행하고, run을 다시 큐에 들어가게 하려면 [`wandb.run.mark_preempting()`](/ko/products/wandb/ref/python/experiments/run#mark_preempting)을 호출하며, 정리 작업(예: 체크포인트 저장)을 수행한 후 0이 아닌 코드로 종료할 수 있습니다. 일반적인 관례는 시그널에 의한 종료 시 `sys.exit(128 + signum)`입니다. W\&B는 해당 종료 코드를 기록하며 동일한 [다시 큐에 들어가는 규칙](/ko/products/wandb/runs/resuming#resume-preemptible-sweeps-runs)이 적용됩니다. 운영 체제 커널이 [`SIGKILL`](#sigkill-uncatchable)로 프로세스를 종료하면 종료 훅을 실행할 수 없으므로 W\&B는 최종 요약을 기록하지 않으며 run이 crashed 또는 killed된 것으로 표시될 수 있습니다. 에이전트는 여전히 다음 run을 시작합니다.

<h2 id="stale-runs-and-server-side-timeouts">
  Stale run과 server-side timeout
</h2>

W\&B는 종료 코드와 run의 활동을 통해 run 상태를 확인합니다. run이 종료되지 않거나 약 5분 동안 새로운 메트릭을 게시하지 않으면 W\&B는 run을 crashed로 표시합니다. 이는 트레이닝 프로세스가 응답하지 않거나, 로깅을 중지하거나, clean exit 없이 종료될 때(예: `SIGKILL` 전송) 발생할 수 있습니다. run 상태를 실제 상황과 일치시키려면 일정한 주기로 메트릭을 로깅하거나 정의된 코드로 종료하세요.

<h2 id="catchable-signals-and-preemption">
  포착 가능한 시그널과 선점
</h2>

preemptible 환경에서 발생하는 대부분의 시그널은 포착 가능(catchable)합니다. 즉, 트레이닝 스크립트가 시그널을 가로채서 정상적으로 종료할 수 있습니다. 트레이닝 스크립트에 맞춤형 시그널 핸들러를 등록할 수 있습니다. 시스템이 포착 가능한 시그널을 보내면 핸들러가 실행됩니다. W\&B는 이미 수신한 메트릭을 보존합니다. 에이전트는 프로세스 종료를 감지하고 다음 run을 시작합니다.

**모범 사례:**

* 핸들러는 가능한 한 일찍 등록하세요(예: 메인 트레이닝 루프에 진입하기 전).
* 선점 후 run이 다시 큐에 들어가도록 하려면 핸들러에서 [`wandb.run.mark_preempting()`](/ko/products/wandb/ref/python/experiments/run#mark_preempting)을 호출하고, 정리 작업(예: 체크포인트 저장)을 수행한 다음 0이 아닌 코드로 종료하세요.

다음 예시는 `SIGUSR1`(일반적인 클러스터 선점 시그널)과 `SIGTERM`에 대한 핸들러를 등록합니다. `SIGINT`는 대화형 사용(예: 터미널에서 수동으로 취소)을 위해 남겨 둡니다. 핸들러는 `wandb.run.mark_preempting()`을 호출한 후 `128 + signum`으로 종료합니다.

```python theme={"system"}
import signal
import sys
import wandb


def signal_handler(signum, frame):
    if wandb.run is not None:
        # 선택: 모델 체크포인트 저장, 버퍼 플러시 등을 수행합니다.
        print(f"Preempted with signal: {signal.Signals(signum).name}.")
        wandb.run.mark_preempting()
    sys.exit(128 + signum)


def train():
    signal.signal(signal.SIGUSR1, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    with wandb.init() as run:
        config = wandb.config
        for epoch in range(100):
            # 트레이닝 step. 필요에 따라 wandb.log(...)를 호출합니다.
            pass


if __name__ == "__main__":
    train()
```

<h2 id="sigkill-uncatchable">
  `SIGKILL` (uncatchable)
</h2>

`SIGKILL`은 운영 체제 커널에서 발생하며 잡거나 무시할 수 없습니다. 프로세스는 즉시 종료되며 핸들러나 `atexit` callback을 실행할 기회가 없습니다. W\&B는 run의 최종 요약을 기록할 수 없습니다. 에이전트는 복구되어 스윕을 계속 진행하지만, 해당 run의 데이터는 불완전하게 남습니다. `SIGKILL`은 최후의 수단으로만 사용하세요. 정상 종료(graceful shutdown)가 필요한 경우에는 `SIGTERM` 또는 `SIGINT`를 사용하는 것이 좋습니다.

<h2 id="signal-forwarding-from-agent-to-child">
  에이전트에서 자식으로의 시그널 전달
</h2>

[`wandb agent`](/ko/products/wandb/ref/cli/wandb-agent) CLI를 사용할 때, 에이전트는 트레이닝 스크립트를 자식 프로세스로 실행합니다. 에이전트를 중단하면(예: Ctrl+C를 누르거나 스케줄러가 작업에 `SIGTERM`을 보내는 경우), 자식(트레이닝 프로세스)은 기본적으로 시그널을 받지 못합니다. 트레이닝 스크립트는 핸들러를 실행하거나 `mark_preempting()`을 호출할 수 없습니다. 자세한 내용은 [wandb GitHub 이슈 #3667](https://github.com/wandb/wandb/issues/3667)을 참조하세요.

자식이 정상적으로 종료하고 핸들러에서 `wandb.run.mark_preempting()`을 호출할 수 있게 하려면, `--forward-signals` 옵션으로 CLI 에이전트를 실행하세요:

```bash theme={"system"}
wandb agent --forward-signals entity/project/sweep_ID
```

W\&B는 Python API의 [`wandb.agent()`](/ko/products/wandb/ref/python/functions/agent)에 대해 시그널 전달을 지원하지 않습니다. 이 방식은 트레이닝 함수를 별도의 자식 프로세스가 아닌 스레드에서 실행하므로 동일한 전달 동작이 적용되지 않습니다.

전달이 활성화된 상태에서 CLI 에이전트가 `SIGINT` 또는 `SIGTERM`을 수신하면 해당 시그널을 자식 프로세스로 전달합니다. 그러면 트레이닝 스크립트의 핸들러가 실행되어 `wandb.run.mark_preempting()`을 호출하고, 필요한 경우 0이 아닌 종료 코드로 [`wandb.finish()`](/ko/products/wandb/ref/python/experiments/run#finish)를 호출한 뒤 0이 아닌 코드로 종료할 수 있습니다. 에이전트 프로세스에서 Ctrl+C를 두 번 누르면 기본적으로 에이전트가 `SIGTERM`을 수신합니다. `--forward-signals`를 사용하면 에이전트가 `SIGINT`를 자식 프로세스로 전달하므로 핸들러가 실행될 수 있습니다.

자세한 내용은 [`wandb agent`](/ko/products/wandb/ref/cli/wandb-agent) CLI 레퍼런스를 참조하세요.

<h2 id="preemptible-clusters-like-slurm">
  SLURM과 같은 Preemptible 클러스터
</h2>

이 섹션에서는 SLURM, EC2 Spot 또는 Google Cloud preemptible VM과 같은 클러스터에서 선점 발생 시 run이 유지되도록 스윕을 설정하는 방법을 설명합니다. 선점 시 트레이닝 프로세스는 시그널을 수신해 run을 preempting으로 표시하고 0이 아닌 코드로 종료하여 W\&B가 run을 다시 큐에 들어가게 하도록 해야 합니다. 그러면 새 에이전트(또는 작업이 다시 큐에 들어간 후 동일한 에이전트)가 run을 재개할 수 있습니다.

**트레이닝 프로세스가 시그널을 받도록 하세요:**

* **스케줄러가 에이전트에 시그널을 보낼 때**: `wandb agent --forward-signals`로 에이전트를 실행하여 스케줄러(또는 사용자)가 에이전트에 시그널을 보내면 에이전트가 이를 자식 프로세스로 전달하도록 합니다. 자식의 핸들러는 `wandb.run.mark_preempting()`, [`wandb.finish(exit_code=...)`](/ko/products/wandb/ref/python/experiments/run#finish)을 0이 아닌 코드와 함께 호출한 후 `sys.exit(128 + signum)`(또는 다른 0이 아닌 종료 코드)을 실행할 수 있습니다.
* **스케줄러가 에이전트가 아닌 launch 스크립트에 시그널을 보낼 때**: launch 스크립트가 선점 시그널을 트레이닝 프로세스에 직접 보내도록 합니다. 예를 들어 트레이닝 스크립트가 자신의 프로세스 ID를 파일에 기록합니다. launch 스크립트는 클러스터 시그널(예: `SIGUSR1`)을 트랩하고 `kill -SIGUSR1 $(cat $PID_FILE)`을 실행하여 트레이닝 프로세스의 핸들러가 실행되도록 합니다.

**트레이닝 스크립트 내에서:** 클러스터가 사용하는 시그널(예: `SIGTERM` 또는 `SIGUSR1`)에 대한 핸들러를 등록합니다. 핸들러에서 run이 active 상태이면 `wandb.run.mark_preempting()`을 호출한 다음 0이 아닌 종료 코드로 run을 종료하고 `sys.exit(128 + signum)`(또는 다른 0이 아닌 코드)을 실행하여 W\&B가 run을 다시 큐에 들어가게 하도록 합니다. W\&B가 run을 다시 큐에 들어가게 하는 시점과 `mark_preempting()`과의 상호작용에 대한 자세한 내용은 [Resume preemptible Sweeps runs](/ko/products/wandb/runs/resuming#resume-preemptible-sweeps-runs)를 참조하세요.

**스윕 상태:** 에이전트를 시작하기 전에 `wandb sweep entity/project/sweep_ID --resume`을 실행하여 스윕이 resume 모드에 있도록 하고 다시 큐에 들어간 run을 배포하도록 합니다.

**멀티 에이전트 조정:** 여러 에이전트가 동시에 실행될 때(예: SLURM array jobs) preempted된 동일한 run을 차지하려고 경쟁할 수 있습니다. 이는 알려진 제한 사항입니다. 이를 해결하려면 에이전트 시작을 지연시키거나 잠금과 같은 외부 조정 메커니즘을 사용하세요.

`wandb.agent()`를 호출해야 하는 프로세스가 하나뿐인 multi-GPU SLURM 작업의 경우 [How should I run sweeps on SLURM?](/ko/support/models/articles/how-should-i-run-sweeps-on-slurm)를 참조하세요.

<h2 id="wandb-sweep-cancel">
  `wandb sweep --cancel`
</h2>

이 섹션에서는 `--cancel` 명령이 시그널 및 자식 프로세스와 상호작용하는 방식을 설명합니다. 취소는 OS 시그널을 직접 보내는 것과 다르게 동작하기 때문입니다. 스윕은 W\&B API를 사용하여 취소하며 OS 시그널을 사용하지 않습니다. `wandb sweep --cancel entity/project/sweep_ID`와 같은 명령을 실행하세요. 서버가 에이전트에게 종료를 지시하고, 에이전트는 실행 중인 자식 프로세스를 종료한 후 중지합니다. 취소가 적용되기까지 짧은 지연(에이전트의 API 폴링 간격 정도)이 발생할 수 있습니다.

취소는 run에 `SIGKILL`을 전달합니다. 자식 프로세스는 사용자 정의 시그널 핸들러를 실행할 기회가 없습니다. Sweeps UI의 **Cancel** 컨트롤을 사용할 때도 동일하게 적용됩니다. 전체 스윕을 중지하고 취소됨으로 표시하려면 `--cancel`을 사용하세요. 현재 run을 정상적으로 종료하려면 run에 포착 가능한 시그널을 보내세요(또는 CLI 에이전트에서 `--forward-signals`를 사용하고 에이전트에 시그널을 보내세요). 스윕을 정상적으로 완료하려면 `--cancel` 대신 [`wandb sweep --stop`](/ko/products/wandb/sweeps/pause-resume-and-cancel-sweeps#stop-a-sweep)을 사용하세요.

일시 중지, 재개, 중지, 취소 옵션에 대한 자세한 내용은 [스윕 관리](/ko/products/wandb/sweeps/pause-resume-and-cancel-sweeps)를 참조하세요.

<h2 id="signals-to-the-agent-versus-signals-to-the-run">
  에이전트에 대한 시그널 versus run에 대한 시그널
</h2>

에이전트에 시그널을 보내는 것과 트레이닝 run에 시그널을 보내는 것의 차이를 이해하면 고아 프로세스와 예상치 못한 동작을 방지하는 데 도움이 됩니다. 에이전트 프로세스(자식 트레이닝 프로세스가 아님)에 시그널을 보내면 에이전트가 종료되는 동안 하위 프로세스가 고아 상태로 계속 실행될 수 있습니다. 고아 프로세스는 터미널에 계속 출력할 수 있으며, Enter 키를 누를 때까지 셸에 새 프롬프트가 표시되지 않을 수 있습니다.

`--forward-signals` 옵션과 함께 CLI 에이전트를 사용하지 않는 한, 에이전트를 중지해도 자식 트레이닝 프로세스가 중지된다는 보장은 없습니다.

에이전트가 종료되었는지 확인하려면 프롬프트 표시 여부에 의존하지 말고 `ps -p [AGENT-PID]` 또는 `pgrep -f "wandb agent"`와 같은 OS 명령을 사용하세요.

<h2 id="reference-mark_preempting-and-final-run-state">
  레퍼런스: `mark_preempting()`과 최종 run 상태
</h2>

다음 table은 `mark_preempting()`을 호출하는 시점과 프로세스의 종료 방식에 따라 run 상태가 어떻게 달라지는지 요약합니다. 트레이닝 프로그램을 하위 프로세스로 실행하는 [`wandb agent`](/ko/products/wandb/ref/cli/wandb-agent) CLI를 사용한다고 가정합니다.

| 시나리오 | `mark_preempting()` 호출 없음 | 시그널 핸들러가 `mark_preempting()`을 호출하고 0이 아닌 종료 코드로 종료 | `init()` 직후 항상 `mark_preempting()` 호출 |
| - | - | - | - |
| run이 종료 코드 0으로 정상 완료 | FINISHED | FINISHED | FINISHED |
| run이 0이 아닌 종료 코드로 실패 | FAILED | FAILED | PREEMPTED |
| run이 `SIGKILL` 수신 | 약 5분 후 CRASHED | 약 5분 후 CRASHED (처리 불가능) | 약 5분 후 PREEMPTED |
| run이 `SIGINT` 수신 | KILLED | PREEMPTED (`SIGINT` 핸들러가 있는 경우) | PREEMPTED |
| run이 다른 시그널 수신 (예: `SIGTERM` 또는 `SIGUSR1`) | 약 5분 후 CRASHED | PREEMPTED (해당 시그널의 핸들러가 있는 경우) | 약 5분 후 PREEMPTED |

시그널 핸들러 내부에서만 `mark_preempting()`을 호출하면 `SIGKILL`처럼 핸들러가 실행되지 않는 경우에는 대응할 수 없습니다.

`wandb.init()` 직후 항상 `mark_preempting()`을 호출하면 W\&B가 모든 실패를 선점으로 처리하여 버그나 잘못된 설정으로 인한 실패까지도 run을 반복해서 다시 큐에 들어가게 할 수 있습니다.

선점 시그널이 명확하게 정의된 환경에서는 일반적으로 `init()` 이후 무조건 호출하는 대신, `mark_preempting()`을 호출하고 0이 아닌 종료 코드로 종료하는 시그널 핸들러를 사용합니다.
