モダンにやりたいIoTプロダクトにおけるファームウェアCD環境

まつり!

Nature Engineering Blog祭 11日目、今日はソフトウェアエンジニアの的場がお届けします。 Natureにはファームウェアエンジニアとして入社しましたが、現在はバックエンドメイン、フロントエンド少々という感じでお仕事をしております。何を書こうかなと迷っていたのですが元々IoTが好きでこの会社を選んだこともあり、今回は私がファームウェア開発をメインにやっていた際に構築をしたファームウェアCD環境について紹介させてもらえればと思います。


IoT機器のファームウェアには大半の場合FOTAアップデート機能が実装されていると思います。 FOTAFirmware Over-The-Air の略で、FOTAアップデートとはざっくりいうと無線によるファームウェアのアップデートです。Nature Remoの場合はアプリのデバイス一覧画面に赤いバッジが表示されている場合に選択すると始まるアレです。 アレを実施するとアプリからRemoへコマンドが送られ、コマンドを受け取ったRemoはサーバーから最新のファームウェアイメージをダウンロードして、特定のパーティションに焼くということをやっています。 これがサーバー開発におけるデプロイのようなものだと捉えて、ファームウェアのCD環境を説明していきます。

全体の構成

構成は以下のようになります。 それぞれの関係や流れは後ほど書き足していきます。

overall

GitHub

言わずもがな。

GitHub Actions

CI/CDツールにはGitHub Actionsを使用しています。

CI/CDマシン

ファームウェアのCI/CD用に自前で用意したマシンです。 現状はほとんどできていないのですが、最終的にはこのマシンに実機テスト用のRemoを接続しておき、実機テストを実施するジョブを走らせてからリリースする、というフローを踏襲したいと思っているため、このようなマシンを用意しています。 オフィスに放置されていたミドルタワーがあったので、こちらにUbuntu Serverをインストールして使用しています。

セットアップとしてはself-hosted runner(後述)のagentや、Dockerをインストールするということをしています。 マシンを自前で用意しているため、好きなソフトウェアをあらかじめインストールできるというのは通常の remote runnerとの大きな違いになります。

場合によってはDockerを使わずに直接ホストに各種ソフトウェアをインストールしてそれを使用したり、クレデンシャル情報を直接持たせておいたり、というような運用をとった方がいい場合もあるかもしれません。

self-hosted runner

物理デバイスが絡む開発でお目にかかるのがこの機能です。 自前のマシンでagentを実行しておくことによって、そのマシンをGitHub Actionsのrunnerにできます。

https://docs.github.com/ja/actions/hosting-your-own-runners/about-self-hosted-runners

このagentをCI/CDマシンで実行することで、CI/CDマシンをrunnerとして使用できるようになります。(runs-on で指定できる)

S3

ファームウェアの各リリースイメージは、S3に保存しています。 バケットは下記の2種類を使用しています。

  1. リリースビルド後にイメージを愚直にアップロードするバケット

  2. 公開するイメージのみを保存しておくバケット

テストリリースはしたが一般リリースはしなかった、というケースがあるため、このような構成をとっています。

CLIツール

内製のcliツールです。 ファームウェアリリース周りに使用するAPIをサーバー側に用意してあるので、これらを叩いてファームウェアリリース状態を操作します。

公開までの流れ

tagをpushする

tag_push
releaseしたいコミットにtagをつけてpushします。 現状はこの操作が後述するGitHub Actions workflowのトリガーになっています。

リリースビルドをして、イメージをS3にアップロードする

release_build
リリースビルドのジョブが走ります。 現状はCI/CDマシンでビルドを走らせていますが、ビルドだけなら通常のremote runnerでも問題ありません。

ビルドした後はイメージをS3(バケットA)にアップロードします。 この時点ではまだリリース(アプリからファームウェアアップデートできる)状態にはなっていません。(=デプロイ完了していない) 後述の実機テストで各エンジニアやジョブが同一のイメージを参照できるよう、シンプルにアップロードのみを行います。

リリースイメージをダウンロードして実機テストする

ここは改善の余地があるポイントなので、現状と理想を並べて記載しておきます。

現状

test_current
エンジニアが手元にリリースイメージをダウンロードしてそれを開発用Remoに焼き、手作業でテストを実施します。 お察しの通り、現状CI/CDマシンが力を発揮するシーンは実質ないです・・・。

とは言っても、まずは形だけでもそれっぽくしたい!という気持ちがあったので、CI/CDマシンからリリースイメージをダウンロードして、テスト用Remoに自動で書き込むところまではジョブを用意してあります。

理想

test_ideal
CI/CDマシンがリリースイメージをダウンロードしてテスト用Remoにそれを焼き、実機テストを走らせます。 テストがパスすれば自動で次のステップへ進みます。

課題

実機テストで最優先でテストしたい項目がFOTAアップデートになります。 ここがバグってFOTAアップデートできないファームウェアをばら撒いてしまうとユーザー側でアップデートができなくなり、最悪リコール問題になってしまうからです。

このFOTAアップデートにはBluetoothを使っているため、実機でテストをするには対向のBluetoothバイスとそれ用のソフトウェアを用意する必要があります。 これがなかなか重い作業で時間が取れていない、というのが現状です。

そもそも対向デバイスは何にするのかというのも悩みどころです。 Nature Remoアプリを対向にすればユーザーと最も近い環境でテストができるのですが、自動化のハードルが上がります。 CI/CDマシンを対向として専用のソフトを書くという手などもありますが、この場合はメリット・デメリットが逆転する形になります。

実機テスト、むずい。

イメージを公開(≒デプロイ)する

実機テストに問題がなければいよいよユーザーがファームウェアをアップデートできるようにイメージを公開します。

現状前のステップが手動なため、ここも手動で実施します。 とはいえ一般ユーザーへの公開はそれなりに緊張感がある作業なため、ここはあえて手作業のままにするということも十分にありえるかと思います。

現状、社内への公開と一般公開で異なる方法でリリースしています。

社内

CLIツールからコマンド一発でリリースします。

一般

サーバーにリリースバージョンを指定するファイルがあるため、これを修正してプルリクエストを出します。 こうして複数人の目を通すことでリリース周りの事故を減らしています。


ここまでの流れを経て、ユーザーはファームウェアをアップデートできるようになります。

fota_update

めでたい!

まとめ

ざっくりですがファームウェアCD環境について紹介させていただきました。 タイトルで「モダンに」と謳ってはいますが、まだまだ手作業による温かみ運用が所々にあるのが現状です。 それでもNatureにおけるファームウェアの開発体験はここ数年で劇的に向上していると思います。 みんなすごい。

とは言えまだまだ体験向上の余地があるとも思うので、今後も改善を続けていきたいと思います。 (今はバックエンドにズブズブですが。)