Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Isucon Best Practices

参考: https://github.com/catatsuy/memo_isucon/blob/master/README.md Best Practices Gist: https://gist.github.com/minhquang4334/26e86a84731164581ed25d3fc7fe5211

最初の作業 (40分で終わないといけない)

  • リポジトリを作成する (isucon12q)
  • ポータルサイトにログインしてsshできることを確認
  • インスタンスにログインしてsshできることを確認

インフラ担当

  • Makefileを適切に修正する
  • ssh-keygenして鍵をdeploy keyに登録する
  • コードをリポジトリにpushする
  • Go実装に切り替えてベンチマークを流す
  • 何もせずにベンチマークを流す
  • 動作しているプロセスを確認しておおよその構成を理解する (ps auxwf)
  • 必要なパッケージなどインストール (必須: alp, graphviz)
  • nginxで計測できるようにして(alpを使う)、mysqlでSlowlogを計測できるようにする (mysqldumpslowを使う)
  • 初回の結果をスコア履歴で投稿する (必須:score, top, alp, slowlog)
  • pprof 設定する
  • ハードウェアの構成を調べ (mysql versionなど, nginx http version)
  • MySQL・画像などのバックアップを開発環境用に作成
  • マニュアルを読み取って気になることを記載する

アプリケーション担当1

  • スコア履歴と作業スレ作成
  • マニュアルを読み取って、きになることを記載する
  • Web 画面を見て、機能を確認する
  • スキーマ一覧を共有
  • 各テーブルのサイズを共有
  • 各テーブルのレコード数を共有
  • initialize の動作を確認する
  • キーになる改善余地のある箇所があれば洗い出す

競争中:忘れないといけないこと

インフラ担当

  • Mysql の設定をベストプラティスに沿って設定する (my.cnf)
  • Nginx 通信は Http 1.1で設定したのか
  • Nginx のWorker Connectionsとworker_rlimit_nofileを十分ふやす (=OSの ulimitふやす)
  • DBは別のインスタンスで管理する
  • それに合わせてMakefileを修正する
  • Net 通信を設定したのか (/etc/sysctl.cnf)
  • 静的ファイルをNginx経由で通信しているか (js/css/gif/ico)
  • NginxのCPUが高すぎる場合、gzipをoffにしておく
  • 同じクエリパラメータで同じ結果を返すAPIがあれば、Nginxでちゃんとキャッシューできているか (nginx.confのSectionsでcached_endpointの例を使う)

アプリケーション担当

  • sql.DBに接続する箇所に interpolateParams=true追加する
  • jsonを使う場合、gocy-jsonを使うように修正する
  • exec.Commandをよく呼ばれている箇所はgolangのライブラリを使う
  • sql.DBに接続するConnections数と Idle Connections数を適切に設定する
  • アプリケーションログを出力しないようにする
  • mysql 8 の場合、bin log出力を止めておく

終了する時に

  • 再起動試験
  • nginx access log off
  • slow log off
  • Os drop caches

sar

sudo apt install sysstat
sudo systemctl start sysstat
# Open /etc/default/sysstat using your favorite file editor and change ENABLED="false" to ENABLED="true"

alp

wget https://github.com/tkuchiki/alp/releases/download/v1.0.9/alp_linux_amd64.zip
sudo apt-get install unzip
unzip alp_linux_amd64.zip
sudo mv alp /usr/local/bin
rm alp_linux_amd64.zip

graphviz and pprof

for pprof

sudo apt install graphviz
import _ "net/http/pprof"
go func() {
	log.Println(http.ListenAndServe("localhost:6060", nil))
}()
# run when starting benchmark
go tool pprof -http=0.0.0.0:8080 /home/isucon/webapp/go/isucholar http://localhost:6060/debug/pprof/profile
[mysqld]
bind-address = 0.0.0.0
# mysqlx-bind-address = 0.0.0.0 # for version8.0
key_buffer_size = 16M
myisam-recover-options = BACKUP
innodb_buffer_pool_size = 1G
performance_schema = off
log_error = /var/log/mysql/error.log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 0
max_binlog_size = 100M
# innodb_log_writer_threads = off # for version 8.0
innodb_doublewrite = 0 # When enabled (the default), InnoDB stores all data twice, first to the doublewrite buffer, then to the actual data files
disable-log-bin
innodb_flush_log_at_trx_commit = 2 # With a setting of 2, logs are written after each transaction commit and flushed to disk once per second. Transactions for which logs have not been flushed can be lost in a crash.
innodb_flush_method = O_DIRECT # InnoDB uses O_DIRECT (or directio() on Solaris) to open the data files, and uses fsync() to flush both the data and log files.
innodb_autoinc_lock_mode = 2 # In this lock mode, no “INSERT-like” statements use the table-level AUTO-INC lock, and multiple statements can execute at the same time

Drop OS caches

sudo su
sync; sync; sync;
echo 3 > /proc/sys/vm/drop_caches
sysctl -p
systemctl daemon-reload

Increase nofile

# /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
# 一度ログアウトしてからログインしたら、適用される

# bash
sysctl -p
systemctl daemon-reload

Net

# /etc/sysctl.conf
net.ipv4.tcp_max_tw_buckets = 2000000
net.ipv4.ip_local_port_range = 10000 65000
net.core.somaxconn = 32768
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 10
# bash

sysctl -p systemctl daemon-reload

# https://gist.github.com/south37/d4a5a8158f49e067237c17d13ecab12a
worker_processes 2; # or equal cpu core
worker_rlimit_nofile 65535; # must change os nofile before
events {
worker_connections 8192; # must change os nofile before
accept_mutex_delay 100ms;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
proxy_cache_path /dev/shm/proxy_cache keys_zone=zone1:1m max_size=1g inactive=2m;
proxy_temp_path /dev/shm/nginx_tmp;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
open_file_cache max=2000 inactive=240s;
log_format ltsv "time:$time_local"
"\thost:$remote_addr"
"\tforwardedfor:$http_x_forwarded_for"
"\treq:$request"
"\tstatus:$status"
"\tmethod:$request_method"
"\turi:$request_uri"
"\tsize:$body_bytes_sent"
"\treferer:$http_referer"
"\tua:$http_user_agent"
"\treqtime:$request_time"
"\tcache:$upstream_http_x_cache"
"\truntime:$upstream_http_x_runtime"
"\tapptime:$upstream_response_time"
"\tvhost:$host";
access_log /var/log/nginx/access.log ltsv;
#access_log off;
# client_body_temp_path /dev/shm/client_body_temp 1 2;
# keepalive_requests 10000; # increase if needed
gzip on;
gzip_http_version 1.1;
gzip_min_length 1000;
gzip_types text/css
application/font-tff
application/font-woff
application/javascript
application/json
application/octet-stream
application/vnd.ms-fontobject
application/x-www-form-urlencoded
image/gif
image/jpeg
image/png
image/svg+xml
image/vnd.microsoft.icon;
upstream local_app {
server 127.0.0.1:1323 max_fails=0 fail_timeout=0;
keepalive 30;
}
server {
# static file の配信用の root
root /home/isucon/webapp/public/; # change with specific app
# add http1.1 version to location
location /initialize {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_pass http://local_app;
}
location /api {
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_pass http://local_app;
}
location /api/cached_endpoint {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_buffering on;
proxy_cache zone1;
proxy_cache_valid 200 302 60s;
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
add_header X-Nginx-Cache $upstream_cache_status;
proxy_pass http://local_app/api/cached_endpoint;
}
location ~* \.(png|ico|gif|jpg|jpeg|css|js|svg)$ {
expires 7d;
add_header Cache-Control "public";
}
#location ~ .*\.(htm|html|css|js|jpg|png|gif|ico) {
# expires 24h;
# add_header Cache-Control public;
# open_file_cache max=100; # file descriptor などを cache
# gzip on; # cpu 使うのでメリット・デメリット見極める必要あり。gzip_static 使えるなら事前にgzip圧縮した上でそちらを使う。
# gzip_types text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/jpeg image/svg+xml image/x-icon application/octet-stream;
# gzip_disable "msie6";
# gzip_static on; # nginx configure時に --with-http_gzip_static_module 必要
# gzip_vary on;
#}
}
}

os

sudo lsof -i -P -n

sudo journalctl -xe

sudo journalctl -f | grep 'ERROR'

sar

# CPU使用率
sar -u 1 70
# ロードアベレージ
sar -q 1 70
# メモリ利用状況
sar -r 1 70
# スワップ発生状況
sar -W 1 70
# ネットワークトラフィック
sar -n ALL 1 70

alp

# recreate nginx access.log each benchmarking
sudo mv /var/log/nginx/access.log /var/log/nginx/access-old.log
sudo systemctl reload nginx

# need to change -m options
sudo alp ltsv --file=/var/log/nginx/access.log --sort sum --reverse -o count,method,uri,min,max,sum,avg,p99 -q --nosave-pos --qs-ignore-values -m "/api/course/*,  /api/announcements/*"

sudo alp ltsv --file=/var/log/nginx/access.log --sort sum --reverse -q --nosave-pos --qs-ignore-values -m "/api/course/*,  /api/announcements/*"

mysqldumpslow

# recreate mysql slow log for each benchmark test
sudo mv /var/log/mysql/mysql-slow.log /var/log/mysql/mysql-slow-old.log
sudo mysqladmin flush-logs

# run after benchmark
sudo mysqldumpslow -s t /var/log/mysql/mysql-slow.log | head -n 20
SELECT
  TABLE_NAME AS `Table`,
  ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024) AS `Size (MB)`
FROM
  information_schema.TABLES
WHERE
  TABLE_SCHEMA = "bookstore"
ORDER BY
  (DATA_LENGTH + INDEX_LENGTH)
DESC;
  • Grant isucon user
GRANT ALL PRIVILEGES ON *.* TO 'isucon'@'%' IDENTIFIED BY 'isucon' WITH GRANT OPTION

dstat

sudo apt install -y dstat
nice -n 19 dstat -tc -C 0,1 -lmdrns 10
  • disable nginx access_log off; and error_log off;
  • disable mysql slow log: slow_query_log = 0
  • disable pprof: remove listen on 6060 port in main.go
  • disable sar
  • disable mysql binlog
  • can reboot test
  • disable golang webapp log
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment