Raspberry PI シリーズで分散環境構築(その8: Webクラスタ構築のケース(前半))

# 序(連載一覧)
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)

本稿は連載のその8、となります。[前回](https://qiita.com/p_cub/items/ecef742e86ab2991c1b0)までの連載で、分散環境のベースとなる基盤環境(PXEサーバ+NFSサーバ+ディスクレスラズパイ群)は一通り構築完了しているものとし、今回から、この基盤環境の上に、数種類のクラスタ環境を構築していきます。

# 1. システム概要
![RaspiCloudSystem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267238/9d0ab7af-1f13-cf7b-3cc9-9b4e8c6cb4fd.png)

今回は、上図の「分散環境コントローラ」または「PXEサーバ」相当のホストに mod_proxy_loadbalance を有効化した Apache2.4(ユーザーアクセス向け、リバースプロキシ専用 httpd )を、ディスクレスラズパイノードには何もいじらない[^ポート番号] Nginx をインストールして、負荷分散を考慮した Webクラスタを構築して行きたいと思います。

リバースプロキシに Apache を採用した理由は、負荷分散(ロードバランシング)モジュールが簡単に導入できる点+Nginx だと有料商品となってしまう点となります。

そして、ディスクレスノード側に導入する Web サーバデーモンは、本来何でもいいんですが(Nodejs でもいいし、Tomcat でもいいです)、本稿でディスクレスラズパイに Nginx を採用している理由は、ラズパイの貧弱なメモリにとって、デーモン自体が軽量であるという最大のメリットの他に、設定がシンプルなので本稿での説明量が減る、という点も大きいです。

なお、Nginx が提供するコンテンツは NFSサーバ上に置き、全ディスクレスラズパイで同一の共有フォルダをマウントし、Webルートとするものとします。

本稿の内容は、上記システム構築作業の前半部分である、NFSサーバの設定,ディスクレスラズパイ上の Nginx 設定まで、となります。

[^ポート番号]: ポート番号だけは変更します。

# 2. NFS サーバ上に、Web ルートフォルダを作成、共有設定
NFSサーバ上で、Nginx 用コンテンツ格納フォルダを作成、共有設定します。

```shell-session
# ドキュメントルート
root@NFSサーバ:/# mkdir -p /exports/www/html
# FastCGI の実行を考えているなら
root@NFSサーバ:/# mkdir -p /exports/www/cgi-bin
root@NFSサーバ:/# cat >> /etc/exports
/exports/www  192.168.172.0/24(rw,sync,nohide,no_root_squash,subtree_check)
[Ctrl+D を押す]
root@NFSサーバ:/# exportfs -avr
(出力省略)
root@NFSサーバ:/#
```

# 3. ラズパイ向けOSに Nginx をインストール、設定

## 3.1. chroot 環境下で Nginx をインストール
連載[その6](https://qiita.com/p_cub/items/73dc833302c3f064dc39)で qemu-user-static をインストールしたNFS/PXEサーバ上で、/usr/local/sbin/runRaspbian として保存した[その6の中で作成したスクリプト](https://qiita.com/p_cub/items/73dc833302c3f064dc39#71-%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3-%E4%B8%80%E9%80%A3%E3%81%AE%E4%BD%9C%E6%A5%AD%E3%82%92%E3%81%BE%E3%81%A8%E3%82%81%E3%81%9F%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)を利用して chroot 環境に入り、OS全体をアップデートした後、Nginx をインストールします。
ちなみにディスクレスクライアント下では、インストール直後の状態のままではサービス起動に失敗します。ですから、デフォルトでは自動起動不可とします。

また、お好みに応じて、
<dl>
<dt>Perl 等で CGI を実行させたい場合</dt>
<dd>fcgiwrap + spawn-fcgi 両 deb パッケージ</dd>
<dt>PHP を実行させたい場合</dt>
<dd>php-fpm deb パッケージ</dd>
</dl>
等を追加インストールしてください(これらのパッケージの説明はしません)。

```shell-session
$ sudo /usr/local/sbin/runRaspbian
mesg: ttyname failed: No such device
root@ホスト名:/# apt-get update; apt-get full-upgrade; apt-get install nginx
(出力省略)
root@ホスト名:/# systemctl disable nginx
(出力省略)
root@ホスト名:/#
```
(まだ、chroot 環境からは抜けないでください。)

## 3.2. nginx 起動時に必要となるテンポラリディレクトリを書き込み可能化
nginx は起動時に /var/log/nginx ディレクトリがなければ作り、この下にエラーログを出力しようとします[^nginxsyslog]。ですが、ディスクレスクライアントでは /var/log 配下にフォルダを作ることができず、nginx は起動に失敗します。しかし、systemd に予めこの事実を伝えておくと、systemd は自動的に書き込み可能なテンポラリフォルダを作成してくれるようになります。


[^nginxsyslog]: nginx のログをリモートの syslog サーバへ転送する設定にしていても、起動時のエラーを /var/log/nginx 下に吐き出そうとします。

```shell-session
root@ホスト名:/# cat >> /etc/tmpfiles.d/nginx.conf
D /var/log/nginx 0755 root adm
[Ctrl+D を押す]
root@ホスト名:/#
```

また、nginx サービスを起動する際、ファイルアップロードやセッション管理用に /var/lib/nginx フォルダも書き込み可能なフォルダである必要があります。ですが、上記と同等の方法を /var/lib/nginx に対して試してみても解決できませんでした(2020/06現在。今後状況が変わる可能性が有ります)。よって、/etc/fstab に以下の行を追加して書き込み可能な状態にしてください。


```shell-session
# フォルダがなければ
root@ホスト名:/# mkdir /var/lib/nginx
root@ホスト名:/# cat >> /etc/fstab
tmpfs /var/lib/nginx tmpfs noauto,defaults,mode=755 0 0
[Ctrl+D を押す]
root@ホスト名:/#
```

(まだ、chroot 環境からは抜けないでください。)

## 3.3. ドキュメントルートのマウントポイントを作成、マウント設定

chroot環境下なので実際にはマウントしません

```shell-session
root@ホスト名:/# mkdir /mnt/www
root@ホスト名:/# cat >> /etc/fstab
# Web cluster node
192.168.172.17:/exports/www /mnt/www  nfs noauto,proto=tcp,vers=3,ro,noatime 0 0
[Ctrl+D を押す]
root@ホスト名:/#
```

(まだ、chroot 環境からは抜けないでください。)

## 3.4. /etc/nginx/sites-enabled/default の設定

Raspberry PI OS 版 Nginx の設定ファイルを編集します。リッスンする TCP ポート番号の変更、ドキュメントルートの変更のみを行います。
HTTPS化や認証関連はコントローラの Apache に任せればいいので、 Nginx はほぼデフォルト状態で全く構いません。

```shell-session
root@ホスト名:/# vim /etc/nginx/sites-enabled/default
root@ホスト名:/#
```

変更後の内容は以下のようになります。
(まだ、chroot 環境からは抜けないでください。)

```configuration
# /etc/nginx/sites-enabled/default
(先頭のコメント類省略)
server {
   # コメントアウト
   # listen 80 default_server;
   # listen [::]:80 default_server;
  
   # 2行追加
   listen 8000 default_server;
   listen [::]:8000 default_server;

   (中略)

   # コメントアウト
   # root /var/www/html;
   # 1行追加
   root /mnt/www/html;

  (後略)
}
(末尾のサンプルコメント類省略)
```

CGI や PHPでサーバサイドプログラムを実行させる場合は追加設定が必要となりますが、そうでない場合はこれで Nginx 自体の基本設定は終了となります(サーバサイドプログラミング関連の環境設定は本稿の範囲外とします)。 

## 3.5. systemd カスタム target の作成と、nginx.service 設定のオーバーライド

今後、他の用途のクラスタも構築していくわけですが、他の用途のノードになっているラズパイにとって nginx のプロセスは不要ですし、ドキュメントルートのNFSマウントも不要です。どちらもメモリを圧迫する害悪でしかありません。そこで、ラズパイを Web クラスタノードとして利用したい時だけドキュメントルートをマウントし、nginx プロセスを立ち上げる設定をしていくことにします。

### 3.5.1. ターゲットファイルの新規作成

/etc/systemd/system 配下に web-cluster-node.target ファイルを新規作成します。

```shell-session
root@ホスト名:/# vim /etc/systemd/system/web-cluster-node.target
```

ファイル内容は以下のようになります

```config
[Unit]
Description=Web cluster node config
Requires=multi-user.target
After=multi-user.target
AllowIsolate=yes
# Conflicts= 別の、同時に実行したくないターゲットファイル名リスト
```

このファイルを作成しておくと、ラズパイ上で以下のコマンドを実行するだけで、Webクラスタノードへと用途が切り替わるようになります。

```shell-session
root@ラズパイ:~# systemctl isolate web-cluster-node.target
```

### 3.5.2. nginx.service の改造(ドロップイン設定ファイルの作成)

systemd には ドロップイン設定、という機能があり、/etc/systemd/system/ 配下に、サービス名.service.d フォルダを作り、このフォルダの下に override.conf ファイルを作成すると、各種パッケージから提供される /lib/systemd/system 配下のユニットファイルを変更することなくサービスの挙動を変更することができます。この機能を使って nginx 起動時の挙動を変えていきます。

```shell-session
# nginx 起動のユニットは nginx.service 
root@ホスト名:/# mkdir /etc/systemd/system/nginx.service.d
root@ホスト名:/# vim /etc/systemd/system/nginx.service.d/override.conf
```

override.conf の内容は以下のようになります

```config
[Service]
# デフォルトのサービス起動前処理を一旦クリア
ExecStartPre=
# tmpfs の /var/lib/nginx をマウント
ExecStartPre=/bin/mount /var/lib/nginx
# ドキュメントルートをマウント
ExecStartPre=/bin/mount /mnt/www
# デフォルトのサービス起動前処理を流用(コンフィグテスト)
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'

# サービス停止後の処理(ドキュメントルートのumount)
ExecStopPost=/bin/umount /mnt/www
# サービス停止後の処理(テンポラリのumount)
ExecStopPost=/bin/umount /var/lib/nginx

[Install]
# multi-user.target から、web-cluster-node.target に変更
WantedBy=web-cluster-node.target
```

### 3.5.3. カスタムターゲットと nginx サービスの紐付け
以上、systemd ユニットの編集が終わった後、web-cluster-node.target が呼び出されたときだけ nginx が起動する設定を行います。

```shell-session
root@ホスト名:/# systemctl add-wants web-cluster-node.target nginx.service
(出力省略)
root@ホスト名:/#
```
これでchroot環境下での作業は終了になります。
もし、起動中のラズパイ実機がある場合は、全機を再起動してください。再起動が完了後、以下のコマンドを実行して、エラーが発生していないことを確認してください。

```shell-session
root@ラズパイ:~# systemctl isolate web-cluster-node.target
root@ラズパイ:~# systemctl status nginx
(エラーが発生していないことを確認。)
root@ラズパイ:~# systemctl isolate multi-user.target
root@ラズパイ:~# systemctl status nginx
(プロセスが起動していないことを確認)
```

今回はここまでとなります。次回(後半)は、Apache で負荷分散装置部分を構築したいと思います。