티스토리 뷰

카테고리 없음

cron에서만 실패하는 쉘 스크립트의 환경변수와 실행 경로 확인

생각방 2026. 10. 31. 13:37

목차


    터미널에서는 정상 실행되는 집계 스크립트가 cron에 등록한 뒤 결과 파일을 만들지 않는다고 가정해 보겠습니다. 같은 파일을 실행해도 계정, 명령어 검색 경로, 작업 디렉터리와 환경변수가 다르면 결과가 달라질 수 있습니다. cron에 등록해놓고 실행되는 쉘이 어떤것인지 확인하지 않는다면 설정한 cron은 정상적으로 동작하지 않아 다음날 작업 확인시 운영자를 당황스럽게 할 수 있습니다. 이 글은 Linux의 Cronie 계열 사용자 crontab과 Bash 스크립트를 예시로, 대화형 실행과 예약 실행의 차이를 좁혀 가는 순서를 설명합니다.

    cron 등록과 시간설정 누락에 따른 작업 미처리

    예약 실행이 시작됐는지와 실행 계정을 먼저 확인

    결과 파일이 없다는 사실만으로 스크립트가 실패했다고 단정할 수는 없습니다.
    cron의 예약 자체가 실행되지 않았는지, 실행 후 내부 명령이 실패했는지부터 구분합니다. 대상 계정에서 crontab -l로 등록 내용을 확인하고, 배포판의 cron 또는 crond 서비스 기록에서 해당 시각의 실행 흔적을 찾습니다. 서비스 기록만으로 업무 처리 성공까지 확인되지는 않습니다.

    아래는 집계 전용 계정에 등록하는 사용자 crontab 예시입니다. /srv/report/log 디렉터리는 미리 만들고 해당 계정에 쓰기 권한을 부여해야 합니다. 리디렉션할 로그 파일을 열지 못하면 스크립트 실행 전에 실패할 수 있습니다.

    0 2 * * * /bin/bash /srv/report/run.sh >> /srv/report/log/cron.log 2>&1

    Cronie 매뉴얼에 따르면 사용자 crontab의 작업은 그 crontab 소유자 계정으로 실행됩니다. /etc/crontab이나 /etc/cron.d 파일은 실행 사용자 필드가 추가되므로 위 형식을 그대로 복사하지 않습니다. root로 수동 실행한 성공 결과를 일반 계정의 예약 실행과 비교하면 환경 차이에 권한 차이까지 섞입니다.

    요약: 예약 등록, 실행 흔적, 실행 계정과 오류 출력을 확보한 뒤 스크립트 내부를 확인합니다.

    PATH와 작업 디렉터리는 같은 경로가 아님

    PATH는 이름만 입력한 외부 명령어를 찾을 디렉터리 목록입니다. 작업 디렉터리는 config.ini나 ./data/input.csv 같은 상대 경로를 해석하는 기준입니다. 스크립트를 /srv/report/run.sh처럼 절대 경로로 호출해도 내부 상대 경로의 기준이 자동으로 /srv/report가 되지는 않습니다.

    점검할 때는 다음 코드를 임시로 스크립트 앞부분에 넣어 실제 예약 실행의 출력을 확인합니다. 변수 전체를 덤프하기보다 필요한 값만 기록하고, 확인이 끝나면 진단 출력을 정리합니다. command -v 결과가 없으면 해당 환경에서 python3를 찾지 못한 것입니다.

    /usr/bin/id
    pwd
    printf 'PATH=%s\nHOME=%s\n' "$PATH" "$HOME"
    command -v python3
    관찰한 출력 먼저 확인할 차이 조정 방향
    command not found PATH와 실제 설치 위치 확인한 실행 파일의 절대 경로 사용
    설정·입력 파일을 찾지 못함 pwd와 내부 상대 경로 절대 경로 또는 명시적인 cd 사용
    Permission denied 실행 계정과 파일·디렉터리 권한 필요한 접근 권한과 마운트 옵션 확인

    오류 문구는 원인 후보를 좁히는 단서입니다. 예를 들어 파일을 찾지 못한다는 오류는 경로 외에도 파일 생성 시점이나 마운트 상태와 관련될 수 있으므로 실제 대상의 존재 여부까지 대조합니다.

    요약: 명령어를 못 찾는 문제와 입력 파일을 못 찾는 문제를 분리해야 수정할 경로가 명확해집니다.
    셸 설정 대신 필요한 작업 확인

    로그인 셸의 설정 대신 cron 설정에 작업에 필요한 조건 명시

    Bash 시작 파일 문서는 대화형 로그인 셸과 일반적인 비대화형 실행이 읽는 파일을 구분합니다. 보통의 비대화형 Bash 스크립트는 로그인할 때의 설정을 그대로 재현하지 않으며, BASH_ENV가 설정되어 있으면 지정된 파일을 읽습니다. 따라서 로그인 후 export한 변수나 초기화 파일이 제공한 실행 경로를 예약 작업도 가진다고 가정하지 않습니다.

    다음은 실행 조건을 명시하는 예시입니다. 파일 경로와 Python 위치는 서버에서 확인한 값으로 바꿔야 합니다. 가상환경을 쓴다면 해당 가상환경의 Python 절대 경로를 지정합니다.

    #!/bin/bash
    export PATH=/usr/bin:/bin
    export REPORT_CONFIG=/srv/report/config/report.ini
    cd /srv/report || exit 1
    exec /usr/bin/python3 /srv/report/report.py --config "$REPORT_CONFIG"

    REPORT_CONFIG는 예시 애플리케이션에 전달하는 변수이며 cron의 내장 설정이 아닙니다. 별도 설정 파일을 읽는다면 용도를 분리하고 접근 권한을 제한합니다. .bashrc 전체를 무조건 불러오기보다 작업에 필요한 값만 지정하는 편이 숨은 의존성을 줄입니다. Bash 문법이 필요한 파일을 sh run.sh로 호출하면 첫 줄의 #!/bin/bash가 인터프리터를 선택해 주지 못하므로 호출 명령도 함께 확인합니다.

    요약: 인터프리터, 실행 파일, 작업 디렉터리와 필요한 변수를 작업 정의에 드러냅니다.
    통제 환경에서 재현과 실제 예약 결과 확인

    제한된 환경에서 재현한 뒤 실제 예약 결과를 검증

    GNU env 문서의 -i 옵션은 상속한 환경을 비우고 지정한 값으로 명령을 실행합니다. 아래 명령은 예약 작업과 같은 계정으로 전환한 뒤, 업무 영향이 없는 테스트 대상에서 실행하는 비교용 예시입니다. /home/report는 해당 계정의 실제 홈 경로로 바꿉니다.

    /usr/bin/env -i HOME=/home/report LOGNAME=report \
      PATH=/usr/bin:/bin /bin/bash -c \
      'cd / && exec /bin/bash /srv/report/run.sh'

    이 테스트는 로그인 환경에 대한 의존성을 드러내지만 cron의 완전한 복제는 아닙니다. PAM, SELinux 정책, 마운트와 실제 실행 시각의 조건은 별도로 남습니다. 제한된 환경에서도 정상이라면 실제 예약 시각의 로그와 출력 파일 내용·수정 시각을 확인하고, 필요하면 계정 권한이나 외부 시스템 준비 상태로 조사 범위를 넓힙니다.

    요약: 환경을 줄인 비교 실행으로 후보를 좁히되 최종 판정은 실제 예약 작업의 결과로 내립니다.

    자주 묻는 질문

    crontab의 SHELL을 Bash로 바꾸면 모두 해결됩니까?

    SHELL은 crontab 명령 부분을 해석할 셸을 정하는 설정입니다. 필요한 환경변수나 작업 디렉터리까지 자동으로 맞춰 주지는 않습니다. 스크립트를 어떤 인터프리터로 호출하는지도 함께 확인해야 합니다.

    날짜 명령을 crontab에 직접 넣어도 됩니까?

    Cronie의 crontab 명령 부분에서는 이스케이프하지 않은 %에 특별한 의미가 있습니다. 날짜 형식처럼 %가 필요한 로직은 스크립트 안에 두면 crontab 단계의 해석과 분리하기 쉽습니다.

    오류 로그가 비어 있으면 성공한 것입니까?

    출력이 없거나 로그 파일을 열지 못했을 수도 있습니다. 예약 실행 기록, 종료 상태를 남기는 방식과 업무 결과물을 함께 확인해야 합니다.

    결론

    cron에서만 실패할 때는 실행 계정을 맞춘 뒤 PATH, 작업 디렉터리, 인터프리터와 필요한 변수를 차례로 대조합니다. 제 판단으로는 로그인 환경을 통째로 복원하는 것보다 작업이 요구하는 조건을 명시하는 편이 변경 영향과 장애 원인을 추적하기 좋습니다. 수정 후에는 실제 예약 시각의 오류 출력과 업무 결과물을 함께 확인해야 합니다.

    참고자료