Step 30. ★ 프로젝트: 모니터링 자동화 — 혼자 일하는 감시자 만들기
Level 0 — 컴퓨터 조작과 구조의 이해 | 난이도 ★★★☆☆ | 예상 소요 시간 5시간(관찰 시간 포함)
전제: Step 18~29 전부(리눅스 명령, 권한, 스크립트, cron)를 끝냈어야 합니다. 이 챕터는 종합 프로젝트로, 새로운 명령어는
free -h하나뿐입니다. 나머지는 전부 배운 것의 조합입니다.
- 준비물: 우분투 가상머신(또는 WSL 우분투), nano, 그리고 하루치 시간.
- 주의: 오늘 만드는 것은 내 홈 폴더의 스크립트와 로그 파일뿐입니다. 프로젝트가 끝나면 cron 예약을 해제할지 계속 둘지를 스스로 결정하고 기록하세요 — 방치가 아니라 결정이어야 합니다.
기술을 하나씩 배울 때와 실제로 무언가를 만들 때의 차이는, 개별 악기 연습과 합주의 차이와 같습니다. 여러분은 ls, grep, 권한, 스크립트, cron이라는 악기를 하나씩 다뤄 봤습니다. 오늘은 합주입니다. 만들 것은 "혼자 일하는 감시자" — 사람이 보든 말든 정해진 시간마다 깨어나 시스템의 상태를 기록해 두는 자동화 시스템입니다. 완성 조건은 하나, 24시간치 상태 로그가 쌓인 파일입니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- 모니터링이 무엇이며, 왜 "기록"이 정상의 기준선(baseline)이 되는지 설명한다
- Step 18~29에서 배운 기술을 하나의 실용 시스템으로 조립한다
- 스스로 상태를 기록하는 "감시자 스크립트"를 설계·작성·다듬는다
- cron으로 주기 실행을 등록하고, 쌓인 로그를 grep으로 추려 변화를 해석한다
- "작게 만들어 손으로 확인 → 다듬기 → 자동화 → 관찰"의 프로젝트 순서를 몸에 익힌다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | bash 셸 스크립트 + cron — 우분투 터미널 |
| 오늘의 명령어 | 새 명령: free -h(메모리 사용량). 복습 조합: date, df -h(Step 27), who(로그인 사용자), ps aux --sort=-%cpu(Step 26), >>, 2>&1, crontab -e/-l(Step 28), grep -c, grep -A(Step 20) |
| 필요한 개념 | 모니터링과 기준선(baseline), 로그 설계, 로그 회전(log rotation) |
| 오늘의 산출물 | monitor.sh + monitor.log — 24시간치 시스템 상태 기록 |
2-1. 모니터링이란 무엇인가
모니터링(monitoring)은 "계속 지켜보고 기록하는 일"입니다. 왜 기록할까요? 문제는 대부분 순간이 아니라 흐름으로 오기 때문입니다. 디스크는 어느 날 갑자기 100%가 되는 게 아니라, 매일 조금씩 차오르다가 터집니다. 기록이 있으면 "언제부터, 얼마나 빨리" 나빠졌는지 추적할 수 있고, 기록이 없으면 "터진 그날"만 알 뿐입니다.
보안도 마찬가지입니다. 이상한 로그인도 "평소의 정상"을 알아야 "오늘의 이상"이 보입니다. 정상의 기준선(baseline)을 쌓는 것이 모니터링의 첫 번째 가치입니다 — Step 15의 베이스라인 문서와 같은 철학이 리눅스에서 다시 나옵니다.
2-2. 우리 감시자의 설계도
[설계]
monitor.sh ← 매 실행마다:
1. 현재 시각을 찍고
2. 디스크 사용량(df -h)
3. 메모리 사용량(free -h)
4. 로그인 중인 사용자(who)
5. CPU 상위 5개 프로세스(ps aux --sort=-%cpu)
를 한 덩어리로 출력
↓
cron이 1시간마다 실행
↓
monitor.log에 계속 추가(>>)
↓
24시간 뒤: 24개의 상태 덩어리 = 시스템의 하루
실제 데이터센터의 감시 시스템도 원리는 같습니다. 정해진 주기로, 상태를 모아, 기록으로 남기고, 이상하면 알립니다. 우리는 그중 "모아서 기록하기"까지를 직접 만들고, "알리기"의 씨앗까지 심어 봅니다.
2-3. 작성 순서의 원칙
프로젝트는 이렇게 진행합니다: ① 작게 만들어 손으로 실행 → ② 출력을 보고 다듬기 → ③ 자동화(cron) → ④ 관찰. "처음부터 완벽하게"가 아니라 "일단 굴리고, 보면서 고친다" — 이 순서가 실무의 순서이기도 합니다.
3. 따라 하기
3-1. monitor.sh 초안 작성
nano monitor.sh
다음 내용을 입력하고 저장(Ctrl+O, Enter), 종료(Ctrl+X)합니다:
#!/bin/bash
# monitor.sh — 시스템 상태를 한 덩어리로 출력하는 감시자
# 작성: Step 30 프로젝트
echo "========== $(date) =========="
echo "--- 디스크 ---"
df -h /
echo "--- 메모리 ---"
free -h
echo "--- 로그인 사용자 ---"
who
echo "--- CPU 상위 5 ---"
ps aux --sort=-%cpu | head -6
echo ""
읽는 법: 전부 아는 명령입니다. echo로 구역 제목을 찍어 사람이 읽기 좋게 만든 것이 포인트입니다 — 로그는 기계가 아니라 미래의 내가 읽습니다. 마지막 빈 echo ""는 덩어리 사이의 여백입니다.
3-2. 손으로 실행하고 다듬기
chmod +x monitor.sh
./monitor.sh
========== Wed Sep 9 11:29:10 KST 2026 ==========
--- 디스크 ---
Filesystem Size Used Avail Use% Mounted on
/dev/sdd 1007G 2.6G 954G 1% /
--- 메모리 ---
total used free shared buff/cache available
Mem: 7.5Gi 592Mi 6.2Gi 3.6Mi 827Mi 6.9Gi
Swap: 2.0Gi 0B 2.0Gi
--- 로그인 사용자 ---
root pts/1 2026-09-09 11:28
--- CPU 상위 5 ---
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 ...
(각 명령의 출력은 2026-09-09 WSL 우분투 24.04에서 실측한 것을 조립한 것입니다. 여러분 환경에서는 디스크 이름, 메모리 양, 사용자명이 다르게 나옵니다.)
읽는 법: 한 덩어리의 상태 기록입니다. 실측 출력에서도 보이듯 df는 제목 줄 때문에 두 줄입니다. 로그가 간결했으면 한다면 df -h / | tail -1로 데이터 줄만 남길 수 있습니다.
예측해 보기:
df -h / | tail -1로 바꾸면 출력이 어떻게 달라질까요? 예측하고 스크립트를 고쳐 실행해 보세요. (정답: 제목 줄이 빠지고 데이터 줄만 남아 로그가 간결해집니다. 대신 "어느 컬럼이 뭐였는지"는 기억해야 합니다 — 기록의 간결함과 해석 가능성은 줄다리기입니다.)
왜: 이 "써 보고 → 불편 찾고 → 고치고"의 순환이 개발의 본질입니다. 완벽한 초안은 없습니다.
3-3. cron에 등록
crontab -e
맨 아래에 추가합니다 (lee는 자기 계정으로):
0 * * * * /home/lee/monitor.sh >> /home/lee/monitor.log 2>&1
읽는 법: 0 * * * * = 매시 정각. Step 28의 규칙대로 전부 절대경로이고, 오류도 로그로 본냅니다(2>&1).
확인:
crontab -l
(등록한 줄이 그대로 보이면 완료입니다 — 출력 형식은 Step 28에서 실측으로 확인했습니다. 등록 전이라면 no crontab for 계정명이 나옵니다.)
왜: 이제 감시자는 혼자 일합니다. 매시 정각마다 깨어나 상태를 적고 다시 잡니다.
3-4. 첫 수확 확인
정각을 넘긴 뒤:
cat /home/lee/monitor.log
(출력 예시: ========== ... ==========로 시작하는 덩어리가 하나, 또는 시간에 따라 둘 보이면 성공입니다. 덩어리의 모양은 3-2절의 실측 출력과 같습니다.)
읽는 법: 첫 자동 기록입니다. 덩어리가 안 쌓였다면 Step 28의 2대 함정(절대경로, 실행 권한)부터 점검하세요.
3-5. 24시간 관찰
여기서부터는 시간이 일합니다. 가상머신을 켜 두고 하루를 지냅니다. 그동안 평소처럼 실습도 하고, 일부러 큰 파일을 만들어 디스크를 좀 채워 보기도 합니다. 하루 뒤:
grep -c "==========" /home/lee/monitor.log
24
(출력 예시 — 기대값입니다.)
읽는 법: 24개의 덩어리 = 24시간의 기록. 이 숫자가 오늘 프로젝트의 완성 도장입니다. grep -c는 "찾은 줄의 개수를 세라"입니다.
3-6. 쌓인 로그 읽기 — 손으로 하는 이상 탐지
로그를 쌓는 것만으로는 반쪽입니다. 쌓인 것을 읽고 해석해야 모니터링입니다:
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
(첫 줄은 2026-09-09 실측, 나머지는 시간 경과를 가정한 출력 예시입니다.)
읽는 법: 시간대별 메모리 사용량(used 컬럼)만 추려 놓은 것입니다. 숫자가 오르내리는 리듬이 보이나요? 어느 시각에 올랐는지, 그때 여러분은 무엇을 하고 있었는지 — 기억과 기록을 맞춰 보는 것이 분석의 시작입니다.
grep -A5 "CPU 상위" /home/lee/monitor.log | head -20
읽는 법: -A5는 "찾은 줄의 아래 5줄까지 함께"입니다. CPU 순위 구간만 뽑아 봅니다. 매 시각 1위가 같은 프로그램인가요? "평소의 1위"를 알아야 "낯선 1위"가 보입니다. 이것이 기준선의 실체입니다.
4. 미션과 연습문제
미션 — 감시자 완성과 회고 보고서
- 3-1~3-4를 완료해 monitor.log에 덩어리가 쌓이기 시작한 것을 확인하세요
- 스크립트 맨 아래에 "경보 씨앗"을 추가하세요 — 디스크 사용률이 80%를 넘으면 경고 줄이 찍힙니다:
USE=$(df -h / | tail -1 | tr -s ' ' | cut -d' ' -f5 | tr -d '%')
if [ "$USE" -gt 80 ]; then echo "!!! 경고: 디스크 ${USE}% 사용 중 !!!"; fi
각 부분을 지금 다 몰라도 됩니다. "조건문과 문자열 다듬기로 경보를 만든다"는 구조만 가져가세요.
3. 24시간 뒤 grep -c "=========="로 덩어리 수를 확인하고, "가장 한가했던 시각"과 "가장 바빴던 시각"을 근거(메모리? CPU 1위?)와 함께 한 문장씩 적으세요
4. 로그 회전 체험: wc -c monitor.log로 크기를 재고, 백업 후 비우기(cp monitor.log monitor.bak && > monitor.log)를 연습하세요
5. 이 프로젝트에 쓰인 기술을 Step 번호로 정리한 회고 지도(예: 리다이렉션 → Step 19)를 review.txt로 저장하세요
연습문제
문제 1. 모니터링에서 "기록"이 핵심인 이유를, "문제는 순간이 아니라 흐름으로 온다"는 관점에서 설명해 보세요.
문제 2. 우리 감시자가 기록하는 다섯 가지(시각, 디스크, 메모리, 로그인 사용자, CPU 상위) 중, 침해 탐지에 가장 직접 쓰이는 것 하나를 고르고 이유를 말해 보세요.
문제 3. 24시간을 기다렸는데 덩어리가 24개가 아니었습니다. 범인으로 의심할 것은 무엇이며, "기록이 끊긴 시간" 자체가 알려 주는 사실은 무엇인가요?
문제 4. 미션 2번의 경보 씨앗에서 -gt 80은 무슨 뜻이며, 이 구조("모으기 → 비교 → 알리기")가 실무 모니터링 도구와 어떻게 이어지나요?
5. 모범 답안과 완료 기준
미션 모범 답안
완성된 monitor.sh의 모습:
#!/bin/bash
# monitor.sh — 시스템 상태를 한 덩어리로 출력하는 감시자 (최종본)
echo "========== $(date) =========="
echo "--- 디스크 ---"
df -h /
echo "--- 메모리 ---"
free -h
echo "--- 로그인 사용자 ---"
who
echo "--- CPU 상위 5 ---"
ps aux --sort=-%cpu | head -6
USE=$(df -h / | tail -1 | tr -s ' ' | cut -d' ' -f5 | tr -d '%')
if [ "$USE" -gt 80 ]; then echo "!!! 경고: 디스크 ${USE}% 사용 중 !!!"; fi
echo ""
회고 지도의 예:
리다이렉션(>>, 2>&1) → Step 19
grep 개수 세기·주변 줄(-c, -A) → Step 20
절대경로와 권한 → Step 22~24
프로세스 목록 읽기(ps) → Step 26
디스크 보기(df) → Step 27
스크립트와 cron → Step 28
검증하는 법: ① grep -c "=========="가 시간만큼의 숫자를 보여 주는가(정각마다 1개). ② 경보 줄이 조건을 넘었을 때만 찍히는가 — 평소엔 안 찍히는 게 정상입니다. ③ 백업 후 비우기에서 wc -c로 확인한 크기가 백업 파일로 옮겨갔는가. ④ 회고 지도에 Step 번호가 스스로 써지는가 — 써진다면 "배운 것이 어떻게 조립됐는가"가 보이는 것입니다.
연습문제 해답
문제 1 해답. 디스크 고갈·메모리 누수 같은 문제는 매일 조금씩 나빠지다가 어느 날 장애로 터집니다. 기록이 있으면 "언제부터, 얼마나 빨리"를 추적해 터지기 전에 대응할 수 있지만, 기록이 없으면 터진 날만 알 뿐입니다. 그리고 "평소의 정상"을 쌓아 둬야 "오늘의 이상"을 알아볼 수 있습니다 — 기록이 곧 기준선입니다.
문제 2 해답. who(로그인 사용자)입니다. 새벽 3시의 로그인이 이상한지는, 평소 그 시간에 아무도 로그인하지 않았다는 기록이 있어야 말할 수 있기 때문입니다. CPU 상위 목록도 "낯선 1위 프로세스"를 잡는 재료가 됩니다 — 둘 다 "평소와 다른 것"을 찾는 장치입니다.
문제 3 해답. 가장 흔한 범인은 가상머신이 꺼져 있거나 잠들어 있던 시간입니다 — cron도 컴퓨터가 켜져 있을 때만 돕니다. "감시 시스템도 잠들면 기록이 끊긴다"는 것 자체가 중요한 배움이며, 실제 서버가 24시간 켜져 있는 이유이기도 합니다. 로그의 시각들로 빠진 시간대를 추적해 보세요.
문제 4 해답. -gt 80은 "80보다 크면"(greater than)이라는 조건 비교입니다. df 출력을 파이프로 다듬어(tr로 공백 정리, cut으로 다섯 번째 칸 추출) 숫자만 뽑고 if로 비교했습니다. 실무 도구(Prometheus, Grafana, Zabbix 등)도 뼈대는 같습니다 — 주기적으로 모으고, 조건과 비교하고, 맞으면 알립니다. 도구가 커질 뿐 원리는 오늘 만든 것과 동일합니다.
완료 기준 체크리스트
- [ ] 모니터링과 기준선(baseline)의 관계를 설명할 수 있다
- [ ] monitor.sh를 직접 작성하고 손으로 실행해 다듬었다
- [ ] df, free, who, ps가 각각 무엇을 보여 주는지 설명할 수 있다
- [ ] cron으로 매시간 자동 실행을 등록하고 확인했다
- [ ] grep -c와 grep -A로 쌓인 로그를 추려 해석했다
- [ ] 로그 백업과 비우기(회전의 손동작)를 해 봤다
- [ ] 미션: 24시간 관찰과 review.txt 회고 지도를 완성했다
6. 흔한 실수와 해결
벽 1. "cron에 등록했는데 로그가 비어 있어요(또는 없어요)"
증상: 손으로는 잘 도는데 자동 실행 흔적이 없습니다.
원인: Step 28의 2대 함정 — 경로와 권한입니다. 프로젝트라고 예외가 아닙니다.
해결: ① crontab -l로 등록줄의 경로가 전부 절대경로인지 ② ls -l monitor.sh로 x 권한이 있는지 ③ 2>&1이 붙어 있는지 ④ 스크립트 안의 출력 경로도 절대경로인지 확인하세요. 이 넷이면 cron 문제의 90%가 해결됩니다.
벽 2. "로그는 쌓이는데 내용이 이상해요 (command not found)"
증상: 로그에 "command not found"가 찍혀 있습니다.
원인: cron의 PATH가 좁아서 스크립트 안의 일부 명령을 못 찾습니다 (Step 28 벽 2).
해결: 스크립트 첫부분에 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 한 줄을 추가하면 대부분 해결됩니다. "내 환경과 cron의 환경은 다르다"는 교훈은 평생 갑니다.
벽 3. "어디까지가 한 덩어린지 로그가 읽기 어려워요"
증상: 시간이 지날수록 로그가 벽처럼 느껴집니다.
원인: 구분 장치가 약하거나, 덩어리 안의 정보가 너무 많습니다.
해결: 그게 다듬기의 기회입니다. 구분선을 더 눈에 띄게(##########) 바꾸거나, 정말 필요한 정보로 덩어리를 줄이세요. 로그는 "있는 것"보다 "읽히는 것"이 중요합니다.
벽 4. "24시간 기다렸는데 덩어리가 24개가 아니에요"
증상: grep -c 결과가 예상보다 적습니다.
원인: 가상머신이 꺼져 있거나 잠들어 있던 시간에는 cron도 쉽니다. 또는 중간에 예약을 건드렸거나.
해결: 정상적인 현상으로 이해하고, 로그의 시각들로 몇 시가 빠졌는지 추적해 보세요. "감시도 켜져 있어야 일한다"가 서버 운영의 출발점입니다.
벽 5. "경보 줄이 한 번도 안 찍혀요 — 고장인가요?"
증상: 경보 씨앗을 넣었는데 !!! 경고 줄이 안 나옵니다.
원인: 디스크 사용률이 80%를 넘은 적이 없으면 안 찍히는 게 정상입니다 (실측 환경은 1%였습니다 — 2026-09-09).
해결: 고장이 아닌지 시험하려면 조건을 일시적으로 -gt 1처럼 낮춰 손 실행해 보세요. 경고가 찍히면 조건문은 살아 있는 것이고, 확인 후 원래대로 돌리면 됩니다. "평소엔 울리지 않는 경보"가 좋은 경보입니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| 모니터링 | 정해진 주기로 상태를 모아 기록하는 일 |
| 기준선(baseline) | 평소의 기록 — "이상"을 알아보는 기준점 |
| 로그 설계 | 미래의 내가 읽을 문서 — 구분선과 간결함이 핵심 |
| 로그 회전 | 커진 로그를 백업하고 비우는 수명 관리 |
| 경보(alert) | 수집 → 조건 비교 → 알림. 모니터링의 다음 단계 |
오늘의 명령어
| 명령 | 하는 일 |
|---|---|
free -h |
메모리 사용량 보기 (오늘의 유일한 새 명령) |
df -h / |
루트 디스크 사용량 |
who |
지금 로그인한 사용자 |
ps aux --sort=-%cpu | head -6 |
CPU 상위 5개 프로세스 |
grep -c 패턴 파일 |
찾은 줄 개수 세기 |
grep -A5 패턴 파일 |
찾은 줄 아래 5줄까지 |
wc -c 파일 / cp a b && > a |
크기 재기 / 백업 후 비우기 |
명령어보다 중요한 감각
프로젝트를 끝낸 지금, 여러분의 가상머신은 더 이상 "연습용 장난감"이 아닙니다. 스스로 자기 상태를 기록하는, 작지만 진짜 시스템입니다. "내가 만든 것이 내가 없는 시간에도 일한다"는 감각을 기억하세요 — 자동화의 세계로 문을 연 것입니다.
두 가지를 더 기억해 두세요. 첫째, 우분투에는 이미 "로그가 커지면 자르고 압축하고 오래된 것은 지우는" logrotate가 돌고 있습니다 — /etc/cron.daily/에 그 실행 스크립트가 있는 것을 실측으로 확인했습니다 (2026-09-09). 여러분이 미션 4번에서 손으로 한 일을 시스템은 매일 자동으로 하고 있습니다. 둘째, 침해 탐지의 기본 원리는 "평소와 다른 것"을 찾는 것이고, 평소를 모르면 "다른 것"도 모릅니다. 여러분의 monitor.log에 찍힌 who의 24시간이 바로 그 기준선의 첫 페이지입니다. 감시는 공격을 막는 기술이기 전에, 시스템을 아는 기술입니다.
이 프로젝트의 진짜 성적표는 monitor.sh 파일이 아닙니다. 작성 과정에서 만난 모든 오류 — 안 되던 cron, 못 찾던 경로 — 를 하나씩 원인을 찾아 해결한 그 목록입니다. 시행착오가 있었기에 "베낀 것"이 아니라 "이해하고 만든 것"이 됐습니다. 그 차이가 앞으로의 모든 학습에서 속도를 만듭니다.
전부 체크되면 Step 30 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.