RaspberryPI 緊急メンテナンス用 microSD 作成のススメ(その1:下準備まで)

# 序
ラズパイ2Bの最新リビジョンや3B/3B＋の CPU は 64bit化しているというのに、公式OSである Raspbian は 64bit化する気配がゼロだからクソ、なんて言ってはいけません。全ての内蔵デバイスが安定稼働する貴重な OS なんですから、緊急時のために1枚余分に microSDを用意して、Raspbian をインストールしておきましょうよ、というお話。

## 前提事項（1）： この記事の対象読者
既に RaspberryPI シリーズ(Zero系以外の2B以降)を 1台につき1枚の microSD のみで稼働させている人、すなわち

- Zero系や初代しか所有していない人
- 3B以降の機種でPXEブート(ディスクレスクライアント化)している人([→参照](http://tech.nikkeibp.co.jp/atcl/nxt/column/18/00079/00004/?ST=nxt_thmit_develop))
- SDはブート時のみ利用し、実際のルートディレクトリはUSB接続のHDD/SDD や NFSに設定している人
- 定期的に SD カードのバックアップを取っている人
- 初めて RaspberryPI シリーズを購入し、これからOSをインストールしようとしている人

これらの**どれかに当てはまる人には関係無い話**、ということですね。
(ただし、初ラズパイの人は、OSインストール、各種設定終了直後から以後の作業を行うと幸せになれます)

## 前提事項（2）： 心構えと準備するもの
ただでさえ論理エラーが発生しやすいデバイスである microSD が、シャットダウン処理なしで簡単に USB 電源の抜き差しされる状況下にあるわけです。起動不可となるようなディスクエラーはいつ発生してもおかしくない、発生して当たり前、という心構えを持って、次の機材を準備しましょう。

- 正常に稼働している現在のラズパイ環境(Linuxであればディストリビューション不問)
    - Windows 10 IoT? なんですかそれ？（すっとぼけ）
- 新しい microSD x1枚(4GB-32GB)
- USB 接続の外付けSDカードリーダ
    - ラズパイに取りつけるため、PC内蔵の奴はダメです。
- [最新版の Raspbian LITE ディスクイメージファイル(ZIP形式)](https://downloads.raspberrypi.org/raspbian_lite_latest)
    - レスキュー用なので GUI なんて要りません。

## 前提事項(3) : 最終目標と基本方針

### 最終目標

既存環境の microSD がダメ、または挙動がおかしくなったと思われる時、電源を引っこ抜いてmicroSDを差し替え、即座に起動、障害切り分け、メンテできるようなレスキュー環境を用意する

### レスキュー環境の基本方針

#### ネットワーク環境（IP・WiFi設定など）

既存環境と同一になるようにする

- IPの設定は systemd-networkd を使う(NetworkManager,dhcpcdの類は使わない)
- 既存環境から流用する主なファイル
    - /etc/hosts
    - /etc/hostname
    - /etc/wpa_supplicant/wpa_supplicant\*.conf (ラズパイ3B 以降)

#### ユーザーアカウント関連

ほぼすべて流用

- 既存環境から流用する主なファイル
    - /etc/passwd + /etc/shadow
    - /etc/group + /etc/gshadow
    - /etc/sudoers
    - /home/\*

#### 自動起動サービス

当然、最小限に。

- 一時的な環境なので、crond も firewalld もいらん。 
- systemd-jounald があるので rsyslogd もいらん。
- NTPでの時刻合わせは必要。systemd-timesyncd で同期。
- レスキュー環境と言えどもマルチコアを効率よく利用するために irqbalance は必要ですね
- sshd だけは起動する(ただし、USBシリアルケーブルを持っている人は不要) 
    - /etc/ssh/sshd_config 流用
    - /etc/ssh/ssh_host_\*_key\* ファイル流用
        - メンテ環境を起動後、他ホストからssh接続を試みた際に、sshクライアントが「俺が知っているホストキーと違う。クラックされたかもよ？」と判断して勝手に切断する[^1]ことを防ぐのが目的。
        - この措置でssh クライアント側に保持されているknown_hostsファイルに変更が加えられることを防ぐこともできる
            
#### インストールするソフトウェア

Raspbian LITE に最初から入っている物がありますが、一応列挙していきます。

- dig,nslookup,ifconfig,dhcping,nmap,traceroute等ネットワーク診断系各種コマンド
- irqbalance
- patch コマンド
- rsync コマンド
- ~~chrony (新しめの NTP クライアント+サーバ)~~
    - ~~2018/07/05 現在、なぜか Raspbian の systemd-timesyncd がまともに動作しないため採用~~
- bsdtar,xz, unzip 等各種アーカイブ関連コマンド
- parted もしくは fdisk コマンド
- テキストブラウザ(w3m)
- (既存環境や各人の好みに依存する部分)
    - avahi-daemon (利用しているなら)
    - vim/emacs の好きなほう
    - お好みで zsh とか tcshとか。
    - LVM2, xfs, NTFS 等、利用している各種ローカルファイルシステムのメンテナンスコマンド
    - NFS,Samba 等、リモートファイルシステムのクライアント/メンテナンスコマンド
    - LDAP,RADIUSをはじめとする認証系ソフトのクライアントコマンド
    - MySQL,PostgreSQL 等各種DBのクライアントコマンド

[^1]: クラックされてしまったホストに不用意に接続してしまうことを防ぐための、ssh クライアントとしては正しい挙動。

ということで、これから実際に作業していくことにします

# 下準備1: まずはディスクイメージのダウンロードと書き込み

**注意: ディスク作成直後に、作成したSDに差し替えてラズパイを起動してはいけません**

## Windows PC で作業を行う場合

稼働中のラズパイを持っているはずなので、Windows PCを使って作業をするメリットは全くないんですが、一応説明します。Mac?知らんがな(使ってますけどね)

1. 今見ているブラウザで上記リンクから最新版をダウンロード
2. ダウンロードした ZIP ファイルを[7-zip](https://7-zip.org/)などで解凍
3. 新しい microSD をセットしたUSBカードリーダを接続
4. [Win32DiskImager](https://ja.osdn.net/projects/sfnet_win32diskimager/)等で、準備した Raspbian LITE イメージを SD に書き込み
    - 書き込み終了後に「このメディア、フォーマットされてねーよ。フォーマットするか？」と聞かれることがあります。ですが絶対に「はい」を押してはいけません。キャンセルボタンを押しましょう。
5. 安全に取り外し(USB接続解除)
6. 既存環境のラズパイにログイン
7. ラズパイにWindows から外した USB カードリーダを接続
8. dmesg コマンドで、接続したディスクの識別名を確認( "sda"　など)
9. 「[下準備2](https://qiita.com/p_cub/items/a039c758d769f8ff6aab#%E4%B8%8B%E6%BA%96%E5%82%992-%E3%83%AC%E3%82%B9%E3%82%AD%E3%83%A5%E3%83%BC%E7%92%B0%E5%A2%83%E3%81%AE%E3%83%91%E3%83%BC%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%8B%A1%E5%BC%B5)」セクションに飛んでください

## 稼働中の RaspberryPI で作業を行う場合

### (1) ディスクイメージをダウンロード

公式ダウンロードURLは https://downloads.raspberrypi.org/raspbian_lite_latest なので
何のオプションも付けないで wget しても全く構わないんですが、デカいファイルはチェックサム位確認したいよね・・・
ところが

- 負荷分散の為でしょうが、公式ダウンロードURLを指定すると、別サーバにリダイレクトされてしまう
- そのせいで、オプションなしの curl コマンドではダウンロードできない(HTTPレスポンスである 302 Found が表示されておしまい)
- ついでに、チェックサムファイルを追加ダウンロードするためのURLがわからん

さらに

- オプションなしで wget に公式URLをそのままに渡してしまうと "raspbian_lite_latest" という名前で保存されてしまう
- しかし実際にダウンロードされるファイルの、Webサーバ上でのファイル名は "年-月-日-raspbian-Debianバージョン名-lite.zip" となっている。
    - 公式URLはこのファイルへのエイリアス
- チェックサムファイルの中で用いられているファイル名はサーバ上の名前そのもの

といろいろ不具合があるので、ログが冗長な wget コマンドで --trust-server-name オプションをつけてダウンロードすることにする。
この方法だと次のようなメリットが。

- Webサーバ上での本来の名前で保存される
- ログをたどれば実際のダウンロード先サーバ/ディレクトリ名もわかる
- 最終的にチェックサムファイルのURLも推測できる

というわけで実行。

```shell-session
[hoge@fuga ~]$ cd /tmp; wget --trust-server-name https://downloads.raspberrypi.org/raspbian_lite_latest
--2018-07-04 14:45:13--  https://downloads.raspberrypi.org/raspbian_lite_latest
downloads.raspberrypi.org (downloads.raspberrypi.org) をDNSに問いあわせています... 93.93.130.214, 93.93.128.133, 93.93.130.104, ...
downloads.raspberrypi.org (downloads.raspberrypi.org)|93.93.130.214|:443 に接続 しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 302 Found
場所: https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip [続く]
--2018-07-04 14:45:14--  https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip
downloads.raspberrypi.org (downloads.raspberrypi.org)|93.93.130.214|:443 に接続 しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 302 Found
場所: http://director.downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip [続く]
--2018-07-04 14:45:15--  http://director.downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip
ww.xx.yy.zz:portに接続しています... 接続しました。
Proxy による接続要求を送信しました、応答を待っています... 200 OK
長さ: 366980542 (350M) [application/zip]
`2018-06-27-raspbian-stretch-lite.zip' に保存中
2018-06-27-raspbian 100%[===================>] 349.98M  1.63MB/s    in 6m 38s

2018-07-04 14:51:53 (901 KB/s) - `2018-06-27-raspbian-stretch-lite.zip' へ保存完了 [366980542/366980542]
[hoge@fuga /tmp]$
```

という結果になったので

- ダウンロードログで最後に出てきたURLをコピー
- 公式ページの HTML 中に SHA-256 サムが埋め込まれていたので、チェックサムファイル名は "オリジナルファイル名.sha256" と推測
- コマンドラインにURLをペースト後 ".sha256" 文字列を追加してダウンロードを試す。

ビンゴ。

```shell-session
[hoge@fuga /tmp]$ wget --trust-server-name http://director.downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip.sha256
--2018-07-04 14:54:14--  http://director.downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-06-29/2018-06-27-raspbian-stretch-lite.zip.sha256
ww.xx.yy.zz:port に接続しています... 接続しました。
Proxy による接続要求を送信しました、応答を待っています... 200 OK
長さ: 103 [text/plain]
`2018-06-27-raspbian-stretch-lite.zip.sha256' に保存中

2018-06-27-raspbian 100%[===================>]     103  --.-KB/s    in 0s

2018-07-04 14:54:14 (1.95 MB/s) - `2018-06-27-raspbian-stretch-lite.zip.sha256' へ保存完了 [103/103]
```

やっとのことでチェックサムを確認

```shell-session
[hoge@fuga /tmp]$ sha256sum -c 2018-06-27-raspbian-stretch-lite.zip.sha256
2018-06-27-raspbian-stretch-lite.zip: 完了
[hoge@fuga /tmp]$ 
```

### (2) zipファイルを解凍

```shell-session
[hoge@fuga /tmp]$ unzip 2018-06-27-raspbian-stretch-lite.zip
Archive: 2018-06-27-raspbian-stretch-lite.zip
  inflating: 2018-06-27-raspbian-stretch-lite.img 
[hoge@fuga /tmp]$
```

### (3) 新しいmicroSDをセットしたUSBカードリーダをラズパイに刺す

USB マスストレージ、/dev/sda と認識されることが多いでしょう。
dmesg コマンドで確認してください

```shell-session
[hoge@fuga /tmp]$ dmesg
(途中省略,このログは一例)
[13787.484043] usb 1-1.4: new high-speed USB device number 4 using dwc_otg
[13787.616156] usb 1-1.4: New USB device found, idVendor=058f, idProduct=6331
[13787.616172] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13787.616183] usb 1-1.4: Product: Mass Storage Device
[13787.616193] usb 1-1.4: Manufacturer: Generic
[13787.616207] usb 1-1.4: SerialNumber: 058F63316337
[13787.617893] usb-storage 1-1.4:1.0: USB Mass Storage device detected
[13787.624694] scsi host0: usb-storage 1-1.4:1.0
[13788.635176] scsi 0:0:0:0: Direct-Access     Multi    Flash Reader     1.00 PQ: 0 ANSI: 0
[13788.669749] sd 0:0:0:0: Attached scsi generic sg0 type 0
[13789.061878] sd 0:0:0:0: [sda] 31116288 512-byte logical blocks: (15.9 GB/14.8 GiB)
[13789.062443] sd 0:0:0:0: [sda] Write Protect is off
[13789.062460] sd 0:0:0:0: [sda] Mode Sense: 03 00 00 00
[13789.063001] sd 0:0:0:0: [sda] No Caching mode page found
[13789.063014] sd 0:0:0:0: [sda] Assuming drive cache: write through
[13789.072317]  sda: sda1
[13789.077143] sd 0:0:0:0: [sda] Attached SCSI removable disk
[hoge@fuga /tmp]$ 
```

microSD はスマホやタブレットで即座に使用される前提で販売されていますから、vfat か fat32 でフォーマットされたパーテーション(/dev/sda1)が1個あるはずです。次の手順ですぐに消えてしまうはずですが、一応削除。

```shell-session
# USB でカードリーダーを接続したら SD は /dev/sda だと言われたので
# 現状を確認
[hoge@fuga /tmp]$ sudo parted /dev/sda
[sudo] hoge のパスワード:
GNU Parted 3.2
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: Multi Flash Reader (scsi)
Disk /dev/sda: 15.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      4194kB  15.9GB  15.9GB  primary  fat32        lba

# FAT32パーテーションはすぐに dd コマンドで消されるとわかっていても一応削除
(parted) rm 1
(parted) quit
Information: You may need to update /etc/fstab.
[hoge@fuga /tmp]$ 
```

### (4) dd で書き込み[^2]

zip 解凍後に現れた "*.img" ファイルを入力元、/dev/sda を出力先として dd 実行[^3]。

[^3]: やることは簡単ですが、時間がかかるのでコーヒーなりタバコなり、ご自由に。

```shell-session
[hoge@fuga /tmp]$ sudo dd if=2018-06-27-raspbian-stretch-lite.img of=/dev/sda
3637248+0 レコード入力
3637248+0 レコード出力
1862270976 bytes (1.9 GB, 1.7 GiB) copied, 557.679 s, 3.3 MB/s
[hoge@fuga /tmp]$ 
```

[^2]: ここで提示するログは余っていた2B上で実行した際のログです。このログを見ると、約10分かかってますね。CPUの速さよりもI/O速度の方が影響力大のコマンドですから、同じSDメディアを利用するのであれば3Bを利用した場合でも終了までの時間はそう変わらないと想定されます。ただし、USB接続が3倍速となった3B＋であれば圧倒的に速くなるはずです。


# 下準備2: レスキュー環境のパーテーション+ファイルシステム拡張 
Windows PC で下準備1の作業を行った方は、ここから稼働中のラズパイ環境での作業となります。
以後、ディスクイメージの書き込みが終了し、USBカードリーダに刺さっている新規 microSD はラズパイ上で /dev/sda と認識されている、という前提で話を進めます。

## パーテーションの確認と拡張

ディスクイメージの書き込みが終了した時点でのパーテーション状態を確認します。

```shell-session
[hoge@fuga /tmp]$ sudo parted /dev/sda
[sudo] hoge のパスワード:
GNU Parted 3.2
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: Multi Flash Reader (scsi)
Disk /dev/sda: 15.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      4194kB  49.5MB  45.3MB  primary  fat32        lba
 2      50.3MB  1862MB  1812MB  primary  ext4
(parted) 
```

FAT32 と ext4 でフォーマットされた二つのパーテーションがありますね。
FAT32のほうは /boot, ext4 の方は / （ルート）にマウントされる領域となります。
配布されているディスクイメージは 2GB の microSDを想定したディスクイメージなので、今回利用した16GBの奴ですと、約14GBが未使用領域として放置されている、ということになります。 この状態は非常に無駄なので、parted を終了させる前に / としてマウントされる予定のパーテーションを拡張してしまいます。 

```shell-session
# 2番目のパーテーションをディスクの最後まで拡張しろ、というコマンド
(parted) resizepart 2 100%
# 実際に拡張されたか確認
(parted) print
Model: Multi Flash Reader (scsi)
Disk /dev/sda: 15.9GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      4194kB  49.5MB  45.3MB  primary  fat32        lba
 2      50.3MB  15.9GB  15.9GB  primary  ext4　　　　　　　　　　　　　←確かに増えてる

(parted) quit
Information: You may need to update /etc/fstab.
```
パーテーションの拡張という作業は、現実世界で言い換えると「自分の土地に隣接した原野を買い上げて自分のものにする」ことです。原野は整地しないと利用できませんから、次の作業に入ります。

## ext4ファイルシステムの拡張

先の例で言及した「買い上げた原野を整地する作業」は、「ファイルシステムをパーテーションのサイズ上限まで拡張すること」となります。resize2fs という専用コマンドが準備されているので実行してみます。

```shell-session
# fsck しないまま ext4 ファイルシステムを拡張しようとすると怒られる
[hoge@fuga /tmp]$ sudo /sbin/resize2fs /dev/sda2
resize2fs 1.43.4 (31-Jan-2017)
Please run 'e2fsck -f /dev/sda2' first.
```

怒られました。「今までの土地と新しく追加した土地に問題がないか確認して、測量してから整地しろ」という意味ですな。
言われた通り e2fsck コマンドを実行します。

```shell-session
# 怒られたので fsck をかける
[hoge@fuga /tmp]$ sudo e2fsck -f /dev/sda2
e2fsck 1.43.4 (31-Jan-2017)
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: 39348/110656 files (0.1% non-contiguous), 257066/442368 blocks
```

問題なかったようなので、もう一度 resize2fs コマンドを実行します。

```shell-session
# ext4 ファイルシステムを拡張
[hoge@fuga /tmp]$ sudo /sbin/resize2fs /dev/sda2
resize2fs 1.43.4 (31-Jan-2017)
Resizing the filesystem on /dev/sda2 to 3877248 (4k) blocks.
The filesystem on /dev/sda2 is now 3877248 (4k) blocks long.

```
問題なく終了すれば、下準備は完了です。
この続きは、[その2](https://qiita.com/p_cub/items/440c02090071cbb8c850)で行います

---
[脚注]