注目の記事

Raspberry Piを極限まで無駄なくバックアップする

あなたのラズパイ力とLinux力、おまけに業務効率を強力にブーストする…かもしれません。
2020.04.02

はじめに

Raspberry PiのSDカードの取り扱う上で、複製したり、イメージとして保存しておきたいシーンは多いと思います。 しかし、大容量のSDカードが一般的になった今では、空き容量が大半を占めるベタイメージを作成することは、以下の点から現実的でありません。

  • イメージファイルの大半を空き容量が占め、時間およびストレージの効率が悪い
  • 元より容量の小さいSDカードに書き込むことができない
  • SDカードに書き込む際、空き領域にも書き込みが行われ、カードの寿命を縮めてしまう
  • ファイルシステムが破損していても、コピー時に気づくことがない

パーティションの構造とLinuxの操作を学びながら、SDカードのバックアップをしていきましょう。

背景

Raspberry PiのSDカードの論理構造を見ていきましょう。難しく考える必要はありません。大体こんな感じといったイメージを掴むだけでOKです。

まず、セクタ単位でパーティションの一覧を表示してみます。 SDカードのデバイス名は、/dev/sdeとします。

mc@u-west:~/share/temp$ sudo parted /dev/sde u s p
Model: Generic- SD/MMC (scsi)
Disk /dev/sde: 62586880s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number Start End Size Type File system Flags
1 8192s 96663s 88472s primary fat32 lba
2 98304s 30031871s 29933568s primary ext4
  • 1つ目のパーティションが/bootで、Windows PCに挿したときにファイルが見える部分です。
  • 2つ目のパーティションが/で、Linuxファイルシステム(ext4)が格納されています。Windows PCに挿すとフォーマットするか聞かれる部分です。

では、これを図にしてみましょう。

2つのパーティションのほかに、パーティションが割り当てられていない領域が二か所ありますね。 ここにはセクタ0のMBRのほか、機種依存のデータなどが格納されている場合がありますので、パーティション2以外の領域は念のため完全なコピーを行う方針としましょう。

おや、パーティション2の後に空き領域がありますね。このSDカード自体が別のイメージを複製して作成されたものだからです。今回の手順では、ファイルシステムの内容だけをコピーするため、問題ありません。

バックアッププランとして下記のことを行えばよいことになります。

  • SDカード全体のうち、パーティション2の開始セクタの手前までをダンプする
  • パーティション2の内容をバックアップする

やるべきことを書き出してみると、Linuxの基本的なツールで出来そうな気がしてきませんか?

  • デバイスのセクタをダンプしたい…ddコマンド
  • Linuxファイルシステム(ext2/3/4)をバックアップ/復元したい…dump/restoreコマンド

バックアップ

では早速やってみましょう。

パーティションテーブルを確認する

mc@u-west:~/share/temp$ sudo parted /dev/sde u s p
Model: Generic- SD/MMC (scsi)
Disk /dev/sde: 62586880s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number Start End Size Type File system Flags
1 8192s 96663s 88472s primary fat32 lba
2 98304s 30031871s 29933568s primary ext4

パーティション2の開始セクタは98304sです。

※この値は最初に書き込んだイメージごとに違いますので、かならずバックアップするSDカード毎に確認してください

念のためパーティションテーブルをテキストに保存する

ddで保存したイメージ内のパーティションテーブルを見るのは大変そうなので、念のためです。

mc@u-west:~/share/temp$ sudo parted /dev/sde u s p >pipart.txt

SDカード全体のうち、パーティション2の開始セクタの手前までをダンプする

セクタサイズは512バイトです。今回の場合は0~98303の98304セクタを保存したいので、

mc@u-west:~/share/temp$ sudo dd if=/dev/sde of=piboot.bin bs=512 count=98304
98304+0 レコード入力
98304+0 レコード出力
50331648 バイト (50 MB) コピーされました、 2.70878 秒、 18.6 MB/秒

パーティション2をdumpコマンドでバックアップする

せっかくなので、-z9 オプションを付けて圧縮し小さくしてみましょう。

mc@u-west:~/share/temp$ sudo dump -0 -z9 -f piroot.dump /dev/sde2
DUMP: Date of this level 0 dump: Thu Apr 2 11:14:49 2020
DUMP: Dumping /dev/sde2 (an unlisted file system) to piroot.dump
DUMP: Label: rootfs
DUMP: Writing 10 Kilobyte records
DUMP: Compressing output at compression level 9 (zlib)
DUMP: mapping (Pass I) [regular files]
DUMP: mapping (Pass II) [directories]
DUMP: estimated 3275017 blocks.
DUMP: Volume 1 started with block 1 at: Thu Apr 2 11:15:02 2020
DUMP: dumping (Pass III) [directories]
DUMP: dumping (Pass IV) [regular files]
DUMP: 89.42% done at 9762 kB/s, finished in 0:00
DUMP: Closing piroot.dump
DUMP: Volume 1 completed at: Thu Apr 2 11:20:33 2020
DUMP: Volume 1 took 0:05:31
DUMP: Volume 1 transfer rate: 4418 kB/s
DUMP: Volume 1 3281440kB uncompressed, 1462543kB compressed, 2.244:1
DUMP: 3281440 blocks (3204.53MB) on 1 volume(s)
DUMP: finished in 331 seconds, throughput 9913 kBytes/sec
DUMP: Date of this level 0 dump: Thu Apr 2 11:14:49 2020
DUMP: Date this dump completed: Thu Apr 2 11:20:33 2020
DUMP: Average transfer rate: 4418 kB/s
DUMP: Wrote 3281440kB uncompressed, 1462543kB compressed, 2.244:1
DUMP: DUMP IS DONE

※もし、この手順の途中にエラーが発生した場合は、ファイルシステムにエラーが残っている可能性がありますので、次の項を参照してください。

mc@u-west:~/share/temp$ ls -al
合計 1511744
drwxr-xr-x 2 mc mc 58 4月 2 13:09 .
drwxrwxrwx 11 root root 45056 4月 2 11:22 ..
-rw-r--r-- 1 root root 50331648 4月 2 11:05 piboot.bin
-rw-r--r-- 1 mc mc 304 4月 2 11:04 pipart.txt
-rw-r--r-- 1 root root 1497644568 4月 2 11:20 piroot.dump

バックアップデータは約1.5GBとなりました。

バックアップ時にエラーが起きたときは

見る

石橋をたたいて(-n オプション:書き込みを行わない)fsckします。

mc@u-west:~/share/temp$ sudo fsck -vnf /dev/sde2
fsck from util-linux 2.20.1
e2fsck 1.42.5 (29-Jul-2012)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
'..' directory entry in directory inode 171251 is not NULL terminated
Fix? no

Directory inode 171251, block #0, offset 80: directory corrupted
Salvage? no

e2fsck: aborted

rootfs: ********** WARNING: Filesystem still has errors **********

fsckします(本番)。

mc@u-west:~/share/temp$ sudo fsck -vf /dev/sde2
fsck from util-linux 2.20.1
e2fsck 1.42.5 (29-Jul-2012)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
'..' directory entry in directory inode 171251 is not NULL terminated
Fix? yes
(中略)
Pass 5: Checking group summary information

rootfs: ***** FILE SYSTEM WAS MODIFIED *****

105912 inodes used (11.38%, out of 931040)
69 non-contiguous files (0.1%)
111 non-contiguous directories (0.1%)
# of inodes with ind/dind/tind blocks: 0/0/0
Extent depth histogram: 87618/18
872219 blocks used (23.31%, out of 3741696)
0 bad blocks
1 large file

78683 regular files
8668 directories
55 character device files
25 block device files
0 fifos
1375 links
18470 symbolic links (18186 fast symbolic links)
2 sockets
------------
107278 files

これで、エラーなくdumpコマンドが実行できるはずです。

リストア

パーティションテーブルを確認する

間違ったデバイスに書き込んでしまうと取り返しがつかないので、かならず確認しましょう。

mc@u-west:~/share/temp$ sudo parted /dev/sde u s p
[sudo] password for mc:
Model: Generic- SD/MMC (scsi)
Disk /dev/sde: 62586880s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number Start End Size Type File system Flags
1 8192s 62586879s 62578688s primary fat32 lba

新品のSDHCカードはこのように、パーティションが一つだけ存在します。 SDXCカードの場合は、exfatのパーティションが存在しますが、いずれにせよ次の手順で上書きされてしまうため、気にしなくて問題ありません。

パーティション2の開始セクタの手前までを復元する

mc@u-west:~/share/temp$ sudo dd if=piboot.bin of=/dev/sde bs=65536
768+0 レコード入力
768+0 レコード出力
50331648 バイト (50 MB) コピーされました、 4.11515 秒、 12.2 MB/秒

パーティションテーブルを確認する

mc@u-west:~/share/temp$ sudo parted /dev/sde u s p
Model: Generic- SD/MMC (scsi)
Disk /dev/sde: 62586880s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number Start End Size Type File system Flags
1 8192s 96663s 88472s primary fat32 lba
2 98304s 30031871s 29933568s primary

コピー元のカードのパーティションテーブルが上書きされるため、元のSDカードと同様のパーティションテーブルになります。 パーティション2はまだ中身がからっぽのため、File systemの欄が空欄になっています。

パーティション2を作り直す

mc@u-west:~/share/temp$ sudo parted /dev/sde
GNU Parted 2.3
Using /dev/sde
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) rm 2
(parted) mkpart primary ext4 98304s -1s
(parted) q
Information: You may need to update /etc/fstab.

rm 2 でパーティション2をいったん削除します。

mkpart primary ext4 98304s -1s で98304セクタからディスク末尾までのパーティションを作成します。

末尾を「-1s」と指定できることを知るまでは、全セクタ数から1引いて入力していました。こういう指定ができるのは便利ですね。

パーティションテーブルを確認する

mc@u-west:~/share/temp$ sudo parted /dev/sde u s p
Model: Generic- SD/MMC (scsi)
Disk /dev/sde: 62586880s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number Start End Size Type File system Flags
1 8192s 96663s 88472s primary fat32 lba
2 98304s 62586879s 62488576s primary

パーティション2はSDカードの末尾までぴったり収まっています。理想的ですね。

パーティション2をext4でフォーマットする

mc@u-west:~/share/temp$ sudo mkfs.ext4 /dev/sde2
mke2fs 1.42.5 (29-Jul-2012)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
1954064 inodes, 7811072 blocks
390553 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
239 block groups
32768 blocks per group, 32768 fragments per group
8176 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000

Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

これで、パーティション2がext4でフォーマットされました。次の手順で内容を復元していきましょう。

パーティション2をマウントし、restoreコマンドを実行

restoreコマンドの転送先はカレントディレクトリになるので、ファイルシステムをマウントし、マウントされたディレクトリにcdします。

mc@u-west:~/share/temp$ mkdir mount
mc@u-west:~/share/temp$ sudo mount /dev/sde2 mount
mc@u-west:~/share/temp$ cd mount

restoreコマンドを実行します。

mc@u-west:~/share/temp/mount$ sudo restore -rf ../piroot.dump
Dump tape is compressed.
restore: ./lost+found: File exists

これで、パーティション2の内容が復元されました。

アンマウントして取り外す

最後の手順です。ここで焦ると台無しになります。 大事なデータは、まだ書き込みバッファにあるかもしれません。

mc@u-west:~/share/temp/mount$ cd ..
mc@u-west:~/share/temp$ sudo umount mount
mc@u-west:~/share/temp$ sync
mc@u-west:~/share/temp$ sync
mc@u-west:~/share/temp$ sync

お疲れさまでした。これで、SDカードリーダを抜いてOKです。

応用

dumpコマンドとrestoreコマンドはパイプでつなぐことができます。(その際、圧縮は無効にしましょう!)SDカードからSDカードに直接コピーする際も、この方法なら無駄な書き込みが発生しないので、時間と寿命を節約できます。

パーティション2の開始セクタを認識して自動で処理するスクリプトにしてしまうのもよいでしょう。

起動中のRaspberry Pi自身のバックアップも取れます。(mount -o ro,remount / で一時的に読み取り専用でマウントする必要があるかもしれません。バックアップ先はUSBデバイスやネットワークなど、物理的に別のメディアが必要です。)

そのほかにも、Linuxを使う上での色々なアイデアを合わせることができますので、工夫して便利にしてみましょう。