Skip to content
isdnetworks
Go back

손으로 돌리면 되는데 예약으로는 안 됐다

리워드 정산 스크립트를 새벽에 돌리게 등록했는데 아침에 확인하니 결과가 없었다. 터미널에서 같은 명령을 치면 잘 되고 등록한 것만 안 된다.

Table of contents

Open Table of contents

오류를 어디서 보나

먼저 오류를 찾을 자리가 없었다. crontab 줄에 출력 경로가 없어서 실행이 됐는지도 몰랐다.

0 4 * * * /home/deploy/bin/settle.sh >> /home/deploy/log/settle.log 2>&1

출력을 파일로 받게 바꿨다. 뒤의 2>&1 이 중요하다. 이게 없으면 stderr 가 빠져서 stdout 만 쌓인다. 실패해도 로그가 비고, 빈 로그는 안 돌았다는 것과 구분이 안 된다.

다음 날 로그에 자바를 못 찾는다는 줄이 남았다.

환경 변수가 다르다

로그인해서 치면 되는 이유가 여기 있었다. 로그인 셸은 /etc/profile~/.bash_profile 을 읽고 거기서 PATH 에 JDK 가 붙는다. cron 은 비대화형·비로그인 셸이라 그 파일들을 읽지 않는다.

실제 값을 보려고 env > /tmp/cronenv.txt 를 한 줄 걸어 뽑아 봤다. cron 이 넣어 주는 것은 HOME·LOGNAME·SHELL=/bin/sh·PATH=/usr/bin:/bin 넷뿐이었다. /usr/local/bin 도 없다.

밖에서 넣어 주기를 기대하지 않고 스크립트가 직접 정하게 했다.

JAVA_HOME=/usr/local/jdk1.6.0_25
PATH=$JAVA_HOME/bin:/usr/bin:/bin
export JAVA_HOME PATH

/usr/local/jdk1.6.0_25/bin/java 처럼 절대 경로로 쓰는 방법도 있다. 앞쪽이 읽기 편하고 뒤쪽이 확실해서, 스크립트에 명령이 여럿이면 환경 변수를 정하고 하나뿐이면 절대 경로로 썼다.

디렉터리도 권한도 달랐다

경로를 고치니 다음 오류가 나왔다. 실행할 jar 를 못 찾는다는 것이다. 터미널에서는 그 디렉터리로 들어가 쳤는데 cron 은 HOME 에서 시작한다.

cd /home/deploy/settle || exit 1

뒤의 || exit 1 이 필요했다. 없으면 HOME 에서 나머지 명령이 다 돈다.

그다음은 권한이었다. 로그 파일을 못 만든다고 났다. 로그인해서 칠 때는 sudo 를 붙였고 cron 은 등록한 계정으로 돈다. chown 으로 로그 디렉터리 소유자를 그 계정으로 바꿨다. sudo 가 필요한 명령은 애초에 이 방식에 안 맞는 것 같다.

겹쳐 도는 것과 되돌리기

정산이 오래 걸리는 날 다음 실행이 들어왔다. 같은 데이터를 두 번 정산해 MySQL 에 중복 행이 생겼다. 잠금 파일로 막았다.

LOCK=/tmp/settle.lock
if [ -f "$LOCK" ]; then exit 0; fi
echo "$$ $(date)" > "$LOCK"
trap 'rm -f "$LOCK"' EXIT

trap 으로 걸어 두면 중간에 죽어도 지워진다. 죽는 방식에 따라 안 지워질 수 있어 파일에 PID 와 시각을 적어 뒀다. 오래된 잠금이면 ps 로 그 번호가 살아 있는지 보고 지울지 정한다.

등록만 하고 다음 날을 기다리면 하루가 그냥 간다. 간격을 2분으로 줄여 한 번 돌려 보고 로그를 확인한 뒤 원래 시각으로 되돌렸다. 시험용 줄을 안 지우면 그게 계속 도니 되돌리는 것까지가 한 묶음이다.

정리


Share this post on:

Previous Post
임시 디렉터리가 비워져 있었다
Next Post
화면 파일 안의 조회 코드