Raspberry Piの起動SSDをddでバックアップする

## 1. はじめに

色々と試行錯誤する中で起動メディアを作り直す作業を10回以上行いましたが、今後もまだまだ同じことを繰り返しそうなので、せめて毎回行う定型的な作業が終わった状態の起動メディアをバックアップすることにします。

### 1.1. 改訂履歴

2021/09/30 ファイルシステムを縮小する際に`-M`オプションを使用し、自動的に最小サイズになるようにしました

## 2. 用意した物

- Raspberry Piとは別のLinux環境 (CentOS 7.9)

USB 3.xが利用できる実機なりVMなりがあれば作業が早そう。

## 3. 作業の流れ

すべてLinux環境での作業

1. 大きなパーティションを縮小する
2. ddで読み取り、圧縮し、ファイルに書き出す

## 4. 大きなパーティションを縮小する

### 4.1. 装置名を確認する

USBハードディスクケースに入ったSSDを別のLinuxにつなぐ前に、現状を見ておきます。

```bash
# ls -al /dev/sd*
brw-rw---- 1 root disk 8,  0  9月 29 21:02 /dev/sda
brw-rw---- 1 root disk 8,  1  9月 29 21:02 /dev/sda1
brw-rw---- 1 root disk 8,  2  9月 29 21:02 /dev/sda2
brw-rw---- 1 root disk 8,  3  9月 29 21:02 /dev/sda3
brw-rw---- 1 root disk 8, 16  9月 29 21:02 /dev/sdb
brw-rw---- 1 root disk 8, 17  9月 29 21:02 /dev/sdb1
```

このシステムには`/dev/sda`、`/dev/sdb`が接続されています。
USBハードディスクケースに入ったSSDをつなぎます。

```bash
# ls -al /dev/sd*
brw-rw---- 1 root disk 8,  0  9月 29 21:02 /dev/sda
brw-rw---- 1 root disk 8,  1  9月 29 21:02 /dev/sda1
brw-rw---- 1 root disk 8,  2  9月 29 21:02 /dev/sda2
brw-rw---- 1 root disk 8,  3  9月 29 21:02 /dev/sda3
brw-rw---- 1 root disk 8, 16  9月 29 21:02 /dev/sdb
brw-rw---- 1 root disk 8, 17  9月 29 21:02 /dev/sdb1
brw-rw---- 1 root disk 8, 32  9月 29 21:03 /dev/sdc
brw-rw---- 1 root disk 8, 33  9月 29 21:03 /dev/sdc1
brw-rw---- 1 root disk 8, 34  9月 29 21:03 /dev/sdc2
```

`/dev/sdc`が増えたので、これが今接続したSSDなのでしょう。
とすれば、`/dev/sdc1`はbootパーティション、`/dev/sdc2`がルートパーティションです。

### 4.2. ルートパーティションをチェックする

```bash
# e2fsck -f /dev/sdc2
e2fsck 1.42.9 (28-Dec-2013)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
rootfs: 39288/29263600 files (0.2% non-contiguous), 2230322/122030086 blocks
```

問題ないようです。

### 4.3. ルートパーティションのファイルシステムを縮小する

`resize2fs -M`とすることで、最小サイズになります。
古い`resize2fs`では機能しないかもしれません。
`man resize2fs`で確認してみてください。

```bash
# resize2fs -M /dev/sdc2
resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/sdc2 to 647400 (4k) blocks.
The filesystem on /dev/sdc2 is now 647400 blocks long.
```

`647400 (4k) blocks.`ということは、4k=4096byteなので、セクタ(512byte)に直すと`647400 * 4096 / 512 = 5179200`です。
`5179200`という数字は、後で使います。


### 4.4. ルートパーティションを縮小する

```bash
# fdisk /dev/sdc

The device presents a logical sector size that is smaller than
the physical sector size. Aligning to a physical sector (or optimal
I/O) size boundary is recommended, or performance may be impacted.
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

コマンド (m でヘルプ): 
```

`p`を入力し、現状のパーティションを表示します。

```bash
コマンド (m でヘルプ): p

Disk /dev/sdc: 500.1 GB, 500107862016 bytes, 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト
Disk label type: dos
ディスク識別子: 0xb6da2e9c

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)
/dev/sdc2          532480   976773167   488120344   83  Linux

コマンド (m でヘルプ): 
```

現状のルートパーティション`/dev/sdc2`は、始点が`532480`、終点が`488120344`です。
これを一度削除します。削除すると言ってもデータが消えるわけではありません。
削除するのはパーティションの「開始位置」「終了位置」の記録です。
`d`を入力します。

```bash
コマンド (m でヘルプ): d
```

次にパーティション番号を入力します

```bash
パーティション番号 (1,2, default 2): 2
Partition 2 is deleted

コマンド (m でヘルプ): 
```

削除されました。
次に新しいパーティションの位置を定義します。
`n`を入力します。

```bash
コマンド (m でヘルプ): n
```

次にパーティションのタイプを指定します。
プライマリパーティションを作成しますので、`p`を入力します。

```bash
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
```

次にパーティション番号を指定します。
`2`を入力します。

```bash
パーティション番号 (2-4, default 2): 2
```

次にパーティションの始点を指定します。
ここで入力するのは、以前の手順で表示したパーティション情報の数値です。

```bash
デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)
/dev/sdc2          532480   976773167   488120344   83  Linux
```

以前あった`/dev/sdc`と始点は同じですので、この例では`532480`を入力します。
環境により異なる可能性がありますので、注意してください。

```bash
最初 sector (2048-976773167, 初期値 2048): 532480
```

次にパーティションの終点を指定します。
ここで入力するのは、以前の手順で縮小した`始点のセクタ数 + ファイルシステム上のセクタ数`です。
今回は`532480 + 5179200 = 5711680`となります。

しかし今回のように、始点セクタ数が偶数でファイルシステム上のセクタ数も偶数の場合、実際に確保されるセクタ数は奇数となり、pコマンドでパーティションを表示させた際に`+`が表示されます。

```bash
コマンド (m でヘルプ): p

Disk /dev/sdc: 500.1 GB, 500107862016 bytes, 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト
Disk label type: dos
ディスク識別子: 0xb6da2e9c

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)
/dev/sdc2          532480     5711680     2589600+  83  Linux

コマンド (m でヘルプ): 
```

この場合、入力する値には`1`を加えてください。
今回の場合、`5711681`を入力します。

```bash
Last sector, +sectors or +size{K,M,G} (532480-976773167, 初期値 976773167): 5711681
Partition 2 of type Linux and of size 2.5 GiB is set

コマンド (m でヘルプ): 
```

始点と終点がそれぞれ指定されました。
ここまでの作業結果を確認します。
`p`を入力し、パーティション情報を表示します。

```bash
コマンド (m でヘルプ): p

Disk /dev/sdc: 500.1 GB, 500107862016 bytes, 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト
Disk label type: dos
ディスク識別子: 0xb6da2e9c

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)
/dev/sdc2          532480     5711681     2589601   83  Linux

コマンド (m でヘルプ): 
```

`/dev/sdc2`の終点が`976773167`から`5711681`になりました。
ここまでの結果をディスクに書き込み、反映させます。

```bash
コマンド (m でヘルプ): w
パーティションテーブルは変更されました！

ioctl() を呼び出してパーティションテーブルを再読込みします。
ディスクを同期しています。
# 
```

無事おわりました。

## 5. ddで読み取り、圧縮しながら書き出す

今回使う`dd`コマンドの書式はこうです。

```bash
dd if=入力デバイス bs=ブロックサイズ count=読み取り回数
```

そして`pv`で進捗を出力しつつ`gzip`で圧縮しながら出力します。
`pv`がなければ`apt`や`dnf`でインストールしましょう。

```bash
dd if=入力デバイス bs=ブロックサイズ count=読み取り回数 | pv | gzip > ./バックアップ先ファイル名.gz
```

`gzip`の替わりに`pigz`を使うと早いそうです。
なければインストールしましょう。

<https://qiita.com/taiyodayo/items/7c60c9b3bc00b13baf43>

```bash
dd if=入力デバイス bs=ブロックサイズ count=読み取り回数 | pv | pigz > ./バックアップ先ファイル名.gz
```

`入力デバイス`はパーティションではなく装置全体を示す`/dev/sdc`です。
`ブロックサイズ`はある程度大きい方が早いのですが、`読み取り回数`にも関係します。
間違えたくないので、先ほどのパーティションの数値を流用できるような`ブロックサイズ`にします。

- 終点`5711681`(Units = sectors of 1 * 512 = 512 bytes)までを読み取ればいい
- `bs=512`とすると`5711681`回読み取らなければならないので時間がかかりそう
- `I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト`と書いてあるので、そもそも`bs=512`は小さすぎる
- `bs`を10000倍の大きさにして`bs=5120000`とすれば、読み取り回数は`5711681 / 10000 = 571.1681(回)`となる
- 切り上げて`572`なので、終点の`5711681`をコピペして5文字消して1足した数字を書く

書いていてとても意味のないことを頑張っている気がしてきましたが、将来見返した時にはきれいに忘れていそうなので。
最終的にこうなります。

```bash
dd if=/dev/sdc bs=5120000 count=572 | pv | pigz > ./raspi_$(date "+%Y%m%d")-$(date "+%H%M").gz
```

結果はこうでした。

```bash
# dd if=/dev/sdc bs=5120000 count=572 | pv | pigz > ./raspi_$(date "+%Y%m%d")-$(date "+%H%M").gz
572+0 レコード入力
572+0 レコード出力
2928640000 バイト (2.9 GB) コピーされました、 56.8524 秒、 51.5 MB/秒
```

bsを1桁減らし、countを1桁増やして試してみましたが、時間はほとんど変わりませんでした。

```bash
# dd if=/dev/sdc bs=512000 count=5720 | pv | pigz > ./raspi_$(date "+%Y%m%d")-$(date "+%H%M").gz
5720+0 レコード入力
5720+0 レコード出力
2928640000 バイト (2.9 GB) コピーされました、 57.2286 秒、 51.2 MB/秒
```

3割程度に圧縮されています。

```bash
-rw-r--r--   1 root root   939996203  9月 30 01:39 raspi_20210930-0138.gz
```

## 6. バックアップのために小さくした諸々を戻しておく

```bash
# fdisk /dev/sdc

The device presents a logical sector size that is smaller than
the physical sector size. Aligning to a physical sector (or optimal
I/O) size boundary is recommended, or performance may be impacted.
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


コマンド (m でヘルプ): p

Disk /dev/sdc: 500.1 GB, 500107862016 bytes, 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト
Disk label type: dos
ディスク識別子: 0xb6da2e9c

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)
/dev/sdc2          532480     5711681     2589601   83  Linux

コマンド (m でヘルプ): d
パーティション番号 (1,2, default 2): 2
Partition 2 is deleted

コマンド (m でヘルプ): p

Disk /dev/sdc: 500.1 GB, 500107862016 bytes, 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト
Disk label type: dos
ディスク識別子: 0xb6da2e9c

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)

コマンド (m でヘルプ): n
Partition type:
   p   primary (1 primary, 0 extended, 3 free)
   e   extended
Select (default p): p
パーティション番号 (2-4, default 2): 2
最初 sector (2048-976773167, 初期値 2048): 532480
Last sector, +sectors or +size{K,M,G} (532480-976773167, 初期値 976773167): 
初期値 976773167 を使います
Partition 2 of type Linux and of size 465.5 GiB is set

コマンド (m でヘルプ): p

Disk /dev/sdc: 500.1 GB, 500107862016 bytes, 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O サイズ (最小 / 推奨): 4096 バイト / 4096 バイト
Disk label type: dos
ディスク識別子: 0xb6da2e9c

デバイス ブート      始点        終点     ブロック   Id  システム
/dev/sdc1            8192      532479      262144    c  W95 FAT32 (LBA)
/dev/sdc2          532480   976773167   488120344   83  Linux

コマンド (m でヘルプ): w
パーティションテーブルは変更されました！

ioctl() を呼び出してパーティションテーブルを再読込みします。
ディスクを同期しています。

# resize2fs /dev/sdc2
resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/sdc2 to 122030086 (4k) blocks.
The filesystem on /dev/sdc2 is now 122030086 blocks long.
```

Raspberry Piに取り付け、確認。

```bash
$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
/dev/root        459G  1.5G  439G    1% /
devtmpfs         326M     0  326M    0% /dev
tmpfs            455M     0  455M    0% /dev/shm
tmpfs            182M  684K  182M    1% /run
tmpfs            5.0M  4.0K  5.0M    1% /run/lock
/dev/sda1        253M   30M  223M   12% /boot
tmpfs             91M     0   91M    0% /run/user/1000
```

`/dev/root`、サイズ`459G`を確認。

今日はここまで。