타사 프록시를 시작하는 시스템 서비스 단위가 있습니다. 이를 "서비스 c"라고 합니다. 서비스 장치는 잘 작동하고 있습니다. 적어도 제가 아는 한에서는요! 패치 주기 후 systemd는 (예상대로) 서비스 단위를 시작하지만, 그 다음에는 서비스 단위가 다시 시작됩니다.멈추다서비스 장치는 성공적으로 시작된 후 약 2초 후에 시작됩니다. 나는 이 서비스가 처음으로 성공적으로 시작되었다고 믿을 만한 모든 이유를 가지고 있습니다. 다시 시작한 후 로그인하면 이 시점에서 서비스가 실제로 실행되고 있지 않은 것을 볼 수 있습니다. 서비스 단위( systemctl start service-c
)를 수동으로 시작할 수 있으며 예상대로 서비스가 시작됩니다.
systemd가 서비스 단위를 중지해야 한다고 생각하는 이유를 알고 싶습니다. systemd가 "중지" 작업을 수행하는 이유를 확인하기 위해 무엇을 구성하거나 활성화할 수 있습니까?
알아요시스템 로그 수준 옵션기본 "정보" 대신 "디버그"로 설정했습니다.
비슷한 아이디어는 서비스 단위 파일에 설정하는 것이지만 Environment=SYSTEMD_LOG_LEVEL=debug
특별히 필요하지는 않습니다.제공하다디버깅 중이지만 자체적으로 시스템화되었습니다.
서비스 단위는 다음과 같이 구성됩니다.
# /etc/systemd/system/service-c.service
[Unit]
Description=service c
After=network-online.target local-fs.target
[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=/local-path/stop.service-c
Restart=on-failure
[Install]
WantedBy=multi-user.target
...증거는 다음과 같습니다.
$ systemctl status service-c
● service-c.service - service c
Loaded: loaded (/etc/systemd/system/service-c.service; enabled; vendor preset: disabled)
Active: inactive (dead) since Wed 2021-04-07 17:49:30 EDT; 14h ago
Process: 3162 ExecStop=/local-path/stop.service-c (code=exited, status=0/SUCCESS)
Process: 1319 ExecStart=/local-path/start.service-c (code=exited, status=0/SUCCESS)
Main PID: 1478 (code=exited, status=0/SUCCESS)
/local-path
시스템에 있는 로컬 디렉터리의 난독화된 버전입니다.
이는 지속적인 문제이므로 마지막 재부팅 후 "stop" 래퍼 스크립트를 사용하여 프로세스 상위 트리를 기록했습니다. pstree -a -A -l -p -s $$)
로그 파일은 다음과 같습니다.
04/07/2021 17:49:19 stop.service-c:
systemd,1 --switched-root --system --deserialize 22
`-stop.service-c,3162 /local-path/stop.service-c
`-pstree,3178 -a -A -l -p -s 3162
...여기서 PID 3162는 systemd의 중지 스크립트 호출에 해당합니다. systemd가 서비스의 ExecStop을 호출하는 것 같습니다.
systemd는 시작이 완료된 후 약 2초 후에 서비스를 중지합니다. 에이전트의 로그 파일에는 다음 타임스탬프가 있습니다.
04/07/2021 17:49:12 start.service-c: Starting agent
04/07/2021 17:49:17 start.service-c: startup success
04/07/2021 17:49:19 stop.service-c: Executing from /agent/home as user
... 로 끝나다...
04/07/2021 17:49:30 stop.service-c: Finished with RC=0
...systemd의 "죽음" 타임스탬프 17:49:30에 해당합니다.
"Restart=on-failure" 지시문은 서비스를 다시 시작하지만 systemd는 서비스가 성공적으로 시작되었다고 알려줍니다.
Apr 07 17:49:10 hostname systemd[1]: Starting service c...
Apr 07 17:49:17 hostname systemd[1]: Started service c.
서비스가 깔끔하게 시작되고 systemd가 시도하지 않기 때문에재시작서비스에서는 다시 시작 매개변수가 작동하지 않는 것 같습니다.
흥미롭게도 Journalctl에는 해당 "Stop service..." 로그가 없지만(서비스를 수동으로 중지할 때) systemd가 ExecStop을 호출한다는 증거가 있습니다.
현재 systemd 219를 실행하고 있습니다.
답변1
systemd가 서비스 단위를 중지해야 한다고 생각하는 이유를 알고 싶습니다. systemd가 "중지" 작업을 수행하는 이유를 확인하기 위해 무엇을 구성하거나 활성화할 수 있습니까?
서비스의 실시간 상태를 보려면 다음을 수행할 수 있습니다.
- 다음 명령을 사용하십시오
systemd-cgls -l <service-cgroup-path>
. 해당 시점의 모든 서비스 프로세스가 표시됩니다. 서비스의 cgroup 경로는 명령을 사용하여 검색할 수 있습니다systemctl show -p ControlGroup <service-name>
. 최신 버전(v219 아님)에서는 서비스의 cgroup 경로 대신 편의 옵션을systemd
사용할 수도 있습니다.-u <service-name>
systemd-cgls
- 자세한 통찰력을 얻으려면 매우 장황한
systemctl show <service-name>
명령을 사용할 수 있습니다. 이는 알려진 서비스 상태에 대한 많은 정보를 제공하며systemd
해당 정보로부터 무슨 일이 일어나고 있는지 더 자세히 추론할 수 있습니다.
ExecStop
"의심스러운 정지" 상황을 조사하려면 이러한 명령을 명령으로 추가하는 것이 옳습니다. 당신은 그것들을 추가하기 만하면됩니다처음에자신만의 stop.service-c
스크립트(실제로 스크립트인 경우)
ExecStop
또는 자신만의 추가 명령으로 추가할 수 있습니다.앞으로귀하의 stop.service-c
명령은 다음과 같습니다.
[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=-/bin/sh -c 'systemd-cgls -l -u %n && systemctl show %n'
ExecStop=/local-path/stop.service-c
Restart=on-failure
%n
지정자는 인용된 문자열 내에 나타날 때에도 올바르게 처리 됩니다.systemd
또는 다음을 수행할 수 있습니다.
[Service]
Type=forking
ExecStart=/local-path/start.service-c
ExecStop=-/usr/bin/systemd-cgls -l -u %n
ExecStop=-/bin/systemctl show %n
ExecStop=/local-path/stop.service-c
Restart=on-failure
또한 -
이해할 수 없는 이유로 실패하는 경우 명령의 종료 상태가 무시되도록 명령 앞에 접두사가 붙습니다.
물론 ExecStartPost
서비스가 "성공적으로 시작됨"으로 간주되자마자 활성 상태를 고려하는 명령 으로 사용할 수도 있습니다 systemd
. (다시 종료 상태가 무시되거나 systemd
실패할 경우 전체 서비스가 중단됩니다.)
systemd-cgls
다음으로 실행 명령의 출력을 보려면 그 당시 프로세스가 여전히 표시되는지 ExecStop
확인해야 합니다 MainPID
. 표시된다면 이는 ExecStop
실제로 systemd
제안한 대로 자율적으로 실행되었다는 증거입니다. 그렇지 않은 경우( MainPID
프로세스가아니요systemd-cgls
"중지된" 시간에 출력에 나타납니다 . 이는 ExecStop
실행되었음을 의미합니다.그러므로프로세스가 MainPID
자체적으로 종료됩니다.(더 많은 추론은 아래 참조). 서비스 프로세스의 PID 번호와 ExecStart
(현재는 존재하지 않는) 명령의 PID 번호를 기록하여 서비스가 시작된 이후 무슨 일이 있었는지 추론 할 수도 있습니다 fork(2)
. 이는 서비스와 관련하여 매우 중요하기 때문입니다. type=forking
잘 행동하고 있는지 평가합니다.(더 많은 추론은 아래 참조).
systemctl show
명령으로 실행한 결과 와 관련하여 ExecStop
주의해야 할 가장 관련성이 높은 속성은 다음과 같습니다.귀하의 특정한 경우예:
MainPID
: 서비스의 메인 프로세스가 자체적으로 종료되었는지 여부를 읽고0
, 그렇지 않으면 서비스의 메인 프로세스의 PID를 읽습니다(아직 존재하여 실제로 중지된 경우).systemd
ExecMainExitTimestamp
: 서비스의 메인 프로세스가 스스로 종료한 경우 종료 시간을 형식으로 읽습니다date
. 그렇지 않은 경우 프로세스가 아직 살아 있으면 전혀 읽지 않으므로 실제로 중지됩니다.systemd
ExecMainExitTimestampMonotonic
: 위와 같지만 Linux의 단조 시계를 읽고0
프로세스가 아직 살아 있는지 읽습니다.ExecMainCode
: 이는 영어 단어로 변환되는 대신 기호의 10진수 값을 보고한다는 점을 제외하고 1 의 문자열 에 해당합니다.code=
이 필드는 현재 기호 값을 기반으로 프로세스가 아직 살아 있고 따라서 실제로 중지될 것인지 여부를 읽습니다. Linux에서는 (처음부터), 그렇지 않으면 읽기 프로세스가 자체적으로 수행됩니다. -ed된 경우(분명히 이 사용 사례에서는)systemctl status
CLD_*
CLD_*
enum
1
ExecMainCode
0
systemd
1
_exit(2)
2
kill(2)
아니요systemd
) 등 으로
노트그러나 위의 필드는아니요해당 서비스에 해당하는현재의systemd
서비스가 시작될 때 서비스의 기본 프로세스를 감지할 수 없는지 여부를 나타냅니다 .(아래 설명 참조). 가장 최근 실행에 해당하는 것이 좋습니다.systemd
예전에는테스트를 완전히 완료할 수 있습니다.
추가 통찰력
귀하의 추론에서 추가 설명이 필요한 두 가지 핵심 사항을 볼 수 있습니다.
type=forking
제공하다
type=forking
서비스는 특히 사용하기 까다롭습니다 systemd
. 특히 사용할 때 GuessMainPID=yes
(기본값이므로 현재 프록시에 사용 중인 것) 이러한 서비스 유형의 경우 명령 자체 ExecStart
는 다음과 같아야 합니다.fork(2)
한 번그런 다음 종료하면 포크된 프로세스가 MainPID
오랫동안 서비스로 유지되고 번창할 것으로 예상됩니다. 기타:
- 이러한 포크된 프로세스가 다시 포크된 다음 종료되어 실제 서비스 역할을 자체 "두 번째" 포크된 프로세스에 위임하는 경우 이는
GuessMainPID
단순히 경로를 잃고systemd
서비스가 정기적으로 자발적으로 완료되었다고 가정하므로 다음의 임무를 완료합니다. 모든 것을 청소하지만(예: 실행 중ExecStop
등)아니요Stopping service...
메시지는systemd
의도적인 서비스 종료에만 반응하기 때문에 기록됩니다 . - 으로 변경된 경우
ExecStart
원래종료하기 전에fork(2)
두 번(또는 그 이상) 처리한 다음GuessMainPID
항복하고systemd
종료 시 모든 것을 파괴하지 마십시오.ExecStart
원래프로세스는 결국 종료됩니다. 서비스의 실제 프로세스가 여전히 존재하므로 이는 더 나은 상황이지만systemd
이벤트도 완전히 추적되지 않아 최소한 일관성이 없거나 불완전한 로그가 발생하므로 이상적이지는 않습니다.
ExecStop
구현하다
명령이 ExecStop
실행되었습니다반품MainPID
메인 프로세스도 종료된 경우 프로세스가 자체적으로 성공적으로 종료되는 경우시작성공(이것이 현재 상황입니다). 이것이 직관에 반하는 것처럼 보이지만 이는 정상적인 동작입니다 systemd
. 서비스 ExecStop
명령이 해당 서비스 이후에 정리하는 데 선호되는 방법이라고 생각하고 SIGTERM을 먼저 보낸 다음(기본적으로 참조 systemd.kill(5)
) SIGKILL을 보낼 수도 있습니다.
맨페이지 어디에도 이에 대해 명시적으로 나와 있지는 않지만 systemd.service(5)
일부 문서, 특히 명령에 사용할 수 있는 환경 변수와 관련된 문서에서 추론할 수 있습니다 Exec*
. 보다$SERVICE_RESULT
, $EXIT_CODE
그리고$EXIT_STATUS
변수가 취할 수 있는 값은 무엇인지, 변수가 갖는 의미론적 의미는 무엇인지, 그리고 변수가 정확하게 명령에 사용 가능하다는 ExecStop
사실 ExecStopPost
.
명시적이지 않은(또는 개인적으로 해석된) 문서 외에도 이 동작을 수행하는 소스를 살펴보겠습니다. v219에서 가져온 것입니다.여기서 말하는 service_sigchld_event()
것은service_enter_running()
"실행 중" 상태인 것으로 알려진 어린이와 관련된 이벤트에서후자의 함수 호출service_enter_stop()
서비스의 주요 프로세스가 감지 RemainAfterExit=yes
되지 않는 한 모든 경우에 작업을 "중지"합니다.type=dbus
( type=forking
위 설명 참조)또는 통제 그룹이 건강하지 않습니다.
에 관해서는왜사람들은 systemd
이렇게 하기로 결정했습니다. 저는 개발자가 아니기 때문에 잘 모르겠습니다 . 그러나 서비스의 아직 존재하지만 "알 수 없는" 프로세스가 있을 때 systemd
알림을 받을 수 있는 기회를 제공하기 위해 이 동작이 유용하다는 것은 알 수 있습니다. systemd
전체 제어 그룹 종료 최후의 수단으로 가혹한 SIGTERM 및 SIGKILL을 받기 전에 가능한 최선의 방법으로 종료하십시오. 이 조치는 서비스에 특히 유용합니다. 단락에서 언급한 것처럼 제대로 추적하기가 type=forking
가장 어렵고 , 종료 후 정리를 시도하기 전에 정상적으로 종료되지 않은 레거시/게으른/잘못 구현된 서비스 때문입니다 .systemd
type=
systemd.service(5)
systemd
화타이
1. code=
프로세스의 "종료 이유"를 나타내는 단어가 뒤에 옵니다. 즉 exited
, 존재했거나 짝수 killed
인지 여부 는 문자 그대로 다양한 유효한 값을 번역하는 단어를 의미합니다.trapped
dumped
CLD_*
siginfo_t.si_code
에 설명된 필드sigaction(2)