はじめに
速いhttpサーバが欲しかったので昔々10年くらい前にやったnginxのチューニングを再現してみようと思った。
あと、実際その時フロントエンドサーバのパフォーマンス改善用として作ったローカルプロキシも紹介する。
なお、チューニングに正解はない。これはたまたま当たったときの話。
nginxが速いっていう話
今回の構成
シンプルな感じに同一ネットワークでt3.microを使いapache1とnginx1とclient1を作った。
名前のとおりapache1はapache、nginx1はnginxを動かしているサーバ。
client1は文字通りclientとして各EC2ヘhttpリクエストを出すサーバ。
httpリクエスト(ベンチマーク)は手軽にabコマンド使う。
abコマンドはデフォルトだとKeepAlive使わないので-kオプションを使う。
ちなみに登場人物全員selinuxはdisabledにしている。
デフォルト設定(http)
まずはそれぞれデフォルトの状態で比較
デフォルトでもうnginxが速い。昔はデフォルトだとここまで差はついてなかった気がする。
# ab -c 10 -n 10000 -k http://apache1/index.html |grep "Requests per second"
Requests per second: 15801.16 [#/sec] (mean)
# ab -c 10 -n 10000 -k http://nginx1/index.html |grep "Requests per second"
Requests per second: 39050.75 [#/sec] (mean)
デフォルト設定(https)
httpsはこんな感じ。2倍くらい違う。
# ab -c 10 -n 10000 -k https://apache1/index.html |grep "Requests per second"
Requests per second: 11478.42 [#/sec] (mean)
# ab -c 10 -n 10000 -k https://nginx1/index.html |grep "Requests per second"
Requests per second: 21561.81 [#/sec] (mean)
nginxチューニング
nginx1のnginx.confを↓に差し替える。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log crit ;
pid /run/nginx.pid;
worker_rlimit_nofile 100000;
events {
worker_connections 32000;
multi_accept on;
use epoll;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log off;
sendfile on;
keepalive_timeout 30;
access_log off;
tcp_nopush on;
tcp_nodelay on;
reset_timedout_connection on;
client_body_timeout 10;
send_timeout 2;
keepalive_requests 100000;
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
types_hash_max_size 2048;
gzip on;
gzip_disable msie6;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 32 16k;
gzip_min_length 250;
gzip_types image/jpeg image/bmp image/svg+xml text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
# Settings for a TLS enabled server.
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
server_name _;
root /usr/share/nginx/html;
ssl_certificate "/etc/pki/server.crt";
ssl_certificate_key "/etc/pki/server.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers PROFILE=SYSTEM;
ssl_prefer_server_ciphers on;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
serverディレクティブはデフォルトから変更していないので任意で大丈夫。
worker_processesが1なのは意図しているもの。チューニングだけが目的であればautoでも構わない。
worker_connectionはもっと少なくてもいい実際そんなに使わない(使えない?)ので。
あと忘れずに以下の設定も入れてnginxをrestartする。
ulimit -n 100000
sysctl -w net.ipv4.ip_local_port_range='1024 65535'
echo "100000" > /proc/sys/fs/file-max
チューニング後
改めて比較するためにデフォルト時のものも記載する。
一番下が今回のチューニングした後のもの。
nginxのデフォルトから50%前後伸びている。apacheのデフォルトからは4倍。
# ab -c 10 -n 10000 -k http://apache1/index.html |grep "Requests per second"
Requests per second: 15801.16 [#/sec] (mean)
# ab -c 10 -n 10000 -k http://nginx1/index.html |grep "Requests per second"
Requests per second: 39050.75 [#/sec] (mean)
# ab -c 10 -n 10000 -k http://nginx1/index.html |grep "Requests per second"
Requests per second: 60312.90 [#/sec] (mean)
チューニング後(https)
httpsも比較してみる。
nginxのデフォルトから2倍以上伸びてしまっているがこれは出来すぎ。
ブレ幅もあるので少なくともhttpと同様にnginxのデフォルトから50%↑くらい伸びているくらいの認識。
# ab -c 10 -n 10000 -k https://apache1/index.html |grep "Requests per second"
Requests per second: 11478.42 [#/sec] (mean)
# ab -c 10 -n 10000 -k https://nginx1/index.html |grep "Requests per second"
Requests per second: 21561.81 [#/sec] (mean)
# ab -c 10 -n 10000 -k https://nginx1/index.html |grep "Requests per second"
Requests per second: 50646.50 [#/sec] (mean)
大きいファイル
比較はここまでの予定だったが気になったのでindex.htmlを40kbくらいのファイルにしてみた。
こちらもhttps。ブレ幅はやはりあるがファイルのサイズによって大きく倍率が変わることはなかった。
すくなくともファイルサイズで著しく性能が変わるチューニングではなさそうだ。
# ab -c 10 -n 10000 -k https://apache1/index.html |grep "Requests per second"
Requests per second: 6686.85 [#/sec] (mean)
# ab -c 10 -n 10000 -k https://nginx1/index.html |grep "Requests per second"
Requests per second: 14857.72 [#/sec] (mean)
# ab -c 10 -n 10000 -k https://nginx1/index.html |grep "Requests per second"
Requests per second: 18544.55 [#/sec] (mean)
チューニングの話はここまで。
ローカルプロキシ
ここからは実際にフロントエンドサーバに適用した時の話。
先ほど変更したnginx.confを以下のようにしてローカルプロキシ化する。
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log crit ;
pid /run/nginx.pid;
worker_rlimit_nofile 100000;
events {
worker_connections 32000;
multi_accept on;
use epoll;
}
http {
log_format main ' - [] "" '
' "" '
'"" ""';
access_log off;
sendfile on;
keepalive_timeout 30;
access_log off;
tcp_nopush on;
tcp_nodelay on;
reset_timedout_connection on;
client_body_timeout 10;
send_timeout 2;
keepalive_requests 100000;
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
types_hash_max_size 2048;
gzip on;
gzip_disable msie6;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 32 16k;
gzip_min_length 250;
gzip_types image/jpeg image/bmp image/svg+xml text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 8080 default_server;
server_name _;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
server {
listen 8443 ssl http2 default_server;
ssl on;
ssl_certificate /etc/pki/server.crt;
ssl_certificate_key /etc/pki/server.key;
server_name _;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers PROFILE=SYSTEM;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
upstream backend {
server 127.0.0.1:80;
keepalive 10240;
}
}
カーネル周りの分もふくめてちゃんとjobにしている↓。
nginxローカルプロキシ
- defaultTab: nodes
description: チューニングしたnginxのconfigを使ってローカルプロキシする。
executionEnabled: true
id: 7f9f2e50-52ca-4b39-bffc-ba542c1b5766
loglevel: INFO
name: nginxローカルプロキシ
nodeFilterEditable: false
nodefilters:
dispatch:
excludePrecedence: true
keepgoing: false
rankOrder: ascending
successOnEmptyNodeFilter: false
threadcount: '1'
filter: 'name: apache1'
nodesSelectedByDefault: true
plugins:
ExecutionLifecycle: null
scheduleEnabled: true
sequence:
commands:
- description: ulimit変更
exec: sudo ulimit -n 100000
- description: net.ipv4.ip_local_port_range変更
exec: sudo sysctl -w net.ipv4.ip_local_port_range='1024 65535'
- description: ' /proc/sys/fs/file-max変更'
exec: sudo sysctl fs.file-max=100000
- description: yum clean packages(なぜかyum通らないので)
exec: sudo yum clean packages
- description: nginxインストール
exec: sudo yum install -y nginx
- description: nginx.conf変更
script: |-
sudo tee /etc/nginx/nginx.conf <<EOF >/dev/null
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log crit ;
pid /run/nginx.pid;
worker_rlimit_nofile 100000;
events {
worker_connections 32000;
multi_accept on;
use epoll;
}
http {
log_format main ' - [] "" '
' "" '
'"" ""';
access_log off;
sendfile on;
keepalive_timeout 30;
access_log off;
tcp_nopush on;
tcp_nodelay on;
reset_timedout_connection on;
client_body_timeout 10;
send_timeout 2;
keepalive_requests 100000;
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
types_hash_max_size 2048;
gzip on;
gzip_disable msie6;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 32 16k;
gzip_min_length 250;
gzip_types image/jpeg image/bmp image/svg+xml text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 8080 default_server;
server_name _;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
server {
listen 8443 ssl http2 default_server;
ssl on;
ssl_certificate /etc/pki/server.crt;
ssl_certificate_key /etc/pki/server.key;
server_name _;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers PROFILE=SYSTEM;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
upstream backend {
server 127.0.0.1:80;
keepalive 10240;
}
}
EOF
- description: nginx起動
exec: sudo service nginx start
keepgoing: false
strategy: node-first
uuid: 7f9f2e50-52ca-4b39-bffc-ba542c1b5766
で、これを当時フロントエンドサーバに適用したら3割速くなった(負荷が下がった)という話。
ただ、今回の環境ではapache1のEC2へ実際に適用しても速くはならなかった。
業界的にapache側にもそれ相応のチューニングが入っていたはずなのでプロキシにも相性があるということなのだろうか。
結構頑張って試行錯誤したのだが何しろ当時のことをあまり覚えていなかった。私はまた挫折した。
(Centos8のapacheはeventMPMらしいしそのあたりも影響しているんだろうか)
あとがき
今回ローカルプロキシの再現チャレンジとしては惨敗の結果に終わってしまったが目的のものは手に入ったのでよかった。
Webサーバをいくつか作りたいのであとはこれをなんとなく使いやすい仕組みに組み込んでラクに生活したいと思っている。
ただ、多分なんにもラクではない「ansible with rundeck」を試してみるいい機会なのではないかと今なにかに囁かれている。私はラクがしたい。
あと最近アウトプットに重点を置きすぎてユーモアが欠乏している感を感じている。早急な対策が必要。