손으로는 되는데 cron으로는 실패한다면, 십중팔구 “환경”이 다르다
같은 스크립트가 셸에서는 되고 cron에서는 죽는다면 코드가 아니라 실행 환경이 다르기 때문이다. cron은 로그인 셸이 아니고, 환경변수도 최소만 준다. 순서대로 흔한 원인을 지운다.
1) PATH가 거의 비어 있다
cron의 기본 PATH는 대개 /usr/bin:/bin뿐이다. 스크립트가 docker, kubectl, aws 같은 명령을 PATH로 찾으면 “command not found”로 죽는다. 절대경로를 쓰거나 crontab 맨 위에 PATH를 명시한다.
PATH=/usr/local/bin:/usr/bin:/bin
*/5 * * * * /usr/local/bin/mytool run >> /var/log/mytool.log 2>&1
2) 로그인 셸이 아니라 rc 파일이 로드되지 않는다
~/.bashrc, ~/.profile, nvm·rbenv·pyenv 초기화가 cron에서는 실행되지 않는다. 버전 매니저나 특정 환경변수에 의존한다면 스크립트 안에서 명시적으로 source 하거나 절대경로 바이너리를 쓴다.
3) %는 개행으로 해석된다
crontab에서 %는 특수문자(명령의 개행)라, date +%Y-%m-%d가 그대로 들어가면 명령이 잘린다. date +\%Y-\%m-\%d처럼 이스케이프하거나 스크립트 파일로 감싼다.
4) 작업 디렉터리가 홈이다
cron의 cwd는 사용자 홈이다. 상대경로로 파일을 열면 못 찾는다. 스크립트 앞에서 cd로 기준 경로를 명시한다.
5) 실패 원인이 어디에도 안 보인다
가장 먼저 해야 할 건 사실 이것이다 — stdout·stderr를 파일로 남긴다. >> /path/log 2>&1을 붙이지 않으면 실패 메시지가 로컬 메일함(MAILTO)으로만 가서 “조용히 실패”처럼 보인다.
빈 환경으로 재현하기
env -i /bin/sh -c '/path/to/job.sh' # cron 과 비슷한 빈 환경에서 그대로 재현
이 한 줄로 대부분의 “cron에서만 실패”를 셸에서 즉시 재현할 수 있다. 로그 리다이렉트 → PATH/env → % 이스케이프 → cd 순으로 지우면 끝난다.
빠른 진단 체크리스트
- 먼저
>> log 2>&1로 stdout·stderr를 파일에 남긴다 - cron의 PATH는 최소라 절대경로나 상단 PATH= 지정이 필요하다
- rc 파일(.bashrc·nvm 등)이 로드되지 않음을 감안한다
- 필요한 환경·버전매니저는 스크립트에서 명시적으로 source한다
- crontab의 %는 개행이므로
\\%로 이스케이프한다 - cwd가 홈이므로 상대경로 대신 cd로 기준 경로를 잡는다
env -i /bin/sh -c ...로 빈 환경에서 재현한다- MAILTO로만 가던 실패 원인을 로그로 끌어낸다