Skip to content
isdnetworks
Go back

다시 돌렸더니 두 벌이 등록됐다

설치 스크립트를 다시 돌렸다. 옵션 값 하나를 바꾸려는 것이었다.

며칠 뒤 수집이 두 배로 들어왔다.

Table of contents

Open Table of contents

예약이 두 개였다

예약 목록을 열어 봤다.

$ crontab -l
*/5 * * * * /opt/collect/run.sh --offset 0
*/5 * * * * /opt/collect/run.sh --offset 15

두 줄이 있었다. 처음 돌렸을 때 하나가 들어가고 다시 돌렸을 때 하나가 더 들어간 것이다.

--offset 값이 달라 run.sh 가 5분마다 두 번 돌았다. 옵션만 바뀔 줄 알았던 것이 항목을 하나 더 만든 셈이다.

원인 — 더하기만 하는 등록

설치 스크립트의 해당 줄을 봤다.

(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/collect/run.sh --offset $OFFSET") | crontab -

crontab -l 로 기존 목록을 뽑고 거기에 한 줄을 덧붙여 다시 넣는다. 지우는 단계가 없다.

한 번 돌리는 것을 전제로 쓴 코드였다. 실제로는 OFFSET 을 바꿀 때마다 돌리게 되고 그때마다 줄이 하나씩 는다.

지우고 넣게 바꿨다

기존 것을 지운 뒤 넣게 고쳤다.

MARK="# managed-by-collect-installer"

# 기존 항목 제거 후 재등록
( crontab -l 2>/dev/null | grep -v "$MARK"
  echo "*/5 * * * * /opt/collect/run.sh --offset $OFFSET $MARK"
) | crontab -

MARK 가 붙은 줄만 grep -v 로 걸러 내고 새 줄을 넣는다. 몇 번을 돌려도 그 표시가 붙은 줄은 하나다.

표시를 붙인 것은 남의 항목을 안 건드리기 위해서다. 같은 crontab 에 다른 작업이 함께 있는데 명령 이름만으로 지우면 손으로 넣은 줄까지 날아간다.

검증 — 등록된 개수를 센다

등록한 다음에 개수를 셌다.

CNT=$(crontab -l 2>/dev/null | grep -c "$MARK")
if [ "$CNT" -ne 1 ]; then
  echo "예약 등록 결과가 ${CNT}개입니다 (기대: 1)"
  crontab -l | grep "$MARK"
  exit 1
fi
echo "예약 등록 확인: 1개"

grep -c 로 센 값이 1이 아니면 목록을 보여 주고 exit 1 한다. 등록 명령이 성공했다는 사실이 아니라 지금 몇 개가 있는지를 본다.

이번 일이 딱 그 차이였는데 crontab - 은 두 번 다 성공했고 그래서 아무도 이상을 몰랐다.

같은 문제가 다른 데도 있었다

여러 번 돌리면 늘어나는 자리를 찾아봤다. 설정 파일에 줄을 덧붙이는 것이 먼저 걸렸다.

# 나쁨
echo "PATH=/opt/bin:\$PATH" >> ~/.bashrc

# 나음
grep -q "^PATH=/opt/bin" ~/.bashrc || echo "PATH=/opt/bin:\$PATH" >> ~/.bashrc

>> 로 덧붙이기만 하면 같은 설정이 여러 번 들어간다. 대개 마지막 것이 이기므로 동작은 맞는데 파일이 지저분해지고 읽는 사람이 헷갈린다.

systemd 유닛 등록도 같았고 iptables 규칙도 같았다. 같은 규칙이 여러 벌 들어가 목록만 길어져 있었다.

셋 다 있으면 안 넣거나 지우고 넣는 쪽으로 고쳤다.

판단 기준 — 두 방식의 차이

두 방식이 같은 것이 아니라는 것을 고치면서 알았다.

[있으면 안 넣는다]  값이 바뀌어도 안 바뀐다 — 옛 값이 남는다
[지우고 넣는다]     값이 갱신된다 — 손으로 고친 것도 덮인다

OFFSET 처럼 인자로 들어오는 값은 지우고 넣는 쪽으로 했는데 바꾸려고 다시 돌리는 것이라 안 바뀌면 목적을 못 이룬다.

~/.bashrc 처럼 사람이 고치는 것은 있으면 안 넣는 쪽으로 뒀다. 다시 돌렸다고 손으로 맞춰 둔 값이 날아가면 안 된다.

변경 내용 — 실행 전 상태 출력

돌리기 전에 지금 무엇이 있고 무엇이 들어갈지를 보여 주게 했다.

echo "현재 등록된 항목:"
crontab -l 2>/dev/null | grep "$MARK" || echo "  (없음)"
echo ""
echo "새로 등록할 항목:"
echo "  */5 * * * * /opt/collect/run.sh --offset $OFFSET"
echo ""
printf "계속하시겠습니까? (yes): "
read ans
[ "$ans" = "yes" ] || exit 1

현재 항목과 새 항목을 나란히 찍고 readyes 를 받아야 진행하므로 잘못된 OFFSET 을 넣은 것도 그 자리에서 보인다.

사전 준비 — 제거 스크립트

설치만 있고 제거가 없었다. 지우려면 사람이 crontab -e 로 손수 찾아 지워야 했다.

#!/bin/sh
crontab -l 2>/dev/null | grep -v "$MARK" | crontab -
echo "예약 제거 완료"

CNT=$(crontab -l 2>/dev/null | grep -c "$MARK")
[ "$CNT" -eq 0 ] && echo "확인: 0개" || { echo "아직 ${CNT}개 남음"; exit 1; }

제거 쪽에도 grep -c 확인을 넣어 0개가 아니면 실패시킨다. 설치와 제거가 짝이면 번갈아 돌려 결과가 같은지 볼 수 있다.

여러 번 돌려서 확인했다

고친 뒤에 세 번 연속으로 돌렸다.

1회차: 예약 등록 확인: 1개
2회차: 예약 등록 확인: 1개
3회차: 예약 등록 확인: 1개

몇 번을 돌려도 하나다. 한 번 돌려서 되는 것과 여러 번 돌려도 되는 것은 다른 조건이다.

설치 스크립트는 뒤쪽이어야 하는데 값을 바꾸거나 중간에 실패해 다시 돌리는 일이 반드시 생기기 때문이다.

정리


Share this post on:

Previous Post
특허 둘이 같은 폴더에 있었다
Next Post
시험 계획이 코드보다 앞인 이유