리눅스 자동화는 명령을 한 줄로 줄이는 작업이 아니라, 실패를 감지하고 중복 실행을 막으며 누가 언제 실행했는지 확인 가능하게 만드는 일입니다. 먼저 스크립트를 수동으로 반복 실행해도 안전한 형태로 만든 뒤 예약 작업에 연결합니다.
[목차]
안전한 Bash 기본 골격
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
readonly lock_file="/run/lock/report-job.lock"
readonly output_dir="/srv/reports"
exec 9>"$lock_file"
flock -n 9 || {
echo "another run is active" >&2
exit 75
}
mkdir -p "$output_dir"
tmp="$(mktemp "$output_dir/.report.XXXXXX")"
trap 'rm -f "$tmp"' EXIT
generate-report > "$tmp"
test -s "$tmp"
mv "$tmp" "$output_dir/latest.txt"
trap - EXIT
set -Eeu는 오류와 미정의 변수 누락을 빨리 드러냅니다.pipefail은 파이프 중간 명령의 실패도 전달합니다.flock은 이전 실행이 끝나기 전에 새 작업이 겹치는 것을 막습니다.- 임시 파일을 완성한 뒤
mv하면 불완전한 결과 노출을 줄일 수 있습니다.
systemd service 만들기
# /etc/systemd/system/report-job.service
[Unit]
Description=Generate daily report
[Service]
Type=oneshot
User=reporter
Group=reporter
ExecStart=/usr/local/sbin/report-job
NoNewPrivileges=true
PrivateTmp=true
가능하면 root가 아닌 전용 계정으로 실행하고 필요한 디렉터리에만 쓰기 권한을 줍니다.
systemd timer로 예약하기
# /etc/systemd/system/report-job.timer
[Unit]
Description=Run report job every day
[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=10m
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now report-job.timer
systemctl list-timers report-job.timer
sudo systemctl start report-job.service
journalctl -u report-job.service --since today
Persistent=true는 예약 시간에 시스템이 꺼져 있었다면 다음 부팅 후 놓친 실행을 보완합니다. 여러 서버가 같은 시각에 작업하지 않도록 RandomizedDelaySec를 사용할 수 있습니다.
cron을 쓸 때 주의할 점
cron 환경은 로그인 셸보다 PATH와 환경 변수가 제한적입니다. 실행 파일은 절대 경로를 사용하고 출력과 오류를 로그 또는 모니터링 시스템으로 보냅니다. 비밀번호를 crontab에 직접 넣지 마세요.
운영 전 체크리스트
- 같은 작업을 두 번 실행해도 데이터가 깨지지 않는가?
- 네트워크 오류와 디스크 부족 시 종료 코드가 0이 아닌가?
- 실패 알림과 마지막 성공 시간을 확인할 수 있는가?
- 로그에 비밀번호와 토큰이 출력되지 않는가?
- 삭제 명령은 대상 경로가 비어 있지 않은지 검증하는가?
공식 참고자료
관련 글
내용 검토 및 업데이트: 2026년 8월 27일