간단한 파이썬 프로그램을 백그라운드로 실행할 일이 생겨서 nohup으로 돌리고 있었습니다. 하지만 nohup은 실패시 프로세스가 종료되기 때문에 아래와 같은 스크립트를 크론탭에서 1분 단위로 실행해 다시 프로그램을 실행 할 수 있도록 해뒀습니다.
#!/bin/sh
cd /var/www/myproject
check=`ps -ef | grep mypython_file.py | wc | awk '{print $1}'`
if [ $check -gt 1 ]
then
echo "RUNNING"
else
nohup /root/.poetry/bin/poetry run python mypython_file.py > /dev/null 2>&1 &
fi
이상태로 몇일동안 사용해본 결과 문제점이 몇가지 생겼습니다.
프로세스의 상태를 모니터링 하는 것이 번거로웠습니다.
프로세스를 끄기 위해서는 PID를 찾아서 kill 해주는 것이 생각보다 번거로웠습니다.
위의 문제점들을 개선하기 위해 파이썬 파일을 systemd를 사용해 관리해보려고 방법을 구글링 해가면서 시도해봤습니다.
해당 게시물은 대부분 https://github.com/torfsen/python-systemd-tutorial 의 내용에 따라 작성되었습니다.
systemd는 System과 User 서비스를 지원하는데, 이 둘의 차이는 아래와 같습니다.
System service: 시스템 자체의 systemd 인스턴스에서 작동하고 전체 시스템과 모든 유저들에게서 동작 할 수 있습니다.
User Service: 리눅스 유저에게 종속된 systemd 인스턴스에서 작동합니다.
제가 본 자료에서는 system service를 개발할 예정이라고 해도 user service를 먼저 만들어보기를 권장했는데, 이는 system service가 user service에 비해 설정이 복잡하기 때문에 user 프로세스를 만들어 일단은 서비스를 성공적으로 띄우는것에 집중하고 나중에 이 복잡한 설정들을 완료하기 위해서 라고 합니다.
systemd 서비스를 만들기 위해서는 ini 파일 형식의 unit file
이 필요합니다.
Unit file이 위치할 경로는 여러 장소있습니다.
제가 본 자료들은 ~/.config/systemd/user/
와 /usr/lib/systemd/system/
두 군데를 보통 사용했는데 전자는 User service이고 후자가 System Service입니다.
일단 제가 참고하고 있는 자료에서는 user service를 먼저 띄우는 것을 추천했기 때문에 이에 따라 ~/.config/systemd/user/my_python.service
파일을 작성했습니다.
[Unit]
# Human readable name of the unit
Description=Python Demo Service
my_python.service
를 작성하고 난 후에 systemd에서 .service
를 찾는지 확인해 볼 수 있습니다.
$ systemctl --user list-unit-files | grep python_demo_service
python_demo_service.service static
실행할 파이썬 파일에 대한 내용을 unit 파일에 추가적으로 기입해줍니다.
[Unit]
Description=Python Demo Service
[Service]
# Command to execute when the service is started
ExecStart=/usr/bin/python path/to/your/python_demo_service.py
위의 파일은 제가 참고하고 있는 자료에서 가져온 것이고 실제로 저는 poetry를 이용해 개발을 진행했게 때문에 이에 맞추어 아래의 추가적인 절차를 진행했습니다.
$ which poetry
/home/dong/.poetry/bin/poetry
[Unit]
Description=Python Demo Service
[Service]
# Command to execute when the service is started
ExecStart=/home/dong/.poetry/bin/poetry run python path/to/your/python_demo_Service.py
[Unit]
Description=Python Demo Service
[Service]
# directory of project
WorkingDirectory=path/to/project
# Command to execute when the service is started
ExecStart=/home/dong/.poetry/bin/poetry run python path/to/your/python_demo_Service.py
[service]
의 Restart에 on-failure를 추가해 non-zero-exit-code 즉 비정상 종료일 때 재시작 할 수 있도록 해줍니다.Restart를 비롯한 다양한 옵션에 대해서는 이 문서를 통해 확인해 볼 수 있습니다.
$ systemctl --user daemon-reload
$ systemctl --user start python_demo_service
$ systemctl --user status python_demo_service
● python_demo_service.service - Python Demo Service
Loaded: loaded (/home/torf/.config/systemd/user/python_demo_service.service; static; vendor preset: enabled)
Active: active (running) since So 2018-12-30 17:46:03 CET; 2min 35s ago
Main PID: 26218 (python)
CGroup: /user.slice/user-1000.slice/user@1000.service/python_demo_service.service
└─26218 /usr/bin/python /home/torf/projects/python-systemd-tutorial/python_demo_service.py
User Service로 systemd를 실행하면 해당 유저에 대한 세션이 닫히면 systemd 인스턴스가 꺼지는 문제점이 있다. 해당 문제를 해결하려면 아래 명령어를 실행해야 합니다.
$ sudo loginctl enable-linger $USER
User Service를 성공적으로 만들었다면 이를 System Service로 바꿀 구 있지만 제대로 적용되지 않으면 system service로 만들 경우 시스템의 안정성과 보안상의 문제가 발생할 가능성이 있습니다. 그래서 내가 참고한 자료에서는 System Service로 바꾸지 않고 user service를 사용하기를 장려합니다.
user service를 system service로 바꾸기 전에 먼저 user service를 멈추고 종료해야합니다.
$ systemctl --user stop python_demo_service
$ systemctl --user disable python_demo_service
user service를 system service로 바꾸기 위해서는 Unit File의 경로를 user service를 위한 경로에서 system service를 위한 경로로 바꾸고 권한을 알맞게 수정해줘야합니다. system unit file은 여러 경로에 위치 할 수 있는데 예시에서는 /etc/systemd/system/
에 넣었습니다.
$ sudo mv ~/.config/systemd/user/python_demo_service.service /etc/systemd/system/
$ sudo chown root:root /etc/systemd/system/python_demo_service.service
$ sudo chmod 644 /etc/systemd/system/python_demo_service.service
성공적으로 옮겨지면 이제 user service가 system service가 되어 --user
옵션 없이 실행이 가능합니다.
$ systemctl list-unit-files | grep python_demo_service
python_demo_service.service disabled
추가적으로 파이썬 스크립트의 경로를 홈 디렉토리에서 /usr/local/lib
로 바꾸는 과정과 보안 취약점을 해결하기 위해 system service를 root가 아닌 사용자 계정으로 실행하기 위한 과정이 해당 문서에 나와 있기 때문에 같이 확인해보는것이 좋을 것 같습니다.
끝.
참고자료
https://www.freedesktop.org/wiki/Software/systemd/
https://www.freedesktop.org/software/systemd/man/systemd.directives.html
https://github.com/torfsen/python-systemd-tutorial
https://www.freedesktop.org/software/systemd/man/systemd.service.html
https://unix.stackexchange.com/questions/545195/systemd-service-does-not-last-more-than-6-hours