라즈베리파이, 아두이노

[라즈베리파이 / 리눅스] init.d 서비스 스크립트 작성을 통한 시작 프로그램 만들기

포도알77 2019. 4. 3. 17:24
 리눅스, 특히 라즈베리파이를 이용한 프로젝트를 진행하고 있다면 아두이노와 같이 재부팅(전원 인가)시에 작성한 프로그램이 자동으로 켜지도록 만들어야 한다.


 이러한 시작 서비스를 만드는 방법은 구글에 linux start up program 또는 boot up program의 키워드로 검색하면 다양한 정보를 얻을 수 있다.
 

 가장 간단한 방법은 rc.local이나 crontab에 커맨드를 등록하여 바로 실행하는 방법이 있고, 더 나아가 systemmd를 이용하는 방법도 있다. 어느 사이트에서는 5가지 방법에 대하여 다루고 있다.

 


 이번 포스트에서는 그중 가장 널리 사용되는 init.d 서비스 스크립트를 작성하는 방법을 다룬다. init.d는 리눅스를 어느정도 만져본 대부분의 사람들은 특정 서비스를 start / stop / restart등을 위해서 사용해본 적이 있을 만큼 가장 많이 사용된다.

 
 init.d 스크립트는 일단 등록해두면 알아서 부트업 후에 서비스를 실행하여 주며, 자신이 원하는 어떠한 언어라도 사용할 수 있다.


 이 말은 실제 프로그램을 등록해도 무방하고, 아니라면 fork를 수행하거나 혹은 bash 스크립트가 되어도 된다는 의미이며, 그 만큼 다양성(이른바 원하는데로 뽑아다 쓰는 잡탕)을 가진다는 뜻이다.

 


 나의 경우에는 초기에 Python으로 작성해보았는데,  아무래도 bash으로 하는 편이 일반적이고 더 나아보여서 bash로 된 예제를 이 포스트에 담는다. 그리고 bash 스크립트에 대한 지식이 조금이라도 있는 사람은 쉽게 자신에 맞는 스크립트를 작성할 수 있으리라 본다.(나는 bash를 하나도 모른다.)



먼저 샘플 코드를 보고 이야기하자.

* sample_service 파일
#!/bin/sh
### BEGIN INIT INFO
# Provides:          sample_service
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Sample service init.d script
# Description:       Sample service init.d script
### END INIT INFO

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin

WORK_DIR="/home/pi/Desktop/MyFolder"
FILE_NAME="MyPython.py"

start(){
    cd $WORK_DIR
    python3 $FILE_NAME &
    echo "Python program is started."
}

stop(){
    pid=$(ps aux | grep $FILE_NAME | awk '{print $2}')
    if [ -n "$pid" ]
    then
        kill -9 $pid
    fi
    sleep 1
    echo "Python program is stopped."
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    *)
        echo "Usage: /etc/init.d/sample_service.py {start|stop|restart}"
        exit 1
        ;;
esac
exit 0
 

1) 스크립트 인터프레터 위치 정의
 가장 첫 줄은 #!으로 시작되며, init.d 스크립트를 해석할 인터프레터의 위치를 적는다. python이라면 /usr/bin/python 또는 python3와 같이 작성하면 된다.



2) 프로그램 정보 입력
 INIT INFO에는 해당 init 스크립트의 내용을 입력해야 한다. 만약 이를 입력하지 않으면 재부팅시(boot up) 동작하지 않는다. 원한다면 입력하지 않고 서비스 등록 후, 실행하여보자. 오류가 나타날 것이다.



3) PATH 변수 추가
 스크립트가 동작할때 path 환경 변수가 넘어오지 않는다고 한다. 따라서 스크립트 내부에서 다시금 정의해주어야 한다. 만약 ($PATH를 이용해 외부 path를 그대로 가져오는 경우 보안상 문제가 발생할 수 있다고 하니 자세한 내용은 구글링하자.)



4) 본격적인 서비스 처리 스크립트
 기본적으로 서비스는 start는 기본적으로 제공해주어야 한다. 그리고 편의를 위해 stop 및 restart를 제공해주도록 하자.

 * start
 나는 프로그램안에 상대 주소를 이용한 파일 입출력을 하였기 때문에, 간단하게 디렉터리를 소스코드 위치로 이동하였다. 
 이를 위해 workdir변수를 선언하고 cd 명령과 python 명령을 커맨드로 수행하여 프로그램을 백그라운드 프로세스로 실행하였다.
 
 * stop
 프로세스를 종료하기 위해서는 프로세스 아이디인 pid 값이 필요하다. 인터넷상 다른 소스코드에서는 pid 파일을 이용하여 처리하였지만, 나는 배시 스크립트에 대한 깊은 이해가 없어 ps와 grep 그리고 awk 명령어를 파이프라이닝하여 실행중인 프로그램의 pid를 뽑고 kill 커맨드를 통해 이를 종료하였다. 

 그리고 만약 프로세스가 이미 죽은 시점의 경우를 대비하여 반환되는 pid가 없으면 아무것도 하지 않도록 하였다.

  * restart
 재시작의 경우는 간단하다. 그냥 종료/시작을 순서대로 수행하면 된다.
 


 이 스크립트를 구성하면서 몇가지 깨우친 것이 있다면,
  (1) 변수 정의시에 대입 연산자(=) 좌우로 공백이 들어가면 안된다. Ex) name="123"
  (2) 맨 상단 첫줄에 기입되는 인터프레터는 반드시 해당 스크립트의 언어를 처리할 수 있어야 한다. (Python인데 bash를 사용하면 안됨)
  (3) 배시 스크립트는 어렵다..?



 5) 서비스 스크립트 등록
 서비스 스크립트를 작성하였으면 이제 init.d에 옮겨 이를 등록해야한다.

 아래의 커맨드를 보자.
sudo cp RTU /etc/init.d/sample_service
sudo chmod +x /etc/init.d/sample_service
sudo update-rc.d sample_service defaults

 스크립트를 /etc/init.d 폴더에 복사하고, 루트가 이를 실행할 수 있도록 execute 권한을 부여하여 준다.
 그리고 난 다음 update-rc.d를 실행하여 해당 서비스 스크립트를 로드하여 준다.




 6) 재시작 및 동작확인
 스크립트가 정상적으로 작성되었다면, 이제 재부팅하였을 때 해당 서비스를 자동으로 실행하여 준다.




 7) 서비스 스크립트 삭제
 서비스 스크립트를 삭제하기 위해서는 아래의 커맨드를 이용하면 된다.

sudo update-rc.d -f sample_service remove
sudo rm /etc/init.d/sample_service


 우선 서비스를 제거하여주고, init.d 폴더에서 해당 스크립트를 삭제하면 된다.

 나중에 계속 커맨드 치기 귀찮으니까 Makefile이나 .sh의 배시 파일을 만들어두면 편리하다.




페이스북으로 공유카카오톡으로 공유카카오스토리로 공유트위터로 공유URL 복사