Linux에서 프로세스가 갑자기 종료되는 원인 분석
서버를 운영하다 보면 분명 실행 중이던 프로세스가 어느 순간 흔적도 없이 사라질 때가 있습니다.
웹서비스라면 갑자기 접속이 끊기고, 배치 작업이라면 중간에서 멈춥니다. 다시 실행하면 멀쩡해서 더 헷갈리는 경우도 많습니다.
이럴 때 단순히 프로세스를 다시 올리는 것만으로 끝내면 원인은 그대로 남습니다. 몇 시간 뒤, 혹은 며칠 뒤 똑같은 문제가 다시 생길 수 있습니다.
실제로 확인해야 할 것은 하나입니다.
프로세스가 스스로 종료된 것인지, 외부에서 종료된 것인지, 아니면 Linux Kernel이 강제로 죽인 것인지를 먼저 구분해야 합니다.
정말 종료된 게 맞는지부터 본다
서비스가 응답하지 않는다고 해서 반드시 프로세스가 죽은 것은 아닙니다.
CPU를 과도하게 사용하고 있거나 디스크 I/O를 기다리면서 멈춰 있는 상태일 수도 있습니다.
먼저 프로세스가 남아 있는지 확인합니다.
ps -ef | grep 프로세스명
PID를 알고 있다면 상태까지 같이 보는 편이 좋습니다.
ps -p 18432 -o pid,ppid,stat,etime,cmd
여기서 STAT 값도 눈여겨봅니다.
예를 들어 D 상태라면 프로세스가 사라진 것이 아니라 보통 I/O 응답을 기다리면서 빠져나오지 못하고 있는 상태입니다.
디스크나 NFS 같은 스토리지 문제가 있을 때 이런 상황을 만나기도 합니다.
그래서 첫 단계에서는
프로세스가 죽었다
와
프로세스는 살아 있는데 일을 못 하고 있다
를 구분해 두는 것이 좋습니다.
흔적 없이 사라졌다면 커널 로그부터 본다
프로세스가 실제로 사라졌다면 저는 애플리케이션 로그보다 먼저 커널 로그를 확인하는 편입니다.
특히 Java, 데이터베이스, WAS처럼 메모리를 많이 사용하는 프로세스라면 OOM Killer 흔적을 먼저 보는 것이 빠릅니다.
journalctl -k | grep -Ei "oom|out of memory|killed process"
또는
dmesg -T | grep -Ei "oom|out of memory|killed process"
이런 로그가 발견될 수 있습니다.
Out of memory: Killed process 18432 (java)
anon-rss:6243180kB
이 경우 Java가 자체적으로 종료된 것이 아닙니다.
Linux Kernel이 메모리 부족 상황을 버티지 못하고 특정 프로세스를 골라 강제로 종료한 것입니다.
여기까지만 보고 “RAM 부족”이라고 결론을 내리면 조금 이릅니다.
free -h
vmstat 1
Swap이 급격하게 늘었는지, 시스템 전체가 메모리 압박을 받고 있었는지까지 같이 봐야 합니다.
서버에는 메모리가 남았는데 OOM으로 죽는 경우도 있다
현장에서 꽤 헷갈리는 상황입니다.
free -h를 확인했는데 서버에는 메모리가 남아 있습니다. 그런데 서비스 로그를 보면 분명 OOM으로 프로세스가 종료됐습니다.
이런 경우라면 서버 전체 RAM이 아니라 프로세스가 속한 cgroup의 메모리 제한을 봐야 합니다.
systemd 서비스라면 다음 값부터 확인할 수 있습니다.
systemctl show 서비스명 | grep -Ei "MemoryMax|MemoryCurrent"
프로세스가 어느 cgroup에 속해 있는지도 확인합니다.
cat /proc/18432/cgroup
cgroup v2를 사용하고 있다면 memory.events도 상당히 유용합니다.
cat /sys/fs/cgroup/해당경로/memory.events
예를 들어
oom 3
oom_kill 2
처럼 나온다면 해당 cgroup 안에서 실제 메모리 부족과 프로세스 종료가 발생했다는 뜻입니다.
Docker나 Kubernetes 환경에서 “서버에는 메모리가 남아 있는데 컨테이너만 자꾸 죽는다”면 이쪽을 먼저 의심해볼 만합니다.
종료 코드 137을 보고 바로 OOM이라고 하면 안 된다
프로세스가 종료된 뒤 137이라는 코드를 보고 OOM이라고 단정하는 경우가 많습니다.
137 = 128 + 9
signal 9, 즉 SIGKILL로 종료됐을 때 shell에서 자주 보이는 값입니다.
OOM Killer 역시 프로세스를 강제로 죽일 때 SIGKILL을 사용하기 때문에 OOM 상황에서 137을 자주 보게 됩니다.
하지만 사람이 직접
kill -9 18432
를 실행해도 결과는 똑같을 수 있습니다.
따라서
137 = SIGKILL 가능성
까지는 맞지만
137 = OOM 확정
은 아닙니다.
OOM인지 확인하려면 반드시 커널 로그와 같이 봐야 합니다.
비슷하게 143은 SIGTERM과 연결되는 경우가 많습니다.
143 = 128 + 15
종료 코드는 원인을 확정하는 증거라기보다 방향을 좁히는 단서에 가깝습니다.
애플리케이션 로그가 갑자기 끊겼다면 SIGKILL도 의심한다
정상적으로 종료되는 프로그램이라면 대개 종료 과정에서 로그를 남깁니다.
그런데 로그가 이런 식으로 끊겨 있다면 이야기가 달라집니다.
10:31:01 request complete
10:31:02 request complete
10:31:03 request complete
이후 아무런 종료 메시지도 없다면 프로세스가 정상적인 종료 루틴을 거치지 못했을 가능성이 있습니다.
같은 시간대의 시스템 로그를 같이 봅니다.
journalctl --since "10:25" --until "10:35"
만약 바로 뒤에
10:31:04 Out of memory: Killed process 18432
가 있다면 원인이 거의 연결됩니다.
반대로 OOM 기록이 없다면 관리자 작업, 배포 스크립트, supervisor, systemd 같은 다른 종료 주체도 살펴봐야 합니다.
systemd가 재시작을 반복하고 있을 수도 있다
서비스를 systemd로 관리한다면 systemctl status를 꼭 확인하는 편이 좋습니다.
systemctl status 서비스명
그리고 실제 로그를 봅니다.
journalctl -u 서비스명
다음 같은 문구가 보일 수 있습니다.
Main process exited, code=killed, status=9/KILL
또는
Failed with result 'timeout'
이 경우 애플리케이션 내부 문제만 볼 것이 아니라 systemd 설정까지 확인해야 합니다.
systemctl show 서비스명 | grep -Ei "Timeout|Watchdog|Restart"
특히 Restart=always 같은 설정이 들어가 있으면 프로세스가 죽어도 곧바로 다시 살아납니다.
그래서 겉으로는 서비스가 정상처럼 보이는데 실제로는 하루에도 여러 번 프로세스가 종료되고 재시작되는 서버가 있습니다.
이럴 때 단순히
ps -ef
만 확인하면 이상이 없어 보입니다.
오히려 재시작 횟수와 종료 시점을 같이 보는 것이 중요합니다.
Segmentation Fault라면 완전히 다른 방향으로 봐야 한다
메모리 부족이 아니라 애플리케이션 자체 문제로 죽는 경우도 있습니다.
특히 C/C++ 기반 프로그램이나 네이티브 라이브러리를 사용하는 프로그램이라면 Segmentation Fault가 원인이 될 수 있습니다.
journalctl -k | grep -i segfault
또는
dmesg -T | grep -i segfault
이런 형태의 로그가 보일 수 있습니다.
myapp[23174]: segfault at 0 ip 00007f...
이 경우 단순히 서버 메모리를 늘리는 식으로 해결할 문제가 아닙니다.
애플리케이션 버그나 라이브러리 충돌, 잘못된 메모리 접근 등을 봐야 합니다.
서버에 systemd-coredump가 활성화돼 있다면 core dump도 확인해볼 수 있습니다.
coredumpctl list
특정 PID에 대한 정보를 봅니다.
coredumpctl info 23174
필요하다면 디버거로 연결할 수도 있습니다.
coredumpctl debug 23174
여기까지 자료가 남아 있다면 단순히 “프로세스가 죽었다” 수준이 아니라 어느 코드 구간에서 문제가 발생했는지까지 좁혀갈 수 있습니다.
SSH를 끊었더니 작업도 같이 사라지는 경우
배치나 스크립트를 직접 실행할 때 가끔 볼 수 있는 상황입니다.
SSH 접속 후 다음처럼 실행했다고 해보겠습니다.
./batch.sh
작업이 오래 걸려서 SSH 창을 닫았는데 프로세스까지 같이 사라졌다면 서버 장애가 아닐 수 있습니다.
터미널 세션과 묶여 실행된 프로세스가 SIGHUP의 영향을 받은 것입니다.
장시간 실행할 작업이라면 환경에 맞게 systemd, tmux, screen, nohup 등으로 세션에서 분리하는 편이 낫습니다.
예를 들면
nohup ./batch.sh > batch.log 2>&1 &
처럼 실행할 수 있습니다.
초보적인 문제처럼 보이지만 실제 운영 중에도 종종 생깁니다.
파일 디스크립터 부족도 같이 확인한다
프로세스가 죽기 전 이런 메시지가 반복됐다면
Too many open files
파일 디스크립터 제한도 확인해볼 필요가 있습니다.
cat /proc/18432/limits
현재 FD 개수는 다음처럼 볼 수 있습니다.
ls /proc/18432/fd | wc -l
여기서 하나 구분할 것이 있습니다.
FD 제한에 도달했다고 Linux Kernel이 보통 프로세스를 바로 죽이는 것은 아닙니다.
새로운 파일이나 socket을 열지 못하고 에러가 발생한 뒤, 애플리케이션이 그 상황을 제대로 처리하지 못하면서 종료되는 경우가 더 일반적입니다.
즉, “FD가 부족해서 Kernel이 죽였다”가 아니라 FD 고갈이 먼저 발생하고 그 뒤 애플리케이션이 무너진 것인지를 봐야 합니다.
부모 프로세스가 죽으면서 같이 사라지는 경우도 있다
worker 구조의 프로그램이나 shell script에서 실행된 프로세스라면 부모 프로세스도 같이 볼 필요가 있습니다.
pstree -p
또는
ps -o pid,ppid,cmd -p 18432
로 부모 PID를 확인할 수 있습니다.
겉으로는 worker 하나가 갑자기 사라진 것처럼 보여도 실제 시작점은 master process나 supervisor가 종료된 경우도 있습니다.
서비스 구조가 복잡할수록 죽은 프로세스 하나만 보지 말고 누가 그 프로세스를 관리하고 있었는지를 따라가는 것이 좋습니다.
재부팅한 뒤라면 현재 로그만 보면 놓칠 수 있다
장애가 발생한 뒤 서버를 재부팅했다면 현재 journalctl에서 원인이 안 보일 수도 있습니다.
부팅 기록부터 확인합니다.
journalctl --list-boots
직전 부팅 세션은 다음처럼 볼 수 있습니다.
journalctl -b -1
Kernel 로그만 보고 싶다면
journalctl -k -b -1
을 사용합니다.
이 부분이 생각보다 중요합니다.
서버를 재부팅한 다음 현재 로그만 확인하고
“OOM 기록이 없으니 OOM은 아니네.”
라고 판단하면 이전 부팅 세션에 남아 있는 중요한 흔적을 놓칠 수 있습니다.
실제로는 이 순서로 보면 빠르다
프로세스가 갑자기 사라진 서버를 만났을 때 모든 가능성을 한꺼번에 보는 것보다 범위를 좁혀가는 편이 빠릅니다.
ps로 프로세스가 실제로 종료됐는지 먼저 봅니다.journalctl -k에서 OOM과 segfault 흔적을 찾습니다.- systemd 서비스라면
systemctl status와journalctl -u를 확인합니다. - 종료 코드와 signal 정보를 함께 봅니다.
- 메모리가 남아 있다면 cgroup 제한을 확인합니다.
- segfault라면 core dump가 남아 있는지 확인합니다.
- 종료 직전 5~10분의 journal을 시간순으로 봅니다.
- 관리자 작업, 배포 스크립트, supervisor, 부모 프로세스까지 범위를 넓힙니다.
여기서 중요한 것은 명령어 개수가 아닙니다.
누가 프로세스를 종료했는지를 한 단계씩 좁혀가는 것이 핵심입니다.
프로세스가 살아 있다는 것만 감시해서는 부족하다
운영 서버에서는 PID가 존재하는지만 확인하는 모니터링을 종종 사용합니다.
하지만 systemd가 자동으로 재시작하는 환경에서는 이런 방식만으로 장애를 놓칠 수 있습니다.
예를 들어
10:30:15 프로세스 종료
10:30:17 systemd 재시작
10:30:20 정상 응답
처럼 5초 안에 복구된다면 1분 간격으로 상태를 확인하는 모니터링에서는 아예 잡히지 않을 수도 있습니다.
그래서 중요한 서비스라면 단순 생존 여부뿐 아니라
restart 횟수, OOM 발생, core dump 생성, 비정상 종료 signal, 메모리 증가 추세
같은 정보도 함께 보는 편이 좋습니다.
현재 살아 있다는 사실보다 왜 계속 죽었다가 다시 살아나는지가 더 중요한 경우가 많습니다.
마무리
Linux에서 프로세스가 갑자기 사라졌다고 해서 원인이 항상 메모리 부족인 것은 아닙니다.
OOM Killer일 수도 있고, SIGKILL일 수도 있습니다. systemd가 종료했거나 애플리케이션에서 Segmentation Fault가 발생했을 수도 있고, 컨테이너의 메모리 제한에 걸렸을 수도 있습니다.
그래서 프로세스를 다시 실행하는 것부터 하기보다 먼저 종료 주체를 찾는 것이 좋습니다.
커널 로그와 systemd 로그를 같은 시간대에 맞춰보고, signal과 exit code를 확인한 뒤 필요하면 cgroup이나 core dump까지 들어가면 원인을 꽤 빠르게 좁힐 수 있습니다.
운영 중인 서버에서 프로세스가 갑자기 사라졌다면 기억할 것은 하나입니다.
프로세스가 사라진 것은 장애의 결과일 뿐이고, 실제 원인은 그 직전에 이미 흔적을 남겼을 가능성이 높습니다.
그 몇 분을 제대로 확인할 수 있느냐가 단순 재시작과 실제 장애 분석의 차이를 만듭니다.




댓글 0
첫 댓글을 남겨보세요.