파이썬 스크립트가 서버 재부팅 후 안 살아날 때 자동 시작 등록법

파이썬 스크립트가 서버

서버를 재부팅했는데 돌려두었던 파이썬 스크립트가 다시 살아나지 않아서 검색하셨다면, 원인은 대부분 하나입니다. 그 파이썬 스크립트가 서버의 부팅 과정에 아예 등록되어 있지 않기 때문입니다. nohup python3 app.py &나 터미널에서 그냥 실행한 스크립트는 세션이 끝나거나 서버가 재부팅되면 함께 사라지고, 다시 켜주는 절차가 없으면 영영 안 살아납니다. 이 글에서는 systemd와 cron 두 가지 방식으로 실제 등록하는 절차, 그리고 각 방식이 실패하는 조건까지 정리해 드립니다.

재부팅 후 스크립트가 꺼져 있는 진짜 이유

터미널에서 직접 실행한 프로세스는 그 터미널(정확히는 로그인 세션)에 종속됩니다. screen이나 tmux로 분리해 둬도 마찬가지입니다. 세션 자체는 재부팅과 무관하게 살아있는 것처럼 보이지만, 서버가 꺼졌다 켜지는 순간 그 세션도 함께 초기화되기 때문에 안에서 돌던 스크립트도 같이 죽습니다.

문제는 여기서 끝나지 않습니다. 서버를 다시 켜도 “이 스크립트를 다시 실행하라”고 알려주는 절차가 운영체제 어디에도 등록되어 있지 않으면, 부팅이 아무리 정상적으로 끝나도 스크립트는 그대로 멈춰 있습니다. 리눅스 배포판은 부팅 시점에 systemd가 등록된 서비스 목록을 순서대로 실행하는데, 여기에 이름을 올리지 않은 프로세스는 존재 자체를 모릅니다.

systemd 서비스로 등록하기 (우분투·데비안·라즈베리파이OS 공통)

우분투 15.04, 데비안 8(jessie) 이후 배포판은 기본 init 시스템으로 systemd를 씁니다. 라즈베리파이OS도 동일하기 때문에 아래 방법은 대부분의 리눅스 서버에 그대로 적용됩니다. /etc/systemd/system/ 아래에 서비스 파일을 하나 만들면 됩니다.

sudo nano /etc/systemd/system/my-script.service

내용은 다음과 같이 작성합니다. ExecStartWorkingDirectory는 반드시 절대경로로 써야 합니다.

리눅스 터미널 화면에 파이썬 스크립트 명령어가 표시된 모습

[Unit]
Description=My Python background script
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/myapp
ExecStart=/home/ubuntu/myapp/venv/bin/python3 /home/ubuntu/myapp/app.py
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

가상환경(venv)을 쓴다면 python3가 아니라 venv 안의 bin/python3 경로를 그대로 적어야 라이브러리 임포트 오류가 안 납니다. 작성 후 아래 명령어로 등록하고 바로 실행까지 확인합니다.

sudo systemctl daemon-reload
sudo systemctl enable my-script.service
sudo systemctl start my-script.service
sudo systemctl status my-script.service

정상 등록되면 상태 확인 결과가 대략 이렇게 나옵니다.

● my-script.service - My Python background script
     Loaded: loaded (/etc/systemd/system/my-script.service; enabled; vendor preset: enabled)
     Active: active (running) since ...
   Main PID: 12345 (python3)

enabled 표시가 있어야 다음 재부팅부터 자동으로 실행됩니다. Restart=on-failure를 넣어두면 스크립트가 예외로 죽었을 때 5초 뒤 자동으로 다시 살아나기 때문에, 재부팅뿐 아니라 런타임 중 크래시에도 대응이 됩니다. 서비스 세부 옵션과 Restart= 정책 전체 목록은 systemd 공식 매뉴얼에 정리되어 있습니다.

crontab @reboot로 등록하는 더 가벼운 방법

관리자 권한 없이 등록하고 싶거나, 서비스라기보다 단순 배치 스크립트라면 크론@reboot 구문이 더 간단합니다. crontab -e로 편집기를 열고 한 줄만 추가하면 됩니다.

@reboot sleep 30 && /home/ubuntu/myapp/venv/bin/python3 /home/ubuntu/myapp/app.py >> /home/ubuntu/myapp/log.txt 2>&1

데이터센터 서버랙이 부팅되는 모습

sleep 30을 넣은 이유는 실제 등록 사례에서 자주 나오는 실수 때문입니다. cron의 @reboot는 네트워크나 디스크 마운트가 완전히 준비되기 전에 실행될 수 있어서, DB 연결이나 외부 API 호출이 포함된 스크립트는 부팅 직후 바로 실패하는 경우가 있습니다. systemd처럼 After=network-online.target 같은 정교한 순서 지정이 없기 때문에, 몇 초 지연을 주는 방식으로 우회하는 편이 현실적입니다.

systemd와 crontab, 어느 쪽을 쓸지 이 기준으로 정해보세요

항목 systemd 서비스 crontab @reboot
권한 root(sudo) 필요 일반 사용자도 등록 가능
크래시 시 자동 재시작 Restart=on-failure로 가능 기본 기능 없음(직접 감시 스크립트 필요)
네트워크 준비 대기 After=network-online.target로 제어 가능 순서 보장 안 됨(sleep 등으로 우회)
로그 확인 journalctl -u 서비스명으로 통합 조회 직접 리다이렉트한 파일 확인
적용 환경 systemd 기반 리눅스(우분투·데비안·CentOS 등) init 방식 무관, 더 널리 호환

오래 떠 있어야 하고 죽었을 때 자동 복구가 필요한 백엔드 스크립트라면 systemd 쪽이 관리하기 편합니다. 반대로 부팅 시 한 번만 실행하고 끝나는 초기화 스크립트라면 crontab 한 줄로 충분한 경우가 많습니다.

enable까지 했는데 왜 재부팅 후에도 안 뜨나요?

가장 흔한 원인은 세 가지로 좁혀집니다. 첫 번째는 ExecStart 경로를 상대경로로 적은 경우인데, systemd는 셸 프로파일을 거치지 않기 때문에 상대경로나 ~ 표기가 그대로 깨집니다. 두 번째는 스크립트가 .bashrc.profile에 설정해둔 환경변수에 의존하는 경우로, 이런 변수는 서비스 파일에 Environment= 또는 EnvironmentFile= 지시자로 따로 넣어줘야 인식됩니다.

세 번째는 Docker 컨테이너 안에서 이 방법을 그대로 시도하는 경우입니다. 컨테이너는 대부분 systemd가 PID 1로 동작하지 않기 때문에 위 절차 자체가 적용되지 않고, 이때는 컨테이너 실행 시 --restart=always 옵션이나 오케스트레이션 도구의 재시작 정책을 쓰는 편이 맞습니다. 원인 파악이 안 될 때는 아래 명령으로 부팅 시점 로그를 바로 확인할 수 있습니다.

journalctl -u my-script.service -b

서버가 systemd 기반 리눅스라면 서비스 파일 하나 등록하는 데 5분이면 충분하니, 지금 돌려둔 스크립트가 있다면 바로 서비스 파일로 옮겨서 enable 상태까지 확인해 보시길 권해 드립니다.

Windows에서 파이썬 cp949 오류 없애기: 로그·파일·서브프로세스 3곳 총정리

Leave a Comment