
서비스 · 프로세스 관리 40 / 50 · Part 4. 로그·스케줄링·운영
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
Part 4의 마지막 글이다. 지금까지 상태 해석(24편), 로그 분석(31편), 포트·소켓(16편), 권한(30편), 재시작 정책(29편)을 따로 배웠다. 실제 장애 현장에서는 이 도구들을 정해진 순서대로 써야 빠르게 원인에 도달한다. 순서 없이 재시작만 반복하면 증거는 사라지고 원인은 그대로 남는다.
이번 글에서는 증상 → 상태 → 로그 → 원인 → 조치 → 검증 6단계 절차를 정리하고, labapp 서비스에 두 가지 장애(포트 충돌, 파일 권한)를 일부러 만들어 이 절차로 해결한다.
| 단계 | 질문 | 도구 |
|---|---|---|
| ① 증상 | 지금 어떤 상태인가? | systemctl is-active, is-failed |
| ② 상태 | 어떻게 끝났나? (종료 코드·시그널·실행 시간) | systemctl status, show -p Result,ExecMainStatus |
| ③ 로그 | 프로그램이 무엇이라고 말했나? | journalctl -u UNIT -n 50, -xeu |
| ④ 원인 | 왜 그 오류가 났나? | ss -tlnp, ls -l, systemctl cat, id |
| ⑤ 조치 | 원인을 제거 | 설정 수정, 충돌 프로세스 처리, 권한 수정 |
| ⑥ 검증 | 정말 해결되었나? | is-active, 포트 확인, 실제 요청 |
| 로그·상태 | 원인 후보 | ④에서 볼 것 |
|---|---|---|
Address already in use (errno 98) | 포트 충돌 | ss -tlnp 'sport = :PORT' |
Permission denied (errno 13) | 파일 권한, SELinux, 샌드박스 | ls -l, User=, ausearch -m AVC, 38편 옵션 |
No such file or directory | 경로·WorkingDirectory 오류 | systemctl cat |
status=203/EXEC | 실행 파일 없음·실행 권한 없음 | ExecStart 경로 |
status=217/USER | 계정 없음 | getent passwd |
Start request repeated too quickly | 반복 실패로 한도 초과 | 첫 번째 실패 로그 |
code=killed, status=9/KILL | OOM, 타임아웃, 외부 kill | journalctl -k, 13·20편 |

[장애 1] 포트 충돌
labapp 재시작 → python 이 127.0.0.1:8082 bind 시도 → errno 98 → exit 1
→ Restart=on-failure 로 몇 번 재시도 → 모두 같은 오류 → failed
→ ss 로 8082 주인 확인 → 다른 사용자가 수동으로 띄운 프로세스
[장애 2] 권한 문제
app.py 권한 600 (root 전용) → 서비스는 User=analyst → python 이 파일을 못 읽음 → exit 2
→ ls -l 과 User= 를 비교하면 원인이 바로 보인다
두 장애 모두 systemd 설정은 정상 이다. 상태 화면만 보고 unit 파일을 고치기 시작하면 시간을 낭비한다. 로그의 오류 문구가 방향을 정해 준다.
# 준비: 서비스를 잠시 중지 (점검 시간 가정)
systemctl stop labapp
# 장애 1 유발: 다른 사용자가 같은 포트를 먼저 점유
cd /tmp; nohup python3 -m http.server 8082 --bind 127.0.0.1 > /dev/null 2>&1 & # (analyst)
# ① 증상 → ② 상태 → ③ 로그
systemctl restart labapp; sleep 2; systemctl is-active labapp
systemctl status labapp --no-pager | sed -n '1,8p'
journalctl -u labapp --since "-1min" --no-pager -o cat | grep -E 'Error|error|Address' | tail -3
# ④ 원인 → ⑤ 조치 → ⑥ 검증
ss -tlnp 'sport = :8082'
ps -o pid,ppid,user,lstart,cmd -p <점유 PID>
kill <점유 PID>
systemctl reset-failed labapp; systemctl start labapp; systemctl is-active labapp
python3 -c 'import urllib.request as u; print("HTTP", u.urlopen("http://127.0.0.1:8082/").status)'
# 장애 2: 권한
chmod 600 /opt/labapp/app.py; systemctl restart labapp; systemctl is-active labapp
systemctl show labapp -p Result,ExecMainStatus
journalctl -u labapp --since "-30s" --no-pager -o cat | grep -m1 -i 'permission'
ls -l /opt/labapp/app.py; systemctl show labapp -p User --value
chmod 644 /opt/labapp/app.py; systemctl reset-failed labapp; systemctl start labapp
실무에서 ⑤ 조치로 다른 사용자의 프로세스를 종료하기 전에는 소유자와 용도를 먼저 확인 한다. 이번 실습은 원인 프로세스가 실습용 테스트 프로세스라는 것을 알고 있는 상황이다.





텍스트 원본(실제 출력):
[root@rocky9-lab ~]# systemctl stop labapp; systemctl is-active labapp
inactive
[analyst@rocky9-lab ~]$ cd /tmp; nohup python3 -m http.server 8082 --bind 127.0.0.1 > /dev/null 2>&1 &
[analyst@rocky9-lab tmp]$ sleep 1; ss -tln 'sport = :8082' | tail -1
LISTEN 0 5 127.0.0.1:8082 0.0.0.0:*
[root@rocky9-lab ~]# systemctl restart labapp; sleep 2; systemctl is-active labapp
failed
[root@rocky9-lab ~]# systemctl status labapp --no-pager | sed -n '1,8p'
× labapp.service - Lab web app (python http.server)
Loaded: loaded (/etc/systemd/system/labapp.service; enabled; preset: disabled)
Drop-In: /etc/systemd/system/labapp.service.d
└─50-sandbox.conf, override.conf
Active: failed (Result: exit-code) since Thu 2026-09-24 12:33:15 UTC; 825ms ago
Duration: 96ms
Process: 6896 ExecStart=/usr/bin/python3 /opt/labapp/app.py (code=exited, status=1/FAILURE)
Main PID: 6896 (code=exited, status=1/FAILURE)
[root@rocky9-lab ~]# journalctl -u labapp --since "-1min" --no-pager -o cat | grep -E 'Error|error|Address' | tail -3
OSError: [Errno 98] Address already in use
OSError: [Errno 98] Address already in use
OSError: [Errno 98] Address already in use
[root@rocky9-lab ~]# ss -tlnp 'sport = :8082'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 5 127.0.0.1:8082 0.0.0.0:* users:(("python3",pid=6844,fd=3))
[root@rocky9-lab ~]# ps -o pid,ppid,user,lstart,cmd -p $(ss -tlnpH 'sport = :8082' | grep -oP 'pid=\K[0-9]+')
PID PPID USER STARTED CMD
6844 1 analyst Thu Sep 24 12:33:11 2026 python3 -m http.server 8082 --bind 127.0.0.1
[root@rocky9-lab ~]# kill $(ss -tlnpH 'sport = :8082' | grep -oP 'pid=\K[0-9]+'); sleep 1
[root@rocky9-lab ~]# systemctl reset-failed labapp; systemctl start labapp; sleep 1; systemctl is-active labapp
active
[root@rocky9-lab ~]# ss -tlnp 'sport = :8082'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 5 127.0.0.1:8082 0.0.0.0:* users:(("python3",pid=6951,fd=3))
[root@rocky9-lab ~]# python3 -c 'import urllib.request as u; print("HTTP", u.urlopen("http://127.0.0.1:8082/").status)'
HTTP 200
[root@rocky9-lab ~]# chmod 600 /opt/labapp/app.py; systemctl restart labapp; sleep 2; systemctl is-active labapp
failed
[root@rocky9-lab ~]# systemctl show labapp -p Result,ExecMainStatus
Result=exit-code
ExecMainStatus=2
[root@rocky9-lab ~]# journalctl -u labapp --since "-30s" --no-pager -o cat | grep -m1 -i 'permission'
/usr/bin/python3: can't open file '/opt/labapp/app.py': [Errno 13] Permission denied
[root@rocky9-lab ~]# ls -l /opt/labapp/app.py; systemctl show labapp -p User --value
-rw------- 1 root root 257 Sep 24 12:10 /opt/labapp/app.py
analyst
[root@rocky9-lab ~]# chmod 644 /opt/labapp/app.py; systemctl reset-failed labapp; systemctl start labapp; systemctl is-active labapp
active
| 관찰 | 의미 |
|---|---|
준비: inactive 후 analyst의 python이 127.0.0.1:8082 LISTEN | 서비스가 멈춘 사이 다른 프로세스가 포트를 차지했다 |
① failed | 증상 확인 |
② Drop-In: 50-sandbox.conf, override.conf | 28·38편에서 적용한 drop-in이 함께 표시된다. 설정 출처 도 status에서 확인한다 |
② Duration: 96ms, status=1/FAILURE | 시작 직후 오류로 종료했다. 203처럼 exec 단계 실패가 아니라 프로그램이 실행된 뒤 실패했다 |
③ OSError: [Errno 98] Address already in use × 3 | 오류 문구가 원인을 가리킨다. 같은 줄이 여러 번인 것은 Restart=on-failure로 재시도했기 때문이다 |
④ ss → python3 pid=6844, ps → analyst, PPID 1, python3 -m http.server 8082 | 포트 주인은 서비스가 아니라 사용자가 nohup으로 띄운 프로세스 다 (10편) |
⑤ kill → reset-failed → start → active | 원인 제거 후 실패 기록을 초기화하고 시작했다 (29편) |
⑥ 8082 주인이 새 PID 6951, HTTP 200 | 포트 확인과 실제 요청 까지 성공해야 해결이다 |
장애 2: failed, Result=exit-code, ExecMainStatus=2 | 이번에도 프로그램이 실행된 뒤 실패했다 |
can't open file '/opt/labapp/app.py': [Errno 13] Permission denied | python이 스크립트 파일을 읽지 못했다 |
-rw------- root root vs User=analyst | 파일 권한과 서비스 실행 계정의 불일치 가 원인이다 |
chmod 644 후 active | 권한 복구로 해결 |
| 주제 | 내용 |
|---|---|
| 포트 선점 | 서비스가 재시작되는 짧은 틈에 다른 프로세스가 포트를 차지하면, 사용자 요청이 엉뚱한 프로세스 로 간다. 1024 이상 포트는 일반 사용자도 열 수 있으므로 서비스 재시작 후 포트 주인을 확인한다 |
| 소켓 활성화 | 37편의 socket unit을 쓰면 포트를 PID 1이 계속 쥐고 있어 선점 문제가 생기지 않는다 |
| 권한 변경의 흔적 | 서비스 파일의 권한·소유자가 바뀌었다면 누가 바꿨는지 확인한다. 설정 관리 도구, 배포 스크립트, 또는 무단 변경일 수 있다 |
| 증거 보존 | 원인 프로세스를 바로 종료하지 말고 ps·/proc 정보를 먼저 기록한다 (05편). 장애 원인이 침해일 수도 있다 |
장애 대응 결과는 보고서 로 남겨야 재발 방지와 보안 판단이 가능하다.
[장애 보고서 템플릿]
대상 : host / unit / 발생 시각 (journal 기준, UTC)
증상 : systemctl is-active 결과, 영향 범위
상태 : Result, ExecMainCode/Status, Duration
로그 원문 : journalctl -u <unit> 의 핵심 오류 줄
원인 : 근거 명령과 결과 (ss / ls -l / systemctl cat)
조치 : 실행한 명령 (그대로)
검증 : is-active, 포트, 실제 요청 결과
보안 판단 : 원인이 운영 실수인가, 무단 변경·침해 신호인가
재발 방지 : 소켓 활성화, 권한 관리, 모니터링 항목 추가
| 보안 판단 신호 | 추가 확인 |
|---|---|
| 원인 프로세스의 소유자가 서비스와 무관한 계정 | 해당 계정 로그인 기록, 프로세스 exe·cmdline |
| 서비스 파일 권한이 예고 없이 바뀜 | 파일 ctime, 같은 시각 sudo 로그 |
| 같은 장애가 반복 | 자동화된 원인(스케줄 작업, 39편 경로) 확인 |
| 실수 | 결과 | 예방 |
|---|---|---|
| 로그를 보지 않고 재시작 반복 | StartLimit에 걸려 더 복잡해짐 | ③ 로그 먼저 |
| status의 마지막 몇 줄만 봄 | 첫 번째 원인 로그를 놓침 | journalctl -u -n 50 또는 --since |
| 원인 제거 없이 reset-failed | 같은 장애 반복 | ④ 원인 확정 후 ⑤ |
| is-active만 보고 해결 선언 | 기능은 여전히 장애 | 실제 요청으로 검증 |
| 조치 명령을 기록하지 않음 | 보고·재현 불가 | 명령과 결과를 그대로 기록 |
[ ] 6단계(증상·상태·로그·원인·조치·검증) 절차를 설명할 수 있다
[ ] Address already in use 로그로 포트 충돌을 판단했다
[ ] ss -tlnp 와 ps 로 포트 점유 프로세스의 정체를 확인했다
[ ] Permission denied 로그와 ls -l · User= 비교로 권한 문제를 찾았다
[ ] reset-failed 후 재시작하고 HTTP 요청으로 검증했다
[ ] 장애 보고서 템플릿에 맞춰 결과를 정리할 수 있다
is-active가 아니라 실제 요청 성공 으로 판단한다.Part 5 「보안과 SOC」를 시작하는 「41. 의심 프로세스 식별 기준」 에서는 지금까지 배운 프로세스 정보(exe, cwd, PPID, 사용자, 네트워크, cgroup)를 판단 기준표 로 정리하고, 정상 프로세스와 확인이 필요한 프로세스를 구분하는 점검을 실습한다.