Sabtu, 08 April 2017

Membuat Script Python Berjalan Otomatis (Sebagai Service) Saat Booting

Untuk beberapa proyek saya membuat sebuah service/daemon menggunakan Python agar bisa mengontrol sistem secara keseluruhan dari jarak jauh, service ini juga akan berjalan otomatis ketika Raspberry Pi dinyalakan. Di Linux, lain distribusi lain juga caranya memulai dan menghentikan service (sekarang ini beberapa menggunakan Upstart, dan ada juga systemd). Saya sendiri menggunakan versi Jessie dari distribusi Raspbian di Raspberry Pi 2 saya, dan dalam kasus ini cara yang benar untuk melakukannya adalah dengan menggunakan sebuah "init script".
Letak dari init script adalah di folder /etc/init.d. Disana kamu bisa menemukan berbagai script yang misalnya, untuk menjalakan sistem jaringan atau server printer. Raspbian Jessie menggunakan sistem init yang termasuk lama, yaitu Sys V  yang artinya bahwa script-script tersebut dijalankan berdasarkan tautan simbolis (symbolic links) yang berada dalam folder /etc/rc.xDokumentasi dari Debian menjelaskan secara detil mengenai hal ini.
Dan langsung saja, init script yang akan dijabarkan berikut ini akan membuat script Python (atau script lainnya seperti Perl) bisa berjalan di latar belakang ketika Raspberry Pi dinyalakan. Perlu diingat, service berarti sebuah program yang berjalan di latar belakang tanpa memerlukan interaksi dengan pengguna. Maka dari itu service wajib berjalan dalam mode "daemon" yang mana sangat sulit dan rumit untuk diimplementasikan langsung pada bahasa pemrograman termasuk Python tanpa bantuan dari sisi sistem operasi, karena proses ini akan melibatkan teknik yang namanya forking proses sebanyak dua kali dan trik trik kotor lainnya.
Maka dari itu, kita bisa menggunakan fitur yang sudah ada yang juga sangat mudah untuk digunakan yaitu perintah start-stop-daemon yang telah menyediakan segala kebutuhan kita untuk bisa menjalankan script yang kita buat di latar belakang secara otomatis.
Pada contoh ini nama service saya adalah govindaservice, silahkan disesuaikan.
Buat file service dengan nama govindaservice.sh dan buat menjadi executable, chmod a+x govindaservice.sh
#!/bin/sh
### BEGIN INIT INFO
# Provides:          govindaservice
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Deskripsi singkat dari service
# Description:       Deskripsi lengkap dari service
### END INIT INFO
 
# Ganti 3 baris berikut untuk menyesuaikan dimana kamu menaruh script mu
# dan apa nama service yang kamu inginkan
DIR=/home/pi/govinda
DAEMON=$DIR/main.py
DAEMON_NAME=govindaservice
 
# Path yang diperlukan oleh script mu
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin:/home/pi:/home/pi/bin:home/pi/govinda:/var/data/pi/govinda:/var/data/pi/bin
 
# Tambahkan opsi atau argumen command line disini jika service mu memerlukannya
DAEMON_OPTS=""
 
# Baris berikut menentukan apa user atau level user yang digunakan oleh script mu.
# Root umumnya tidak dianjurkan karena akan membuka celah keamanan, tapi root diperlukan
# jika script memerlukan akses ke sistem seperti penggunaan GPIO di Python
DAEMON_USER=root
 
# Proses ID dari script ketika ia berjalan akan disimpan disini
PIDFILE=/var/run/$DAEMON_NAME.pid
 
. /lib/lsb/init-functions
 
do_start () {
    log_daemon_msg "Memulai service $DAEMON_NAME daemon"
    #masukan perintah tambahan sebelum memulai service
    #misal
    #killall top
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
    log_end_msg $?
}
 
do_stop () {
    log_daemon_msg "Menghentikan service $DAEMON_NAME daemon"
    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
    #masukan perintah tambahan setelah menghentikan service
    #misal
    #killall main.py
    log_end_msg $?
}
 
case "$1" in
    start|stop)
        do_${1}
        ;;
     
    restart|reload|force-reload)
        do_stop
        do_start
        ;;
     
    status)
        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
        ;;
     
    *)
        echo "Cara pakai: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
        exit 1
        ;;
 
esac
exit 0





start-stop-daemon perlu mengetahui dan mengenali proses yang berkaitan dengan service, tujuannya agar (1) dia bisa tahu kalau prosesnya sudah berjalan atau sedang berjalan sehingga dia tidak perlu menjalankannya lagi, dan (2) dia bisa menemukan proses yang telah berjalan tersebut dan mengakirinya ketika diminta. Dalam kasus ini yang menggunakan script Python maka nama prosesnya adalah "python" jadi menggunakan nama proses untuk mengenali proses mana yang dimaksud tidaklah mungkin karena proses lain yang juga dibuat menggunakan Python akan memiliki nama proses yang sama juga, dan ini akan jadi membingungkan dan berbahaya bila salah kill.
Solusinya kita membuat start-stop-daemon agar menyimpan PID (Id proses) menggunakan argumen --pidfile #PIDFILE --make-pidfile. Ketika diminta untuk menjalankan proses ia akan mencari file $PIDFILE yang pada script diatas akan menjadi /var/run/govindaservice.pid (pada Raspberry Pi aslinya terletak di /run/govindaservice.pid) karena adanya fitur tautan simbolis.
Selain itu, kita menggunakan flag --background dari start-stop-daemon untuk menjalankan script kita di background, "--chuid" untuk mengatur user yang menjalankan script (dengan "--user" untuk mencari script yang dijalankan oleh user tersebut ketika kita mencoba untuk menentukan jika ia sudah atau sedang berjalan) dan "--start" untuk menentukan apa yang ingin kita jalankan. Opsi dari start-stop-daemon diakhiri dengan double-hyphen dan kemudian kita menambahkan pada $DAEMON_OPTS jika terdapat parameter atau argumen yang harus dilewatkan ke daemon sendiri.
Ketika menghentikan daemon --retry10 berarti bahwa pertama dari semua tahap sinyal TERM dikirim ke proses, dan 10 detik kemudian ia akan memeriksa jika proses yang diminta untuk mati itu masih berjalan atau tidak dan jika masih ia akan mengirim KILL atau sinyal mati paksa (yang tentunya akan berhasil).

Menggunakan Init Script

Untuk bisa menggunakan script ini secara nyata, tempatkan script Python mu di tempat yang kamu suka dan pastikan ia bisa dieksekusi (dengan chmod 755 main.py atau chmod a+x main.py) dan juga jangan lupa mulai baris script dari main.py dengan menggunakan Python interpreter (misal #!/usr/bin/env python). Sunting init script sesuai keperluan mu.
Langkah selanjutnya adalah menyalin script service yang telah dibuat diatas ke /etc/init.d dengan sudo cp govindaservice.sh /etc/init.d. Sekali lagi pastikan govindaservice.sh juga sudah executable dan menggunakan UNIX line-endings. Terakhir untuk mengupdate database system-ctl jalankan perintah sudo sysctl reload. Pada titik ini kamu seharusnya sudah bisa menjalankan script Python mu menggunakan perintah sudo service govindaservice start, memeriksa statusnya dengan sudo service govindaservice status dan untuk menghentikan menggunakan sudo service govindaservice stop.
Terakhir untuk membuat Raspberry Pi menggunakan init script mu pada waktu dan urutan yang tepat, satu langkah terakhir ini sangat diperlukan: menjalankan perintah sudo update-rc.d govindaservice.sh defaults. Perintah ini akan menambahkan tautan simbolis dari service mu ke folder /etc/rc.x sehingga init scriptnya dijalankan pada waktu default saat start up atau ketika Raspberry Pi dinyalakan. Kamu bisa melihat tautan tersebut jika kamu menjalankan perintah ls -l /etc/rc?.d/*govindaservice.sh. Terkahir jangan lupa reload lagi dengan sudo sysctl reload. Restart dan coba lihat script mu berjalan otomatis!

Tidak ada komentar:

Posting Komentar