Amazon Linux2へのBrotli対応nginxのインストール
EC2(Amazon Linux2)にBrotli圧縮に対応したnginxをインストールする手順。
http通信ではgzip等の圧縮を使って送信データサイズを小さくすることができます。圧縮方式としてはgzipが長く使われてきましたが、より圧縮効率のよいアルゴリズムとしてBrotliが開発されBrotli圧縮も使われるようになっています。
ただ、gzip圧縮などはnginxの設定ファイルを変更するだけで対応できますが、nginxはBrotli圧縮には対応していないので(*1)、Googleの提供するngx_brotliモジュールを組み込んで、nginxごとビルドする必要があります。
今回はそのビルド手順の説明です。手順はAmazon Linux2上でのものですが、他のディストリビューションでもインストールしなければならないパッケージが変わるだけで、手順の大枠は変わらないはずです。
この記事の前提条件
- OSはAmazon Linux2。
- nginxはrpmからすでにインストールされていて、設定済み。動作しているものとする。
インストール方針
nginxのconfigureのパラメータに関わってきますが、インストールの方針は以下のようにします。
- configureのパラメータはrpmインストールしたnginxと基本同じにする
- 新しくインストールするファイルは/usr/local/nginxにまとめる
- configやlogファイルは/usr/local/には分けずに、rpmインストールしたものをそのまま流用する
また、rpmからインストールした既存のnginxはアンインストールせず残しておきます。
configファイルを流用するため、アンインストールによって必要なファイルが削除されるのを防ぐためと、今後、何らかの理由で元のnginxに戻したくなった時に対応しやすくするためです。
注意事項
以下でインストール手順を説明していきますが、インストール時の作業はroot権限が必要になります。コマンドの内容を理解した上で実行するようにしてください。
また、自分でビルドした場合、yumによるアップデートはできないので、nginxに脆弱性が見つかった場合などは、その都度、再ビルド&インストールしていく必要があります。
関連パッケージのインストール
まずは必要になるパッケージをインストールします。
OSS類のbuildをしたことのない環境だった場合、様々なパッケージのインストールが必要になります。
# sudo yum install autoconf automake libtool gcc gcc-c++ rpm-build pcre-devel openssl-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel gperftools-devel
libbrotliのインストール
Amazon Linux2ではlibbrotliもrpm提供されていないので、これもソースビルドします。
https://github.com/google/brotli のリポジトリをcloneして(*2)、ドキュメントの手順にしたがってビルド&インストールします。
libbrotliのビルドとインストール
# mkdir build
# cd build
# git clone https://github.com/google/brotli.git
# cd brotli
# git checkout v1.0.9 ←現時点で1.0.9が最新なので
# ./bootstrap
# ./configure
# make
# sudo make install
これで、/usr/local/lib/にインストールされます。
インストールはしましたが、Amazon Linux2ではshared libraryの検索対象に/usr/local/libが含まれていないため、このままではlibbrotliを参照できません。このため、ダイナミックリンカーの設定を変更します。
root権限で/etc/ld.so.conf.d/に以下のようにファイルを作成します。
# sudo vi /etc/ld.so.conf.d/local.conf
/etc/ld.so.conf.d/local.conf
/usr/local/lib
作成したファイルを読み込ませます。
# sudo ldconfig
これで、libbrotli関連のshared libraryが読み込まれるようになります。
# ldconfig -p | grep libbrotli libbrotlienc.so.1 (libc6,x86-64) => /usr/local/lib/libbrotlienc.so.1 libbrotlienc.so (libc6,x86-64) => /usr/local/lib/libbrotlienc.so libbrotlidec.so.1 (libc6,x86-64) => /usr/local/lib/libbrotlidec.so.1 libbrotlidec.so (libc6,x86-64) => /usr/local/lib/libbrotlidec.so
nginxのビルドとインストール
ようやくnginxのビルドです。buildディレクトリ上に最新安定版のnginxとngx_brotliをダウンロードします。
# cd ~/build # wget http://nginx.org/download/nginx-1.20.1.tar.gz # gzip -dc nginx-1.20.1.tar.gz | tar xvf - # git clone https://github.com/google/ngx_brotli.git # cd ngx_brotli # git submodule update --init
既にインストールされているnginxのcofigureパラメータを確認します。
# /usr/sbin/nginx -V nginx version: nginx/1.20.0 built by gcc 7.3.1 20180712 (Red Hat 7.3.1-13) (GCC) built with OpenSSL 1.1.1g FIPS 21 Apr 2020 TLS SNI support enabled configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules 略
出力されたパラメータをベースに以下のconfigureを実行します。
configureの例
# cd ~/build/nginx-1.20.1 # ./configure --prefix=/usr/local/nginx --sbin-path=/usr/local/nginx/sbin/nginx --modules-path=/usr/local/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx-local.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-compat --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-stream_ssl_preread_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E' --add-module=/home/ec2-user/build/ngx_brotli
元のパラメータから変更したのは以下の項目です。
--prefix=/usr/local/nginx
--sbin-path=/usr/local/nginx/sbin/nginx
--modules-path=/usr/local/nginx/modules
/usr/local/nginx以下にバイナリをインストールするように指定
--pid-path=/run/nginx-local.pid
pidファイルの名前を変更
--add-module=/home/ec2-user/build/ngx_brotli
ngx_brotliモジュールのパスを追加。この形式では静的リンクします。
パスは各々の環境に応じて調整してください。
configファイルやlogファイルは既存のものを使用するので設定は変更していません。
無事configureが完了すれば、ビルドとインストールです。
# make # sudo make install
これでバイナリは/usr/local/nginx/にインストールされました。
後は運用のためにsystemctl用のファイルを作成します。
/usr/lib/systemd/system/nginx.serviceをベースに/usr/lib/systemd/system/nginx-local.serviceを作成します。
nginx-local.serviceの例
[Unit] Description=The nginx HTTP and reverse proxy server After=network-online.target remote-fs.target nss-lookup.target Wants=network-online.target [Service] Type=forking PIDFile=/run/nginx-local.pid # Nginx will fail to start if /run/nginx.pid already exists but has the wrong # SELinux context. This might happen when running `nginx -t` from the cmdline. # https://bugzilla.redhat.com/show_bug.cgi?id=1268621 ExecStartPre=/usr/bin/rm -f /run/nginx-local.pid ExecStartPre=/usr/local/nginx/sbin/nginx -t ExecStart=/usr/local/nginx/sbin/nginx ExecReload=/usr/local/nginx/sbin/nginx -s reload KillSignal=SIGQUIT TimeoutStopSec=5 KillMode=mixed PrivateTmp=true [Install] WantedBy=multi-user.target
青色箇所が変更部分で、/usr/local/nginx/sbin/nginxを参照するように修正しています。
Drop-inファイルもコピーしておきます。
# cd /usr/lib/systemd/system # sudo cp -r nginx.service.d nginx-local.service.d # sudo systemctl daemon-reload
nginxのconfig修正
pidファイルを変更しているので/etc/nginx/nginx.confの設定を変更します。また、デフォルトのnginxの動的モジュール(/usr/lib64/nginx/modules/*.so)を読み込もうとしているので、/usr/local/nginx/modules/のモジュールを読み込むように設定変更します。
/etc/nginx/nginx.conf
# pidファイルを変更 #pid /run/nginx.pid; pid /run/nginx-local.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. # includeをコメントアウトして /usr/lib64/nginx/modules/*.so の読み込みをやめる #include /usr/share/nginx/modules/*.conf; # 代わりに必要なモジュールを/usr/local/nginx/modules/から読み込む load_module "/usr/local/nginx/modules/ngx_http_geoip_module.so";
あとはBrotliの設定を行います。serverセクション等にbrotliの設定を追加します。色々細かい設定はできますが、とりあえず brotli on のみで対応できます。
server {
listen 443 ssl http2;
server_name amzn2.bit-hive.com;
root /var/www/html.local;
<略>
brotli on;
}
logrotationの設定修正 (2022.3.10追記)
ログローテーションの設定も修正します。
/etc/logrotate.d/nginx
/var/log/nginx/*log {
create 0640 nginx root
weekly
rotate 24
missingok
notifempty
compress
sharedscripts
postrotate
/bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
/bin/kill -USR1 `cat /run/nginx-local.pid 2>/dev/null` 2>/dev/null || true
endscript
}
青色が追加箇所です。/run/nginx.pidへのシグナル送信処理は残していますが、空振りするだけなのでそのままにしてあります。
これを忘れると、ログローテーションでログが削除された後も、Nginxは旧ログファイルをopenしたままとなり、ディスク容量を圧迫していきます。
nginxの切り替え
以上で設定は完了です。現在動作中のnginxから新しくインストールしたものに切り替えます。
念の為、configに問題ないかチェックして、、、
# sudo /usr/local/nginx/sbin/nginx -t
nginx-local.serviceの方に切り替えます。
# sudo systemctl stop nginx.service # sudo systemctl disable nginx.service # sudo systemctl start nginx-local.service # sudo systemctl enable nginx-local.service
Brotli圧縮が問題なく動作していれば、"content-encoding: br" のHTTPレスポンスが返るようになっているはずです。
# curl -vk -H "Accept-Encoding: br" https://localhost/test.php 2>&1 | grep -i content-encoding < content-encoding: br
補足
以上でインストールは完了ですが、環境上には既存のnginx(/usr/sbin/nginx)と新たにインストールしたnginx(/usr/local/nginx/sbin/ngin)が存在するので、config testを行いたい時などは、
# /usr/local/nginx/sbin/nginx -t
のように明示的に新しいものを指定する必要があるので注意してください。古い方でconfig testを実行しても、Brotliに対応していないのでエラーになります。
元のnginxに戻したい時は、configのpidファイル設定を戻して、Brotliの設定を削除した後に nginx-local.serviceをstopしてnginx.serviceをstartすれば戻すことができます。
追記
2021.10.19
動的モジュールの読み込み設定変更を追記。
2022.3.10
logrotateの設定変更を追記。
(*1) 有料版のnginx plusでは対応されているようです。
(*2) https://github.com/bagder/libbrotli というリポジトリもありますが、こちらはdeprecatedになっているので、Googleのリポジトリの方を使いましょう。
投稿日:2021/10/11 14:45(最終更新:2022/03/10 11:51)