본문 바로가기
IT/Linux

[Linux] systemd 서비스 오류 진단 및 해결: journalctl 활용법

by 수누다 2026. 7. 11.
반응형

[Linux] systemd 서비스 오류 진단 및 해결: journalctl 활용법

리눅스 서버를 운영하다 보면 갑자기 서비스가 죽어버리거나, 재부팅 이후 특정 데몬(daemon, 백그라운드 서비스)이 올라오지 않는 상황을 꼭 한 번쯤 겪게 됩니다. 저도 홈랩에서 웹 서버, 백업 에이전트, VPN 서비스까지 이것저것 올려두고 쓰다 보니, systemd 서비스 오류 해결이 생각보다 자주 필요한 작업이더라고요. 처음엔 서비스가 실패했다는 메시지만 보고 멍해졌었는데, 실제로는 journalctl(저널 로그 조회 도구)만 제대로 써도 원인 파악 속도가 확 달라집니다. 혹시 서비스는 안 뜨는데 왜 죽는지 감이 안 잡히는 경험 있으신가요? 오늘은 제가 직접 삽질하면서 정리한 방식으로, journalctl 사용법부터 리눅스 서비스 디버깅, systemd 유닛 실패, 그리고 부팅 문제 추적까지 한 번에 정리해보겠습니다.

systemd 서비스 오류 해결과 journalctl 로그 흐름을 설명하는 개요 다이어그램

systemd가 서비스 상태를 관리하고 journalctl이 로그를 추적하는 전체 흐름을 한눈에 보여주는 개요 이미지입니다.

1. 왜 systemd 서비스 오류 진단이 중요한가

예전에는 SysV init(전통적인 리눅스 초기화 방식) 스크립트를 직접 뒤져보던 시절도 있었지만, 요즘 주요 배포판은 대부분 systemd(시스템 및 서비스 관리자)를 씁니다. 서비스 시작, 중지, 재시작은 물론이고 부팅 시 의존성(dependency, 선후관계)까지 systemd가 관리하죠. 문제는 여기서 서비스 하나만 꼬여도 연쇄적으로 장애가 퍼질 수 있다는 점입니다.

예를 들어 이런 상황이 있습니다.

  • 웹 서비스는 죽었는데 포트 충돌인지, 권한 문제인지 모르겠을 때
  • 부팅은 됐는데 특정 유닛(unit, 서비스 정의 파일)이 실패해서 후속 작업이 멈췄을 때
  • 재시작 루프에 빠져 CPU를 계속 잡아먹을 때
  • 로그 파일이 흩어져 있어서 어디부터 봐야 할지 막막할 때

이럴 때 무작정 <code>systemctl restart만 반복하면 해결이 안 됩니다. 저도 처음엔 그러다가 로그 힌트를 놓쳐서 시간을 두 배로 쓴 적이 많았거든요. 여기서 중요한 포인트! systemd 서비스 오류 해결의 핵심은 상태 확인과 로그 확인을 분리해서 보는 것입니다.

2. systemd와 journalctl, 쉽게 말해 뭐가 다른가

쉽게 말해 systemctl은 "서비스를 관리하는 도구"이고, journalctl은 "무슨 일이 있었는지 기록을 읽는 도구"입니다. 둘을 같이 써야 제대로 보입니다.

도구 역할 주요 용도
systemctl 서비스 제어 및 상태 조회 start, stop, restart, status
journalctl 저널 로그 조회 에러 원인 분석, 시간대별 추적
systemd unit 서비스 정의 파일 ExecStart, Restart, After 등 설정

보통 장애 분석은 이런 흐름으로 갑니다.

  1. systemctl status로 현재 실패 상태를 확인합니다.
  2. journalctl -u 서비스명으로 해당 서비스 로그를 좁혀봅니다.
  3. 필요하면 부팅 단위 로그, 이전 부팅 로그까지 확장해서 봅니다.
  4. 원인이 설정 파일인지, 권한인지, 실행 파일 경로인지 확인합니다.

이 패턴만 익혀도 대부분의 리눅스 서비스 디버깅은 훨씬 수월해집니다.

3. 가장 먼저 보는 기본 명령어

실전에서는 저는 거의 항상 아래 순서로 확인합니다. 눈에 익혀두시면 정말 편합니다.

3-1. 서비스 상태 확인

sudo systemctl status nginx.service

여기서 봐야 할 포인트는 세 가지예요.

  • Loaded: 유닛 파일이 정상적으로 읽혔는지
  • Active: active, failed, activating 상태가 어떤지
  • Main PID / Exit code: 프로세스가 왜 종료됐는지

출력 하단에 최근 로그 일부가 붙는 경우도 있는데, 그걸로 끝내면 안 됩니다. 진짜 원인은 뒤에 더 있을 때가 많거든요.

3-2. 특정 서비스 로그 보기

sudo journalctl -u nginx.service

이 명령은 해당 유닛에 연결된 로그만 쏙 빼서 보여줍니다. 로그가 많으면 아래처럼 최근 것만 보는 식으로 줄여서 봐도 좋습니다.

sudo journalctl -u nginx.service -n 50

실시간으로 따라가려면 tail(로그 끝 추적) 같이 -f 옵션을 쓰면 편합니다.

sudo journalctl -u nginx.service -f

제가 직접 해보니 재시작 테스트할 때 이 조합이 제일 편하더라고요. 한쪽 터미널에서는 로그를 보고, 다른 쪽에서는 서비스를 재시작합니다.

sudo systemctl restart nginx.service

3-3. 에러 우선으로 좁혀보기

sudo journalctl -p err..alert

이건 시스템 전체에서 error 이상의 심각한 로그만 골라서 봅니다. 서비스명이 명확하지 않거나, 어디서 문제가 시작됐는지 감이 안 올 때 꽤 유용해요.

4. 실전으로 보는 journalctl 활용법

이제 실제 장애 분석 흐름으로 가보겠습니다. 예시로 사용자 정의 앱 서비스가 실행 실패하는 상황을 가정해볼게요.

4-1. 실패한 유닛만 먼저 확인

sudo systemctl --failed

부팅 직후 이상이 있을 때는 이 명령이 정말 좋습니다. 실패한 유닛을 한 번에 보여주니까요. 특히 systemd 유닛 실패가 여러 개 겹쳐 있을 때 시작점 찾기에 좋아요.

4-2. 해당 유닛 상세 상태 확인

sudo systemctl status myapp.service

예를 들어 여기서 code=exited, status=203/EXEC 같은 메시지가 보이면 실행 파일 경로나 권한을 먼저 의심해봅니다. 저도 처음엔 애플리케이션 문제인 줄 알고 코드부터 뒤졌는데, 알고 보니 ExecStart 경로 오타였던 적이 있습니다. 이런 거 은근 자주 나옵니다 ㅎㅎ

4-3. 시간 범위로 로그 좁히기

sudo journalctl -u myapp.service --since "2026-07-11 09:00:00" --until "2026-07-11 09:10:00"

재배포 직후나 재부팅 직후처럼 문제 발생 시점이 뚜렷하면 시간 범위를 넣는 게 좋습니다. 로그가 긴 서버일수록 이게 체감상 엄청 크다니까요.

systemd 서비스 오류 해결을 위해 상태와 로그를 함께 보는 터미널 화면

서비스 상태와 저널 로그를 나란히 확인하면서 실행 경로, 권한, 포트 충돌 같은 원인을 추적하는 장면을 표현한 이미지입니다.

4-4. 현재 부팅 기준 로그 보기

sudo journalctl -b

-b는 현재 부팅(boot, 시스템 시작 세션) 기준 로그를 보여줍니다. 부팅 직후 서비스가 안 뜨는 경우엔 정말 자주 씁니다. 시스템 전체를 보고 싶을 때는 좋지만, 특정 서비스 분석이라면 유닛 옵션과 같이 쓰는 편이 나아요.

sudo journalctl -b -u myapp.service

4-5. 이전 부팅 로그 확인

sudo journalctl -b -1

이건 부팅 문제 잡을 때 핵심입니다. 오늘은 정상인데 어제 재부팅에서는 실패했다면, 이전 부팅 로그를 보는 게 맞습니다. 실제로 써보니 "지금은 재현 안 되는데 아까는 왜 안 됐지?" 같은 상황에서 아주 큰 도움이 되더라고요.

sudo journalctl -b -1 -u myapp.service

5. 자주 만나는 장애 유형과 해결 패턴

여기부터는 제가 실무와 홈랩에서 자주 본 패턴 위주로 정리해보겠습니다. 모든 에러를 외울 필요는 없고, 로그에서 어떤 방향으로 해석할지만 익혀도 충분합니다.

5-1. 실행 파일 경로 오류

ExecStart에 지정한 바이너리(binary, 실행 파일) 경로가 틀리면 서비스는 시작도 못 합니다.

sudo systemctl cat myapp.service

유닛 내용을 확인해서 ExecStart=/opt/myapp/bin/start.sh 같은 경로가 실제 존재하는지 봅니다.

ls -l /opt/myapp/bin/start.sh

없거나 실행 권한이 없으면 아래처럼 수정합니다.

sudo chmod +x /opt/myapp/bin/start.sh
sudo systemctl daemon-reload
sudo systemctl restart myapp.service

5-2. 포트 충돌

웹 서버나 에이전트 서비스는 포트 충돌 때문에 죽는 경우가 많습니다. 로그에 address already in use 같은 문구가 보이면 거의 이쪽입니다.

sudo ss -lntp

어떤 프로세스가 포트를 잡고 있는지 확인하고, 중복 서비스가 있으면 정리합니다.

5-3. 권한 문제

systemd 서비스는 보통 root 또는 특정 사용자 계정으로 실행됩니다. 파일 접근 권한이 안 맞으면 시작 직후 종료됩니다.

sudo journalctl -u myapp.service -n 100 | grep -i "permission\|denied"

여기서 중요한 건 서비스 실행 계정입니다.

sudo systemctl show myapp.service -p User -p Group

로그에 Permission denied가 보이면 파일 소유권(owner)과 권한부터 확인해봐요.

5-4. 환경 변수 누락

애플리케이션이 쉘에서는 잘 뜨는데 systemd에서는 실패하는 경우가 있습니다. 이건 환경 변수(environment variable) 차이인 경우가 많습니다. 저도 Docker가 아닌 순수 서비스로 앱 띄울 때 여기서 꽤 삽질했어요.

sudo systemctl cat myapp.service

필요하면 유닛에 다음처럼 넣습니다.

[Service]
Environment="APP_ENV=production"
Environment="JAVA_HOME=/usr/lib/jvm/java-17-openjdk"

수정 후엔 꼭 다시 읽혀야 합니다.

sudo systemctl daemon-reload
sudo systemctl restart myapp.service

6. ⚠️ 제가 실제로 자주 겪은 트러블슈팅 포인트

이 섹션은 정말 현장에서 체감 큰 부분입니다. 문법은 맞는데 계속 안 되는 상황, 그거 꽤 답답하거든요.

  • daemon-reload를 빼먹는 실수: 유닛 파일 수정 후 sudo systemctl daemon-reload를 안 하면 이전 설정이 계속 적용됩니다.
  • 로그 보존 정책 차이: 환경에 따라 journal이 메모리 기반으로만 남아 재부팅 후 예전 로그가 제한적일 수 있습니다.
  • 서비스 이름 착각: 패키지명과 유닛명이 다를 수 있습니다. 예를 들어 실제 유닛명이 예상과 다를 수 있으니 systemctl list-units --type=service로 확인하세요.
  • Restart 정책 오해: Restart=always 때문에 서비스가 계속 재기동되면 원인 로그가 빨리 지나갑니다. 이럴 땐 실시간 로그 추적이 필수예요.

특히 재시작 루프는 초보 때 진짜 헷갈립니다. 서비스는 살아있는 것처럼 보이는데, 사실은 죽고 다시 뜨고를 반복하고 있거든요.

sudo systemctl show myapp.service -p Restart -p RestartSec -p ExecMainStatus

이렇게 정책과 종료 상태를 함께 보면 흐름이 좀 더 명확해집니다.

7. 검증과 결과 확인은 이렇게 합니다

문제를 수정한 뒤에는 단순히 서비스가 올라왔는지만 보지 말고, 실제로 안정적으로 동작하는지 확인해야 합니다. 저도 예전엔 active (running)만 보고 끝냈다가 30초 후 다시 죽는 걸 놓친 적이 있었습니다.

  1. 서비스 상태를 다시 확인합니다.
  2. 최근 로그에 에러가 사라졌는지 봅니다.
  3. 필요하면 포트, 프로세스, 응답까지 점검합니다.
  4. 재부팅 후에도 자동 시작이 되는지 확인합니다.
sudo systemctl status myapp.service
sudo journalctl -u myapp.service -n 30
sudo systemctl is-enabled myapp.service
sudo systemctl is-active myapp.service

웹 서비스라면 간단히 응답 체크도 해보면 좋습니다.

curl -I http://127.0.0.1:8080

이 단계까지 통과하면 드디어 됐다! 싶은 순간이 옵니다. 이런 맛에 로그 보는 거죠.

systemd 서비스 오류 해결 후 정상 동작을 검증하는 장면

수정 이후 서비스가 정상 실행되고 최근 로그에 치명적인 오류가 사라진 상태를 확인하는 검증 이미지를 위한 위치입니다.

8. 빠르게 보는 명령어 치트시트

정리 차원에서 많이 쓰는 명령을 표로 묶어보겠습니다. 장애 대응 중엔 이런 요약이 꽤 도움이 됩니다.

상황 명령어 설명
서비스 상태 확인 systemctl status 서비스명 현재 상태와 최근 로그 확인
특정 서비스 로그 journalctl -u 서비스명 해당 유닛 관련 로그만 조회
최근 로그 50줄 journalctl -u 서비스명 -n 50 최근 에러 빠르게 확인
실시간 추적 journalctl -u 서비스명 -f 재시작 테스트와 함께 보기 좋음
현재 부팅 로그 journalctl -b 이번 부팅 기준 전체 로그
이전 부팅 로그 journalctl -b -1 재부팅 전 문제 추적
실패 유닛 확인 systemctl --failed 실패한 서비스 일괄 조회

이 정도만 손에 익으면 대부분의 systemd 서비스 오류 해결 작업은 훨씬 빨라집니다. 그리고 다음 글에서는 systemd unit 파일 작성법Restart 정책 설계도 따로 다뤄볼 예정입니다. 이전 글에서 로그 로테이션(log rotation, 로그 보관 정책) 관련 내용을 보셨다면 함께 연결해서 보셔도 좋습니다.

journalctl 사용법과 systemd 서비스 오류 해결 순서를 요약한 인포그래픽

자주 쓰는 journalctl 옵션과 장애 대응 순서를 요약한 인포그래픽용 이미지 위치입니다.

9. 마무리: 로그를 보면 서비스가 왜 죽는지 보입니다

systemd 서비스 오류 해결은 막연히 어려워 보이지만, 사실 흐름은 꽤 단순합니다. 상태 확인은 systemctl, 원인 추적은 journalctl. 이 두 축만 잘 잡으면 됩니다. 저도 처음엔 서비스가 failed로 뜨면 겁부터 났는데, 하나씩 로그를 따라가다 보니 오히려 장애 대응의 기준점이 생기더라고요.

오늘 정리한 내용을 다시 압축하면 이렇습니다.

  • 서비스가 죽었으면 먼저 status로 현재 상태를 봅니다.
  • 원인은 journalctl로 시간대와 유닛 기준으로 좁혀봅니다.
  • 부팅 문제-b-b -1 조합으로 추적합니다.
  • 실행 경로, 권한, 포트, 환경 변수를 우선 의심하면 해결이 빠릅니다.

혹시 지금도 특정 서비스가 이유 없이 죽고 있다면, 무작정 재시작부터 하지 마시고 저널부터 한 번 열어보세요. 생각보다 답이 로그 안에 그대로 들어있습니다. 이거 진짜 편하더라고요. 다음 글에서는 서비스 자동 재시작 정책과 의존성 설정을 조금 더 깊게 다뤄보겠습니다.

반응형