Step 30. ★ Project: Monitoring Automation — Building a Watcher That Works Alone
Level 0 — Understanding Computer Operation and Structure | Difficulty ★★★☆☆ | Estimated time: 5 hours (including observation time)
Prerequisites: You must have finished all of Steps 18–29 (Linux commands, permissions, scripts, cron). This chapter is a capstone project; the only new command is
free -h. Everything else is a combination of what you’ve learned.
- What you need: An Ubuntu VM (or WSL Ubuntu), nano, and a day’s worth of time.
- Caution: Everything you make today is a script and log files in your own home folder. When the project ends, decide for yourself and record whether to unregister the cron schedule or keep it running — it must be a decision, not neglect.
The difference between learning skills one by one and actually building something is like the difference between practicing individual instruments and playing in an ensemble. You’ve handled ls, grep, permissions, scripts, and cron one instrument at a time. Today is the ensemble. What you’ll build is "a watcher that works alone" — an automated system that wakes at set times and records the system’s state, whether anyone is watching or not. There is one completion condition: a file holding 24 hours of accumulated status logs.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Explain what monitoring is and why "records" become the baseline of normal
- Assemble the skills from Steps 18–29 into one practical system
- Design, write, and refine a "watcher script" that records its own status
- Register periodic execution with cron, and sift the accumulated log with grep to interpret changes
- Internalize the project order: "build small and check by hand → refine → automate → observe"
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language & environment | bash shell script + cron — Ubuntu terminal |
| Today’s commands | New: free -h (memory usage). Review combos: date, df -h (Step 27), who (logged-in users), ps aux --sort=-%cpu (Step 26), >>, 2>&1, crontab -e/-l (Step 28), grep -c, grep -A (Step 20) |
| Concepts needed | Monitoring and baselines, log design, log rotation |
| Today’s deliverable | monitor.sh + monitor.log — 24 hours of system status records |
2-1. What Is Monitoring?
Monitoring is "the work of continuously watching and recording." Why record? Because problems mostly arrive not as moments but as trends. A disk doesn’t suddenly hit 100% one day — it fills a little every day, then bursts. With records, you can trace "since when, and how fast" things got worse; without records, you only learn about "the day it burst."
Security is the same. You can only see "today’s anomaly" in a strange login if you know "the usual normal." Building a baseline of normal is monitoring’s first value — the same philosophy as the baseline document from Step 15, appearing again in Linux.
2-2. Our Watcher’s Blueprint
[Design]
monitor.sh ← on each run:
1. stamps the current time
2. disk usage (df -h)
3. memory usage (free -h)
4. logged-in users (who)
5. top 5 CPU processes (ps aux --sort=-%cpu)
...printed as one block
↓
cron runs it every hour
↓
keeps appending (>>) to monitor.log
↓
After 24 hours: 24 status blocks = the system's day
Real data center monitoring systems work on the same principle. At fixed intervals: gather the state, keep it as a record, and alert if something’s wrong. We’ll build "gather and record" ourselves, and even plant the seed of "alert."
2-3. The Principle of Writing Order
The project proceeds like this: ① build small and run by hand → ② look at the output and refine → ③ automate (cron) → ④ observe. Not "perfect from the start" but "get it rolling, fix it as you watch" — that order is also the order of real work.
3. Follow Along
3-1. Drafting monitor.sh
nano monitor.sh
Enter the following, save (Ctrl+O, Enter), and exit (Ctrl+X):
#!/bin/bash
# monitor.sh — a watcher that prints the system status as one block
# Written for: the Step 30 project
echo "========== $(date) =========="
echo "--- Disk ---"
df -h /
echo "--- Memory ---"
free -h
echo "--- Logged-in users ---"
who
echo "--- Top 5 CPU ---"
ps aux --sort=-%cpu | head -6
echo ""
How to read it: Every command is one you know. The point is stamping section titles with echo to make it pleasant for a human to read — logs are read not by machines but by your future self. The final empty echo "" is the breathing room between blocks.
3-2. Run by Hand and Refine
chmod +x monitor.sh
./monitor.sh
========== Wed Sep 9 11:29:10 KST 2026 ==========
--- Disk ---
Filesystem Size Used Avail Use% Mounted on
/dev/sdd 1007G 2.6G 954G 1% /
--- Memory ---
total used free shared buff/cache available
Mem: 7.5Gi 592Mi 6.2Gi 3.6Mi 827Mi 6.9Gi
Swap: 2.0Gi 0B 2.0Gi
--- Logged-in users ---
root pts/1 2026-09-09 11:28
--- Top 5 CPU ---
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.8 0.1 21740 13188 ? Ss 11:28 0:00 /sbin/init
root 305 0.5 1.0 2278876 85196 ? Ssl 11:28 0:00 /usr/bin/dockerd ...
(Each command’s output is assembled from live captures on WSL Ubuntu 24.04, 2026-09-09. In your environment, the disk name, memory amount, and usernames will differ.)
How to read it: One block of status record. As you can see even in the live output, df takes two lines because of its header. If you want the log leaner, df -h / | tail -1 keeps only the data line.
Make a prediction: If you change it to
df -h / | tail -1, how does the output change? Predict, then edit the script and run it. (Answer: the header line drops out, leaving only the data line, and the log gets leaner. In exchange, you must remember "which column was what" — leanness of records and interpretability are a tug-of-war.)
Why: This cycle of "write it → find the discomfort → fix it" is the essence of development. There is no perfect first draft.
3-3. Registering in cron
crontab -e
Add this at the very bottom (lee is your username):
0 * * * * /home/lee/monitor.sh >> /home/lee/monitor.log 2>&1
How to read it: 0 * * * * = every hour on the hour. Per Step 28’s rules, everything is an absolute path, and errors go to the log too (2>&1).
Verify:
crontab -l
(If the line you registered appears as-is, you’re done — the output format was verified live in Step 28. If you haven’t registered yet, you’ll see no crontab for username.)
Why: From now on, the watcher works alone. Every hour on the hour, it wakes, writes down the state, and goes back to sleep.
3-4. Checking the First Harvest
After the hour has turned:
cat /home/lee/monitor.log
(Sample output: success is seeing one block starting with ========== ... ========== — or two, depending on the time. The block’s shape matches the live output in section 3-2.)
How to read it: Your first automatic record. If no block has accumulated, check Step 28’s two big traps first (absolute paths, execute permission).
3-5. The 24-Hour Observation
From here, time does the work. Leave the VM on and live a day. During it, practice as usual — maybe even deliberately create a big file to fill the disk a bit. A day later:
grep -c "==========" /home/lee/monitor.log
24
(Sample output — the expected value.)
How to read it: 24 blocks = 24 hours of records. This number is the stamp of completion for today’s project. grep -c means "count the matching lines."
3-6. Reading the Accumulated Log — Anomaly Detection by Hand
Piling up a log is only half the job. Reading and interpreting what piled up — that’s monitoring:
grep "Mem:" /home/lee/monitor.log
Mem: 7.5Gi 592Mi 6.2Gi 3.6Mi 827Mi 6.9Gi
Mem: 7.5Gi 610Mi 6.2Gi 3.6Mi 828Mi 6.9Gi
Mem: 7.5Gi 705Mi 6.1Gi 3.6Mi 829Mi 6.8Gi
(The first line is a live capture from 2026-09-09; the rest are sample outputs assuming time has passed.)
How to read it: This is the memory usage (the used column) sifted out by hour. Can you see the rhythm of the numbers rising and falling? At what time did it climb — and what were you doing then? Matching memory against records is where analysis begins.
grep -A5 "Top 5 CPU" /home/lee/monitor.log | head -20
How to read it: -A5 means "plus the 5 lines below the matched line." This pulls out just the CPU ranking sections. Is the same program #1 at every hour? You must know "the usual #1" to see "an unfamiliar #1." That is the substance of a baseline.
4. Missions & Exercises
Mission — Completing the Watcher and a Retrospective Report
- Complete sections 3-1 through 3-4 and confirm that blocks have started accumulating in monitor.log
- Add an "alert seed" to the bottom of the script — a warning line prints when disk usage exceeds 80%:
USE=$(df -h / | tail -1 | tr -s ' ' | cut -d' ' -f5 | tr -d '%')
if [ "$USE" -gt 80 ]; then echo "!!! WARNING: disk at ${USE}% !!!"; fi
It’s fine if you don’t understand every part yet. Just take away the structure: "you build an alert with a conditional and string trimming."
3. After 24 hours, check the block count with grep -c "==========", and write one sentence each on "the quietest hour" and "the busiest hour," with evidence (memory? CPU #1?)
4. Log rotation experience: measure the size with wc -c monitor.log, and practice back-up-then-empty (cp monitor.log monitor.bak && > monitor.log)
5. Save a retrospective map of the skills used in this project, by Step number (e.g., redirection → Step 19), as review.txt
Exercises
Question 1. Explain why "records" are the core of monitoring, from the perspective that "problems arrive as trends, not moments."
Question 2. Of the five things our watcher records (time, disk, memory, logged-in users, top CPU), pick the one most directly useful for intrusion detection and give your reason.
Question 3. You waited 24 hours, but there weren’t 24 blocks. What should you suspect as the culprit, and what fact does "the time gap in the records" itself tell you?
Question 4. In mission 2’s alert seed, what does -gt 80 mean, and how does this structure ("gather → compare → notify") connect to professional monitoring tools?
5. Model Answers & Completion Criteria
Mission Model Answer
What the finished monitor.sh looks like:
#!/bin/bash
# monitor.sh — a watcher that prints the system status as one block (final version)
echo "========== $(date) =========="
echo "--- Disk ---"
df -h /
echo "--- Memory ---"
free -h
echo "--- Logged-in users ---"
who
echo "--- Top 5 CPU ---"
ps aux --sort=-%cpu | head -6
USE=$(df -h / | tail -1 | tr -s ' ' | cut -d' ' -f5 | tr -d '%')
if [ "$USE" -gt 80 ]; then echo "!!! WARNING: disk at ${USE}% !!!"; fi
echo ""
An example retrospective map:
Redirection (>>, 2>&1) → Step 19
grep counting & context lines (-c, -A) → Step 20
Absolute paths and permissions → Steps 22–24
Reading process lists (ps) → Step 26
Viewing disks (df) → Step 27
Scripts and cron → Step 28
How to verify: ① Does grep -c "==========" show a number matching the elapsed time (one per hour on the hour)? ② Does the alert line print only when the condition is exceeded — it’s normal for it not to print usually? ③ In back-up-then-empty, did the size you checked with wc -c move over to the backup file? ④ Do the Step numbers come out of your pen on their own in the retrospective map — if they do, you can see "how what you learned got assembled."
Exercise Solutions
Question 1 solution. Problems like disk exhaustion and memory leaks worsen a little every day, then burst into an outage one day. With records, you can trace "since when, and how fast" and respond before it bursts; without records, you only learn the day it burst. And you can only recognize "today’s anomaly" if you’ve built up "the usual normal" — records are the baseline itself.
Question 2 solution. who (logged-in users). You can only say whether a 3 AM login is strange if you have records showing that nobody ever logged in at that hour before. The top-CPU list also becomes material for catching "an unfamiliar #1 process" — both are devices for finding "what’s different from usual."
Question 3 solution. The most common culprit is hours when the VM was off or asleep — cron only works while the computer is on. "Even a monitoring system stops recording when it sleeps" is itself an important lesson, and it’s also why real servers run 24 hours a day. Use the timestamps in the log to trace which hours are missing.
Question 4 solution. -gt 80 is a conditional comparison meaning "greater than 80." The df output was trimmed through pipes (whitespace cleaned with tr, the fifth field extracted with cut) to pull out just the number, then compared with if. Professional tools (Prometheus, Grafana, Zabbix, etc.) have the same skeleton — gather periodically, compare against a condition, and notify if it matches. The tools grow bigger; the principle is identical to what you built today.
Completion Criteria Checklist
- [ ] I can explain the relationship between monitoring and baselines
- [ ] I wrote monitor.sh myself, ran it by hand, and refined it
- [ ] I can explain what df, free, who, and ps each show
- [ ] I registered and verified hourly automatic execution with cron
- [ ] I sifted and interpreted the accumulated log with grep -c and grep -A
- [ ] I practiced log backup and emptying (the hand motion of rotation)
- [ ] Mission: I completed the 24-hour observation and the review.txt retrospective map
6. Common Pitfalls & Fixes
Wall 1. "I registered it in cron, but the log is empty (or missing)"
Symptom: It runs fine by hand, but there’s no trace of automatic execution.
Cause: Step 28’s two big traps — paths and permissions. Being a project doesn’t grant an exception.
Fix: Check ① whether every path in the registered line is absolute, via crontab -l ② whether ls -l monitor.sh shows the x permission ③ whether 2>&1 is attached ④ whether output paths inside the script are absolute too. These four solve 90% of cron problems.
Wall 2. "The log accumulates, but the content is weird (command not found)"
Symptom: "command not found" is printed in the log.
Cause: cron’s PATH is narrow, so it can’t find some commands inside the script (Step 28, Wall 2).
Fix: Adding one line near the top of the script, PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, solves most cases. The lesson "my environment and cron’s environment are different" lasts a lifetime.
Wall 3. "The log is hard to read — I can’t tell where one block ends"
Symptom: As time passes, the log feels like a wall.
Cause: The divider device is weak, or there’s too much information inside each block.
Fix: That’s your opportunity to refine. Make the divider more visible (##########), or slim the block down to only what’s truly needed. With logs, "being readable" matters more than "existing."
Wall 4. "I waited 24 hours, but there aren’t 24 blocks"
Symptom: The grep -c result is lower than expected.
Cause: cron rests too during hours when the VM was off or asleep. Or you touched the schedule in between.
Fix: Understand it as a normal phenomenon, and use the log’s timestamps to trace which hours went missing. "Even watching only works while it’s on" is the starting point of server operations.
Wall 5. "The alert line never printed — is it broken?"
Symptom: You added the alert seed, but no !!! WARNING line appears.
Cause: If disk usage never exceeded 80%, not printing is normal (the verified environment was at 1% — 2026-09-09).
Fix: To test whether it’s broken, temporarily lower the condition to something like -gt 1 and run it by hand. If the warning prints, the conditional is alive; restore it after checking. "An alarm that doesn’t ring in normal times" is a good alarm.
7. Summary
Today’s Concepts
| Concept | One-line description |
|---|---|
| Monitoring | The work of gathering and recording state at fixed intervals |
| Baseline | Records of the usual — the reference point for recognizing "anomaly" |
| Log design | A document your future self will read — dividers and leanness are key |
| Log rotation | Lifecycle management: back up grown logs and empty them |
| Alert | Collect → compare against a condition → notify. Monitoring’s next stage |
Today’s Commands
| Command | What it does |
|---|---|
free -h |
View memory usage (today’s only new command) |
df -h / |
Root disk usage |
who |
Users logged in right now |
ps aux --sort=-%cpu | head -6 |
Top 5 CPU processes |
grep -c pattern file |
Count matching lines |
grep -A5 pattern file |
The matched line plus 5 lines below |
wc -c file / cp a b && > a |
Measure size / back up then empty |
The Instinct That Matters More Than Commands
Now that the project is done, your VM is no longer a "practice toy." It’s a small but real system that records its own state. Remember the feeling that "what I made works even during hours when I’m not there" — you’ve opened the door to the world of automation.
Remember two more things. First, Ubuntu already runs logrotate, which "cuts, compresses, and deletes old logs when they grow" — we verified live that its execution script sits in /etc/cron.daily/ (2026-09-09). What you did by hand in mission 4, the system does automatically every day. Second, the basic principle of intrusion detection is finding "what’s different from usual," and if you don’t know usual, you can’t know different. The 24 hours of who stamped into your monitor.log is exactly the first page of that baseline. Before watching is a technique for blocking attacks, it’s a technique for knowing your system.
This project’s real report card isn’t the monitor.sh file. It’s the list of every error you met along the way — the cron that wouldn’t run, the path it couldn’t find — each one solved by tracking down its cause. Because there was trial and error, this became "something you understood and built," not "something you copied." That difference creates speed in all your learning ahead.
Once every box is checked, Step 30 is complete.