systemd 서비스 실패 문제 해결: 단계별 가이드

systemd 서비스 실패에 직면하셨나요? 이 포괄적인 가이드는 일반적인 시작 문제를 진단하고 해결하기 위한 단계별 접근 방식을 제공합니다. 로그 분석을 위해 `systemctl status` 및 `journalctl`을 활용하고, 잘못된 구성을 위해 유닛 파일을 꼼꼼히 검사하고, 종속성 문제를 식별 및 수정하고, 환경 변수 장애를 해결하는 방법을 배우세요. 실용적인 예제와 고급 팁을 통해 Linux 서비스를 빠르고 효율적으로 다시 온라인 상태로 만드는 데 자신감을 얻을 수 있을 것입니다.

36 조회수

Systemd 서비스 실패 문제 해결: 단계별 가이드

Systemd는 대부분의 최신 Linux 배포판에서 사실상의 시스템 및 서비스 관리자로 자리 잡았으며, 서비스, 데몬 및 프로세스 관리에 중요한 역할을 합니다. 강력하고 효율적이지만 systemd가 관리하는 서비스는 때때로 시작에 실패하여 애플리케이션 중단 또는 시스템 불안정을 초래할 수 있습니다. 이러한 실패를 진단하려면 systemd의 강력한 로깅 및 자체 검사 기능을 활용하는 체계적인 접근 방식이 필요합니다.

이 가이드는 일반적인 systemd 서비스 시작 실패를 해결하기 위한 포괄적인 단계별 방법론을 제공합니다. 초기 상태 확인 및 로그 심층 분석부터 유닛 파일 검사, 복잡한 종속성 문제 해결까지 모든 것을 다룰 것입니다. 이 글을 마치면 대부분의 systemd 서비스 실패를 효율적으로 진단하고 해결하여 애플리케이션과 서비스가 원활하게 실행되도록 보장하는 실질적인 지식과 도구를 갖추게 될 것입니다.

최전선 방어: systemctl status

서비스 시작에 실패하면 가장 먼저 실행해야 하는 명령은 systemctl status <service_name>입니다. 이 명령은 서비스의 현재 상태에 대한 스냅샷을 제공하며, 활성 상태인지, 로드되었는지, 그리고 가장 중요하게는 최근 로그의 일부를 보여줍니다. 이를 통해 종종 문제를 신속하게 식별할 수 있는 충분한 정보를 얻을 수 있습니다.

웹 애플리케이션 서비스 mywebapp.service가 시작되지 않는다고 가정해 보겠습니다.

systemctl status mywebapp.service

예시 출력 해석:

● mywebapp.service - My Web Application
     Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: disabled)
     Active: failed (Result: exit-code) since Mon 2023-10-26 10:30:05 UTC; 10s ago
    Process: 12345 ExecStart=/usr/local/bin/mywebapp-start.sh (code=exited, status=1/FAILURE)
   Main PID: 12345 (code=exited, status=1/FAILURE)
        CPU: 10ms

Oct 26 10:30:05 hostname systemd[1]: Started My Web Application.
Oct 26 10:30:05 hostname mywebapp-start.sh[12345]: Error: Port 8080 already in use
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Main process exited, code=exited, status=1/FAILURE
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Failed with result 'exit-code'.

이 출력에서 다음을 즉시 확인할 수 있습니다.
* mywebapp.service 서비스가 failed 상태입니다.
* Result: exit-code로 실패했으며, 이는 ExecStart 명령이 0이 아닌 상태로 종료되었음을 의미합니다.
* Process 줄은 mywebapp-start.sh 명령이 status=1/FAILURE로 실패했음을 보여줍니다.
* 가장 중요한 것은 로그 줄에 Error: Port 8080 already in use라고 표시되어 있습니다. 이는 문제의 명확한 지표입니다.

이 명령은 첫 번째 진단 도구이며, 종종 문제의 원인을 직접적으로 가리키거나 다음에 어디를 봐야 할지 범위를 좁혀줍니다.

journalctl로 깊이 파고들기

systemctl status가 빠른 요약을 제공하는 반면, journalctl은 자세한 로깅을 위한 필수 명령입니다. systemd 저널을 쿼리하며, 이 저널은 서비스를 포함한 시스템의 모든 부분에서 로그를 수집합니다.

기본 로그 검토

특정 서비스의 모든 로그(기록 항목 포함)를 보려면 다음을 사용합니다.

journalctl -u mywebapp.service

이렇게 하면 mywebapp.service와 관련된 모든 로그 항목이 표시됩니다. 서비스가 반복적으로 실패하는 경우 각 실패 시도에 대한 항목이 표시됩니다.

필터링 및 시간 기반 쿼리

결과를 좁히려면, 특히 최근 실패 후에는 --since--priority와 같은 플래그를 사용할 수 있습니다.

  • 특정 시간 이후의 로그 표시:
    bash journalctl -u mywebapp.service --since "10 minutes ago" journalctl -u mywebapp.service --since "2023-10-26 10:00:00"
  • 오류 수준 이상의 메시지만 표시:
    bash journalctl -u mywebapp.service -p err
  • -xe와 결합하여 확장된 설명 및 자세한 출력 보기:
    bash journalctl -u mywebapp.service -xe --since "5 minutes ago"
    journalctl -xe는 특정 로그 메시지에 대한 설명과 사용 가능한 경우 스택 추적을 포함한 추가 컨텍스트를 제공하므로 매우 유용합니다.

로그 메시지 이해

Error, Failed, Warning과 같은 키워드 또는 무엇이 잘못되었는지 나타내는 애플리케이션별 메시지를 찾으십시오. 타임스탬프에 주의를 기울여 실패로 이어진 일련의 사건을 이해하십시오.

팁: 서비스의 ExecStart 스크립트가 표준 출력 또는 표준 오류로 메시지를 출력하는 경우, 해당 메시지는 일반적으로 journalctl에 캡처됩니다. 스크립트가 설명적인 오류 메시지를 기록하도록 하십시오.

유닛 파일 검사: 서비스의 청사진

모든 systemd 서비스는 유닛 파일(예: mywebapp.service)에 의해 정의됩니다. 이 파일의 잘못된 구성은 시작 실패의 일반적인 원인입니다. 서비스가 무엇을 하려고 하는지 이해해야 합니다.

유닛 파일 검색

서비스의 활성 유닛 파일을 보려면 다음을 사용합니다.

systemctl cat mywebapp.service

이 명령은 systemd가 사용하는 정확한 유닛 파일을 표시하며, 모든 재정의도 포함됩니다.

확인할 주요 지시어

실행 관련 문제는 [Service] 섹션에, 종속성은 [Unit] 섹션에 집중하십시오.

  • ExecStart: systemd가 서비스를 시작하기 위해 실행하는 명령입니다. 경로가 올바른지, 그리고 명령 자체가 실행 가능한지, 그리고 지정된 User로 호출했을 때 성공적으로 실행되는지 확인하십시오.
    ini ExecStart=/usr/local/bin/mywebapp-start.sh
  • Type: 프로세스 시작 유형을 정의합니다. 일반적인 유형은 다음과 같습니다.
    • simple (기본값): ExecStart가 주 프로세스입니다.
    • forking: ExecStart가 자식 프로세스를 포크하고 부모는 종료됩니다. systemd는 부모가 종료될 때까지 기다립니다.
    • oneshot: ExecStart가 실행되고 종료됩니다. 명령이 실행되는 동안 systemd는 서비스를 활성 상태로 간주합니다.
    • notify: 서비스가 준비되면 systemd에 알림을 보냅니다.
    • 잘못된 Type은 서비스가 실제로 시작되었을 때 systemd가 실패했다고 생각하게 하거나 그 반대의 결과를 초래할 수 있습니다.
  • User / Group: 서비스가 실행될 사용자 및 그룹입니다. 권한 문제는 종종 서비스가 이 사용자 아래에서 액세스 권한이 없는 파일이나 리소스에 액세스하려고 시도하는 데서 비롯됩니다.
    ini User=mywebappuser Group=mywebappgroup
  • WorkingDirectory: 서비스가 실행될 디렉토리입니다. ExecStart 또는 다른 명령의 상대 경로는 여기에 따라 달라집니다.
  • Restart: 서비스가 언제 다시 시작되어야 하는지를 정의합니다. on-failure 또는 always로 설정된 경우, 실패한 서비스가 계속 다시 시작될 수 있어 초기 실패를 감지하기 어렵습니다.
  • TimeoutStartSec / TimeoutStopSec: systemd가 서비스가 시작되거나 중지될 때까지 기다리는 시간입니다. 서비스 초기화에 TimeoutStartSec보다 더 오래 걸리면 systemd가 서비스를 종료하고 실패를 보고합니다.

일반적인 유닛 파일 문제

  • 잘못된 경로: ExecStart 또는 다른 파일 경로의 오타.
  • Environment 변수 누락: 서비스는 종종 특정 환경 변수(예: PATH)를 필요로 하지만, systemd의 깨끗한 환경(아래 참조)에는 존재하지 않을 수 있습니다.
  • 권한: 지정된 User가 스크립트 실행 권한이 없거나 필요한 데이터 파일에 대한 읽기/쓰기 권한이 없는 경우.
  • 구문 오류: 유닛 파일 자체의 간단한 오타.

ExecStart를 수동으로 테스트하려면:

서비스 사용자로 전환하고 명령을 직접 실행해 보세요.

sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh

이렇게 하면 종종 journalctl에서 본 오류가 터미널에 직접 재현되어 디버깅이 더 쉬워집니다.

종속성 관리: 서비스가 혼자 시작할 수 없을 때

서비스는 종종 자체를 시작하기 전에 다른 서비스 또는 시스템 구성 요소가 활성 상태여야 합니다. Systemd는 이러한 종속성을 관리하기 위해 Wants, Requires, After, Before 지시어를 사용합니다.

종속성 식별

systemctl list-dependencies <service_name>을 사용하여 서비스가 명시적으로 요구하거나 실행하려는 항목을 확인합니다.

systemctl list-dependencies mywebapp.service

[Unit] 섹션의 일반적인 지시어:

  • After=: 이 서비스가 나열된 유닛 이후에 시작되어야 함을 지정합니다. 나열된 유닛이 실패해도 이 서비스는 여전히 시작을 시도합니다 (Requires=가 함께 사용되지 않는 한).
  • Requires=: 이 서비스가 나열된 유닛을 요구함을 지정합니다. 필요한 유닛 중 하나라도 시작에 실패하면 이 서비스는 시작되지 않습니다.
  • Wants=: Requires=의 약한 형태입니다. 원하는 유닛이 실패해도 이 서비스는 여전히 시작을 시도합니다.

예시:

[Unit]
Description=My Web Application
After=network.target mysql.service
Requires=mysql.service

여기서 mywebapp.servicenetwork.targetmysql.service가 시작된 후에만 시작되며, mysql.service의 성공을 요구합니다. mysql.service가 실패하면 mywebapp.service는 시작되지 않습니다.

종속성 충돌 해결

서비스가 종속성 문제로 실패하는 경우, journalctl은 일반적으로 어떤 종속성이 충족되지 않았는지 표시합니다. 예를 들어, mysql.service의 실패에 대한 세부 정보와 함께 Dependency failed for My Web Application이라고 표시될 수 있습니다.

해결 단계:
1. 종속 서비스 확인: systemctl status <dependent_service> (예: systemctl status mysql.service) 및 journalctl -u <dependent_service>를 실행하여 먼저 해당 서비스의 실패를 해결합니다.
2. After=Requires= 지시어 확인: 원하는 시작 순서와 엄격성을 올바르게 반영하는지 확인합니다. 때로는 서비스가 서비스가 활성 상태인 것뿐만 아니라 특정 포트가 열릴 때까지 기다려야 할 수도 있습니다. 복잡한 경우 systemd-socket-activate 또는 사용자 지정 ExecStartPre 스크립트가 유용할 수 있습니다.

환경 변수 및 경로: 숨겨진 함정

Systemd 서비스는 매우 깨끗하고 최소한의 환경에서 실행됩니다. 이로 인해 PATH와 같은 필수 환경 변수가 누락되어 systemd에서 실행될 때 사용자 셸에서 완벽하게 작동하는 명령이 실패하는 문제가 자주 발생합니다.

Systemd의 깨끗한 환경

Systemd가 서비스를 시작할 때, systemctl start를 시작한 사용자의 전체 환경을 상속하지 않습니다. 예를 들어, PATH 변수는 종종 축소되므로 /usr/bin 또는 /bin과 같은 표준 위치에 python 또는 node가 없으면 이러한 명령을 찾지 못할 수 있습니다.

증상: ExecStart=/usr/local/bin/myscript.sh가 "