systemd 서비스가 실패했고 User=가 서비스 파일에 표시되었습니다.

systemd 서비스가 실패했고 User=가 서비스 파일에 표시되었습니다.

저는 일부 오래된 System V 유형 서비스를 RHEL 7/8의 "실제" 시스템 서비스로 마이그레이션하는 작업을 진행 중입니다. Redhat 7, SLES 12 및 SLES 15에서 제대로 실행됩니다. 그러나 RHEL 8에서 서비스를 실행하려고 하면 이제 시스템에서 내 애플리케이션의 pidfile이 해당 /run디렉토리에 있어야 한다는 사실을 알게 되었습니다. 또는 적어도 해당 디렉토리에 있을 것으로 예상합니다. (우리 애플리케이션은 일반적으로 애플리케이션의 설치 디렉터리에 pidfile을 작성합니다.)

나는 응용프로그램 시작 스크립트를 변경하고 디렉토리에 기록함으로써 /run이 작업을 수행 할 수 있다는 것을 알았습니다. 그래서 효과가 있었습니다! 하지만 이제 극복할 수 없을 것 같은 문제가 생겼습니다. 루트가 아닌 특정 사용자(내 로그인 ID)의 컨텍스트에서 실행하려면 실행 중인 서비스가 필요합니다. 제가 조사한 바에 따르면 이 작업을 수행하려면 User=파일에 지정 하기만 하면 됩니다. .service하지만 이 줄을 파일에 추가할 때마다 서비스 시작이 실패합니다. 디렉토리에 쓸 때 pidfile이 실패한 것 같습니다 /run. pidfile을 쓸 수 없으며 프로세스가 종료됩니다.

내 서비스 파일:

Description={removed}
After=remote-fs.target
After=network-online.target
Wants=remote-fs.target
Wants=network-online.target

[Service]
Type=forking
Restart=no
User=myid
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=none
GuessMainPID=no
RemainAfterExit=no
SuccessExitStatus=5 6 255
PIDFile=/run/adidmn.pid
ExecStart=<fullPathToScript> start
ExecStop=<fullPathToScript> stop

[Install]
WantedBy=multi-user.target

내 시작 스크립트는 데몬이 다양한 플랫폼에서 실행되는 방식을 정의하고 일부 환경 변수를 설정한 다음 실제 프로세스를 시작하는 셸 스크립트를 호출합니다. sudo 권한으로 호출하면 호출된 데몬 스크립트를 직접 실행할 수 있습니다. 내 사용자 ID는 sudoers 목록에 있지만 물론 실행 중인 프로세스의 소유자도 루트입니다.

.service 파일에 " User=" 속성 이 없으면 서비스를 시작하는 데 문제가 없습니다. 나는 항상 내 사용자 ID로 systemctl 명령을 sudo로 실행합니다. 나의 궁극적인 목표는 루트가 아닌 내 사용자 ID로 실행되는 실제 실행 프로세스를 보는 것입니다. User=<myid>파일에 추가하면 작업이 완료될 것이라고 생각했지만 .service이 줄로 인해 서비스가 시작되지 않습니다.

사용자가 존재하며(내 사용자 ID) sudoers 목록에도 있습니다. /run은 루트가 소유하며 여기에 sudo를 사용하여 pid 파일을 작성하면 pid 파일의 소유자는 루트입니다. 시작 스크립트에서 for 명령을 사용해 보았지만 su <userid>이로 인해 데몬이 전혀 시작되지 않았습니다.

기본적으로 SuSE와 Redhat 간에는 동작이 다릅니다. 내 데몬은 (수년간) 프로그램 설치 경로에 pid 파일을 제공했습니다. su <userid> -c사용자 "a"의 컨텍스트에서 프로세스를 시작하는 명령을 사용하여 프로세스를 시작합니다. pid 파일은 애플리케이션의 설치 경로에 위치하며 사용자 "a"가 소유합니다. SuSE Linux: 전혀 문제 없습니다. 그러나 Redhat에서는 프로세스가 실행 중일 때 systemd가 다음과 같이 불평합니다.

새 마스터 PID 26979는 서비스에 속하지 않으며 PID 파일은 루트가 소유하지 않습니다. 거부하다.

왜 차이가 있나요? 내가 성취하고 싶은 것이 "실행 가능"한가? 아니면 이제 루트가 모든 시스템 서비스 프로세스를 소유해야 합니까?

답변1

문제는 System V init 스크립트가 루트로 실행될 것으로 예상하고 작동 방식에 대한 "사양"의 일부이며 완료하려면 루트가 필요한 단계가 있는 경우가 많다는 것입니다.

귀하의 경우 su <userid> -c ...실제로 루트가 아닌 사용자로 실행을 시작하는 것이 실행에 관한 것이지만, 만약 당신이이미이 사용자로 실행합니다. System V init 스크립트는 종종 루트가 아닌 사용자로 전환하기 위해 su이와 유사한 도구를 사용 runas하지만 이러한 도구는 종종 목적에 완전히 적합하지 않습니다( su원래 대화형 셸에서 실행되도록 의도되었으며 PAM과 통합됩니다. 그렇지 않습니다. 여기서는 별로 의미가 없습니다.)

설상가상으로 일부 System V init 스크립트는 사용자 변경 사항을 처리하지 않고 결국 데몬을 루트로 불필요하게 실행하게 됩니다. 왜냐하면 이것이 System V init 스크립트에서 더 "자연스러워" 느껴지기 때문입니다. 제 생각에는 이것이 System V init 스크립트의 가장 심각한 문제 중 하나입니다. 여기서는 잘못된 일을 하기가 매우 쉽고 올바른 일을 하기는 매우 어렵습니다.

System V init 스크립트와의 호환성을 유지하려면 다음을 수행하십시오.할 수 있다그러한 스크립트를 호출할 때 이것이 "프로토콜"이므로 systemd에서 루트로 실행하십시오. 실제로 호환성을 유지하려면 systemd 단위를 보낼 필요조차 없습니다. systemd는 다음을 통해 자체적으로 하나를 생성할 수 있기 때문입니다.systemd-sysv-생성기. 결과 유닛은 루트로 실행된다는 점을 제외하면 제공한 유닛과 매우 비슷해 보입니다.

만약 너라면하다systemd 서비스 단위를 게시하려면(권장합니다) Type=simple대신 forking.

유일한 전제 조건은 포그라운드에서 데몬을 시작할 수 있다는 것입니다. 많은 데몬은 추가 명령줄 플래그를 전달하거나 일부 구성을 통해 이를 수행할 수 있습니다. (실제로는 상당히 크다.더 적은이에 대해 작업하십시오. 따라서 데몬이 현재 해당 기능을 지원하지 않고 소스를 제어할 수 있는 경우 해당 기능을 추가하거나 요청하는 것이 좋습니다. )

이 시점에서 해야 할 일은 ExecStart=명령에서 포그라운드에 있는 데몬을 호출하는 것뿐입니다. ExecStop=데몬을 종료하라는 신호를 받은 후 데몬이 올바르게 종료되는 한 필요하지 않습니다 .

더 이상 pidfile이 필요하지 않습니다!systemd는 포그라운드에서 데몬을 시작하므로알다데몬 프로세스의 기본 PID는 무엇입니까? pidfile은 종종/일반적으로 잘못 구현되기 때문에(데몬이 서비스할 준비가 되었을 때만 생성되어야 함) 이 요구 사항을 제거하는 것은 매우 큰 문제입니다.

기본 프로세스를 시작하기 전에 특정 환경 변수를 내보내야 하는 경우 systemd를 사용 Environment=하거나 EnvironmentFile=이러한 변수를 설정할 수 있습니다. (변수가 동적으로 생성된 값이 아닌 고정된 값으로 설정되어 있으면 제대로 작동합니다.) 데몬이 시작되기 전에 단계를 수행해야 하는 경우 를 사용할 수 있습니다 ExecStartPre=.

더 많은 유연성이 필요한 경우(예: 변수 설정, 조건부 명령 실행, 변수를 동적 값으로 설정 등) 쉘 스크립트(또는 Python, Perl 등) 및 ExecStart=. 이 스크립트는 필요한 모든 변수를 설정 및 내보내고 기본 데몬을 실행하기 전에 실행해야 하는 모든 명령을 실행합니다.

쉘 스크립트를 사용하여 데몬을 시작할 때 중요한 부분은 다음을 사용하는 것입니다.exec쉘을 데몬으로 교체하는 명령입니다. 즉, 쉘은 더 이상 존재하지 않고 데몬은 쉘에서 사용하는 것과 동일한 PID로 실행되므로 systemd는 여전히 데몬의 기본 PID를 안정적으로 알 수 있습니다. 물론, exec쉘 스크립트로 생성된 데몬은 여전히 ​​포그라운드에서 실행되어야 합니다.

서비스를 사용하면 systemd 장치 자체 내에서 Type=simple서비스를 구성 할 수 있습니다. User=또한 systemd 구성을 통해 추가 보안 조치를 적용할 수 있는 경우가 많으며, 이로 인해 System V init 스크립트가 트리거되어 해당 스크립트의 사용이 방지될 수 있습니다. 또한 simple대신 를 사용하면 forking시스템에서 이 설정을 더욱 안정적이고 효율적으로 만들 수 있습니다.

시스템화된 서비스 단위를 쉽게 배송할 수 있습니다.Type=simple 그리고패키지의 System V 초기화 스크립트. 이름이 같은 한 systemd는 기본 서비스 단위를 선호합니다(따라서 이 경우 systemd-sysv-generator의 레거시 코드는 init 스크립트를 트리거하지 않습니다.) 이렇게 하면 Linux가 아닌 제품과의 호환성을 유지할 수 있습니다. 기타 시스템 호환성. 비 systemd 설정, systemd를 통해 최신 Linux 시스템을 최대한 활용할 수 있습니다.

답변2

문제는 데몬을 시작하는 기본 스크립트의 "su -c" 명령입니다. 시스템이 이것을 좋아하지 않습니다. 하지만 스크립트는 다른 플랫폼에서도 실행되어야 하기 때문에 Linux용 케이스 문을 사용하여 수정했습니다. 어쨌든 systemctl은 sudo에서 실행되므로 이는 문제가 되지 않습니다. 이제 pid 파일은 내가 필요한 어느 곳에서나 작성할 수 있으며 시스템은 만족스러워 보입니다.

답변3

이 답변은시스템 >= 230(2016년 5월 출판). 이는 SELinux가 활성화되어 있고 systemd에서 실행 su하거나 제공 할 수 없는 Fedora 또는 RedHat 및 기타 시스템에 sudo필요합니다 .

"+"해결책은 권한 있는 사용자로 실행해야 하는 행에서 systemd.service의 접두사를 사용하는 것입니다.

예:

[Service]
Type=forking
User=myid
ExecStartPre=+sh -c "if ! test -d /run/myid; then mkdir /run/myid; chown myid:myid /run/myid; fi"
ExecStart=<my script>                                                    
PIDFile=/run/myid/my_service.pid
...

그런 다음 권한이 없는 사용자로 실행되는 스크립트를 통해 PIDFile을 생성할 수 있습니다. 기본적으로 서비스는 PIDFile의 pid에 종료 신호를 보내 종료되므로 "ExecStop=" 줄을 사용할 필요가 없습니다.

시스템 서비스:

ExecStartPre=, ExecStartPre=, ExecStartPost=
...
"+"실행 파일 경로 앞에 "+"가 붙으면 프로세스는 전체 권한으로 실행됩니다. 이 모드에서는 User=, Group=, CapabilityBoundingSet= 또는 다양한 파일 시스템 네임스페이스 옵션(예: PrivateDevices=, PrivateTmp=)을 사용하여 구성된 권한 제한이 호출 명령줄에 적용되지 않습니다(그러나 다른 ExecStart=,ExecStop에는 여전히 영향을 미칩니다). =,...라인).

관련 정보