워크플로 실행 디버깅 기술 (그리고 문제 해결!)

몇 가지 초기 지혜의 말

복잡한 컴퓨터 시스템을 디버깅하는 것은 예술에 가깝습니다. 체계화하기가 쉽지 않기 때문입니다. 디버깅에는 여러 지침과 규칙이 있지만, 모든 상황에서 작동이 보장되는 단일 절차나 접근 방식은 없습니다.

이 장에서는 Snakemake 워크플로를 디버깅하는 방법에 중점을 둡니다. 이 장을 읽고 있다면 무언가를 작동시키기 위해 열심히 노력하고 있을 가능성이 큽니다. 아마도 절박한 심정으로 이 문장들을 읽고 계실지도 모르겠네요.

지금까지의 Snakemake 경험을 바탕으로 디버깅에 대해 드릴 수 있는 가장 유용한 조언은 다음과 같습니다.

첫째, 워크플로를 가능한 한 단순화하여 빠르게 실행할 수 있도록 합니다. 예를 들어 샘플 수를 1~2개로 줄이고, 입력 파일을 하위 샘플링(subsampling)하여 크기를 작게 만듭니다. 이렇게 하면 실행 속도가 빨라져서 수정과 테스트 사이의 피드백 루프를 줄일 수 있습니다.

둘째, 한 번에 하나의 규칙에 집중합니다. 원하는 대로 작동하지 않는 규칙을 찾을 때까지 각 규칙을 하나씩 단계별로 실행해 보세요. 그런 다음 해당 규칙을 수정하는 데 집중합니다. 이렇게 하면 Snakemake 규칙들이 연결되는 경로를 점점 더 견고하게 다질 수 있습니다.

셋째, 실행 중인 명령을 항상 인쇄하고(-p 사용), Snakemake 출력에 나타나는 와일드카드를 주의 깊게 검사합니다. 실행되는 명령어와 와일드카드 값이 모두 예상한 대로인지 확인하십시오. 예상과 다른 첫 번째 지점이 바로 수정해야 할 규칙입니다.

Snakemake 디버깅의 세 단계

Snakemake 워크플로를 만들거나 수정할 때 접하게 되는 일반적인 오류는 크게 세 단계로 나뉩니다.

첫째, 구문 오류(Syntax Errors)입니다. 잘못된 들여쓰기, 공백, 닫히지 않은 따옴표 등으로 인해 발생하며, Snakemake가 Snakefile 자체를 읽지 못하게 만듭니다.

둘째, 워크플로 구성 오류입니다. 규칙을 연결하거나 와일드카드를 채우는 데 문제가 있는 경우입니다. 이로 인해 Snakemake가 실행 계획(DAG)을 세우지 못하고 작업을 시작하지 못합니다.

셋째, 실행 오류(Execution Errors)입니다. 특정 규칙이나 작업이 실제로 실행되는 도중에 실패하는 경우입니다. 명령어 오타, 메모리 부족 등으로 인해 발생하며 워크플로가 중단됩니다.

이 장에서는 이러한 오류의 가장 일반적인 원인들을 다루고, 많은 오류를 피하거나 수정하기 위한 팁과 기술을 제공합니다.

구문 오류 해결 후: Snakemake 워크플로 디버깅 전략

Snakemake가 Snakefile을 읽지 못하게 하는 구문 오류를 해결한 에 사용할 수 있는 간단한 전략들입니다.

  1. -n / --dry-run으로 Snakemake를 실행하고 출력을 검사합니다. 워크플로가 규칙을 올바르게 연결하고 원하는 출력을 생성하려고 시도하는지 확인할 수 있습니다.
  2. -j / --cores 1로 실행합니다. 작업을 하나씩 순차적으로 실행하게 하여 출력이 뒤섞이지 않게 함으로써 문제 지점을 찾기 쉽게 만듭니다.
  3. -p / --printshellcmds로 실행합니다. 실제로 실행되는 셸 명령을 인쇄하여 오타나 경로 오류를 확인합니다.
  4. 명령줄에서 규칙 이름이나 파일 이름을 직접 지정하여 디버깅하려는 특정 규칙만 실행해 봅니다.

구문 오류 찾기, 수정 및 방지

공백 및 들여쓰기 오류

VS Code와 같은 좋은 편집기를 사용하십시오. Snakemake 또는 Python 확장 기능을 설치하여 들여쓰기와 공백 문제를 시각적으로 확인하는 것이 좋습니다.

따옴표와 줄 바꿈

복잡한 셸 명령어를 작성할 때는 세 겹 따옴표(""")를 사용하는 것이 좋습니다. 여러 줄에 걸친 명령을 작성하기 쉽고 따옴표 중복 문제를 피할 수 있습니다.

워크플로 구성 및 선언 디버깅

MissingInputException

새 워크플로를 작성할 때 가장 자주 마주치는 오류 중 하나입니다. 이는 Snakemake가 다음 세 가지 상황임을 알리는 것입니다. 1. 특정 파일이 필요하다는 것을 알아냈습니다. 2. 하지만 그 파일이 아직 존재하지 않습니다. 3. 그리고 그 파일을 만드는 방법(규칙)도 찾지 못했습니다.

예를 들어 다음과 같은 간단한 규칙이 있다고 합시다.

rule example:
    input:
        "file-does-not-exist"

이를 실행하면 다음과 같은 오류가 발생합니다.

MissingInputException in rule example in file ...:
Missing input files for rule example:
    affected files:
        file-does-not-exist

이 오류는 주로 데이터 경로가 틀렸거나(예: 누락된 FASTQ 파일), 해당 파일을 생성해야 하는 이전 규칙의 출력 패턴이 현재 규칙의 입력 패턴과 일치하지 않을 때 발생합니다.

MissingOutputException과 대기 시간

가끔 출력이 누락되었다며 --latency-wait를 늘리라는 제안이 포함된 오류 메시지를 볼 수 있습니다. 이는 실제로는 규칙이 예상한 출력 파일을 생성하지 못했을 때 발생하는 증상인 경우가 많습니다.

rule example:
    output:
        "file-does-not-exist"
    shell: """
        touch file-does-not-exist-typo
    """

위의 예에서 셸 명령어의 오타로 인해 output:에 선언된 이름과 다른 파일이 생성되었습니다. Snakemake는 다음과 같이 경고합니다.

Waiting at most 5 seconds for missing files.
MissingOutputException in rule example...
Job 0 completed successfully, but some output files are missing.

output: 블록은 Snakemake에게 무엇을 만들지 알려주는 선언일 뿐이며, 실제로 파일을 생성하는 책임은 shell: 블록에 있습니다. 셸 블록에서 파일 이름을 직접 타이핑하지 않고 {output} 변수를 사용하면 이러한 실수를 방지할 수 있습니다.

네트워크 파일 시스템을 사용하는 환경이 아니라면, 이 오류는 대기 시간의 문제가 아니라 거의 항상 셸 명령의 오타 문제입니다.

WorkflowError와 와일드카드

“Target rules may not contain wildcards”라는 오류는 와일드카드가 포함된 규칙을 바로 실행하려고 시도할 때 발생합니다.

rule example:
    input: "{name}.input"
    output: "{name}.output"
    shell: "cp {input} {output}"

Snakemake는 {name}에 무엇이 들어갈지 모른 채로는 이 규칙을 실행할 수 없습니다. 이 경우 명령줄에서 구체적인 파일 이름(예: XYZ.output)을 지정하거나, 상단에 all 규칙을 만들어 원하는 구체적인 파일들을 나열해야 합니다.

자세한 내용은 와일드카드로 규칙 일반화대상 지정 장을 참조하십시오.

실행 중인 워크플로 디버깅

오류 메시지 해석하기

Snakemake는 셸 명령 실행 중 오류가 발생하면 실패 알림 에 셸의 오류 출력을 표시합니다.

rule hello_fail:
    shell: """
        ls file-does-not-exist
    """

실행 결과:

ls: cannot access 'file-does-not-exist': No such file or directory
Error in rule hello_fail:
    ...
    shell: ls file-does-not-exist

직관적이지 않을 수 있지만, 실제 원인은 “Error in rule…” 메시지의 바로 윗부분을 보아야 합니다.

메모리 부족 오류: “Killed”

만약 다른 설명 없이 “killed”라는 메시지만 보인다면, 이는 보통 운영 체제가 프로세스가 너무 많은 메모리를 사용하고 있다고 판단하여 강제로 종료한 경우(OOM Killer)입니다.

이 문제를 해결하기 위한 전략은 다음과 같습니다. * 메모리가 더 많은 시스템으로 옮기거나, 슬럼(Slurm) 같은 환경이라면 더 많은 메모리를 요청합니다. * 자바(Java) 프로그램이나 특정 어셈블러처럼 메모리 사용량을 지정할 수 있는 프로그램이라면 명령줄 옵션을 통해 할당량을 조절합니다. * 데이터셋을 하위 분할하거나 하위 샘플링하여 처리할 데이터 크기를 줄입니다.