서비스를 다시 띄우려는데 systemd 가 아예 시도조차 하지 않는다.
Job for myapp.service failed.
See "systemctl status myapp.service" and "journalctl -xe" for details.
myapp.service: Start request repeated too quickly.
myapp.service: Failed with result 'start-limit-hit'.
start-limit-hit 는 애플리케이션의 오류가 아니다. 정해진 시간 안에 정해진 횟수 이상 기동을 시도했기 때문에 systemd 가 더 이상 시도하지 않겠다고 선언한 상태다.
기준은 두 값이다.
| 항목 | 뜻 | 기본값 |
|---|---|---|
StartLimitIntervalSec |
횟수를 세는 시간 창 | 10초 |
StartLimitBurst |
그 창 안에 허용하는 시도 횟수 | 5회 |
즉 기본값으로는 10초 안에 5번 기동에 실패하면 잠긴다. Restart=always 와 짧은 RestartSec 을 함께 쓰면 금방 도달한다.
중요한 것은 이 메시지가 원인이 아니라 결과라는 점이다. 실제 실패 이유는 그보다 앞의 로그에 있다.
잠기기 전의 기동 시도를 본다.
journalctl -u myapp.service -n 200 --no-pager
start-limit-hit 줄을 지나 위로 올라가면 첫 번째 실패가 나온다. 거기에 실제 오류가 있다. 설정 파일 오류, 포트 충돌, 경로 없음, 권한 부족이 흔하다.
journalctl -u myapp.service --since '-30m' --no-pager | grep -v 'start-limit\|Scheduled restart'
원인을 고친 뒤 카운터를 지우고 다시 띄운다.
sudo systemctl reset-failed myapp.service
sudo systemctl start myapp.service
reset-failed 없이 start 만 하면 잠긴 상태 그대로라 다시 거부된다. restart 도 마찬가지다. 잠금 해제가 먼저다.
전체를 한꺼번에 정리하려면 인자 없이 쓴다. 다른 서비스의 실패 표시도 함께 지워지므로 상태를 먼저 확인한다.
systemctl --failed
sudo systemctl reset-failed
기동에 원래 오래 걸리거나 외부 의존 대상이 늦게 뜨는 서비스라면 창을 넓히는 편이 낫다.
sudo systemctl edit myapp.service
[Unit]
StartLimitIntervalSec=300
StartLimitBurst=10
[Service]
Restart=on-failure
RestartSec=15s
두 한계값은 [Unit] 섹션에, 재시작 정책은 [Service] 섹션에 둔다. 섹션을 섞으면 조용히 무시된다.
sudo systemctl daemon-reload
sudo systemctl restart myapp.service
StartLimitIntervalSec=0 을 주면 제한 자체가 없어진다. 실패하는 서비스가 무한히 재기동을 반복하며 로그와 CPU 를 먹으므로 원인을 모른 채로는 쓰지 않는다.
잠금이 반복된다면 재시작 정책이 상황에 맞지 않는 것이다.
| 값 | 동작 |
|---|---|
no |
재시작하지 않는다. 기본값 |
on-failure |
0 이 아닌 종료 코드, 시그널, 시간 초과일 때 재시작 |
always |
정상 종료를 포함해 항상 재시작 |
on-abnormal |
시그널이나 시간 초과일 때만 재시작 |
정상 종료가 의미를 갖는 서비스에 always 를 쓰면 종료할 때마다 다시 뜬다. 대부분은 on-failure 가 맞다.
RestartSec 이 너무 짧으면 의존 대상이 준비되기 전에 계속 시도해 한계에 걸린다. 외부 데이터베이스나 네트워크 저장소를 기다려야 한다면 10초 이상을 준다.