2016년 5월 13일 금요일

라즈베리 파이에서 systemd를 사용해 부팅시 프로그램을 자동 실행 (Execute script on boot using systemd)




Raspbian 최신버젼이 릴리즈 되면서 cron이나 rc.local을 사용해 파이선 스크립트를 부팅시 자동실행 시키는 데서 고생하기 시작하였다. Raspbian의 부트 순서가 변경되어 이 프로세스들이 다른 시점에서 실행되기 때문인 것으로 보인다. 이것이 얼마나 문제가 되는지는 실행할 파이선 스크립트가 어떤 일을 하고 어떤 리소스를 필요로 하는가에 따라 치명적일 수도 있고 별 문제가 되지 않을 수도 있다.

부팅시 실행될 파이선 스크립트가 실행 시점에서 어떤 시스템 기능을 필요로 한다면 이 문제는 매우 치명적이다. 일반적으로 다음과 같은 것들이 포함된다.
  • 네트웍이 연결되어 있어야 함
  • /home/pi 디렉토리가 마운트 되어 읽을 수 있어야 함
  • 시스템의 시간이 NTP로 업데이트 되어야 함
그래서 커스텀 기능을 실행시키는데 권자오디는 방법이라 여려 리눅스 패키지에서 적용하고 있는 ’systemd’를 사용하기로 했다. systemd는 리눅스 시스템의 설정을 관리하기 위한 소프트웨어 툴로 이전에 동일한 목적으로 사용되던 여러 툴들을 대체하기 위한 목적으로 만들어 졌다.

systemd는 각자의 수준에 따라 매우 쉽거나 또는 매우 공포스러울 수 있다. 여기서는 최소한의 수고로 단지 스크립트 하나를 실행되도록 하는것이 목표이다.

Step 1 - 파이선 스크립트
 
여기서 사용할 예제 스크립트는 /home/pi 디렉토리에 저장되어 있고 이름은 ‘myscript.py’이다.  

Step 2 - Unit 파일 만들기
 
다음으로 해야 할 일은 설정 파일(aka Unit file)을 만들어 systemd에게 언제 무엇을 해야 하는지 알려줘야 한다.

$ sudo vi /lib/systemd/system/myscript.service

에디터를 실행한 다음 아래의 내용을 입력한다.

[Unit] Description=My Script Service
After=multi-user.target

[Service]
Type=idle
ExecStart=/usr/bin/python /home/pi/myscript.py

[Install]
WantedBy=multi-user.target

위의 파일은 “My Script Service”라는 이름의 새 서비스를 정의하고, ‘multi-user’ 환경이 사용 가능해 졌을 때 서비스를 한번만 실행하도록 요구한다. “ExecStart” 파라미터는 실행하길 원하는 명령어를 지정한다. “Type”은 모든 것이 다 로드 된 후에만 ExecStart 명령이 실행되는걸 보장하기 위해 ‘idle’로 설정한다. 예제에 사용한 GPIO 기반의 스크립트는 디폴트인 ‘simple’에서는 동작하지 않는다. 

파일의 패스는 절대주소여야 하고 스크립트의 위치 뿐 아니고 파이선의 위치까지도 완전히 지정해 줘야만 한다.

스크립트의 텍스트 출력을 로그파일에 저장하고 싶으면 ExecStart 라인을 다음과 같이 바꾸면 된다.

ExecStart=/usr/bin/python /home/pi/myscript.py > /home/pi/myscript.log 2>&1

Unit 파일의 퍼미션은 644로 설정해야 한다.

$ sudo chmod 644 /lib/systemd/system/myscript.service

Step 3 - systemd 설정
 
unit파일이 정의되었으므로 systemd에게 부팅시 이것을 사용하도록 알려줘야 한다.

$ sudo systemctl daemon-reload
$ sudo systemctl enable myscript.service

이제 시스템을 리부트 시키면 myscript.py가 자동으로 실행될 것이다.

$ sudo reboot

Step 4 - 서비스 상태 확인
 
서버스의 상태는 다음 명령으로 확인해 볼 수 있다.

$ sudo systemctl status myscript.service

댓글 16개:

  1. 블로그 관리자가 댓글을 삭제했습니다.

    답글삭제
  2. 안녕하세요. 파이 공부하고 있는 학생입니다.
    step3 에서 sudo systemctl enable myscript.service 를 입력하면

    Failed to execute operation: Bad messsage 라는 메세지가 나오는데
    혹시 해결 방법을 제시해 주실수 있을까요

    답글삭제
    답글
    1. 저도 같은 문제...ㅠㅠ 어떻게 해결하셨나요?

      삭제
  3. step 4를 먼저 실행해보니 [/lib/systemd/system/myscript.service:1] Invalid section header
    '[Unit] Description=My Script Service' 라고 나옵니다..

    답글삭제
  4. Description 아랫줄로 내리니까 되네요! 감사합니다 ㅎㅎ

    답글삭제
  5. [Service]

    Type=idle 부분 좀 더 알아보고 싶은데 정보의 출처를 알 수 있을까요?

    답글삭제
    답글
    1. http://lunatine.net/about-systemd/

      https://www.freedesktop.org/wiki/Software/systemd/

      이런 링크를 참고하시면 됩니다.

      삭제
  6. 혹시 C 코드를 실행시킬라면 어떻게 해야되나요? 오브젝트 파일은 실행이 안되네요 ㅠㅠ

    답글삭제
  7. 혹시 c코드는 execStart 에 어떻게 적어야되나요?
    필요한 라이브러리는 lbluetooth lwiringpi 이고, 4가지 정도의 c코드를 사용하며 gcc 로 하고있습니다.

    답글삭제
    답글
    1. 질문을 잘 이해하기 힘든데 C로 만들었건 어떤 언어로 만들었건 실행파일(C의 경우 gcc로 컴파일 해 만든 실행파일)은 다 동일합니다. 만일 pi계정에서 로그인해서 홈 디렉토리에서 gcc -o test test.c -lbluetooth -lwiringpi 로 컴파일해 test라는 이름의 실행파일이 만들어졌다면 ExecStart = /home/pi/test 로 써 주면 됩니다.

      삭제
    2. 서비스 상태 확인(step4)명령 입력시, active(runnig)으로 출력으로 잘 나오는데, 음.. 자동실행시 터미널 창이 뜨고 실행되는건가요?? 실행시 블루투스로 rfcomm 리슨 상태로 계속 기다리게 되는 코드입니다. rpi3입니다.. 왜이럴까여 ㅠㅠ?

      삭제
    3. 이미 해결 하셨겠지만...
      bluetooth를 사용하신다면 [Unit] 항목에서 Multi-user.target이 아니라
      bluetooth.target을 사용하시는게 어떨까요?
      해당 환경이 올라온 후에 프로그램이 실행되는게 맞는 것 같은데요...

      삭제
  8. 안녕하세요. 좋은 글 잘 읽고 가요!
    한가지 안 되는 부분이 있어서 이렇게 질문 드립니다.
    분명 오타는 없는데, 결과가 뜨지 않아
    sudo systemctl status myscript.service
    를 치니까 오류가 떴다고 나오네요.
    unable to access the x display, is $display set properly?
    myscript.service: main prcess exited, code = exited, status=1/FAILURE
    대충 이런 오류가 나오는데요, 왜 이러는지 알 수 있을까요?
    참고로 돌리는 프로그램은 wxpython 기반 gui 입니다...

    답글삭제
    답글
    1. unable to access the x display, is $display set properly?
      이 에러는 X윈도우가 실행되지 않은 상태에서 X window server(X 윈도우에서는 어플리케이션이 X window server입니다)를 실행했기 때문에 발생합니다.

      삭제
  9. man 페이지를 지루하게 보다가 혹시나 해서 검색해서 찾게 되었는데, 동일하지는 않지만 저에게 필요한 설정에 대한 셜명이 있어서 덕분에 시간을 절약할 수 있었네요. 감사합니다.

    답글삭제