工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

rc.localで自動起動するときにやっておくべきこと

概要

rc.localで自動起動に失敗したときにすぐに原因がわかるようにする対策をメモした。


背景と目的

Raspberry Pi上で、AWS IoT Device SDKを使ったPythonスクリプトを作成し、rc.localで自動起動しようとして失敗したので、エラー原因を探るためにやったことをメモしておく。


詳細

対策1: そもそもrc.localで起動する場合は、まず標準出力、エラーをファイルに吐かせておく

ログも何もないせいで、原因がわからず右往左往するのは非常に時間の無駄なので、そもそもrc.localで起動する場合は、標準出力、エラーをファイルに吐いておき、起動失敗時にすぐ原因をわかるようにする。正式運用の方法としてふさわしいかどうかは置いておいて。

こちらにあるとおり、なんでもいいから標準出力、エラーをファイルに履いておく。

1.1 まず自動起動したいcommandの標準出力、エラーをファイル/path/to/logfileに吐くシェルスクリプトxxx.shを作成

command > /path/to/logfile 2>&1

1.2 /etc/rc.local

作成したシェルスクリプトをバックグラウンド実行するコマンドを記入。バックグラウンドの指定&は忘れないように。

sudo bash /path/to/xxx.sh &


ここまでで、起動失敗しても最低でも何かしらの情報は残る。

念のため、

sudo bash /etc/rc.local

を実行して、動き出すか確認。


対策2: AWS IoT Device SDKを使ったプログラムへの対策

起動しない原因としてありがちなのは、

起動直後のせいでAWS IoTのエンドポイントの名前解決ができないために、AWSIoTClient.connectでエラーが発生する

というもの。

Traceback (most recent call last):
  File "/path/to/aws_iot_application.py", line 78, in <module>
    myMQTTClient.connect()
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/MQTTLib.py", line 513, in connect
    return self._mqtt_core.connect(keepAliveIntervalSecond)
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/core/protocol/mqtt_core.py", line 196, in connect
    self.connect_async(keep_alive_sec, self._create_blocking_ack_callback(event))
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/core/protocol/mqtt_core.py", line 223, in connect_async
    raise e
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/core/protocol/mqtt_core.py", line 211, in connect_async
    rc = self._internal_async_client.connect(keep_alive_sec, ack_callback)
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/core/protocol/internal/clients.py", line 122, in connect
    rc = self._paho_client.connect(host, port, keep_alive_sec)
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/core/protocol/paho/client.py", line 665, in connect
    return self.reconnect()
  File "/usr/local/lib/python3.5/dist-packages/AWSIoTPythonSDK/core/protocol/paho/client.py", line 789, in reconnect
    sock = socket.create_connection((self._host, self._port), source_address=(self._bind_address, 0))
  File "/usr/lib/python3.5/socket.py", line 694, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
  File "/usr/lib/python3.5/socket.py", line 733, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -3] Temporary failure in name resolution

なので、超簡単な対策としては、

/etc/rc.local

sleep 10 # 少し待つ
sudo python /path/to/aws_iot_application.py >/path/to/logfile 2>&1

とする。秒数が足らない可能性もあるので、より確実にするには、hostコマンドなどを使って、名前解決ができるか調べてから起動するようにするとより良い。

/etc/rc.local

# host のレスポンスにアドレスが含まれるようになるまで待つ
while :
do
    resp=`host *******************.iot.ap-northeast-1.amazonaws.com`
    if [[ $resp =~ .+' has address '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3} ]] ; then
        break
    fi
    sleep 1
    echo "sleeping..."
done
sudo python /path/to/aws_iot_application.py > /path/to/logfile 2>&1


まとめと今後の課題

rc.localで自動起動失敗時の原因探索のための対策をまとめた。