ЁЯПл The SchoolтА║ЁЯз╣ Shell scriptingтА║ЁЯФФ рдзрдбрд╛ 10 тАФ Scheduling рдЖрдгрд┐ automation: рдШрдВрдЯрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯФФ рдзрдбрд╛ 10 тАФ Scheduling рдЖрдгрд┐ automation: рдШрдВрдЯрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 10 ┬╖ рдкреБрдвреЗ: lesson-11-ci-and-containers


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ10. рддрд┐рдереЗ рдХреЛрдгреА рдирд╕рддрд╛рдирд╛ script рдЪрд╛рд▓рд╡рдгреЗ тАФ рдЖрдгрд┐ рддреА рджреЛрдирджрд╛, рдЧреБрдкрдЪреВрдк рдХрд┐рдВрд╡рд╛ рдкрд╕рд╛рд▒реНрдпрд╛рдд рдЪрд╛рд▓реВ рдирдпреЗ рдпрд╛рд╕рд╛рдареАрдЪреНрдпрд╛ рдЪрд╛рд░ рд╕рд╡рдпреА.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдкрд╣рд╛рдЯреЗ рджреЛрди рд╡рд╛рдЬрддрд╛ рдШрдВрдЯрд╛ рд╡рд╛рдЬрддреЗ рдЖрдгрд┐ рдХреЗрдЕрд░рдЯреЗрдХрд░ backup рдЪреЗ рдХрд╛рд░реНрдб рдЪрд╛рд▓рд╡рддреЛ. рдХреЛрдгреА рдкрд╛рд╣рдд рдирд╕рддреЗ, рдореНрд╣рдгреВрди рдХрд╛рд░реНрдбрдиреЗ рд╣реЗ рдХрд░рд╛рдпрд▓рд╛рдЪ рд╣рд╡реЗ: рдПрдХрд╛ рд╡реЗрд│реА рдПрдХрджрд╛рдЪ рдЪрд╛рд▓рдгреЗ (рджрд╛рд░рд╛рд╡рд░ рдХреБрд▓реВрдк), рдкреБрдиреНрд╣рд╛ рдЪрд╛рд▓рд╡рдгреЗ рд╕реБрд░рдХреНрд╖рд┐рдд рдЕрд╕рдгреЗ (idempotent тАФ рддреЛрдЪ рдирд┐рдХрд╛рд▓), рдХрд╛рдп рдЭрд╛рд▓реЗ рддреЗ рд▓рд┐рд╣реВрди рдареЗрд╡рдгреЗ (рд╡реЗрд│рд╛рдВрд╕рд╣ log), рдЖрдгрд┐ рдХрд╛рд╣реАрд╣реА рдЪреБрдХрд▓реЗ рддрд░ рдореЛрдареНрдпрд╛рдиреЗ fail рд╣реЛрдгреЗ (рдзрдбрд╛ 08 рдордзрд▓рд╛ strict mode). рдШрдВрдЯрд╛ рд╕реНрд╡рддрдГ рдмрд╣реБрддреЗрдХ machines рд╡рд░ cron рдЕрд╕рддреЗ, рдЖрдзреБрдирд┐рдХ Linux рд╡рд░ systemd timer, рдХрд┐рдВрд╡рд╛ CI рдордзрд▓реЗ schedule.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
  C["ЁЯФФ cron: 0 2 * * *"] --> B["backup.sh"]
  B --> L{"mkdir lock?"} -->|exists| D["die: already running"]
  L -->|ok| T["tar тЖТ verify тЖТ rotate"] --> U["rmdir lock (trap)"] --> G["log: kept 5"]

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

"backup рдорд╣рд┐рдиреЛрдирдорд╣рд┐рдиреЗ рдЪрд╛рд▓рд▓рд╛" рдЖрдгрд┐ "backup рдорд╣рд┐рдиреЛрдирдорд╣рд┐рдиреЗ рд░рд┐рдХрд╛рдорд╛ рд╣реЛрддрд╛" рдмрд╛рд╣реЗрд░реВрди рд╕рд╛рд░рдЦреЗрдЪ рджрд┐рд╕рддрд╛рдд. рдлрдХреНрдд log, verification рдЖрдгрд┐ рдЕрдзреВрдирдордзреВрди рдХреЗрд▓реЗрд▓реЗ restore рддреНрдпрд╛рдВрдЪреНрдпрд╛рддрд▓рд╛ рдлрд░рдХ рд╕рд╛рдВрдЧрддрд╛рдд.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

backup.sh: defaults рд╕рд╣ KEEP, DEST, SRC; mkdir "$LOCK" || die; trap 'rmdir "$LOCK"; log done' EXIT; timestamp рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдирд╛рд╡рд╛рдд tar -czf; tar -tzf тАж || die; rotation рд╕рд╛рдареА ls -1t | tail -n +$((KEEP+1)) | xargs rm; рдкреНрд░рддреНрдпреЗрдХ step рд╡реЗрд│реЗрд╕рд╣ log рдХреЗрд▓реЗрд▓реА.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

bin/backup.sh --dest /tmp/bk --keep 2; bin/backup.sh --dest /tmp/bk --keep 2; bin/backup.sh --dest /tmp/bk --keep 2
ls -1 /tmp/bk                                                     # only 2 remain
mkdir /tmp/bk/.lock; bin/backup.sh --dest /tmp/bk; echo "exit $?"; rmdir /tmp/bk/.lock   # a second run is refused
tar -tzf "$(ls -t /tmp/bk/*.tar.gz | head -1)"                     # what is inside the newest
echo "0 2 * * * PATH=/usr/bin:/bin $PWD/bin/backup.sh --dest /srv/backups >> /var/log/backup.log 2>&1" # what the crontab line looks like
rm -rf /tmp/bk

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

рддреАрди runs, рддреАрди verified рдУрд│реА, рдордЧ rotated out messages рдЖрдгрд┐ kept 2 backups; lock рдЕрд╕рд▓реЗрд▓рд╛ run another backup is running рдЫрд╛рдкрддреЛ рдЖрдгрд┐ 1 рдиреЗ exit рд╣реЛрддреЛ; archive рдордзреНрдпреЗ register.csv рджрд┐рд╕рддреЗ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреБрдореНрд╣реА рдЕрд╕рд╛ job schedule рдХрд░реВ рд╢рдХрддрд╛ рдЬреЛ рдПрдХрд╛ рд╡реЗрд│реА рдПрдХрджрд╛рдЪ рдЪрд╛рд▓рддреЛ, рдЖрдкрд▓рд╛ рдЗрддрд┐рд╣рд╛рд╕ рдорд░реНрдпрд╛рджрд┐рдд рдареЗрд╡рддреЛ, рдЖрдкрд▓реЗ output рд╕реНрд╡рддрдГ рд╕рд┐рджреНрдз рдХрд░рддреЛ, рдЖрдгрд┐ рддреНрдпрд╛рдиреЗ рдХрд╛рдп рдХреЗрд▓реЗ рддреЗ рд╕рд╛рдВрдЧрддреЛ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: log rotation, certificate renewal, рд░рд╛рддреНрд░реАрдЪреЗ reports, cache warmers тАФ ops рдЪреЗ рдЕрд░реНрдзреЗ рдХрд╛рдо scheduled scripts рдЕрд╕рддреЗ, рдЖрдгрд┐ рддреНрдпрд╛рдВрдкреИрдХреА рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рдпрд╛ рдЪрд╛рд░ рд╕рд╡рдпреА рд▓рд╛рдЧрддрд╛рдд.

тПня╕П рдкреБрдвреЗ

рдзрдбрд╛ 11 тАФ CI рдЖрдгрд┐ containers рдордзрд▓реНрдпрд╛ scripts: рдореЗрд▓рд░реВрдордордзрд▓рд╛ рдХреЗрдЕрд░рдЯреЗрдХрд░, рдЬрд┐рдереЗ terminal рдирд╛рд╣реА рдЖрдгрд┐ exit code рд╣реЗрдЪ рд╕рд░реНрд╡рд╕реНрд╡ рдЖрд╣реЗ.

ЁЯФФ Lesson 10 тАФ Scheduling & automation: the bell

ЁЯУН You are here: Lesson 10 of 12 ┬╖ Next: lesson-11-ci-and-containers


ЁЯУж What's in this branch

Lessons 01тАУ10. Running a script when nobody is there тАФ and the four habits that keep it from running twice, silently, or into a mess.

ЁЯзТ Explain like I'm 5

The bell rings at two in the morning and the caretaker runs the backup card. Nobody is watching, so the card must: run only once at a time (a lock on the door), be safe to run again (idempotent тАФ the same result), write down what happened (a log with times), and fail loudly if anything is wrong (strict mode from lesson 08). The bell itself is cron on most machines, a systemd timer on modern Linux, or a schedule in CI.

ЁЯЧ║я╕П Diagram

flowchart LR
  C["ЁЯФФ cron: 0 2 * * *"] --> B["backup.sh"]
  B --> L{"mkdir lock?"} -->|exists| D["die: already running"]
  L -->|ok| T["tar тЖТ verify тЖТ rotate"] --> U["rmdir lock (trap)"] --> G["log: kept 5"]

тЭУ What

ЁЯдФ Why

"The backup ran for months" and "the backup was empty for months" look the same from the outside. Only a log, a verification and an occasional restore tell them apart.

ЁЯФз How (in this repo)

backup.sh: KEEP, DEST, SRC with defaults; mkdir "$LOCK" || die; trap 'rmdir "$LOCK"; log done' EXIT; tar -czf into a timestamped name; tar -tzf тАж || die; ls -1t | tail -n +$((KEEP+1)) | xargs rm for rotation; every step logged with a time.

ЁЯзк Try it

bin/backup.sh --dest /tmp/bk --keep 2; bin/backup.sh --dest /tmp/bk --keep 2; bin/backup.sh --dest /tmp/bk --keep 2
ls -1 /tmp/bk                                                     # only 2 remain
mkdir /tmp/bk/.lock; bin/backup.sh --dest /tmp/bk; echo "exit $?"; rmdir /tmp/bk/.lock   # a second run is refused
tar -tzf "$(ls -t /tmp/bk/*.tar.gz | head -1)"                     # what is inside the newest
echo "0 2 * * * PATH=/usr/bin:/bin $PWD/bin/backup.sh --dest /srv/backups >> /var/log/backup.log 2>&1" # what the crontab line looks like
rm -rf /tmp/bk

тЬЕ Verify тАФ what you should see

Three runs, three verified lines, then rotated out messages and kept 2 backups; the locked run prints another backup is running and exits 1; the archive lists register.csv.

ЁЯПБ What you just proved

You can schedule a job that runs once at a time, keeps its history bounded, proves its own output, and says what it did.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: log rotation, certificate renewal, nightly reports, cache warmers тАФ half of ops is scheduled scripts, and every one of them needs these four habits.

тПня╕П Next

Lesson 11 тАФ scripts in CI & containers: the caretaker in the mailroom, where there is no terminal and the exit code is everything.

тЖР Previousprocesses jobsNext тЖТci and containers

This page is the lesson's README from the lesson-10-scheduling branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.