JetPack 4.5.1(Jetson Nano) で overlayroot する方法

この記事は DeNA Advent Calendar 2021 の22日目の記事です。

DeNA スポーツ事業本部でエンジニアをしている @yoshimoto です。
最近は 先日リリースされたデジタルムービーコレクションサービス [PLAYBACK9](https://playback9.jp/login) を担当しています。

スポーツ事業本部では、新型コロナウイルス禍においても、イベントの来場者が、安心してエンターテインメントを楽しめる環境の構築に貢献するためのさまざまな取り組みを推進。

その一環として、イベントでの混雑度の状況を可視化する仕組みを開発。デバイスとしてJetson Nanoを使用し組み込み機器を準備していましたが、設置場所は工事等で不定期に電源断が発生する為、それに耐えられるようにシステムを構築する必要がありました。

電源断のタイミングでファイルシステムへの書き込みを行なっていると

* ファイルシステムの論理破損
* 書き込みを行なっているデバイスの物理破損

につながります。

保存が必要なデータはリアルタイムにクラウドへ送信しており、再起動時はクリーンな状態で起動できれば問題ないので、overlayroot を使用して tmpfs に書き込みを行うようにする事で解決することにしました。

しかしながら、JetPack 4.5.1ではoverlayrootが通常の設定手順では動作しなかったのです・・・　:cry: 

# 通常の overlayroot 設定手順

### 1. overlayroot のインストール

パッケージのインストールを行います。

```shell-session
# apt install -y overlayroot
```

### 2. /etc/overlayroot.conf の書き換え

/etc/overlayroot.conf を書き換えて、tmpfs を使用するように設定します。

編集差分は以下
 
```patch
*** /etc/overlayroot.conf.orig	2018-09-20 22:29:41.000000000 +0900
--- /etc/overlayroot.conf	2021-12-03 23:45:14.445943570 +0900
***************
*** 167,170 ****
  #    The result is stored in r-------- /dev/.initramfs/overlayroot.XXXXXXX,
  #    which is a tmpfs in memory.
  overlayroot_cfgdisk="disabled"
! overlayroot=""
--- 167,170 ----
  #    The result is stored in r-------- /dev/.initramfs/overlayroot.XXXXXXX,
  #    which is a tmpfs in memory.
  overlayroot_cfgdisk="disabled"
! overlayroot="tmpfs"
```

### 3. /boot/extlinux/extlinux.conf の書き換え

overlayroot パッケージをインストールしたタイミングで　/boot/initrd.img が更新されているので、それを使うように変更します。

編集差分は以下

```patch
*** /boot/extlinux/extlinux.conf.orig   2021-12-12 00:38:36.076273457 +0900
--- /boot/extlinux/extlinux.conf    2021-12-12 01:08:47.410705223 +0900
***************
*** 6,12 ****
  LABEL primary
        MENU LABEL primary kernel
        LINUX /boot/Image
!       INITRD /boot/initrd
        APPEND ${cbootargs} quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0

  # When testing a custom kernel, it is recommended that you create a backup of
--- 6,12 ----
  LABEL primary
        MENU LABEL primary kernel
        LINUX /boot/Image
!       INITRD /boot/initrd.img
        APPEND ${cbootargs} quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0

  # When testing a custom kernel, it is recommended that you create a backup of
```

### 4. 再起動

```shell-session
# reboot
```

再起動すると overlayroot が有効になっているハズなのですが、永遠と再起動が繰り返されてしまいます・・・


# /boot/initrd に overlayroot を組み込む

標準で使われている /boot/initrd は update-initramfs で作成されるものとは異なっており、カスタムされたものになっています。

ここでは、/boot/initrd に必要なファイルを組み込む方法で overlayroot が動作するようにしてみたいと思います。
※ 現時点の最新バージョン 4.6 でも同様の手順で overlayroot が可能です

### 1. 準備
/boot/initrd をバックアップし、作業ディレクトリを作成します。

```shell-session
# cp -p /boot/initrd /boot/initrd.orig
# tempdir=$(mktemp -d)
```

### 2. overlayroot のインストール

パッケージのインストールを行います。

```shell-session
# apt install -y overlayroot
```

### 3. initrd に overlayroot を組み込む

作成した作業ディレクトリに initrd を展開します。

```shell-session
# (cd $tempdir; zcat /boot/initrd |cpio -id)
```

initramfs-tools から overlayroot に使用されるスクリプトをコピーします。

```shell-session
# mkdir $tempdir/scripts
# cp -p /usr/share/initramfs-tools/scripts/functions /usr/share/initramfs-tools/scripts/init-bottom/overlayroot $tempdir/scripts
```

init から overlayroot を実行するためのスクリプトを作成します。

```shell-session
# cat <<"EOF;" >>$tempdir/scripts/overlayroot-jetpack
if ! mount -o remount,ro /mnt; then
  echo "ERROR: remounting /mnt fail..." > /dev/kmsg;
  exec /bin/bash;
fi

if ! insmod "/mnt/lib/modules/$(uname -r)/kernel/fs/overlayfs/overlay.ko"; then
  echo "ERROR: insmod overlay.ko fail..." > /dev/kmsg;
  exec /bin/bash;
fi

export rootmnt=/mnt
bash /scripts/overlayroot
EOF;
```

init を修正し、上記で用意したスクリプトを実行する様にします。

```shell-session
# cat <<"EOF;" |(cd $tempdir; patch)
*** init.orig   2021-05-14 16:11:10.420039353 +0900
--- init    2021-05-14 16:11:50.944686024 +0900
***************
*** 218,223 ****
--- 218,226 ----
  mount -o bind /proc /mnt/proc;
  mount -o bind /sys /mnt/sys;
  mount -o bind /dev/ /mnt/dev;
+ 
+ . /scripts/overlayroot-jetpack
+ 
  cd /mnt;
  cp /etc/resolv.conf etc/resolv.conf
  
EOF;
```

スクリプトで必要なコマンドを用意します。

```shell-session
# cp -p /bin/kmod $tempdir/bin
# ln -s /bin/kmod $tempdir/sbin/modprobe
# ln -s /bin/kmod $tempdir/sbin/lsmod
# ln -s /bin/kmod $tempdir/sbin/insmod
# cp -p /bin/uname $tempdir/bin
# cp -p /bin/dash $tempdir/bin
# ln -s /bin/dash $tempdir/bin/sh
```

編集した initrd をアーカイブし、/boot/initrd を置き換えます。

```shell-session
# (cd $tempdir; find . |cpio -R 0:0 -o -H newc) |gzip > /boot/initrd
```

### 4. /etc/overlayroot.conf の書き換え

/etc/overlayroot.conf を書き換えて、tmpfs を使用するように設定します。

編集差分は以下
 
```patch
*** /etc/overlayroot.conf.orig	2018-09-20 22:29:41.000000000 +0900
--- /etc/overlayroot.conf	2021-12-03 23:45:14.445943570 +0900
***************
*** 167,170 ****
  #    The result is stored in r-------- /dev/.initramfs/overlayroot.XXXXXXX,
  #    which is a tmpfs in memory.
  overlayroot_cfgdisk="disabled"
! overlayroot=""
--- 167,170 ----
  #    The result is stored in r-------- /dev/.initramfs/overlayroot.XXXXXXX,
  #    which is a tmpfs in memory.
  overlayroot_cfgdisk="disabled"
! overlayroot="tmpfs"
```
### 5. 再起動

```shell-session
# reboot
```

起動後 mount コマンドを実行する事で overlayroot が有効化されている事が確認できます。

```shell-session
# mount
/dev/mmcblk0p1 on /media/root-ro type ext4 (ro,relatime,data=ordered)
proc on /media/root-ro/proc type proc (rw,relatime)
sysfs on /media/root-ro/sys type sysfs (rw,relatime)
none on /media/root-ro/dev type devtmpfs (rw,relatime,size=1784236k,nr_inodes=446059,mode=755)
tmpfs-root on /media/root-rw type tmpfs (rw,relatime)
overlayroot on / type overlay (rw,relatime,lowerdir=/media/root-ro,upperdir=/media/root-rw/overlay,workdir=/media/root-rw/overlay-workdir/_)
(snip)
```

起動中、overlayroot　されるまでは /dev/mmcblk0p1 が rw の状態ですので、電源断のタイミングによっては破損する可能性はあります。
理想的には ro で起動し overlayroot に切り替わる方が良いでしょう。
設置場所では、短時間に電源OFF/ONが繰り返されることはないため、今のところ破損なく運用できています　:smile: 


設定スクリプトは以下に置いてあります。
https://github.com/negachov/overlayroot-jetpack

# 最後に
この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ Twitter や facebook、はてなブックマークにてコメントをお願いします！

また [DeNA 公式 Twitter アカウント @DeNAxTech](https://twitter.com/DeNAxTech) では、 Blog記事だけでなく様々な登壇の資料や動画も発信してます。ぜひフォローして下さい！