isucon本メモ
Goへの切り替え
sudo systemctl stop isu-ruby
sudo systemctl disable isu-ruby
sudo systemctl start isu-go
sudo systemctl enable isu-go
AMI ID:
競技者
ベンチマーカー
sudo su - isucon
/home/isucon/private_isu.git/benchmarker/bin/benchmarker -u /home/isucon/private_isu.git/benchmarker/userdata -t http://<target IP>
競技者用インスタンスでnginxのログ設定
sudo vim /etc/nginx/nginx.conf
log_format json escape=json '{"time":"$time_iso8601",'
'"host":"$remote_addr",'
'"port":$remote_port,'
'"method":"$request_method",'
'"uri":"$request_uri",'
'"status":"$status",'
'"body_bytes":$body_bytes_sent,'
'"referer":"$http_referer",'
'"ua":"$http_user_agent",'
'"request_time":"$request_time",'
'"response_time":"$upstream_response_time"}';
access_log /var/log/nginx/access.log json;
設定ファイルの検証
sudo nginx -t
nginx再起動
sudo systemctl reload nginx
Warning: The unit file, source configuration file or drop-ins of nginx.service changed on disk. Run 'systemctl daemon-reload' to reload units.
warining通りに
sudo systemctl daemon-reload
を実行(あとで調べる)
aarch64用のalpをインストール
wget https://github.com/tkuchiki/alp/releases/download/v1.0.21/alp_linux_arm64.zip
unzip alp_linux_arm64.zip
sudo mv alp /usr/local/bin/
sudo chmod +x /usr/local/bin/alp
alp --version
sudo rm alp_linux_arm64.zip
sudo rm /var/log/nginx/access.log
sudo systemctl reload nginx
cat /var/log/nginx/access.log | alp json
古いアクセスログを消しておかないと、jsonフォーマット適用前のcombinedのアクセスログもalp json
の対象になるため、エラーが起きる
また、nginxをreloadしないと新しくaccess.logが出力されない。
cat /var/log/nginx/access.log | alp json
invalid character '.' after top-level value
alpのコマンド
alp json \
--sort sum -r \
-m "/posts/[0-9]+,/@\w+,/image/\d+" \
-o count,method,uri,min,avg,max,sum \
< /var/log/nginx/access.log
abコマンドインストール
sudo apt update
sudo apt install apache2-utils
which ab
ab -c 1 -n 10 http://localhost/
ログにタイムスタンプを付けてローテート
#! /bin/sh
#実行時点の日時をYYYMD-HHMMSS 形式で付与したファイル名にローテートする
sudo mv /var/10g/nginx/access.log /var/log/nginx/access.log. date `Y%m%d-%H%M%S`
#nginx にログファイルを開き直すシグナルを送信する
sudo nginx -s reopen
mysqlのスロークエリログを出力させる
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 0
sudo systemctl restart mysql
ベンチマーカー
ab -c 1 -t 30 http://localhost/
スロークエリログを出力
sudo mysqldumpslow /var/log/mysql/mysql-slow.log
スロークエリログを消して、再度出力できるようにする
sudo rm /var/log/mysql/mysql-slow.log
sudo mysqladmin flush-logs
mysqlにアクセス
sudo mysql
show database;
use isuconp;
show tables;
show create tables comments\G
rubyのワーカーの並列プロセス数を4にする
sudo vim /home/isucon/private_isu/webapp/ruby/unicorn_config.rb
systemctl restart isu-ruby
systemctl status isu-ruby
worker_processes 4
公式のk6 installだとaarch64に対応していないらしくエラーが出たので、gptに聞いて出てきたこれでできた
sudo apt update
sudo apt install -y golang
git clone https://github.com/grafana/k6.git
cd k6
make
sudo mv k6 /usr/local/bin/
vscodeの拡張機能remote sshを使ってec2内のファイルをvscodeで編集
ローカルでアクセスする際に使用した ssh -i 〜のコマンドを打つ
ssh -i ~/Downloads/private-isu.pem ubuntu@....
configでuserがrootになっているので、ubuntuに直す
k6 run --vus 1 --duration 10s ab.js
5章
スロークエリログツールのインストール
sudo apt update
sudo apt install percona-toolkit
git clone https://github.com/kazeburo/query-digester.git
cd query-digester
sudo install query-digester /usr/local/bin
sudo pt-query-digest /var/log/mysql/mysql-slow.log | tee digest_$(date+%Y%m%d%h%M).txt
インデックスを作成
alter table comments add index post_id_idx(post_id, created_at desc);
alter table comments add index idx_user_id(user_id);
6章
my.cnfのserverブロックに
innodb_flush_method=O_DIRECT
を指定しmysqlを再起動するとmysqlのbuffer poolとOSのディスクキャッシュの2重管理を防げる
fsync: ディスクキャッシュ上に書いたデータをストレージデバイスに同期させるOSの命令。ミリ秒単位で時間がかかるため、OSが行う非同期のフラッシュ操作に任せることもできるが、OSダウン等によるデータの損失に注意。
innodb_flush_log_at_trx_commit=2
とすれば、1秒ごとにログをフラッシュする。デフォルトの1ではコミットごとにログをフラッシュする。
disable-log-bin=1
sync_binlog=1000
ngx_http_gzip_static_module
を使用すると、事前にgzip圧縮したファイルをnginxから配信できるので、圧縮によるCPU消費を抑えられる。Zopfliを使ってgzip圧縮すると圧縮効率がよい。
ngx_http_gunzip_module
を使うとgzip圧縮したファイルおくだけでgzip対応/非対応どちらのリクエストにも対応できる。
アプリケーションサーバーでもgzip圧縮をしたほうがよい。少ない容量でプロキシに送れるため。
アップストリームサーバーとのコネクションをkeep aliveしたいときはHTTP/1.1を利用し、Connection headerを空文字にする
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://app;
}
upstream app {
server localhost:8080;
keepalive 32;
keepalive_requests 10000;
}
sendfile on;
tcp_nopush on;
上記はデフォルトでは無効だが、基本的には有効にしておくとよい。
sendfile
: ファイル読み込みと送信時にsendfileシステムコールを使うことで、カーネル空間からユーザー空間へのメモリコピーが発生しないので効率が上がる。ネットワークファイルシステムでは無効にする。
tcp_nopush
: sendfileを有効にしたときのみ有効にできる。送信するパケット数を減らして効率よくファイル送信ができる。
7章
インメモリのキャッシュは短めに設定して、問題があるデータをキャッシュしたとしてもすぐに消されるようにしておく。
データを保存する際に自動でシリアライズしてくれるライブラリもあるが、シリアライズ方法を変えると古いデータが読み込めなくなる可能性があるので注意
nginxのproxy_cache_lockを有効にすると、1つのリクエストがキャッシュを更新する間はproxy_cache_keyが同一のリクエストをブロックしてoriginに送らないようにできる。
nginx経由でoriginに行くこと、nginxでOriginからのHTTPレスポンスをキャッシュする設定にしているときのみ使える
Goではsingleflightを使うと同時発生した複数の呼び出しを1つにまとめられる
8章
Goのexecはシェルを経由せずに実行している
開発用の設定を変更するのもパフォーマンス向上に必要
- デバッグモードを無効化する
- その他、mysqlなど必要以上にログ出力していないかをまず確認する
- ライブラリやフレームワークのデフォルト設定が開発用になっていないか確認
httpクライアントは使いまわした方が効率が良い。コネクションはOSのプロセス単位で保持するので、マルチプロセスの場合はその数だけコネクションが保持される。
Goのhttp.Clientは使用法に注意
- http.DefaultClientはtimeoutが設定されないので、http.Clientを使う
hClient := http.Client{
Timeout: 5 * time.second,
}
-
res.Body.Close()
を忘れるとTCPコネクションが再利用されない - BodyをReadせずにCloseするとTCPコネクションが切断されるのでレスポンスボディを読み切る必要
defer res.Body.Close()
_, err = io.ReadAll(res.Body)
9章
ストレージの性質を理解することで、アプリケーションからのファイルI/Oを効率的に行える。
ファイルシステムの性能計測にはfio
コマンドを使用する
lsblk
, df
コマンド:
接続されたブロックストレージ(=ブロックデバイス)を確認する
/etc/fstab
ファイル:OSが起動するときに自動でディスクをマウントする設定を書くファイル
defaultではファイルのメタデータのみ削除し、ファイルの実体は後で消す手法
これだと実体を削除する際にディスク負荷が発生。
マウントオプションをdiscard
にしておくと、メタデータと一緒にファイルの実体も削除するので、突発的な負荷を回避できる
/dev/sda
のI/Oスケジューラは/sys/block/ada/queue/scheduler
で確認できる
top -1
コマンド: CPU利用率を見れる。1をオプションにつけると、各CPUコアの利用率が見られる。
topで表示される値
-
us
: ユーザ空間(システムコールを利用するLinux OS上のアプリケーション動作部分)におけるCPU利用率。デプロイされているWebアプリケーションがCPUを利用している際に上昇する値 -
sy
: カーネル空間(Linux Kernel内の処理)におけるCPU利用率。forkやコンテキストスイッチが多く発生しているときに上昇。 -
ni
: nice値。どのプロセスを優先して切り替える(コンテキストスイッチ)のかの優先度。19→ー20になるにつれて優先度があがる。nice -n19 XXX
のように-nの後に数字を書くことで処理XXXの優先度を変更できる。-
renice
コマンド: 実行中のプロセスのnice値を変更できる -
ionice
コマンド:I/Oスケジューラの優先度を変更できる。ログファイルの圧縮・削除といった大きなI/O処理を行うときに優先度を下げることがある。
-
-
id
: 利用されていないidleのCPU -
wa
: I/O処理待ちのプロセスのCPU利用率 -
hi
: Hardware Interrupt。ハードウェア割り込みプロセスの利用率 -
si
: Soft Interrupt。ソフト割り込みプロセスの利用率 -
st
: Steal。パブリッククラウドなどの仮想化された環境のLinux上で利用されているCPU利用率。
ulimit
コマンド: User limit。プロセスが利用できるリソースの制限を設定する概念。開けるファイル数の上限やCPU稼働時間などの制限を設定できる。/proc/PID/limits
のファイルを見ればわかる。制限を変更することもできる。mysqlの場合は/etc/systemd/sysytem/mysql.service.d/limits.conf
というフォルダとファイルを新規作成し、そこで値を設定することで変更できる。
LinuxのKernelパラメータ
-
net.core.somaxconn
: socket max connection。接続要求のキューを貯めるbacklogのサイズを決める設定。sysctl net.core.comaxconn
で確認、sudo sysctl -w net.core.comaxconn=8192
で一時的な変更ができる。永続な変更も行える -
net.ipv4.ip_local_port_range
:ポート番号の範囲の設定。コネクションが過多になるとデフォルトのポート番号では足りないことがある。IANAでは0~1023番ポートが「System Ports」、1024~49151が「User Ports」、49152 ~65535が 「Dynamic and/ or Private Ports」とされており、動的なポート(Ephemeral Ports)としては1024以降が適切。sysctl
コマンドで変更ができる。 -
同一ホスト内の別プロセスに接続する際は、ポート番号を指定せずにUnix domain socketをnginxで使うように設定するとパフォーマンスがあがる。
listen unix:/var/run/nginx.sock
MTU(Maximum Transmission Unit): ネットワークインターフェースから送信できる最大送信サイズ。1500 byteが基本だが、9000 byteまで拡大できる。ip link
コマンドで設定を確認・一時変更できる。udev
というLinux Kernelにおけるデバイス管理ツールを使用すると、恒久的に設定を変更できる。
MTUはパケットをやり取りするクライアント/サーバー、経由するネットワーク機器すべてにおいて同一の設定がされていないと意味がないため、効率化に寄与することはそうそうない。