CentOS7でもHTTP/3なnginxを使いたいがrpmとかninja使いたくねーって人のためのメモ

# 元記事と経緯

https://zenn.dev/haan/articles/8be49ed0cdea35

CentOS7でHTTP/1.1・HTTP/2・HTTP/3完全対応のnginxを使用したく、検索していたらこの記事を見かけました。
QuicheのパッチはどうやらHTTP/3が実験的なものだった時のパッチらしく、OpenSSLのGoogleフォークであるBoringSSLを利用したほうが今後苦労しないだろう、と考えこの構成に至りました。

が、私は`ninja`をビルドしている暇もなく、 **そもそもnpmがなんなのかよく理解していません。** パッケージのインストールにつかわれるもの？？くらいな認識です。[^1]

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

## Cmake

BoringSSLにCmake 3.10以上が必要です。
CentOS7のyumでは2.8.12しかインストールできません。そのため、Cmakeをソースからインストールします。
記事執筆時点での最新版`v3.25.1`を使用していますが、より新しいバージョンに各自置き換えてください。

```console:Cmakeのインストーラー実行
$ wget https://github.com/Kitware/CMake/releases/download/v3.25.1/cmake-3.25.1-linux-x86_64.sh
$ sh cmake-3.25.1-linux-x86_64.sh
```

インストーラーが起動します。

```:Cmakeインストーラー
CMake Installer Version: 3.25.1, Copyright (c) Kitware
This is a self-extracting archive.
The archive will be extracted to: /root/cmake

If you want to stop extracting, please press <ctrl-C>.
CMake - Cross Platform Makefile Generator
Copyright 2000-2022 Kitware, Inc. and Contributors
All rights reserved.

（中略）

Do you accept the license? [yn]: y
```

Cmakeのインストール先を聞かれます。違いがよくわからないのでとりあえずNoを選択し、自分で`/usr/local/`にインストールします。

```:Cmakeインストーラー
By default the CMake will be installed in:
  "/root/cmake/cmake-3.25.1-linux-x86_64"
Do you want to include the subdirectory cmake-3.25.1-linux-x86_64?
Saying no will install in: "/root/cmake" [Yn]: n

Using target directory: /root/cmake
Extracting, please wait...

Unpacking finished successfully
```

自分で`/usr/local/`にぶち込みます。mvだと上書きしたときに絶望的なので、cpを使用しましょう。

```console:Cmakeのものを/usr/local/にコピー
$ cp -r bin doc man share /usr/local/
```

Cmakeが最新版になりました。

```console:Cmakeのバージョン確認
$ cmake --version
cmake version 3.25.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).
```

## Perl

BoringSSLにPerlが必要です。これはyumで問題ないのでyumでインストールします。

```console:Perlのインストール
sudo yum install perl
```

## Go

BoringSSLにGoが必要です。（BoringSSL要求ソフトウェア多くね？）これもyumでインストールします。

```console:Goのインストール
sudo yum install golang
```

## gcc

CentOS7標準の4.8.5ではバージョンが古く、アップデートする必要があります。
そのため、yumでdevtoolset-11をインストールし、PATHを通した後gccを使用します。

devtoolset-11はレポジトリcentos-release-sclに含まれているので、centos-release-sclをインストールしてからdevtoolset-11をインストールします。

```console:devtoolset-11をyumでインストール
$ yum install centos-release-scl
$ yum install devtoolset-11
```

もしこれを実行したのであれば、BoringSSLの`cmake`時に以下のコマンドを実行してください。

```console:PATHを通す
export PATH=/opt/rh/devtoolset-11/root/bin:$PATH
```

# 競合ライブラリのアンインストール

## OpenSSL

すでにOpenSSLをインストールしてしまっている場合、アンインストールしないとBoringSSLと競合する（と思われる）ので、アンインストールします。

### yumでアンインストール

```console:yumを用いてアンインストール
$ sudo yum remove uninstall
```

### make installしたものをアンインストール

```console:makeを用いてアンインストール
$ cd /path/to/source/openssl
$ sudo make uninstall
```

# 必要ライブラリのインストール

<!--
## Brotli

https://github.com/google/brotli

`README.md`の通りにインストールします。

```console:Brotliのインストール
$ git clone https://github.com/google/brotli.git
$ cd brotli

$ mkdir out && cd out
$ ../configure-cmake
$ make
$ make test
$ make install
```
-->

## BoringSSL

https://github.com/google/boringssl

こちらも、`BUILDING.md`の通りにインストールします。
が、このままでは意図しない場所[^2]にインストールされてしまうので、configureではなくcmakeにprefixを設定します。各自で設定してください。

```console:BoringSSLのインストール
$ git clone https://github.com/google/boringssl.git
# cd boringssl

$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local/lib/boringssl ..
$ make
$ make install

$ # リンカにlibssl.aやlibcrypto.aなどの静的ライブラリを読み込ませる必要があるので、コピーする
$ cp */*.a /usr/local/lib/boringssl/lib/
```

:::note warn
もしgccアップデートのためにdevtoolset-11のアップデートを実行したのであれば、BoringSSLの`cmake`前に以下のコマンドを実行してください。

BoringSSLがcmakeでC++11対応のgccを要求するためです。

```console:PATHを通す
export PATH=/opt/rh/devtoolset-11/root/bin:$PATH
```
:::

# Nginxのインストール

https://hg.nginx.org/nginx-quic

いよいよNginxをインストールします。
Nginxは記事執筆時点での最新版の1.23.2でもQUICにはまだ対応していないので、nginx-quicのリポジトリからダウンロードします。

:::note info
Nginxが既に存在している場合は、各自オプションの値を変更してください。
:::

```console:Nginxのビルド
$ wget -O nginx.tar.gz https://hg.nginx.org/nginx-quic/archive/quic.tar.gz
$ tar -zxvf nginx.tar.gz
$ cd nginx-quic-quic/

$ ./auto/configure --with-debug \
              --with-http_ssl_module \
              --with-http_v2_module \
              --with-http_v3_module \
              --with-stream_quic_module \
              --with-cc-opt="-I/usr/local/lib/boringssl/include" \
              --with-ld-opt="-L/usr/local/lib/boringssl/lib" \
              --prefix=/etc/nginx \
              --sbin-path=/usr/sbin/nginx \
              --conf-path=/etc/nginx/nginx.conf \
              --error-log-path=/var/log/nginx/error.log \
              --http-log-path=/var/log/nginx/access.log \
              --pid-path=/run/nginx.pid \
              --lock-path=/run/nginx.lock \
              --user=nginx \
              --group=nginx
$ make
$ make install
```

<details>
<summary>私の場合、Nginxをリバースプロキシとして利用する予定があったので、
とりあえずいろいろ入れておけば今後再ビルドしなくていいだろうと思い、
いろいろオプションをつけることにしました。</summary>

```console:Nginxのconfigure
$ ./auto/configure --with-debug \
              --with-http_ssl_module \
              --with-http_v2_module \
              --with-http_v3_module \
              --with-stream_quic_module \
              --with-threads \
              --with-stream \
              --with-stream_ssl_module \
              --with-mail \
              --with-mail_ssl_module \
              --with-http_dav_module \
              --with-http_flv_module \
              --with-http_mp4_module \
              --with-http_gunzip_module \
              --with-http_gzip_static_module \
              --with-http_realip_module \
              --with-cc-opt="-I/usr/local/lib/boringssl/include" \
              --with-ld-opt="-L/usr/local/lib/boringssl/lib" \
              --prefix=/etc/nginx \
              --sbin-path=/usr/sbin/nginx \
              --conf-path=/etc/nginx/nginx.conf \
              --error-log-path=/var/log/nginx/error.log \
              --http-log-path=/var/log/nginx/access.log \
              --pid-path=/run/nginx.pid \
              --lock-path=/run/nginx.lock \
              --user=nginx \
              --group=nginx
```

</details>

# systemdに追加する

こんな感じのサービスファイルを入れてやれば動きます。[^3]

```systemd:nginx.service
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
```

# トラブルシューティング

## nginx: [emerg] getpwnam("nginx") failed

ユーザーが存在しないことによるエラーです。
上のコマンドとで「nginx」ユーザー、「nginx」グループで作成していれば下記コマンドで作成できます。

```console:nginxグループを作成する
$ groupadd nginx
```

```console:nginxユーザーを作成する
$ sudo useradd -g nginx -s /sbin/nologin -r nginx
```

# ちなみに（割と困る話）

```nginx
listen 443 http3 reuseport;
```

reuseportをつけないとHTTP/3は使用されません。
しかし、reuseportは複数使用できないようです。

```:ログ
nginx: [emerg] duplicate listen options for 0.0.0.0:443 in /etc/nginx/conf.d/virtualhost.conf:5
nginx: configuration file /etc/nginx/nginx.conf test failed
```

つまり、**HTTP/3のNginxでは複数ドメインをホストできません。**[^4]

早くHTTP/3普及してくれー。サーバーソフトウェアは簡単に変えられないんだぞ。[^5]

さて。私はインストールしたNginxを止めてApacheに戻してきます。

[^1]: サーバー管理者として大問題な気もするけど`yum`と`apt`がわかってればどうにでもなるよね！
[^2]: 私は`/root/boringssl`で作業していたため、`/root/boringssl/install`にインストールされました。
[^3]: [http://mogile.web.fc2.com/nginx_wiki/nginx_wiki201510/start/topics/examples/systemd.html](http://mogile.web.fc2.com/nginx_wiki/nginx_wiki201510/start/topics/examples/systemd.html)
[^4]: もし方法があれば、コメントで教えていただけると私が土下座します。
[^5]: Apacheは未対応、Nginxは実験的。LiteSpeedは対応