Raspberry PI シリーズで分散環境構築(その6: クラスタノード向け Raspberry PI OS のカスタマイズ(2))

# 序(連載リスト)
1. [Raspberry PI シリーズで分散環境構築(その1:モデル別、ディスクレスクライアント化の可否まとめ)](https://qiita.com/p_cub/items/e81dd0f49b3f6fb024a5)
2. [Raspberry PI シリーズで分散環境構築(その2: PiServer の解析と代替システムの設計まで)](https://qiita.com/p_cub/items/69fa32a9a62890ca1d04)
3. [Raspberry PI シリーズで分散環境構築(その3: dnsmasq のインストールと設定)](https://qiita.com/p_cub/items/e7c889b0f92a868a6c20)
4. [Raspberry PI シリーズで分散環境構築(その4: NFSサーバの構築とクライアント用OSのインポート)](https://qiita.com/p_cub/items/dd682de7de28276129e6)
5. [Raspberry PI シリーズで分散環境構築(その5: クラスタノード向け Raspberry PI OS のカスタマイズ(1))](https://qiita.com/p_cub/items/ecf4c48b7ecb08d971b1)
6. [Raspberry PI シリーズで分散環境構築(その6: クラスタノード向け Raspberry PI OS のカスタマイズ(2))](https://qiita.com/p_cub/items/73dc833302c3f064dc39)
7. [Raspberry PI シリーズで分散環境構築(その7: 各ラズパイ向けの tftp ルート設定と起動試験)](https://qiita.com/p_cub/items/ecef742e86ab2991c1b0)
8. [Raspberry PI シリーズで分散環境構築(その8: Webクラスタ構築のケース(前半))](https://qiita.com/p_cub/items/10751e2daa2d3b74c5d5)
9. [Raspberry PI シリーズで分散環境構築(その9: Webクラスタ構築のケース(後半))](https://qiita.com/p_cub/items/c4c837a57e6487a076d4)

本稿は その6 となります

[前回](https://qiita.com/p_cub/items/ecf4c48b7ecb08d971b1)は NFS サーバにクライアント向け Raspberry PI OS をコピーした後、テキストベースの変更を加えましたが、今回は qemu-static をインストールしたマシンでクライアント向け OS イメージをカスタマイズしていきます。

# 参考(筆者の環境)
![RaspiCloudSystem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267238/9d0ab7af-1f13-cf7b-3cc9-9b4e8c6cb4fd.png)

# 下準備
qemu(user-static) と binfmtサポート をインストールした Linux マシンで chroot すると、ラズパイ環境のコンソール起動状態を簡単にエミュレート可能となります。 原理は [ここ](http://blog.kmckk.com/archives/2342452.html) に詳しく載っていますので、参照してください。

## 1. qemu-user-static のインストール
NFS サーバ(市販のNASを利用している場合は PXEサーバ)にARM(armhf,aarch64)エミュレータをインストールします。ただし、ダイナミックリンクライブラリを利用する通常の qemu パッケージでは駄目です。各種ライブラリがスタティックリンクされているバージョンをインストールする必要が有ります。

### 1.1. 各ディストリビューション向けインストール方法
以下に、NFS サーバのディストリビューションが何であるかにより、インストール方法が異なりますので、簡単に方法を記載します

<dl>
<dt>Debian/Ubuntu 等の場合</dt>
<dd>qemu-user-static の deb パッケージを apt-get install します</dd>
<dt>RedHat/Fedora 等の場合</dt>
<dd>qemu-user-static の RPM パッケージを dnf install します</dd>
<dt>ArchLinux の場合</dt>
<dd>公式バイナリパッケージは存在しないので、pcre-static, glib2-static, qemu-user-static を AUR からダウンロードして、全部 makepkg -Csi します</dd>
<dt>Gentoo の場合</dt>
<dd>下記リンクを参考に、頑張ってください</dd>
</dl>
[Gentoo の方向け参考ページ](https://wiki.gentoo.org/wiki/Crossdev_qemu-static-user-chroot)

### 1.2. インストール後の処理

qemu-user-static パッケージをインストール後、/usr/bin/qemu-arm-static (2Bまで向けエミュレータ), 及び /usr/bin/qemu-aarch64-static (3B以降向けエミュレータ)が現れます。qemu-arm-static を 2B に配布する OS イメージ、qeum-aarch64-static を 3B 以降のラズパイに配布する OS イメージにコピーします。

```shell-session
# cp -pf /usr/bin/qemu-arm-static /exports/os4pi/raspbian/armhf/usr/bin/
# cp -pf /usr/bin/qemu-aarch64-static /exports/os4pi/raspbian/armhf_64/usr/bin/
# cp -pf /usr/bin/qemu-aarch64-static /exports/os4pi/raspbian/aarch64/usr/bin/
``` 

## 2. binfmt サポートのインストール(大抵の場合、デフォルトでインストール済み)

systemd を採用しているシステムの場合、systemd がこの機能を取り込んだので、最新版の systemd にアップデートされていれば特に作業は発生しません。
確認方法は

```shell-session
$ systemctl list-unit-files -t service | grep binfmt
binfmt-support.service                 enabled         enabled
systemd-binfmt.service                 static          disabled
$
```

のように systemd-binfmt か binfmt-support の行が含まれていて、enable/static 状態にあれば OK です。
上記の行が出てこなかった場合、binfmt-support パッケージ(Debian/Ubuntu の場合)をインストールする必要があります。

# 実際のカスタマイズ作業
以下の作業は armhf, armhf(カーネルのみ64bit化), aarch64 の 3アーキテクチャに関して全く同じ処理を行う必要が有りますが、 代表して armhf での処理を記載します。

## 1. chroot 環境の起動

### 1.1. chroot 実行前に特殊フォルダをマウント
クライアントを電源投入したときに自動マウントされる特殊ディレクトリをマウントしておきます。

```shell-session
# cd /exports/os4pi/raspbian
# mount -t proc proc armhf/proc
# mount --bind /dev armhf/dev
# mount -t devpts devpts armhf/dev/pts
# mount --bind /run armhf/run
# mount --bind /sys armhf/sys
```

### 1.2. chroot の実行

```shell-session
# chroot armhf /bin/bash --login
(TTY が無い、という内容のエラーメッセージが出るが問題無い)
root@ホスト名:/#
```
ここからが実際の Raspberry PI OS 内での作業、ということになります。

### 1.3. chroot 環境に入った直後に行うべきこと

何も手を入れていないデフォルト状態の今、どのようなパッケージがインストールされていて、systemd の unit がどのような状態であるかを保存しておきます。chroot 環境を抜ける直前にも同じ処理で別ファイルを生成し、 diff を取れば、他のアーキテクチャへの水平展開が楽になります。

```shell-session
root@ホスト名:/# apt-mark showmanual > /tmp/installed_pkg_$(date +'%Y%m%d%H%M%S').txt
root@ホスト名:/# systemctl list-unit-files > /tmp/systemd_unit_$(date +'%Y%M%D%H%M%S').txt
```
なお、chroot 環境下の /tmp はディスクレスホスト側では tmpfs としてマウントされますから、ディスクレスホストからディレクトリの内容は見えませんし、書き換えられたり自動消去される恐れもない、永続的データ保存領域となります。ですから、安心して NFS サーバ側での作業内容などを保存しておくことができます。[^TMP]
[^TMP]: 当然、NFSサーバ側で不要ファイルを削除する必要が有ります。

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

### 2.1. root のパスワード変更

当然ですね。

```shell-session
root@ホスト名:/# passwd
New password: 
Retype new password: 
passwd: password updated successfully
root@ホスト名:/#
```

### 2.2. デフォルトユーザーアカウントの変更
Raspberry PI OS のデフォルトユーザー名は 'pi' なので、このアカウントのログイン名、パスワード、グループ名を変更します

```shell-session
root@ホスト名:/# usermod -l 新ログイン名 -d /mnt/home/新ログイン名 pi
root@ホスト名:/# passwd 新ログイン名
New password: 
Retype new password: 
passwd: password updated successfully
root@ホスト名:/# groupmod -n 新ログイン名 pi
```

## 3. OS のアップデート

```shell-session
root@ホスト名:/# apt-get update; apt-get full-upgrade; apt-get autoremove
```

## 4. 日本語サポートのインストールとロケール設定

### 4.1. フルサポートにしたい場合
この処理を行うと、コンソールで日本語フォントを表示可能なコンソール(fbterm)と、このコンソールでも表示可能な日本語フォント(unifont)、日本語 man ページがインストールされます。

```shell-session
root@ホスト名:/# apt-get install task-japanese locales-all
(出力省略)
root@ホスト名:/# localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
```

### 4.2. glibc のロケールメッセージだけでいい場合

```shell-session
root@ホスト名:/# locale-gen
Generating locales (this might take a while)...
  en_US.UTF-8... done
  ja_JP.UTF-8... done
Generation complete.
root@ホスト名:/#
```

## 5. 不要なサービス/タイマージョブの無効化
ディスクレスクライアントには不要なサービスをガンガン無効化していきます。
最初に、デフォルトの起動モードを X から コンソールにしています。2行目以降で mask にしているサービスは起動されては絶対にまずいもの、disable にしているものは、お好みに応じて無効化してください、というメッセージになっています。

また、末尾が timer となっているものは、旧来の crond に変わって systemd が管理するタイマージョブとなっています。これは mask ではなく disable のみで無効化できます。

```shell-session
root@ホスト名:/# systemctl set-default multi-user
root@ホスト名:/# systemctl mask apply_noobs_os_config.service
root@ホスト名:/# systemctl mask checkfs.service
root@ホスト名:/# systemctl mask cryptdisks-early.service
root@ホスト名:/# systemctl mask cryptdisks.service
root@ホスト名:/# systemclt mask dphys-swapfile.service
root@ホスト名:/# systemctl mask raspi-config.service
root@ホスト名:/# systemctl mask regenerate_ssh_host_keys.service
root@ホスト名:/# systemctl mask sshswitch.service
root@ホスト名:/# systemctl disable apparmor.service
root@ホスト名:/# systemctl disable avahi-daemon.service
root@ホスト名:/# systemctl disable autologin@.service
root@ホスト名:/# systemctl disable bluetooth.service
root@ホスト名:/# systemctl disable rsync.service
root@ホスト名:/# systemctl disable triggerhappy.service
root@ホスト名:/# systemctl disable wpa_supplicant-nl80211@.service      
root@ホスト名:/# systemctl disable wpa_supplicant-wired@.service    
root@ホスト名:/# systemctl disable wpa_supplicant.service    
root@ホスト名:/# systemctl disable wpa_supplicant@.service
root@ホスト名:/# systemctl disable apt-daily-upgrade.timer 
root@ホスト名:/# systemctl disable apt-daily.timer 
root@ホスト名:/# systemctl disable man-db.timer
```

ちなみに、起動時にログを隠す plymouth 関連も不要だと思いますが、カーネル起動パラメータで quiet 指定を外しているので、実行されることはないはずです。

## 6. 追加ソフトウェアのインストール

### 6.1. 共通のソフトウェア
分散環境に有用なソフトウェアを追加していきます。

```shell-session
root@ホスト名:/# apt-get install cpufreq cpufrequtils irqbalance rng-tools nfs-common sysstat
(出力省略)
root@ホスト名:/#
```

### 6.2. (オプション)お好みのエディタ/シェル/各種ユーティリティ/ライブラリ等
以下は筆者がインストールした物たちです

```shell-session
root@ホスト名:/# apt-get install zsh vim nmap iotop jnettop net-snmp nkf 
```

これで、分散環境ノードの基本環境は出来上がり、となります。
ただ、このままでは何も分散してない、「連携のない Linux ディスクレスホスト群」でしかありません。
何を分散処理するか、によってこの後どんなソフトウェアをインストールするか、という部分が変化していきますので、
次回以降に目的別ケーススタディという形を取っていきたいと思います。

## 7. chroot 環境の終了と後始末

chroot 環境を抜ける前に、他のアーキテクチャで同じ作業を行うために、作業開始時と同じ手法で現状のサマリを保存しておきます。
その後、chroot環境を抜け、利用したマウントを解除していきます。

```shell-session
root@ホスト名:/# apt-mark showmanual > /tmp/installed_pkg_$(date +'%Y%m%d%H%M%S').txt
root@ホスト名:/# systemctl list-unit-files > /tmp/systemd_unit_$(date +'%Y%M%D%H%M%S').txt
root@ホスト名:/# exit
Logout
# umount armhf/dev/pts
# umount armhf/proc
# umount armhf/run
# umount armhf/sys
# umount armhf/dev
```

## 7.1. (オプション) 一連の作業をまとめたシェルスクリプトの作成
今回行った作業の、マウント→chroot環境→アンマウント という流れは、クライアントOSイメージをアップデートする際に必ず行う処理なので、NFSサーバにシェルスクリプトとして記載しておくと幸せになります。

```shell
#!/bin/bash
TARGETARCH=${1:-"armhf"}
DISTRIBUTION=${2:-"raspbian"}

BASEDIR=/exports/os4pi

ROOTDIR=${BASEDIR}/${DISTRIBUTION}/${TARGETARCH}

if [ ! -d ${ROOTDIR} ]
then
   echo "ディレクトリ ${ROOTDIR} が存在しません"
   exit 2
fi

case "${TARGETARCH}" in
    "armhf" )
      if [ ! -x "${ROOTDIR}/usr/bin/qemu-arm-static" ]
      then
        cp -pf /usr/bin/qemu-arm-static "${ROOTDIR}/usr/bin/"
      fi
      ;;
    "armhf_64" )
      if [ ! -x "${ROOTDIR}/usr/bin/qemu-aarch64-static" ]
      then
        cp -pf /usr/bin/qemu-aarch64-static "${ROOTDIR}/usr/bin/"
      fi
      ;;
    "aarch64" )
      if [ ! -x "${ROOTDIR}/usr/bin/qemu-aarch64-static" ]
      then
        cp -pf /usr/bin/qemu-aarch64-static "${ROOTDIR}/usr/bin/"
      fi
      ;;
    *)
      echo "対象外のアーキテクチャです"
      exit 3
      ;;
esac



mount -t proc proc ${ROOTDIR}/proc
mount --bind /dev ${ROOTDIR}/dev
mount -t devpts devpts ${ROOTDIR}/dev/pts
mount --bind /sys ${ROOTDIR}/sys
mount --bind /run ${ROOTDIR}/run

chroot ${ROOTDIR} /bin/bash --login

umount ${ROOTDIR}/run
umount ${ROOTDIR}/sys
umount ${ROOTDIR}/dev/pts
umount ${ROOTDIR}/dev
umount ${ROOTDIR}/proc
```

今回はここまでとなります。

# 次回予告
次回は、PXE サーバの tftp ルートの設定を行った後、実際にディスクレスブートの起動試験を行いたいと思います。