EQVPS
대시보드시작하기

VPS에서 cron과 systemd 타이머로 작업 예약하는 법

백업, 정리, 스크립트를 일정에 맞춰 실행하세요. 5분 만에 배우는 cron과 작업을 조용히 망가뜨리는 함정 세 가지, 그리고 로그, 중단 후 놓친 실행 보충, 정확한 시각을 위한 systemd 타이머. 아래 동작은 모두 새 Ubuntu 24.04 서버에서 검증했습니다.

서버는 여러분이 자는 동안 일할 때 제값을 합니다. 백업, 로그 로테이션, 인증서 갱신, 9시 보고서. Linux에는 이를 위한 스케줄러가 두 가지 있습니다. cron은 40년 된 도구이고 한 줄이면 됩니다. systemd 타이머는 작은 파일 두 개가 필요하지만, 그 대가로 로그, 중단 후 보충, 정확한 시각을 줍니다. 이 가이드는 Ubuntu 24.04에서 둘 다 다루며, 모든 동작을 당사 이미지로 만든 새 서버에서 확인했습니다. cron 작업이 한마디 없이 실패하게 만드는 실수 세 가지도 포함합니다.

sudo 권한이 있는 사용자가 필요합니다. Ubuntu 설정 가이드에서 만들 수 있습니다.

1. 5분 만에 배우는 cron

cron은 당사 Ubuntu와 Debian 이미지에 설치되어 실행 중입니다. 사용자마다 작업 표가 있으며, 자신의 표는 다음으로 편집합니다.

crontab -e

각 줄은 시간 필드 다섯 개와 명령 하나입니다.

# min hour day month weekday  command
  30  3    *   *     *        /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  */5 *    *   *     *        /usr/local/bin/check.sh
  0   9    *   *     1-5      /usr/local/bin/report.sh
  @reboot                     /usr/local/bin/start-tunnel.sh

즉, 매일 밤 03:30, 5분마다, 평일 09:00, 그리고 부팅할 때마다 한 번입니다. */5는 다섯 번째 값마다, 1-5는 범위, 1,15는 목록입니다. 서버의 시간대가 적용되며 당사 이미지에서는 UTC입니다. 호스트 이름과 시간대 가이드로 한 번 설정하면 cron이 따릅니다.

무엇이 실행되었는지는 journalctl -u cron으로 확인합니다. 실행마다 사용자와 명령이 기록되며, 세 가지 함정도 거기서 드러납니다.

함정 1: 출력이 사라진다. cron은 작업 출력을 사용자에게 메일로 보냅니다. 당사 이미지에는 메일 서버가 없어서 journal에 (CRON) info (No MTA installed, discarding output)가 남고 출력은 사라졌습니다. 첫 번째 예처럼 >> /var/log/backup.log 2>&1로 직접 리디렉션하세요.

함정 2: 퍼센트 기호. crontab에서 %는 명령을 끝내고 나머지를 표준 입력으로 바꿉니다. date +%F > /tmp/today를 예약했더니 journal에는 cron이 실제로 실행한 CMD (date +)가 남았습니다. 파일은 생기지 않았고 오류도 전달되지 않았습니다. \%로 쓰거나 명령을 스크립트로 만들어 스크립트를 예약하세요.

함정 3: 다른 환경. cron은 작업을 bash가 아니라 /bin/sh로, 홈 디렉터리에서, 최소한의 환경으로 실행합니다. Ubuntu 24.04의 PATH는 평판보다 나았습니다. root든 일반 사용자든 /usr/local/bin과 /snap/bin을 포함한 전체 시스템 경로였습니다. 여전히 빠진 것은 홈 디렉터리 아래의 모든 것으로, ~/.local/bin, nvm의 node, pipx, cargo입니다. 작업에는 전체 경로를 쓰거나 줄을 . $HOME/.profile;로 시작하세요.

사용자가 아니라 시스템에 속한 작업은 사용자 필드를 하나 더한 파일로 /etc/cron.d/에 둡니다.

30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Ubuntu는 /etc/cron.hourly, cron.daily, cron.weekly, cron.monthly 안의 모든 것도 실행합니다. 매시 17분, 06:25, 일요일 06:47, 매월 1일 06:52입니다. 실행 가능한 스크립트를 넣으면 나머지와 함께 실행됩니다.

2. systemd 타이머

타이머는 두 유닛으로 이루어집니다. 일을 하는 서비스와 언제 할지 정하는 타이머입니다. 먼저 스크립트를 만들고 그다음 유닛을 만듭니다.

sudo nano /usr/local/bin/backup.sh
sudo chmod +x /usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night at 03:30

[Timer]
OnCalendar=*-*-* 03:30
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

활성화할 것은 서비스가 아니라 타이머입니다.

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers backup.timer

list-timers는 다음 실행, 남은 시간, 마지막 실행을 보여 줍니다. 로그는 따로 설정할 필요가 없습니다. journalctl -u backup.service에 시작, 종료, 출력의 모든 줄이 타임스탬프와 함께 남습니다. 작업을 지금 바로 실행하려면 sudo systemctl start backup.service를 쓰세요. 일정을 믿기 전에 스크립트를 테스트할 때도 이렇게 합니다.

캘린더 문법. OnCalendar는 요일 연-월-일 시:분:초로 읽으며 *는 아무 값이나 뜻합니다. 쓰기 전에 표현식을 확인하세요.

systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2

정규화된 형태와 다음 날짜를 출력합니다. 확인한 표현식 몇 가지입니다.

OnCalendar의미
*-*-* 03:30매일 03:30
Mon..Fri *-*-* 09:00평일 09:00
*:0/1515분마다
weekly월요일 00:00
*-*-01 04:00매월 1일 04:00
*-*-* 03:00 Europe/Kyiv키이우 시간 03:00, 테스트에서 01:00 UTC에 실행

알아 둘 설정 세 가지. Persistent=true는 마지막 실행을 디스크에 기록하고, 서버가 꺼져 있거나 타이머가 멈춰 있던 동안 놓친 실행을 보충합니다. 2분마다 도는 타이머를 멈추고 예정된 실행이 지나기를 기다린 뒤 다시 켰더니, 놓친 실행이 같은 초 안에 시작되었습니다. Persistent가 없으면 다음 일정을 기다릴 뿐입니다.

놀라운 점은 AccuracySec입니다. systemd는 기본적으로 깨우는 횟수를 줄이려고 타이머를 1분 범위로 묶기 때문에, 첫 실행이 07:42:00이 아니라 07:42:18에 왔습니다. AccuracySec=1s로 하자 이후 실행은 07:46:00과 07:48:00에 초 단위로 정확히 맞았습니다. 분이 중요할 때 설정하고, 야간 백업이라면 기본값으로 두세요.

RandomizedDelaySec=10m는 일부러 반대로 합니다. 시작 시각을 일정 범위에 흩어, 서버 열 대가 같은 초에 같은 백업 저장소로 몰리지 않게 합니다.

한 번만 실행할 작업에는 파일이 필요 없습니다.

sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh

한 번 실행되고 사라지는 임시 타이머가 만들어지며, 그때까지 systemctl list-timers에 표시됩니다.

3. 무엇을 쓸까

cronsystemd 타이머
설정1줄파일 2개
로그출력을 리디렉션할 때만journalctl -u name.service
서버가 꺼져 있던 동안 놓친 실행사라짐Persistent=true면 부팅 시 실행
다음 시각에 작업이 아직 실행 중두 번째 복사본 시작첫 번째가 끝날 때까지 건너뜀
정확한 시각분 단위AccuracySec=1s로 초 단위
상태없음list-timers, systemctl status

1년 뒤에 봐도 알아볼 간단한 한 줄이라면 cron을 쓰세요. 실패를 알아야 하는 것, 겹치면 안 되는 것, 재부팅으로 놓쳐도 실행해야 하는 것에는 타이머를 쓰세요. 백업, 인증서 갱신, 데이터베이스 정리 같은 것입니다. cron 작업이 겹칠 수 있다면 flock -n /tmp/backup.lock /usr/local/bin/backup.sh로 감싸면 두 번째 복사본이 바로 종료됩니다.

두 스케줄러 모두 작업을 실행하고 끝납니다. 계속 실행되어야 하고 충돌 후 복구되어야 하는 프로그램에는 일정이 아니라 서비스가 필요합니다. systemd 서비스 만드는 법과 Windows라면 Windows Server에서 스크립트를 24시간 실행하는 법을 참고하세요. 첫 타이머로는 restic 백업 가이드의 타이머가 좋습니다. 자동 보안 업데이트는 Ubuntu에 이미 들어 있는 타이머 apt-daily-upgrade.timer로, 새 서버에서 도는 17개 중 하나입니다.

자주 묻는 질문

터미널에서는 되는데 cron에서는 안 되는 이유는?

흔한 이유는 세 가지입니다. cron은 작업을 bash가 아니라 sh로 실행하므로 bash 전용 문법은 실패합니다. nvm, pipx, cargo처럼 홈 디렉터리에 설치한 도구는 cron의 PATH에 없으니 전체 경로를 쓰세요. 그리고 명령 줄의 %는 거기서 명령을 끝냅니다. 당사 서버에서 date +%F는 date +가 되었습니다. \%로 쓰거나 명령을 스크립트로 옮기세요.

cron의 출력은 어디로 가나요?

기본적으로 이메일로 갑니다. 당사 이미지에는 메일 서버가 없어서 cron은 'No MTA installed, discarding output'을 기록하고 출력은 사라집니다. 작업에 >> /var/log/myjob.log 2>&1을 붙이거나, 출력이 자동으로 journal에 쌓이는 systemd 타이머로 바꾸세요.

cron 대비 systemd 타이머의 장점은?

journalctl의 로그, 아직 실행 중인 작업의 두 번째 복사본을 띄우지 않음, Persistent=true로 서버가 꺼져 있던 동안 놓친 실행 보충, 그리고 다음과 마지막 실행을 보여 주는 상태 줄입니다. 간결함은 cron이 이깁니다. 파일 두 개 대신 한 줄입니다.

타이머가 18초 늦게 실행됐어요. 왜죠?

systemd는 기본적으로 시스템을 깨우는 횟수를 줄이려고 타이머를 1분 범위로 묶습니다. 이것이 AccuracySec 설정입니다. 2분마다 도는 타이머가 07:42:00이 아니라 07:42:18에 실행되었습니다. AccuracySec=1s로 하자 정확히 07:46:00에 실행되었습니다. 정확한 분이 중요할 때 설정하세요.

cron과 타이머는 어떤 시간대를 쓰나요?

서버의 시간대이며 당사 이미지에서는 UTC입니다. timedatectl로 한 번 설정하면(호스트 이름과 시간대 가이드 참고) cron과 타이머가 모두 따릅니다. 타이머는 자체 시간대도 가질 수 있습니다. OnCalendar=*-*-* 03:00 Europe/Kyiv는 테스트에서 01:00 UTC에 실행되었습니다.

댓글

아직 댓글이 없습니다. 첫 번째가 되세요.

댓글 남기기

댓글은 표시되기 전에 검토됩니다.