Mac上でPiShrinkがインストールされたDockerコンテナを使用してRaspberry PiのSDカードのバックアップを作成する方法

(The English version of this article is here)

Mac上でラズベリーパイのSDカード全体のバックアップを行う際にddコマンドを使用できます。ddコマンドで単純にSDカード全体を読み出すと、未使用領域分も含めてSDカード全容量分の巨大なイメージファイルが作成されてしまうため、ストレージの容量を圧迫し、SDカードに復元する際に長時間を要してしまいます。そこで、 PiShrinkというツールがインストールされたDockerコンテナを使用してイメージファイルのサイズを小さくしてみました。本記事ではその手順を説明します。

環境

  • Mac: 10.14.6
  • ラズベリーパイ3 Model B+
  • Docker for Mac(docker desktop): 2.1.0.4
  • PiShrinkがインストールされたDockerコンテナ
    • tureeさんのpishrink-dockerを使用、Docker Hubには他にもいくつか公開されていましたが、今回は未検証

不要なアプリ・ソフトウェアパッケージ等の削除

イメージを作成する前に、不要なアプリ・ソフトウェアパッケージ等の削除をしておきます。ラズベリーパイに圧縮したいSDカードを挿入して起動します。

不要なアプリの削除

sudo apt-get purge libreoffice wolfram-engine sonic-pi scratch

使っているアプリを間違って削除しないように注意しましょう!

不要なソフトウェアパッケージの削除

sudo apt-get clean
sudo apt-get autoremove

SDカードイメージファイルの作成

ラズベリーパイをシャットダウンしてSDカードを取り出します。SDカードをMacに接続し、ddコマンドでイメージファイルとして読み出します。まずはコマンドラインでdiskutil listコマンドを実行し、SDカードのデバイスファイルの場所を見つけます。

$ diskutil list
 /dev/disk0 (internal):
    #:                       TYPE NAME                    SIZE       IDENTIFIER
    0:      GUID_partition_scheme                         251.0 GB   disk0
    1:                        EFI EFI                     314.6 MB   disk0s1
    2:                 Apple_APFS Container disk1         250.7 GB   disk0s2
~ ~ ~ snip ~ ~ ~ 
 /dev/disk3 (external, physical):
    #:                       TYPE NAME                    SIZE       IDENTIFIER
    0:     FDisk_partition_scheme                        *32.0 GB    disk3
    1:             Windows_FAT_32 boot                    46.0 MB    disk3s1
    2:                      Linux                         32.0 GB    disk3s2

/dev/disk3にSDカードがマウントされているので、このパスをddコマンドに指定すればOKですが、Macでは/dev/diskXの他に、読み書きがバッファされないrつきのデバイスファイル(/dev/rdiskX)も作成されており、こちらを使用する方が高速に読み出すことができます。

$ ls /dev/rdisk*
 /dev/rdisk0   /dev/rdisk0s2 /dev/rdisk1s1 /dev/rdisk1s3 /dev/rdisk2   /dev/rdisk2s2 /dev/rdisk3s1
 /dev/rdisk0s1 /dev/rdisk1   /dev/rdisk1s2 /dev/rdisk1s4 /dev/rdisk2s1 /dev/rdisk3   /dev/rdisk3s2
$ sudo dd if=/dev/rdisk3 of=./XXXX.img bs=1m

Macにインストールされているddコマンドは実行中に何も進捗の状態を表示してくれませんが、ターミナル上でCtrl+Tを入力すると、その時点での処理済みバイト数を表示してくれます。

SDカードイメージファイルの圧縮

イメージファイルの圧縮にはPiShrinkというツールを使用します。 Docker Hubを検索するとPiShrinkをインストール済みのDockerコンテナがいくつか公開されています。今回はtureeさんのpishrink-dockerを使用させてもらいます。Docker Desktop for Macはインストールして起動を済ませておきます。

$ docker pull turee/pishrink-docker
 Using default tag: latest
 latest: Pulling from turee/pishrink-docker
 Digest: sha256:31e16c7d382b7a96e28cd92458a0cc1bf1e9b88bad116b68fd7dd4ca95fbf6b8
 Status: Image is up to date for turee/pishrink-docker:latest
 docker.io/turee/pishrink-docker:latest

docker run実行時にホストマシンのボリュームをDockerコンテナにマウントすることができますが、バックアップしたSDカードイメージファイルのディレクトリをマウントしてPiShrinkの実行したところエラーになってしまいました。

$ docker run --rm --privileged=true -v (pwd):/workdir turee/pishrink-docker pishrink XXXX.img Warning: Unable to open /workdir/XXXX.img read-write (Permission denied).  /workdir/XXXX.img has been opened read-only.
 Warning: Unable to open /workdir/XXXX.img read-write (Permission denied).  /workdir/XXXX.img has been opened read-only.
 mount: /tmp/tmp.nDyP6lWYv6: WARNING: device write-protected, mounted read-only.
 Creating new /etc/rc.local
~ ~ ~ snip ~ ~ ~
  Couldn't find valid filesystem superblock.
 resize2fs: Operation not permitted while trying to open /dev/loop0
 ERROR: resize2fs failed...
 mount: /tmp/tmp.nDyP6lWYv6: can't read superblock on /dev/loop0.
 mv: cannot stat '/tmp/tmp.nDyP6lWYv6/etc/rc.local.bak': No such file or directory
 umount: /tmp/tmp.nDyP6lWYv6: not mounted.
 losetup: /dev/loop0: detach failed: No such device or address

若干手間が増えてしまいますが、コンテナ側にイメージファイルをコピーして圧縮、圧縮されたイメージファイルをホスト側にあらためてコピー、という手順を踏むことにします。pishrinkコマンドに引数一つ(圧縮元イメージファイルのパス)しか指定しないと、圧縮元ファイルが圧縮後のファイルで上書きされます。

$ docker cp XXXX.dmg 32f4176aa68a:/root/XXXX.img
$ docker exec -it 32f4176aa68a bash
 root@32f4176aa68a:/workdir# cd /root/
 root@32f4176aa68a:~# pishrink XXXX.img 
 Creating new /etc/rc.local
 rootfs: 114911/950272 files (0.5% non-contiguous), 938002/3793920 blocks
 resize2fs 1.44.1 (24-Mar-2018)
~ ~ ~ snip ~ ~ ~ 
 Begin pass 4 (max = 12048)
 Updating inode references     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 The filesystem on /dev/loop0 is now 937412 (4k) blocks long.

 Shrunk XXXX.img from 30G to 3.7G
 root@32f4176aa68a:~# exit
 exit
$ docker cp 32f4176aa68a:/root/XXXX.img XXXX-shrink.img

最後のダメ押しで、pishrinkで圧縮したイメージファイルをxzコマンドでも圧縮してみます。イメージファイルの内容次第にはなりますが、執筆時、pishrinkで圧縮した時点で3.7GBあったファイルが1.1GB程度まで圧縮できました。

$ xz XXXX.img 
 load: 2.88  cmd: xz 30200 running 58.35u 0.42s

ddコマンドと同様に、xzコマンド実行中にCtrl+Tを入力すると進捗が表示されます。xzコマンドに圧縮元ファイル名のみを指定すると、元ファイル名に.xzの拡張子がついた圧縮後のファイルで上書きされます。

圧縮したSDカードイメージファイルの書き込み

圧縮されたSDカードイメージの.xzファイルは、Etcherを使うとそのままSDカードに書き込むことができます。

圧縮したSDカードイメージでラズベリーパイを起動

初回起動時に、SDカードの空きスペースいっぱいまでパーティションが拡張されます。

まとめ

DockerコンテナにインストールされたPiShrinkというツールを使用して、ラズベリーパイのSDカードイメージファイルのサイズを小さくする手順を説明しました。イメージファイルのサイズは小さい方がストレージ容量の節約になり書き込み時間も少なくて済むため、容量の大きいSDカードからバックアップを作成した後には圧縮を行うことをおすすめします。

参考