MenderでRaspberry PiのアプリケーションをOTAアップデートしてみる

メカトラックス株式会社の@kiyonagaです。
Mender と Raspberry Pi を組み合わせてソフトウェアアプリケーションの OTA（Over the Air）アップデートを試してみます。

## Menderとは
Menderは、オープンソースの OTA アップデートマネージャーです。
リモートでデバイスのソフトウェアをアップデートおよび管理するためのプラットフォームを提供します。
有料版では、動的なグループ化、段階的アップデート、差分更新などの高度な機能も利用可能となります。
|[公式ホームページ](https://mender.io/)|[github](https://github.com/mendersoftware/mender)|
|---|---|

## 実行環境
・Windows PC（Windows10）
・Raspberry Pi 3 Model B
・4GPi
・slee-Pi3
・SIMカード（4GPiで使用、標準サイズを推奨）

デバイスセットアップ用
・USB-シリアル変換ケーブル
・Ethernet ケーブル

## 構成
アプリケーションを実行するデバイスは弊社の Pi-field（パイフィールド）を模して間欠動作させます。
![block.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/04dadf13-3d6a-e1e6-4fcd-cd2cfe05d5e0.png)

### Pi-field とは
Pi-field は、電源やネットワーク環境がない屋外等でもラズベリーパイを使用した環境モニタリング等が可能となる屋外稼動キットです。
|外観|内部レイアウト|
|:---:|:---:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/b6159561-b9c9-c9e4-4917-4ed42e20423c.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/e67d85eb-6c28-ae1c-e154-88730797130c.png)|

Pi-field の詳細は[こちら](https://mechatrax.com/products/pi-field/)から

## デバイスのセットアップ
今回は Pi-field 用 OS イメージを利用します。この OS イメージには 4GPi と slee-Pi3 の各ユーティリティソフトウェアがインストールされています。

Raspberry Pi のコンソールへのアクセスとネットワーク接続にはUSB-シリアル変換ケーブルと Ethernet ケーブルを使用します。

OS イメージを書き込んだ SD カードと SIM カードをスロットに挿し、Raspberry Pi を起動します。なお、4G（LTE）通信の設定はのちほど行います。

アップデートは起動後に適宜実行しておいてください。
![IMG_2606.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/5f8413f1-0c72-6fb0-9424-720814de43d7.jpeg)
・Pi-field 用 OS イメージは[こちら](https://mechatrax.com/data/pi-field/)からダウンロード可能です。詳細は [github](https://github.com/mechatrax/pi-field/blob/main/os/Readme.md#os-%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8
) をご参照ください
・SD カードへの OS イメージの書き込み手順については[こちら](https://qiita.com/kiyonaga/items/73fa842ddcde0e028294)から
・4GPi のセットアップ方法は[こちら](https://mechatrax.com/blog/setup-4gpi/)から

## mender.io のアカウント作成
mender.io にアクセス後、アカウントを作成します。
https://mender.io/
Try for Free から進んでください。

今回はお試しなのでFreeプランを利用します。
https://mender.io/product/pricing

プランによる利用可能機能の違いは以下をご覧ください。
https://mender.io/product/features

アカウント登録が完了するとダッシュボードがご覧になれます。
![dashboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/859d71a0-f5d2-32ec-b8c2-38aa022275b4.png)

## デバイスを mender.io に登録
Raspberry Pi を mender.io に登録します。
mender.io ダッシュボード上の Connect a device を押すと以下のようなポップアップが出ます。

__１．GET STARTED クリック__
![getstarted.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/a1773cae-bd16-dae6-000c-80988434c43c.png)
***
__２．Device Type を選択して NEXT__
![devicetype.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/b95dc993-18ca-1900-5147-40742c0839ca.png)
***
__３．JWT_TOKEN、TENANT_TOKEN、wgetコマンドが表示される__
![client.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/7ce16011-fab6-ac6f-aa12-5173bca707bd.png)
***
__４．JWT_TOKEN、TENANT_TOKEN、wgetコマンドを順にRaspberry Piのコンソールで実行__
![token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/6ef52500-b411-26a8-3403-d975b716b115.png)
![wget.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/1416e78d-ec22-0288-1866-f7f67be2692f.png)
***
__５．しばらく置いておくとダッシュボードの DEVICES に登録される__
![name.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/a8c70a9a-46ad-f7ce-8f0d-ddad49946746.png)
※このとき Name は変更済みです
***
__６．デバイスを選択して、Actions の Accept をクリック__
![device.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/5a5bb3ac-d3a5-275d-3e2f-654b9a8984c8.png)
***
__７．SAVE をクリックしてデバイスの登録が完了__

## アプリケーションの作成
bash を使用して、Raspberry Pi のシステム時刻とCPU温度を取得し、4G（LTE）回線を介してサーバ（Google スプレッドシート）にデータを送信します。
OTA アップデートは、データの変化を見て確認します。

__１．サーバの設定__
適当なスプレッドシートで Google Apps Script を作成します。
シートに項目（sysdate、cpu_temp）を記載します。
```:Google Apps Script
function doGet(e) {
  //データを取り出す
  var sysdate = e.parameter.sysdate;
  var cpu_temp = e.parameter.cpu_temp;
  
  //現状Activeになっているsheetを取得
  var sheet = SpreadsheetApp.getActiveSheet();

  //1列目に日時、2列目にCPU温度 を追記
  sheet.appendRow([sysdate,cpu_temp]);
}
```
Google Apps Script の紹介、説明は省きます。
デプロイを忘れずに。
***
__２. 実行スクリプトの作成（アップデートの対象）__
/opt/demo に gas.sh を配置します。demo ディレクトリは事前に作成しておいてください。
取得したデータをサーバに送信後、起動アラームを設定してシャットダウンするという内容です。
```bash:gas.sh
#!/bin/bash

interval_min=5
DATE=$(date +'%Y/%m/%d %H:%M:%S')
CPU_TEMP=$(vcgencmd measure_temp | sed -e "s/temp=\(.*\)'C/\1/")

wget --timeout=10 --tries=1 -O /dev/null "https://script.google.com/macros/s/***/exec?sysdate=$DATE&cpu_temp=$CPU_TEMP"

current_min=$(date +'%M')
remainder=$((current_min % interval_min))
add_min=$((interval_min - remainder))
wakeup_datetime=$(date +'%Y%m%d %H:%M:%S' -d "now +$add_min minutes")

sleepi3alarm set "$wakeup_datetime"
sleep 60
shutdown -h now
```
※1. 起動アラームの設定は [slee-Pi3 の機能](https://github.com/mechatrax/sleepi3-utils#usrsbinsleepi3alarm)を利用します。
※2. スクリプト中の `sleep 60` は、OTA アップデート用の時間確保のために入れています。想定されるアップデートの容量によって Raspberry Pi の稼働時間を調整してください。
※3. Pi-field 実機の場合は、間欠起動の間隔を10分以上取ってください。また、起動時間が長くなるほどバッテリーを消費するのでご注意ください。
***
__３. スクリプトの自動実行を設定__
/etc/systemd/system に mender-demo.service を配置します。
起動時、4G（LTE）通信の接続確立後に gas.sh を実行する設定です。
```:mender-demo.service
[Unit]
Description=Demo Application
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c "while ! ip a show wwan0 | grep -q 'inet '; do sleep 1; done"
ExecStart=/usr/bin/bash /opt/demo/gas.sh

[Install]
WantedBy=multi-user.target
```
***
__４．自動実行設定の有効化__
以下コマンドをコンソールで実行します。
```
sudo systemctl enable mender-demo.service
```
***
__５．4G（LTE）通信の設定__
以下コマンドをコンソールで実行します。
接続名、APN、ユーザー、パスワードはご利用の SIM カードに合わせて変更してください。
```
sudo nmcli con add type gsm ifname "*" con-name 「接続名」 apn 「ＡＰＮ」 user 「ユーザー」 password 「パスワード」
```
詳細は[github](https://github.com/mechatrax/4gpi/wiki/%E3%81%9D%E3%81%AE%E4%BB%96#%E6%8E%A5%E7%B6%9A%E8%A8%AD%E5%AE%9A)をご参照ください。

コマンド実行後、4GPi 基板上の青 LED が点滅すると 4G（LTE）通信の接続が確立しています。
***
__６．動作確認__
Raspberry Pi を再起動させます。
Ethernetケーブルは抜いておいてください。
しばらく放置して、だいたい5分ごとにデータが送られてきているのを確認できました。
![spr (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/083976ed-343e-95f7-a273-395632f59f4d.png)

## アプリケーションをアップデート
ようやく本題の OTA アップデートを行います。
基本的な操作はマネージャー（Webアプリケーション）上で行います。
あらかじめ Raspberry Pi 内の gas.sh と同じものを Windows PC に保管しておいてください。

__１．サーバとスクリプトに変更を加える__
Windows PC で cpu_volt の取得と送信を追加します。
```:Google Apps Script
function doGet(e) {
  //データを取り出す
  var sysdate = e.parameter.sysdate;
  var cpu_temp = e.parameter.cpu_temp;
  var cpu_volt = e.parameter.cpu_volt;
  
  //現状Activeになっているsheetを取得
  var sheet = SpreadsheetApp.getActiveSheet();

  //1列目に日時、2列目にCPU温度、3列目にCPU電圧を追記
  sheet.appendRow([sysdate,cpu_temp,cpu_volt]);
}
```
```bash:gas.sh
#!/bin/bash

interval_min=5
DATE=$(date +'%Y/%m/%d %H:%M:%S')
CPU_TEMP=$(vcgencmd measure_temp | sed -e "s/temp=\(.*\)'C/\1/")
CPU_VOLT=$(vcgencmd measure_volts | sed -e "s/volt=\(.*\)V/\1/")

wget --timeout=10 --tries=1 -O /dev/null "https://script.google.com/macros/s/***/exec?sysdate=$DATE&cpu_temp=$CPU_TEMP&cpu_volt=$CPU_VOLT"

current_min=$(date +'%M')
remainder=$((current_min % interval_min))
add_min=$((interval_min - remainder))
wakeup_datetime=$(date +'%Y%m%d %H:%M:%S' -d "now +$add_min minutes")

sleepi3alarm set "$wakeup_datetime"
sleep 60
shutdown -h now
```
***
__２．アーティファクトの作成__
ダッシュボードの RELEASES に移動後、画面右上の UPLOAD をクリックします。
![add0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/731bf5c4-25e9-d384-31d2-379903f95bf8.png)
***
__３．アップデート用のファイルを選択__

ファイルを選択
![up.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/d149cba2-9e92-8748-6dec-0855ee7eb537.png)
/opt/demo へ配置する設定に変更
![add1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/3dbf1574-dbba-fc18-bfcb-cdd32d409c77.png)
問題なければ UPLOAD ARTIFACT をクリック
![add2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/ee43845e-f248-0452-5193-6e0671aabdaa.png)
※5. __4G（LTE）回線を使用するため、アップデート容量にはご注意ください。__
***
__４．アーティファクトの作成完了後、右下の Release actions から Deployment for this release を選択__
![add3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/e05cb0b4-73a0-24aa-8e5a-1a7f4f415683.png)
***
__５．ダッシュボードの DEPLOYMENTS からアーティファクトの View detail をクリック__
アップデートの状況が確認できます。
![add4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/800fddeb-6bdd-198f-6987-bb352c127e80.png)
***
__６．アップデートの進捗確認__
成功すると SUCCESS となります。
![add5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/f61df389-3678-e147-94d9-ab081e0166e1.png)
***
__７．動作確認__
cpu_volt の項目が送られてくるようになりました。
![check (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/a16efa38-fae2-51b5-cfb7-8fef5e301ecb.png)

以上で OTA アップデートのデモは終了です。
今回は1台分のアプリケーションを書き換えただけですが、複数台同時にアップデートも可能ですし、プランや設定を変更することでさらに柔軟な OTA アップデートができそうです。

## 最後に
Pi-field は屋外での利用を想定しており、電源やネットワークの制約下でも稼動できるように設計されています。
一方で以下のような課題もあります。

・人の手の届かない場所に設置されることもあるため、ソフトウェア変更やアップデートが困難
・間欠動作を前提としているため、遠隔ログインでアップデートなどを行う際は起動タイミングに注意が必要

Menderの導入により、ユーザはアップデート内容を登録し、アップデートの完了を待つだけです。
これにより、先述の課題が解決し、Pi-fieldのような屋外稼動機器の保守作業が効率的に行えます。

## 補足
__１．デバイス側の Mender の設定について__
基本的にはアップデートの有無確認をデバイス側からポーリングします。
ポーリング間隔やリトライ回数などの調整は以下の設定ファイルで行います。
/etc/mender/mender.conf
![config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1174732/3dad2100-91e5-0ba5-47b2-822feac20355.png)
設定可能項目の詳細は[こちら](https://docs.mender.io/client-installation/configuration-file)から
今回のデモでは設定値を変更していません。

__２．データ収集用のサーバについて__
今回の目的は OTA アップデートを試すことなので視覚化が簡単そうな Google スプレッドシートでデータを収集しました。
設定等が簡易な一方で、URL が分かれば第三者からでもデータの改ざんができてしまいます。
あくまでも簡易的な動作確認用のものとご留意ください。