isucon 秘伝のタレ集

ISUCONに参加するに辺り参考になる資料やコマンドをまとめておく

競技開始直後にやること

GitHubにprivateリポジトリを作成

競技用のprivateリポジトリを作成する

ssh

sshできることを確認する

$ ssh isucon@133.152.5.xxx

そして~/.ssh/configに

Host isu1
  HostName 133.152.5.xxx
  User isucon

を記載すると

$ ssh isu1

で入れる。 作成されたサーバは登録しておく。

各種ミドルウェアの確認

以下の実行結果をissueに貼っておく

nginx -v
go version
mysql --version
systemctl list-units --type=service

GitHubリポジトリ接続用のキーを作成

  • ssh-keygen -t rsaで鍵を作成する
  • 作成した秘密鍵を他サーバにも移動させる
  • 権限は600にする

GitHubリポジトリへの公開鍵登録

  • 作成した公開鍵をdeploy keyとしてリポジトリに登録する。その時にwrite権限のチェックボックスも入れる

各種コードをGitHubにpushする

cd ~/webapp
git init
git config --global user.email "yusuke.hamano@mixi.co.jp"
git config --global user.name "Yusuke Hamano"
git add --all
git commit -m "first commit"
# sshのURLになってること確認!!!!!!!!!!!!
git remote add origin git@github.com:yhamano0312/isucon12.git
git push origin master

newrelicインストール

logs:
  - name: syslog
    file: /var/log/syslog
  - name: mysql-error-log
    file: /var/log/mysql/error.log
  - name: nginx-error-log
    file: /var/log/nginx/error.log
  • sudo systemctl restart newrelic-infra
  • systemctl status newrelic-infra.service でサービス起動しているか確認

tool_setup.shの各種設定を変更して実行する

nginx,mysqlの初期設定

他サーバにはdeploy.shでデプロイするので1サーバでだけ設定する

nginx設定

# /etc/nginx/nginx.conf
## httpディレクティブ
### アクセスログ設定
    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;

### nginx高速化設定
sendfile        on;
tcp_nopush     on;

### APとのkeepalive設定
upstream backend {
        server 127.0.0.1:1323;
        keepalive 32;
}

## serverディレクティブ
### gzip & client_body_buffer_size設定
    gzip on;
    gzip_types text/css text/javascript application/javascript application/xjavascript application/json;
    gzip_min_length 1k;

    client_body_buffer_size 5m;

## locationディレクティブ
### APとのkeepalive設定
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      keepalive_requests 10000;
      #### proxy_passは書き換える
      proxy_pass http://backend;

設定したら設定ファイルの文法を確認し、再起動する

sudo nginx -t

sudo systemctl restart nginx

mysqlのスロークエリログ設定

# /etc/mysql/conf.d/my.cnf
[mysqld]
slow_query_log=1
slow_query_log_file=/var/log/mysql/mysql-slow.log
long_query_time=0
max_connections = 1024

mysqlのバイナリログを無効化

# /etc/mysql/mysql.conf.d/mysqld.cnf
innodb_flush_log_at_trx_commit=2
disable-log-bin=1

設定したら再起動する

sudo systemctl restart mysql

各種設定ファイルをgit管理にする

git管理ディレクトリに設定ファイルをcpして管理する。ディレクトリ名はmiddle-settingにしておく 修正したconfファイル以外で管理しておいた方が良いもの

  • ~/env.sh
git add --all
git commit -m "add middleware conf"
git push origin master

apのデフォルト設定

# main.go

# apからdbへのコネクション設定
db.SetConnMaxLifetime(10 * time.Second)
db.SetMaxIdleConns(512)
db.SetMaxOpenConns(512)

# 自動prepareの無効化
db,err:=sql.Open("mysql","isuconp:@tcp(127.0.0.1:3306)/isuconp?interpolateParams=true")

deploy.sh,analyze.shの各種設定を変更して動作することを確認する

ベンチマークを実行する

pprof導入

pprof導入

tool_setup.shでライブラリ等は導入しているので サーバ上でpprof -http=0.0.0.0:8080 http://localhost:6060/debug/pprof/profile?seconds=90と叩いておけばローカルから8080で繋がる

tips

  • 間違いとかでgit rebaseでコミットをまとめたりするとpull時にconflictしてめんどいので、rebaseで綺麗にしようとしない
  • スキーマファイルがあるので見て把握すること!
  • スキーマが複雑な場合はtblsでER図を作ってみる
  • 修正いれたら空コミットでベンチ結果を保存しておくと良いぞ。issueにも分析結果を貼っておこう
    • {"pass":true,"score":1108,"messages":[{"text":"GET /api/estate/:id: リクエストに失敗しました (タイムアウトしました)","count":8},{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":20}],"reason":"OK","language":"go"}の後に複数行のベンチ結果を貼り付けて最終行にEOMを入れる
  • ミドルウェアの設定ファイルもgit管理のリポジトリに新しくディレクトリを切って保存しておくと良さそう
    • nginx
    • mysql
    • go.service
  • APログはgolangだとSaaSに送るのにも時間がかかってしまうので/var/log/syslogで確認するようにする
  • new relicのlogsクエリでは以下のようにやるとsyslogの200以外のログを出してくれる
    • filePath:"/var/log/syslog" isucondition -"\"status\":200"
  • EXPLAINで調べる時は実際に実数値が指定されたクエリを叩いて確認する
  • nginxでの正規表現記法
  • var でmapを初期値で定義するとにるぽが起きるためmakeで初期化すること
    • var c_chairs = make(map[string][]Chair)
  • 全文検索を行いたい場合はFULLTEXT INDEXの作成とクエリ修正を行う
  • sqlxでjoinした結果をstructにbindする
  • sqlxでIN句を使う場合

後片付けチェックリスト

チェックポイント

全般

  • 大量にログ出力している部分を削る
    • debugログ
  • 各systemdのLimitNOFILE,LimitNPROCの上限を1006500に上げておく
  • 最初から複数台のサーバが用意されているが、負荷リクエストは1台に対してしか行かないので、負荷分散する必要がある
    • https://isucon.net/archives/56082639.html
      • CPU リソースが足りない問題を複数台を使うことで改善
    • 分散させるのは競技の後半にして、indexとかの修正を先にやる
    • 場合によってはDBサーバを分割させる
    • 分割させたら使ってないサービスの常時起動は切っておく
  • ベンチマーク実行時にアプリケーションに書き込まれたデータは再起動後にも取得できることとレギュレーションにあるので再起動した後にブラウザから動作確認すること
[Service]
StartLimitBurst=999

nginx

  • 画像ファイル等の静的ファイルがnginxから返すようになっているか確認する
    • nginxから入ってない場合はAP側でファイルに保存するようにして、nginxからtry_files設定で読むようにする(ISUCON本参照)
  • cacheを使って良い静的ファイルを配信するlocationディレクティブにはexpires 1dを設定する
    • cacheがうまく効いていない(304で返っていない)ようならadd_header Cache-Control: publicをつけてみる
    • nginxを複数台にしていると同じファイルでも更新時刻が異なってLast-Modifiedが異なってしまい上手くcacheが効かないのでrsyncで同期等させる(ISUCON本参照)
  • client request body is buffered to a temporary fileが発生している場合はclient_body_buffer_sizeを調整する

Go

DB

参考になるサイト集