서비스 · 프로세스 관리 47 / 50 · Part 5. 보안과 SOC
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정 analyst)

1. 들어가며

관제 대시보드에서 가장 자주 보는 경보 중 하나가 "CPU 사용률 90% 이상"이다. 원인은 대부분 정상 업무(배치 작업, 트래픽 증가)지만, 가끔은 승인되지 않은 계산 작업 이나 장애의 전조다. 중요한 것은 경보가 울렸을 때 몇 분 안에 원인 프로세스와 소속을 특정 하고, 일시적인 피크와 지속적인 이상을 구분하는 것이다.

이번 글에서는 07편(top), 18편(cgroup), 20편(메모리)의 도구를 하나의 절차로 묶는다. 실습용 계산 부하(40초 후 자동 종료)를 만들고, 지표 → 프로세스 → 소속 → 지속 여부 순서로 좁힌 뒤, 두 번 측정해 지속적인 고부하만 보고하는 점검 스크립트를 만든다.


2. 핵심 개념

2-1. 지표와 해석

지표확인해석 기준
load average/proc/loadavg, uptime코어 수(nproc)와 비교 (07편)
%Cpu us / sy / watop -b -n 2us 높음 = 계산, wa 높음 = I/O 대기
프로세스 %CPUps --sort=-%cpu (수명 평균), top (최근 구간)코어 1개 = 100%
메모리 RSSps --sort=-rss실제 물리 메모리 사용량
소속/proc/PID/cgroup, systemd-cgtop서비스 / 사용자 세션 / 컨테이너

2-2. 오탐을 줄이는 방법

방법설명
지속 시간한 번의 측정이 아니라 일정 간격으로 여러 번 임계치를 넘은 경우만
소속 확인승인된 배치 서비스(cgroup)라면 정상 가능성 높음
시간대예정된 작업 시간과 비교
기준선서버별 평소 사용률 대비 변화 (42편)

2-3. ps와 top의 %CPU 차이

ps의 %CPU는 프로세스 수명 전체의 평균 이고 top은 최근 갱신 구간 의 값이다(07편). 방금 시작한 프로세스는 두 값이 비슷하지만, 오래된 프로세스는 크게 다를 수 있다. 지속 여부를 보려면 짧은 간격으로 두 번 이상 측정한다.


3. 동작 원리

자원 이상 탐지 — 지표 → 프로세스 → 소속 → 지속 여부

[경보] CPU 사용률 임계치 초과
  ① nproc · loadavg · top -b -n 2        → 코어 대비 부하, us/wa 구분
  ② ps --sort=-%cpu / --sort=-rss         → 상위 프로세스
  ③ readlink /proc/PID/exe · cgroup      → 실행 파일 · 서비스인가 세션인가
  ④ 5초 후 재측정                        → 두 번 모두 임계치 이상이면 ALERT
  → 41편 기준 판단 → 필요 시 43편 증거 수집

4. 명령어 실습

# 1) (analyst) 실습용 부하: CPU 계산 루프와 400MB 메모리 — 모두 40초 후 자동 종료
nohup timeout 40 bash -c 'exec -a lab-compute sh -c "while :; do :; done"' > /dev/null 2>&1 &
nohup timeout 40 python3 -c 'b=bytearray(400*1024*1024); import time; time.sleep(40)' > /dev/null 2>&1 &

# 2) (root) 지표 → 프로세스 → 소속
nproc; cat /proc/loadavg
top -b -n 2 -d 2 -w 110 | grep '^%Cpu' | tail -1
ps -eo pid,user,%cpu,rss,etimes,comm --sort=-%cpu | head -4
ps -eo pid,user,%cpu,rss,etimes,comm --sort=-rss | head -3
for p in $(ps -eo pid= --sort=-%cpu | head -2); do
  printf '%-6s %-30s %s\n' $p "$(readlink /proc/$p/exe)" "$(tail -1 /proc/$p/cgroup | sed 's#.*/##')"; done

# 3) 지속 여부를 보는 점검 스크립트 /root/cpu-watch.sh LIMIT
#    5초 간격 두 번 측정 → 두 번 모두 LIMIT% 이상인 PID 만 ALERT (exe · unit · cmd 포함)
/root/cpu-watch.sh 80

실습 부하는 timeout으로 반드시 종료 시간을 둔다. 운영 서버에서 부하 테스트를 할 때는 사전 승인과 시간대 조정이 필요하다.


5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — 부하 발생 (실습용 계산 작업, 40초 후 자동 종료)

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — ① 시스템 지표 → ② 원인 프로세스 → ③ 소속

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — 임계치 기반 점검 스크립트 (5초 간격 2회 측정)

텍스트 원본(실제 출력):

[analyst@rocky9-lab ~]$ nohup timeout 40 bash -c 'exec -a lab-compute sh -c "while :; do :; done"' > /dev/null 2>&1 &
[analyst@rocky9-lab ~]$ nohup timeout 40 python3 -c 'b=bytearray(400*1024*1024); import time; time.sleep(40)' > /dev/null 2>&1 &
[analyst@rocky9-lab ~]$ sleep 5; echo started
started
[root@rocky9-lab ~]# nproc; cat /proc/loadavg
2
0.65 0.25 0.08 2/191 2581
[root@rocky9-lab ~]# top -b -n 2 -d 2 -w 110 | grep -A0 '^%Cpu' | tail -1
%Cpu(s): 42.2 us,  8.2 sy,  0.0 ni, 49.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.5 st
[root@rocky9-lab ~]# ps -eo pid,user,%cpu,rss,etimes,comm --sort=-%cpu | head -4
    PID USER     %CPU   RSS ELAPSED COMMAND
   2516 analyst   103  3808       7 sh
   2515 analyst   3.1 416824      7 python3
      1 root      0.5 14460     254 systemd
[root@rocky9-lab ~]# ps -eo pid,user,%cpu,rss,etimes,comm --sort=-rss | head -3
    PID USER     %CPU   RSS ELAPSED COMMAND
   2515 analyst   3.1 416824      7 python3
     31 analyst   0.0 17944     254 python3
[root@rocky9-lab ~]# for p in $(ps -eo pid= --sort=-%cpu | head -2); do printf '%-6s %-30s %s\n' $p "$(readlink /proc/$p/exe)" "$(cut -d: -f3 /proc/$p/cgroup | tail -1 | sed 's#.*/##')"; done
2516   /usr/bin/bash                  session-51.scope
2515   /usr/bin/python3.9             session-51.scope
[root@rocky9-lab ~]# cat > /root/cpu-watch.sh <<'EOF'
> #!/bin/bash
> # 두 번 측정해 두 번 모두 CPU 사용률이 LIMIT% 이상인 프로세스만 보고
> LIMIT=${1:-80}
> a=$(ps -eo pid=,%cpu= --sort=-%cpu | awk -v l=$LIMIT '$2>=l{print $1}'); sleep 5
> for p in $(ps -eo pid=,%cpu= --sort=-%cpu | awk -v l=$LIMIT '$2>=l{print $1}'); do
>   grep -qw $p <<<"$a" || continue
>   printf 'ALERT pid=%s user=%s cpu=%s%% exe=%s unit=%s cmd=%s\n' $p "$(ps -o user= -p $p)" "$(ps -o %cpu= -p $p | tr -d ' ')" \
>     "$(readlink /proc/$p/exe)" "$(tail -1 /proc/$p/cgroup | sed 's#.*/##')" "$(tr '\0' ' ' < /proc/$p/cmdline | cut -c1-40)"
> done
> EOF
[root@rocky9-lab ~]# chmod 700 /root/cpu-watch.sh; /root/cpu-watch.sh 80
ALERT pid=2516 user=analyst cpu=96.5% exe=/usr/bin/bash unit=session-51.scope cmd=lab-compute -c while :; do :; done

6. 결과 해석

관찰의미
nproc 2, loadavg 0.65부하를 방금 시작해 1분 평균이 아직 1 미만이다. load는 느리게 반영 되므로 순간 판단에는 %Cpu를 함께 본다
%Cpu 42.2 us ... 49.0 id코어 2개 중 약 1개가 사용자 계산에 쓰이고 있다
%CPU 상위 sh 103 (PID 2516)계산 루프. ps의 %CPU는 짧은 수명에서 100%를 조금 넘게 계산되기도 한다. ps에는 exec -a로 바꾼 이름이 아니라 comm인 sh 로 보인다
RSS 상위 python3 416824 KB400MB 메모리 사용 프로세스. CPU는 거의 쓰지 않는다 — CPU와 메모리 상위는 다를 수 있다
exe /usr/bin/bash, /usr/bin/python3.9실행 파일은 정상 경로의 패키지 프로그램이다
소속 session-51.scope두 프로세스 모두 서비스가 아니라 analyst의 로그인 세션 에서 시작되었다
ALERT pid=2516 user=analyst cpu=96.5% exe=/usr/bin/bash unit=session-51.scope cmd=lab-compute -c while ...두 번 측정 모두 80% 이상이라 보고되었다. cmdline에는 exec -a로 붙인 이름 lab-compute가 보인다
exe(bash)와 cmdline 이름(lab-compute) 불일치이름은 바꿀 수 있지만 exe는 바꿀 수 없다. 판단은 exe 기준 으로 한다 (41편)

7. 보안 관점

주제내용
승인되지 않은 자원 사용서버 자원을 무단으로 쓰는 계산 작업은 비용·가용성 문제이자 침해의 결과일 수 있다. 소속·실행 파일·시작 세션으로 출처를 확인한다
가용성고부하 자체가 서비스 장애로 이어진다. 보안 여부와 관계없이 CPUQuota=(18편) 같은 제한이 방어선이 된다
이름 신뢰 금지cmdline·comm은 바뀔 수 있다. exe와 패키지 소유를 기준으로 판단한다
은폐 가능성낮은 우선순위(nice, 09편)나 간헐적 실행으로 경보를 피하는 경우도 있다. 짧은 순간값보다 추이 와 ni 지표를 함께 본다

8. 보안관제 관점

[경보]     모니터링: web01 CPU 90% 이상 10분 지속
     ↓
[1분 확인] top -b -n 2 | head -15 > top.txt ; /root/cpu-watch.sh 80
     ↓
[소속]     unit=*.service → 해당 서비스의 예정 작업·배포 기록 확인
           unit=session-N.scope → 로그인 기록(journal) · 사용자 확인
     ↓
[판단]     예정된 정상 작업 → 경보 규칙에 예외(시간대·unit) 추가
           설명되지 않음 → 43편 수집 · 44편 네트워크 확인 · 에스컬레이션
     ↓
[완화]     필요 시 systemctl set-property <unit> CPUQuota=.. --runtime (서비스 영향 최소화)
보고서 항목예
지표CPU us 42.2%, 코어 2
원인PID 2516, analyst, /usr/bin/bash, session-51.scope
지속5초 간격 2회 모두 ≥ 80%
판단 근거승인된 배치 목록에 없음 / 사용자 확인 결과

9. 실무에서 자주 발생하는 실수

실수결과예방
순간값 한 번으로 경보일시 피크 오탐두 번 이상 측정, 지속 시간 조건
load만 보고 판단반영 지연·I/O 대기와 혼동%Cpu us/wa 함께
프로세스 이름으로 판단이름 변경에 속음exe 기준
CPU 상위만 확인메모리 문제 누락RSS 정렬 병행
원인 프로세스 즉시 종료증거·업무 손실소속·소유자 확인 → 수집 → 조치

10. 실습 체크리스트

[ ] nproc · loadavg · %Cpu 로 시스템 지표를 확인했다
[ ] %CPU 와 RSS 상위 프로세스가 다를 수 있음을 확인했다
[ ] exe 와 cgroup 으로 원인 프로세스의 실행 파일과 소속을 확인했다
[ ] 두 번 측정 방식의 점검 스크립트로 지속적인 고부하만 보고했다
[ ] cmdline 이름과 exe 가 다른 것을 확인하고 exe 기준으로 판단했다
[ ] 경보 판단 결과별 조치(예외·수집·완화)를 정리했다

11. 핵심 정리

  • 자원 이상은 지표 → 원인 프로세스 → 소속 → 지속 여부 순서로 좁힌다.
  • load는 느리게 반영되므로 %Cpu(us·wa)와 함께 보고, CPU와 메모리 상위를 각각 확인한다.
  • 소속(cgroup)으로 서비스 작업인지 사용자 세션 작업인지 구분한다.
  • 두 번 이상 측정해 지속적인 고부하만 보고하면 오탐이 줄어든다.
  • 프로세스 이름이 아니라 exe로 판단하고, 조치 전 증거를 수집한다.

12. 다음 편 예고

다음 글 「48. auditd로 프로세스 실행 감사」 에서는 "누가 언제 무엇을 실행했는가"를 커널 수준에서 기록하는 auditd 규칙을 설계한다. 로그인 사용자의 실행 기록과 예약 작업·unit 디렉터리 변경 감시 규칙을 적재하고, ausearch·aureport로 조회한다.


참고 자료


시리즈 이동

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글