systemd 서비스가 갑자기 멈췄을 때 — status=126, ExecStart 권한 문제 트러블슈팅
📌 핵심 요약
systemd 서비스가 status=126 또는 Main process exited, code=exited, status=203/EXEC로 실패하고 있다면, ExecStart에 지정된 스크립트의 실행 권한(+x)이 사라졌을 가능성을 먼저 확인하세요. git checkout, git reset, 파일 재생성 등이 원인일 수 있습니다.
상황
어느 날 아침, 매시간 돌아야 할 데이터 수집 파이프라인이 약 8시간째 멈춰있다는 걸 발견했습니다.
$ systemctl status data-pipeline.service
● data-pipeline.service - News Pipeline
Loaded: loaded (/etc/systemd/system/data-pipeline.service; enabled)
Active: failed (Result: exit-code)
Process: 12345 ExecStart=/opt/pipeline/pipeline.sh (code=exited, status=126)
status=126은 처음 보는 코드였습니다.
원인 파악
Exit Code 126이란?
126은 POSIX shell에서 정의된 종료 코드로, 명령은 찾았지만 실행할 수 없음(permission denied)을 뜻합니다.
| 코드 | 의미 |
|---|---|
| 126 | Command found but not executable |
| 127 | Command not found |
| 128+N | Signal N으로 종료 |
systemd에서 status=203/EXEC는 ExecStart 프로세스를 아예 시작조차 못 했을 때 나타납니다. 두 코드 모두 실행 권한 문제를 강하게 시사합니다.
확인
$ ls -la /opt/pipeline/
-rw------- 1 bell bell 2048 Feb 19 06:00 pipeline.sh
-rw------- 1 bell bell 1024 Feb 19 06:00 collect.sh
-rw------- 1 bell bell 512 Feb 19 06:00 curate.sh
-rwx------여야 할 스크립트들이 모두 -rw-------였습니다. 실행 비트(x)가 모두 사라진 상태.
조치
chmod +x /opt/pipeline/pipeline.sh \
/opt/pipeline/collect.sh \
/opt/pipeline/curate.sh
이후 수동 실행으로 status=0/SUCCESS 확인:
$ systemctl start data-pipeline.service
$ systemctl status data-pipeline.service
Active: inactive (dead)
...
Process: 12400 ExecStart=/opt/pipeline/pipeline.sh (code=exited, status=0/SUCCESS)
원인 추적 — 왜 실행 권한이 사라졌나?
정확한 시점을 확인하지 못했지만, 몇 가지 가능성을 정리했습니다.
1. git checkout / git reset
Git은 파일 권한 중 실행 비트(+x)만 추적하고, 소유자/그룹 등은 무시합니다. 만약 해당 스크립트를 Git으로 관리하고 있고, 어느 시점에 +x 없이 커밋된 상태가 있었다면 git checkout이나 git reset --hard가 권한을 초기화했을 수 있습니다.
# Git이 추적하는 권한 확인
$ git ls-files --stage pipeline.sh
100644 a1b2c3... 0 pipeline.sh # 실행 비트 없음 (644)
100755 a1b2c3... 0 pipeline.sh # 실행 비트 있음 (755)
커밋 시 실행 비트를 함께 설정하려면:
$ git update-index --chmod=+x pipeline.sh
$ git commit -m "fix: restore execute permission on pipeline.sh"
2. 파일 재생성
일부 텍스트 편집기나 CI/CD 파이프라인이 파일을 삭제 후 재생성하는 경우 기본 umask(보통 0022)가 적용되어 실행 비트가 빠질 수 있습니다.
3. 잘못된 배포 스크립트
cp, rsync, scp 등을 사용한 배포 시 권한 유지 옵션을 빠뜨린 경우:
# 권한 유지 없음 (위험)
cp pipeline.sh /opt/pipeline/
# 권한 유지 (권장)
cp -p pipeline.sh /opt/pipeline/
rsync -a pipeline.sh /opt/pipeline/
재발 방지 전략
1. Git에서 실행 비트 관리
스크립트를 Git으로 관리한다면, 처음부터 실행 비트 포함 커밋:
git update-index --chmod=+x pipeline.sh collect.sh curate.sh
2. systemd 서비스 진단 루틴
서비스 장애 시 체크리스트:
# 1. 종료 코드 확인
systemctl status <service>.service
# 2. 상세 로그 확인
journalctl -u <service>.service -n 50 --no-pager
# 3. 실행 파일 권한 확인
ls -la $(systemctl cat <service>.service | grep ExecStart | awk '{print $2}')
# 4. 실행 테스트
sudo -u <서비스 실행 유저> /path/to/script.sh
3. 배포 후 권한 검증 추가
배포 스크립트나 Makefile에 검증 단계를 추가하면 조기에 문제를 발견할 수 있습니다:
# Makefile 예시
deploy:
rsync -a --delete ./scripts/ /opt/pipeline/
@echo "Verifying execute permissions..."
@for f in /opt/pipeline/*.sh; do \
[ -x "$$f" ] || (echo "ERROR: $$f is not executable!" && exit 1); \
done
@echo "All scripts executable. ✓"
systemctl restart pipeline.service
4. 모니터링 — 다음 번엔 빨리 감지하기
이번에는 약 8시간 후에 발견했습니다. 더 빠르게 감지하려면:
# /etc/systemd/system/data-pipeline.service 에 OnFailure 추가
[Unit]
OnFailure=notify-failure@%n.service
또는 systemd의 Restart=on-failure와 RestartSec을 조합해 자동 재시도:
[Service]
Restart=on-failure
RestartSec=60
[Unit]
# StartLimit 옵션은 [Unit] 섹션에 위치 (systemd v230+)
StartLimitIntervalSec=300
StartLimitBurst=3
단, 실행 권한 문제는 재시도해도 해결되지 않으므로 재시도 후에도 실패하면 반드시 알림이 가도록 구성해야 합니다.
트레이드오프
| 방법 | 장점 | 단점 |
|---|---|---|
Git --chmod=+x |
히스토리 추적 가능, 일관성 | Git 초보자에게 낯선 옵션 |
| 배포 후 chmod 스크립트 | 단순, 확실 | 배포 절차에 의존 → 수동 배포 시 누락 가능 |
Restart=on-failure |
일시적 장애 자동 복구 | 권한 문제엔 무효, 로그 노이즈 증가 |
| CI/CD 권한 검증 단계 | 배포 시점에 즉시 발견 | CI/CD 구성 필요 |
마치며
status=126은 사실 매우 명확한 오류 코드입니다. 하지만 처음 마주쳤을 때는 "왜 갑자기?"라는 의문이 앞서서 다른 곳부터 뒤지게 됩니다. 결론은 단순했습니다 — ls -la 한 줄로 바로 찾을 수 있는 문제였죠.
중요한 건 재발 방지입니다. Git 권한 관리, 배포 스크립트 검증, 빠른 장애 감지를 함께 챙기면 같은 실수를 반복하지 않을 수 있습니다.
참고 자료
- systemd.service man page — ExecStart
- Bash exit codes (GNU Manual)
- git update-index documentation
- systemd OnFailure
조회수: 0