はじめてのISUCON7



ISUCONの過去問で毎回毎回セットアップをするのはめんどくさいのでまとめてみた。ISUCONは初心者なので色々わかり次第追記していく。

参考: https://github.com/isucon/isucon7-qualify

### AMIを自前で準備

```shell

# ubuntu@16.04 LSTで作成 sshでログイン

# nginxとmysql(RootUserで実行)
sudo su
apt-get update
apt-get install mysql-server nginx # Password設定するの勧めてくるけど全部OK

# Apllicationを設定
sudo adduser isucon
gpasswd -a isucon sudo
sudo su - isucon
cd ~
git clone https://github.com/isucon/isucon7-qualify.git isubata

# xbuild関連をInstall
sudo apt install -y git curl libreadline-dev pkg-config autoconf automake build-essential libmysqlclient-dev libssl-dev python3 python3-dev python3-venv openjdk-8-jdk-headless libxml2-dev libcurl4-openssl-dev libxslt1-dev re2c bison libbz2-dev libreadline-dev libssl-dev gettext libgettextpo-dev libicu-dev libmhash-dev libmcrypt-dev libgd-dev libtidy-dev
git clone https://github.com/tagomoris/xbuild.git

# GolangのインストールとPath設定
mkdir local
xbuild/go-install -f 1.9 /home/isucon/local/go

mkdir go
echo 'export GOROOT=/home/isucon/local/go' >> ~/.bashrc
echo 'export GOPATH=/home/isucon/go' >> ~/.bashrc
echo 'export PATH=/home/isucon/local/go/bin:$PATH' >> ~/.bashrc
echo 'export PATH=/home/isucon/go/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

# ベンチマーカーのBuild
go get github.com/constabulary/gb/...
cd /home/isucon/isubata/bench
gb vendor restore
make

# 初期データを準備
cd /home/isucon/isubata/bench
./bin/gen-initial-dataset

# DBのセットアップ
sudo /home/isucon/isubata/db/init.sh
sudo mysql
# mysqlの内部
CREATE USER isucon@'%' IDENTIFIED BY 'isucon';
GRANT ALL on *.* TO isucon@'%';
CREATE USER isucon@'localhost' IDENTIFIED BY 'isucon';
GRANT ALL on *.* TO isucon@'localhost';
exit;

# データの投入
zcat /home/isucon/isubata/bench/isucon7q-initial-dataset.sql.gz | sudo mysql isubata

# nginx設定
sudo cp /home/isucon/isubata/files/app/nginx.* /etc/nginx/sites-available
cd /etc/nginx/sites-enabled
sudo unlink default
sudo ln -s ../sites-available/nginx.conf
sudo systemctl restart nginx

# ApplicationのBuildと設定
sudo cp /home/isucon/isubata/files/app/*.service /etc/systemd/system
sudo vim /home/isucon/isubata/files/app/env.sh #DBのHostをlocalhostに書き換える
sudo cp /home/isucon/isubata/files/app/env.sh /home/isucon/
cd /home/isucon/isubata/webapp/go
make

sudo systemctl enable isubata.golang
sudo systemctl enable nginx

```


## セットアップ

### 初期設定

**SSH設定**
メンバーの分のSSHを設定する。
`/home/isucon/.ssh/authorized_keys` にPublicKeyを追記。

```shell
pbcopy < ~/.ssh/id_rsa.pub
mkdir /home/isucon/.ssh
touch /home/isucon/.ssh/authorized_keys
echo '[PUBLIC_KEY]' >> /home/isucon/.ssh/authorized_keys
```

**Processの確認**

```shell

ps auxwf # | grep Nginx
```

**Github**
GithubにPrivate Repositoryを作成してPushしておく。ついでにDeployスクリプトも作成しておく。

```sh

cd /home/isucon/isubata
rm -fr .git
git init
git add .
git commit -m "first commit"
git remote add origin [git repository]
git push -u origin master
```

```sh:deploy.sh

# ブランチを最新に
git checkout master
git fetch
git pull origin master


# build
cd /home/isucon/isubata/webapp/go
make

# confファイルと環境変数を更新
cp /home/isucon/isubata/files/app/env.sh /home/isucon/
cp /home/isucon/isubata/files/app/nginx.* /etc/nginx/sites-available
cp /home/isucon/isubata/files/db/mysqld.cnf /etc/mysql/my.cnf

#/home/isucon/isubata/files/db/mysqld.cnfのlog_slow_queriesは間違いなので気をつける。
# 参考: https://dev.mysql.com/doc/refman/5.6/ja/server-system-variables.html

# ログを削除
rm -fr /var/log/nginx/access.log
rm -fr /var/log/mysql/access.log

# Restart
systemctl restart mysql
systemctl restart nginx
systemctl restart isubata.golang
```

```sh

chmod 777 /home/isucon/isubata/deploy.sh
/home/isucon/isubata/deploy.sh
```


### 監視系ツール
ボトルネックを特定するために必須。全ての参加者が導入していると思われるので、サクッとやりたい。大きく分けて3種類。

**CPリソース監視系**
CPU, RAMなどの使用量を監視するもの。

htop

```sh

sudo apt-get install htop
```

**Nginx監視系**
Nginxなどのログを監視する。特定のPathに対して負荷がかかってい他場合の特定に役に立つ。

alp



```config:/etc/nginx/site-enabled/nginx.conf

# nginx設定のLog設定
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;
```

```sh

wget https://github.com/tkuchiki/alp/releases/download/v0.3.1/alp_linux_amd64.zip
apt install unzip
unzip alp_linux_amd64.zip

mv alp /usr/local/bin/
alp -f /var/log/nginx/access.log
```

**Application監視系**
pt-query-digest


参考: https://www.percona.com/doc/percona-toolkit/LATEST/pt-query-digest.html



```sh

wget https://www.percona.com/downloads/percona-toolkit/3.0.13/binary/debian/xenial/x86_64/percona-toolkit_3.0.13-1.xenial_amd64.deb
sudo dpkg -i percona-toolkit_3.0.13-1.xenial_amd64.deb
sudo dpkg -s pt-query-digest
```

```sh
# Pattern1
tcpdump -s 65535 -x -nn -q -tttt -i any -c 10000 port 3306 > mysql.tcp.txt
pt-query-digest --type tcpdump mysql.tcp.txt >> query-report.txt

# Pattern2
mysql
# mysqlの内部
SHOW VARIABLES;
SET GLOBAL slow_query_log_file='/var/log/mysql/access.log';
SET GLOBAL slow_query_log=1;
SET GLOBAL log-queries-not-using-indexes=1;
exit;
pt-query-digest --type genlog /var/log/mysql/access.log
```

```conf:mysqld.conf

slow_query_log_file     = /var/log/mysql/mysql-slow.log
slow_query_log
# long_query_time = 2
log-queries-not-using-indexes
```



pprof

https://github.com/pkg/profile

```golang
import (
  _ "net/http/pprof"
  "net/http"
)

func main() {
  // DefaultはCPU計測
  go http.ListenAndServe(":3000", nil)
  ...
}
```

```shell
sudo apt-get install graphviz gv
# ./bin/main = 実行ファイル cpu.pprof = pprofファイル
go tool pprof -png ./bin/main cpu.pprof > out.png
```



## ISUCONでやれそうなこと

ブログなどの情報を見ながらこれなら自分にもできそうと思ったものをまとめてみる。

**N+1問題**
基本形。どれだけ早く特定できるかが鍵。見つけるだけでなく、多く叩かれているところを優先的に潰す。重いQueryなども発見して潰せるようなら潰していきたい。

**DB周り**
Index（複合Indexまで視野に入れて）などを貼ってみる。

**Nginx設定**
静的ファイルをCacheするなど。他にもConnectionsやWorkerの数を増やすなど。色々いじれる。

**Redis導入/オンメモリ化**
重いQueryなどをメモリに載せたい。


**複数台構成**
Nginxをロードバランサーとして用いて複数台構成にする。複数にすることで発生する問題もあるので行う場合には慎重にやりたい。


**DBの非正規化**
これも多少ハードルが高い。時間の短いisuconであれば、難しいかも。サマーインターンのもので時間があるものであれば試そうかと思う。



### 参考
ISUCON7 予選
https://blog.bitjourney.com/entry/2017/11/09/101740