RaspberryPI 緊急メンテナンス用 microSD 作成のススメ(その3:起動確認→プロセスゴルフ)

# 序

この文書は [その1](https://qiita.com/p_cub/items/a039c758d769f8ff6aab)、[その2](https://qiita.com/p_cub/items/440c02090071cbb8c850) の続きとなります。内容としては、

1. 既存環境をシャットダウン[^1]
2. 新しく作成したレスキュー環境SDに差し替えてテスト起動
3. パッケージのアップデート
4. 追加ソフトのインストール
5. 不要サービス無効化/不要ソフト削除
6. (作成終了)レスキュー環境の基本的な使い方

までとなります。

なお、このレスキュー環境の詳細な運用方法に関しては別記事(未執筆)ということでお願いします。

[^1]: 既存環境も 32bit 版 Linux であれば、シャットダウンせず、chroot で各種設定作業を行ってもいいんですが、条件分岐が多岐にわたって文書を書く私のほうも読者の方も色々面倒なことになるので、再起動で1本化します(・∀・)

# 既存環境のシャットダウン

1. シャットダウンプロセスの途中で、USBカードリーダにある新環境構築中のSDは umount されるはずですが、万全を期してシャットダウンを実行する前に umount しておきます[^2]。
2. 問題なく umount できたら、シャットダウンを実行します。

```shell-session
[root@fuga tmp]# sync;sync;sync;umount /tmp/raspbian_root/boot
[root@fuga tmp]# umount /tmp/raspbian_root
[root@fuga tmp]# /sbin/poweroff
```
[^2]: 年齢がバレますが、大して意味ないとわかっていても sync 3回はお約束、ってことで

# SDを差し替えて電源投入(テスト起動)

シャットダウンが正常に終了したら、以下の作業を行います。

1. SD のアクセスランプ(緑)点滅が完全になくなったことを確認
2. 電源用 USB ケーブル引き抜き
3. USB カードリーダ引き抜き
4. カードリーダに刺さっているレスキュー環境 SD とラズパイ本体に刺さっている既存環境SDを入れ替え
5. Raspbian LITE のデフォルトは SSH が無効化されているので、（接続されていないなら）キーボード、モニタを接続
6. 電源用 USB ケーブルを差し込み、経過観察[^3]
7. ログインプロンプトまで表示されたら暫定的にOK
8. (sudoが可能で、root以外の) 既存環境アカウントでログインできるか確認
9. ホスト名が既存環境と同一であるかどうかを確認
10. glibc の各種メッセージデータの日本語化を実行
10. ifconfig か ip address コマンドで、既存環境と同一の IP が設定され、リンクがされているかを確認
11. デフォルトゲートウェイに ping が通るか確認
12. ssh サービスを自動起動設定し、その後 sshd サービスを開始
13. 以前このラズパイにssh接続したことのある他のマシンから ssh 接続できるかどうかを確認
14. ssh 接続ができて、なおかつ「ホストキーが違う」といったメッセージが出ないことを確認

```shell-session
login: hoge
password:
[hoge@fuga ~]$ sudo locale-gen
[sudo] hoge のパスワード:
Generating locales (this might take a while)...
ja_JP.UTF-8... done
en_US.UTF-8... done
Generation complete.
[hoge@fuga ~]$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:MM:NN:OO brd ff:ff:ff:ff:ff:ff
    inet x.y.z.w/24 brd x.y.z.255 scope global dynamic eth0
       valid_lft 26128sec preferred_lft 26128sec
[hoge@fuga ~]$ ping -c 2 x.y.z.r
PING x.y.z.w (x.y.z.r) 56(84) bytes of data.
64 bytes from x.y.z.r: icmp_seq=1 ttl=64 time=0.540 ms
64 bytes from x.y.z.r: icmp_seq=2 ttl=64 time=0.453 ms

--- x.y.z.r ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1074ms
rtt min/avg/max/mdev = 0.453/0.496/0.540/0.048 ms　
[hoge@fuga ~]$ sudo systemctl enable ssh
(出力省略)
[hoge@fuga ~]$ sudo systemctl start sshd
# ローカルログインで実行する作業はここまで。ここから先はssh接続でOK
[hoge@fuga ~]$ exit
```


上記すべてに問題がなければ、レスキュー環境はほぼ出来たも同然となります。

[^3]: 途中で固まってしまう場合は、ネットワーク設定ファイルの内容がおかしいか、DHCPサーバが死んでいる（有線LANでDHCP鯖まで到達できない状況も含む）、デーモンを起動するために必要なユーザー/グループアカウントが passwd ファイル上に存在しない、のどれかであろうと思われます。

# ソフトウェアのアップデート

Raspbian のディスクイメージは、半月から1ヶ月に1回の頻度で更新されますので、ディスクイメージの最終更新日から、作業している今日までの間に更新されたセキュリティアップデートが存在すれば、それらを取り込みます。

## **注意: rpi-update コマンドは厳禁**
Raspbian にはカーネルファームウェアのみを最新版に更新する rpi-update コマンドというものが存在します。ですが、このコマンドでアップデートをかけた後、まともに起動できなくなる場合があります(私も以前経験したことがあります)。レスキュー環境である、ということを踏まえ、この環境では rpi-update コマンドは実行しないようにしましょう。

## 最終的に実行することは

1. リポジトリの更新 ( apt-get update )
2. パッケージのアップデート ( apt-get dist-upgrade )
3. 不要/古くなってしまったパッケージの自動削除 ( apt-get autoremove ) 

となりますが、実際にこれらのコマンドを実行する前に、以下の作業が必要になります。

## _apt アカウントの存在確認、(ないなら)追加

Raspbian や Ubuntu といった Debian 系ディストリビューションの Linux は、パッケージ管理コマンドである apt* コマンドを呼び出すと、‘_apt’ という名前のアカウントで実行しようと試みます。ですが、いま実行している環境は環境構築時に passwd ファイルを上書きした変則環境です。ですから、既存環境が FedoraやArchLinux といった Debian系ではないディストリビューションだった場合、このアカウントは登録されていない可能性が高いため、以下の措置を施します。

```shell-session
# 多分存在しないと思われるが、一応存在するかどうかを確認
[hoge@fuga ~]$ id _apt
id:  _apt: そのようなユーザーは存在しません
# 存在しなかったのでシステムアカウントとして追加
[hoge@fuga ~]$ sudo /usr/sbin/useradd -r -s /bin/nologin -G nogroup -M -N _apt
```

(ネットワークが繋がっていれば)これで apt-get コマンドを実行しても大丈夫、となります。

## (必要なら) Proxy 接続設定

Proxy 環境を利用していないのならば、[こちら](https://qiita.com/p_cub/items/df8ead671fa4482903e8#apt-get-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E5%AE%9F%E8%A1%8C)へ飛んでください。

セキュリティやキャッシュの効果を意図して企業/家庭内で何らかのHTTP(S)/FTPプロキシを利用している環境下から外部サイトへアクセスするためには、HTTP_PROXY に代表される複数の環境変数を定義する必要があります。ですが、状況に応じてどのファイルでどんな環境変数の値を設定するかは変わってきますので、シェルスクリプトを作って面倒な処理を回避しましょう。ちなみに「状況に応じて」の部分は次のようになります。

#### sudo で apt 等外部サイトに接続する処理を行う場合

sudo は認証をパスすると /etc/environment ファイルを読み込んで環境設定するので、このファイルに "KEY=VALUE" 形式で HTTP_PROXY 等の環境変数書き込んでおいて設定します。

#### 通常アカウントで w3m/wget/curl などを実行する場合

/etc/profile.d/ファイル名.sh[^4] で "export KEY=VALUE" の形式で記載しておくことで、ログイン可能な全アカウントが、ログイン直後から同一の環境変数を利用可能になります。

#### 接続先プロキシが接続用アカウント(パスワード無し)を要求してくるとき

環境変数の値は http://アカウント@ホスト:ポート番号/ の形式を持つ値にする必要があります。

#### 接続先プロキシが接続用アカウント+パスワードを要求してくるとき

環境変数の値は http://アカウント:パスワード@ホスト:ポート/ の形式を持つ値とする必要があります。(**パスワードを平文で書く必要があるので、超絶おすすめできない**方法です)

#### 接続先プロキシがアカウント認証を行わない場合

環境変数の値は http://ホスト:ポート/ の形式を持つ値とする必要があります


[^4]: /etc/profile.d/ 配下で拡張子部分が.sh であればファイル名は何でも良い。また、tcsh がインストールされていて、tcsh をデフォルトシェルにしているアカウントがある場合は .csh も準備するとよい。ただし記載方法は export ではなく setenv となるので注意。



### 環境変数設定用シェルスクリプトの作成

ラズパイを移動させることがない、という方はここをすっ飛ばして、/etc/environment と /etc/profile.d/proxy.sh を直接編集していただいて構いません。

以下の内容[^5]を参考に、シェルスクリプトファイルを新規に作成し、~/bin/ 配下にでも保存して、chmod +x してください。

[^5]: getopt を使い、引数を与えて柔軟に設定可能にすることも一瞬だけ考えましたが、面倒なのでパラメタはハードコーディングで ^^; パラメタ部分を別のファイルに出して source させることが最初の改善点でしょうね

```bash
#!/bin/bash

# このスクリプトを実行し、
# /etc/environment にコピペしたり /etc/profile.d/proxy.sh にリダイレクトすれば、
# ラズパイを出先に持っていたときに即座に設定変更が可能となる

# パラメタ設定
# --------------
# 認証用アカウント(必要なら値を設定)
A=

# 認証用パスワード(認証アカウントが設定されていて、パスワードも必要なら値を設定)
P=

# Proxy ホスト(必須)
H=

# Proxy ポート(必須)
N=

# http:// で始まる URL のデータを Proxy に要求する際の、Proxy との接続プロトコル
# (デフォルトは http)
PROTO_HTTP="http"

# https:// で始まる URL のデータを Proxy に要求する際の、Proxy との接続プロトコル
# (デフォルトは http)
PROTO_HTTPS=$PROTO_HTTP

# ftp:// で始まる URL のデータを Proxy に要求する際の、Proxy との接続プロトコル
# (デフォルトは http)
PROTO_FTP=$PROTO_HTTP

# プロキシ接続除外ホスト一覧
NO_PROXY="127.0.0.1 localhost"

# ------- (パラメタ設定はここまで) ----

# 最終的な接続先プロキシサーバの文字列
PROXY=

# 認証アカウントが設定されている
if [ "$A" != "" ]
then
    # 認証パスワードが設定されている
    if [ "$P" != "" ]
    then
　　　　PROXY="$A:$P@$H:$N"
    else
　　　　PROXY="$A@$H:$N"
    fi
# 認証アカウント無しで接続できる
else
    PROXY="$H:$N"
fi

# デフォルトではプロキシ解除とみなす
HTTP_PROXY=""
HTTPS_PROXY=""
FTP_PROXY=""

# ホスト+ポートの必須項目両方が設定されている場合に限り値を設定
if [ ( "$H" != "" ) && ( "$N" != "" ) ]
then
   HTTP_PROXY="$PROTO_HTTP://$PROXY/"
   HTTPS_PROXY="$PROTO_HTTPS://$PROXY/"
   FTP_PROXY="$PROTO_FTP://$PROXY/"
fi

# ここからが実際の処理
# --export オプションをつけるて起動すると /etc/profile.d/ 配下用のファイル向け出力とする
# ついでに、現在のセッションにも値を適用させる
if [ "$1" == "--export" ]
then
    echo "export HTTP_PROXY=$HTTP_PROXY"; export $HTTP_PROXY
    echo "export HTTPS_PROXY=$HTTPS_PROXY"; export $HTTPS_PROXY
    echo "export FTP_PROXY=$FTP_PROXY"; export $FTP_PROXY
    echo "export NO_PROXY=$NO_PROXY"; export $NO_PROXY
else
    echo "HTTP_PROXY=$HTTP_PROXY"
    echo "HTTPS_PROXY=$HTTPS_PROXY"
    echo "FTP_PROXY=$FTP_PROXY"
    echo "NO_PROXY=\"$NO_PROXY\""
fi
```

上記スクリプトを作成、保存→実行可能に。そして実行。

```shell-session
[hoge@fuga ~]$ vi ~/bin/changeproxy.sh
(編集内容は上記のスクリプト.現在の Proxy 環境をパラメタとして埋め込んでおくこと)
[hoge@fuga ~]$ chmod +x ~/bin/changeproxy.sh
[hoge@fuga ~]$ sudo su
パスワード:
[root@fuga hoge]# ./bin/changeproxy.sh --export > /etc/profile.d/proxy.sh
[root@fuga hoge]# ./bin/changeproxy.sh >> /etc/environment
[root@fuga hoge]# vi /etc/environment
(以前の Proxy 環境変数設定行が存在する場合はその行を削除)
[root@fuga hoge]# exit
[hoge@fuga ~]$ 
```

## apt-get コマンドを実行

ここから先は淡々と。

```shell-session
[hoge@fuga ~]$ sudo apt-get update; sudo apt-get dist-upgrade; sudo apt-get autoremove
(出力省略)
[hoge@fuga ~]$
```
これでレスキュー環境の基本設定は終了、ここから先は趣味の領域? なので、[運用](https://qiita.com/p_cub/items/df8ead671fa4482903e8#%E3%83%AC%E3%82%B9%E3%82%AD%E3%83%A5%E3%83%BC%E7%92%B0%E5%A2%83%E3%81%AE%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9)のセクションへジャンプしていただいても一向に差し支えありません。

# 必要なソフトの追加インストールと必要なサービスの有効化

Debian 系ディストリビューションではおなじみの下記コマンドで、あなたにとって必要なパッケージをインストールしていきます。

```shell-session
[hoge@fuga ~]$ sudo apt-get -y install パッケージ名1 パッケージ名2 ・・・ パッケージ名n
```

ちなみに、私のレスキュー環境で追加したパッケージ群は以下のようになっています[^6]

[^6]: 明らかに依存関係で自動的にインストールされたであろう、 'libホニャララ' とか 'python-' モジュールは予め一覧から除外したのですが、これらの名前に該当しないものに関しては、依存関係で自動的にインストールされたパッケージも少量含まれています

```shell-session
acl attr bsdtar console-data console-tools 
dhcping dnsutils ebtables firewalld ipset 
irqbalance localehelper lrzip lzip mountall 
ndiff neofetch netcat netsed nkf nmap p7zip p7zip-full 
quota rng-tools vim vim-runtime 
w3m xfsdump xfsprogs zsh zsh-common
```
これらのうち、2,3,説明をしておいたほうがいいものがあります。

## firewalld (サービスは無効化)
これらのソフトのうち firewalld に関してですが、ファイアウォールを稼働させようとしてインストールしたのではなく、既存環境と同じ設定を投入してテストをするために必要だからインストールしたものです。なので、常時稼働のサービスとしては不要のため、予め無効化しておきます.

```shell-session
[hoge@fuga ~]$ sudo systemctl disable firewalld
```
## irqbalance

irqbalance は、マルチコアの CPU を効率よく利用するためのデーモンです。このソフトを起動していない場合ですと、たとえマルチコアであろうとも、割り込み(IRQ)の処理は CPU0 のコアのみが担うことになってしまいます。インストール直後に有効化されているはずですが、万全を期して有効化。

```shell-session
[hoge@fuga ~]$ sudo systemctl enable irqbalance
```
## rng-tools
あまり知られていませんが、RaspberryPI にはハードウェアの乱数発生器が備えられています(/dev/hwrng として読み取りのみでアクセスが可能)。/dev/hwrng から定期的に乱数を読み取り、[カーネルのエントロピープール](http://blog.daionet.gr.jp/knok/2014/12/04/random-numbers-on-linux/)に質の良い値を与えてくれるデーモンが rngd です。レスキュー環境といえども、ユーザー向け ssh キーを生成したりすることはあるでしょうから、こちらも有効化しておきます。

```shell-session
[hoge@fuga ~]$ sudo systemctl enable rng-tools
```

# 不要サービスの無効化/不要ソフトの削除

## 初期状態の Raspbian-LITE で自動起動が有効化されているサービス一覧

```shell-session
autovt@.service                        enabled
avahi-daemon.service                   enabled
bluetooth.service                      enabled
bthelper.service                       enabled
console-setup.service                  enabled
cron.service                           enabled
dbus-org.bluez.service                 enabled
dbus-org.freedesktop.Avahi.service     enabled
dhcpcd.service                         enabled
dhcpcd5.service                        enabled
fake-hwclock.service                   enabled
getty@.service                         enabled
hciuart.service                        enabled
keyboard-setup.service                 enabled
networking.service                     enabled
raspberrypi-net-mods.service           enabled
rpi-display-backlight.service          enabled
rsync.service                          enabled
rsyslog.service                        enabled
sshswitch.service                      enabled
syslog.service                         enabled
systemd-timesync                       enabled
triggerhappy.service                   enabled
wifi-country.service                   enabled
wpa_supplicant.service                 enabled
```

上に挙げたサービスのうち、不要なものをガンガン削除していきます。

### avahi-daemon (既存環境で利用していないなら)

固定 IP の人にとっては全く不要です。

```shell-session
# この処理で dbus-org.freedesktop.Avahi.service  も無効化される
[hoge@fuga ~]$ sudo systemctl-disable avahi-daemon
```

### Bluetooth 関連 (2B環境限定) 

ラズパイ2B では Bluetooth がありませんから、無効化して構いません。ただし、3B以降も所有していて、このSDカードをそちらでもレスキュー環境として利用したい、という場合は無効化しないほうがいいでしょう。

```shell-session
# Bluetooth モデム用サービス
[hoge@fuga ~]$ sudo systemctl disable hciuart
# Bluetooth ヘルパーサービス
[hoge@fuga ~]$ sudo systemctl disable bthelper
# この処理で dbus-org.bluez.service も自動で無効化
[hoge@fuga ~]$ sudo systemctl disable bluetooth
```
### crond

あくまでもレスキュー環境であって、常時起動している環境ではないので cron は全く以て不要。

```shell-session
[hoge@fuga ~]$ sudo systemctl disable cron
```
### dhcpcd (継続して使いたい方はそのままでOK)

初回起動時には仕方なく利用しましたが、本来はsystemd-networkd を利用したいので、このサービスは無効化します。ついでに systemd-networkd を有効化します。

```shell-session
# このコマンド実行で、dhcpcd5.service も自動削除される
[hoge@fuga ~]$ sudo systemctl disable dhcpcd
[hoge@fuga ~]$ sudo systemctl enable systemd-networkd
```

### rsyncd

バックアップから復元する際に、コマンドとしての rsync は必要ですが、レスキュー環境が rsync サーバを提供する必要はないのでサービス無効化。

```shell-session
[hoge@fuga ~]$ sudo systemctl disable rsync
```

### rsyslog 関連

レスキュー環境で見たいログは、既存環境の /var/log 配下のログであって、レスキュー環境のそれではありません。なので、rsyslog は不要。

```shell-session
# このコマンドを実行すると、連動して syslog.service も削除される
[hoge@fuga ~]$ sudo systemctl disable rsyslog
```
### sshswitch
このサービスは、配布イメージを SD に書き込んで初めて Raspbian-LITE を起動する際に1回だけ実行されるものです。処理内容を順を追って説明します

1. /boot 配下に 'ssh' または 'ssh.txt' という名前のファイルが存在するか確認
2. 存在するときは regenerate_ssh_host_keys サービスを呼び出して既存の ssh_host_*_key ファイルを全削除、新しいホストキーを生成
3. SSH サービスを有効化+起動

というものになります。
この環境では、わざわざ既存環境から ssh_host_*_key ファイルたちをインポートしているわけですから、当然不要です、というよりもこんなサービスはこの環境にとっては害悪です。

```shell-session
[hoge@fuga ~]$ sudo systemctl mask sshswitch
```

### triggerhappy

X環境を含め、ローカルログインしているアカウントにシステムワイドのホットキー機能を提供するデーモンですが、これが便利なのはローカルログイン/ゲームパッドなどを接続しているときだけなので、コンソール/SSH前提のこの環境では不要です。

```shell-session
[hoge@fuga ~]$ sudo systemctl disable triggerhappy
```

### wifi-country

これもセットアップが終了していない Raspbian 向けの機能となります。wpa_supplicant の設定内で国指定がされていない場合は WiFi 環境を起動させない、というサービスです。WiFi/Bluetooth のない 2B 環境では無意味ですし、3B 以降の機種でも、wpa_supplicant 設定はきちんとなされているはずですから、全く以て不要。害悪。

```shell-session
[hoge@fuga ~]$ sudo systemctl mask wifi-country
```

### wpa_supplicant (デバイス不問バージョン)
wpa_supplicant パッケージには、systemd 向けユニットファイルが2種類入っています。
- 1つ目は、udev が発見したすべての WiFi デバイスに対応可能な「デバイス不問バージョン」(wpa_supplicant.service)
- 2つ目は特定の WiFi デバイスでのみ wpa_supplicant を実行させる「デバイス指定バージョン」(wpa_supplicant@.service)
となります

2Bしか持っていない方は当然 wpa_supplicant サービスを無効化して構いません。
3B以降の方も、systemd-networkd と連携させるために、このデバイス不問バージョンを無効化+デバイス指定バージョンを有効化 したほうが幸せになれます。

```shell-session
# 2B 環境の人はこれだけで良い
[hoge@fuga ~]$ sudo systemctl disable wpa_supplicant
# 3B 以降の環境の人向け
[hoge@fuga ~]$ sudo systemctl enable wpa_supplicant@wlan0
```

## 明らかに不要なパッケージの削除

plymouth はブート時にスプラッシュイメージを表示させ、ブートログを隠すためのソフトです。この環境では明らかに不要なので削除します。

```shell-session
[hoge@fuga ~]$ sudo apt-get remove plymouth
```
 
もともと LITE はインストールされているソフトが少ないため、削除するべきパッケージはこれだけ、ということになります。


# レスキュー環境の基本的な使い方

環境作成だけの書きっぱなしというわけにも行かないので、
最も手っ取り早い運用方法を記載します。

それは以下のようなものになります。
なお、ここから文中に出てくる「別メディア」という単語はファイルサーバ/USB 接続HDD/新規 SD 等を指します。

- 既存環境が安定稼働してるとき
    - 既存環境 SD のバックアップを取るために月に1回程度の頻度でレスキュー環境で起動
    - 既存環境 SD を fsck
    - 既存環境 SD 全体のバックアップを別メディアに保存
        - (コピー元メディアが信頼できないので、ddではなくtarで)
    - ついでにレスキュー環境そのもののソフトアップデートも行う

- 既存環境のSDカードの挙動がおかしくなり始めたら
    - 既存環境を即座にシャットダウン
    - 既存環境の SD を抜き、レスキュー環境SDに入れ替えて再起動
    - 既存環境の SD を USB カードリーダに挿して fsck
    - (読めるなら)既存環境にある、最近更新した大事なファイルを別メディアにバックアップ
    - (こりゃだめだ、となったら)新しい SD を用意して、レスキュー環境下で別メディアに保存しておいたバックアップを最大限利用しながら環境構築をやり直す

という流れになります。お疲れ様でした。

---
[脚注]
---