ISUCON10予選問題の疑似環境をGCP上に作る(初期スコア400前後)
概要
ISUCON10予選をなるべく当時と同じように解きたいので、疑似環境を作るためのメモ。
初期スコアが456だったので、近い値が出るようにしたい。
結論
N1の1vCPU/2GBメモリ、ゾーン永続ディスク20GBで作ったインスタンスでは(ベンチマーカー内包で測って)初期スコア400前後になる。 cloudshellですぐ起動したい場合以下のスクリプトでインスタンス作成可能。
gcloud beta compute instances create isucon10q --zone=asia-northeast1-b --machine-type=custom-1-2048 --subnet=default --network-tier=PREMIUM --maintenance-policy=MIGRATE --scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/trace.append --tags=http-server --image=ubuntu-1804-bionic-v20200923 --image-project=ubuntu-os-cloud --boot-disk-size=20GB --boot-disk-type=pd-standard --boot-disk-device-name=isucon10q --no-shielded-secure-boot --shielded-vtpm --shielded-integrity-monitoring --reservation-affinity=any
サーバスペック
GitHub - isucon/isucon10-qualify: ISUCON10予選より基本スペックは以下の通り。
OSはUbuntu 18.04.5 LTSだった。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.5 LTS Release: 18.04 Codename: bionic
GCPで再現する
スペック選定
CPU/Memory/Interface
GCPのマシンタイプ | Compute Engine ドキュメント | Google Cloudから似ているスペックの構成を探すと、 共有ではないコアで1vCPUが選べるのが汎用(N1)だけのようである。 汎用(N1)のカスタムで1vCPU2GBにできる。 カスタム時のネットワーク下り(外向き)帯域幅(Gbps)は書いてない気がするが、近いn1-standard-1の帯域幅は2Gbps。
#インスタンス自体が1コアでなくても、maxcpus=1を指定する方法もあるらしい。
#「vCPU」と「Core」の対応関係が調べてもいまいちよくわからない
IO throughput/IOPS
Storage options | Compute Engine Documentation | Google Cloudによると、ゾーン永続ディスクの最大持続 IOPS、最大持続スループット(MB/秒)は
ゾーンバランス永続ディスクの最大持続 IOPS、最大持続スループット(MB/秒)は
らしい。とはいっても、この最大持続 IOPS、最大持続スループットが本番インスタンスのIO throughput/IOPS制限と近い意味の数値なのかは不明。
IOPS(Input/Output Per Second)とは - IT用語辞典 e-Words
装置の特性により、読み込み(リード)か書き込み(ライト)か、シーケンシャルアクセス(連続した領域にアクセス)かランダムアクセス(飛び飛びに様々な領域にアクセス)か、転送するデータの量がどれくらいかによって1回の動作に要する時間が異なる。
このため、実用上は「4KBランダムライトIOPS」のように計測条件を明示することが多い。この例では、4キロバイトのデータを装置内の不連続な箇所に飛び飛びに書き込む動作を行った際のIOPSを表している。
例1:ゾーン永続ディスク20GB
カタログ上以下のはず。
fioを使ってストレージの性能を計測してみた - Qiitaを参考にfioというツールで測ってみる。fioのドキュメントは1. fio - Flexible I/O tester rev. 3.23 — fio 3.23-28-g7064-dirty documentation
IOPS/スループット測定目的×シーケンシャル/ランダム×R/Wの8パターン。
sudo apt update sudo apt-get install -y fio head -c 10G /dev/urandom > /tmp/test2g ls -l /tmp/test2g fio -filename=/tmp/test2g -direct=1 -rw=read -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 > 1--4k-seq--r.log fio -filename=/tmp/test2g -direct=1 -rw=write -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 > 2--4k-seq--w.log fio -filename=/tmp/test2g -direct=1 -rw=randread -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 > 3--4k-rand-r.log fio -filename=/tmp/test2g -direct=1 -rw=randwrite -bs=4k -size=2G -numjobs=64 -runtime=10 -group_reporting -name=file1 > 4--4k-rand-w.log fio -filename=/tmp/test2g -direct=1 -rw=read -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1 > 5-32m-seq--r.log fio -filename=/tmp/test2g -direct=1 -rw=write -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1 > 6-32m-seq--w.log fio -filename=/tmp/test2g -direct=1 -rw=randread -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1 > 7-32m-rand-r.log fio -filename=/tmp/test2g -direct=1 -rw=randwrite -bs=32m -size=2G -numjobs=16 -runtime=10 -group_reporting -name=file1 > 8-32m-rand-w.log $ grep IOPS * 1--4k-seq--r.log: read: IOPS=15.0k, BW=58.7MiB/s (61.5MB/s)(3521MiB/60018msec) 2--4k-seq--w.log: write: IOPS=15.1k, BW=59.1MiB/s (61.9MB/s)(3546MiB/60017msec) 3--4k-rand-r.log: read: IOPS=145, BW=600KiB/s (614kB/s)(36.2MiB/61714msec) 4--4k-rand-w.log: write: IOPS=296, BW=1201KiB/s (1230kB/s)(71.4MiB/60849msec) 5-32m-seq--r.log: read: IOPS=3, BW=111MiB/s (116MB/s)(6688MiB/60238msec) 6-32m-seq--w.log: write: IOPS=2, BW=71.9MiB/s (75.4MB/s)(4320MiB/60096msec) 7-32m-rand-r.log: read: IOPS=2, BW=79.9MiB/s (83.8MB/s)(4960MiB/62094msec) 8-32m-rand-w.log: write: IOPS=0, BW=11.9MiB/s (12.4MB/s)(736MiB/62051msec)
値が多いが、おそらくカタログのIOPSと比べる対象は4KBランダムリードライトの3,4だと思う。R/Wで145/296とカタログから想定した値の10倍くらいになっている。 スループットは32Mのシーケンシャルなのかランダムなのかわからないけどシーケンシャルだとすれば、R/Wで111/71.9MiB/sとカタログから想定した値の30~40倍くらいか。
例2:ゾーンバランス永続ディスク30GB
カタログ上以下のはず。
同様に計測する。
$ grep IOPS * 1--4k-seq--r.log: read: IOPS=445, BW=1797KiB/s (1840kB/s)(107MiB/60851msec) 2--4k-seq--w.log: write: IOPS=294, BW=1196KiB/s (1224kB/s)(71.1MiB/60853msec) 3--4k-rand-r.log: read: IOPS=295, BW=1197KiB/s (1226kB/s)(71.2MiB/60852msec) 4--4k-rand-w.log: write: IOPS=294, BW=1196KiB/s (1224kB/s)(71.0MiB/60853msec) 5-32m-seq--r.log: read: IOPS=0, BW=16.9MiB/s (17.7MB/s)(1024MiB/60527msec) 6-32m-seq--w.log: write: IOPS=0, BW=15.6MiB/s (16.4MB/s)(960MiB/61391msec) 7-32m-rand-r.log: read: IOPS=0, BW=16.9MiB/s (17.7MB/s)(1024MiB/60515msec) 8-32m-rand-w.log: write: IOPS=0, BW=15.6MiB/s (16.4MB/s)(960MiB/61404msec)
4KBランダムリードライトの3,4をみるとR/Wで295/294とカタログから想定した値の1.5倍くらいになっている。 32Mランダムリードライトのスループットは、R/Wで16.9/15.6MiB/sとカタログから想定した値の2倍くらい。 RWで揃うのは想定どおりだけど、普通の永続ディスクよりもスループットが下回ったのが想定外だった。単にfio実行パラメータが適切ではないからな気もする。
まあ結局よくわからないのでゾーン永続ディスク20GBで試してスコアを見ることにする。
構築
公式のansibleを使ってみる。普通にaptインストールすると古い2.5.1が入ってしまうので以下のようにする。
$ sudo apt update $ sudo apt install software-properties-common $ sudo apt-add-repository --yes --update ppa:ansible/ansible $ sudo apt install -y ansible $ ansible --version ansible 2.9.13 config file = /etc/ansible/ansible.cfg configured module search path = [u'/home/hogehoge/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python2.7/dist-packages/ansible executable location = /usr/bin/ansible python version = 2.7.17 (default, Jul 20 2020, 15:37:01) [GCC 7.5.0]
Ansible のインストール — Ansible Documentationより
127.0.0.1宛で1回試してみるが、ssh鍵設定していなかったので怒られる。
git clone https://github.com/isucon/isucon10-qualify.git cd isucon10-qualify/provisioning/ansible vi inventory/hosts (127.0.0.1記載) ansible-playbook allinone.yaml -i inventory/hosts fatal: [127.0.0.1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '127.0.0.1' (ECDSA) to the list of known hosts.\r\nno such identity: /home/hogehoge/.ssh/id_rsa: No such file or directory\r\nhogehoge@127.0.0.1: Permission denied (publickey).", "unreachable": true}
鍵追加
touch .ssh/authorized_keys cat isucon10.pub >> .ssh/authorized_keys cat .ssh/authorized_keys chmod 600 .ssh/authorized_keys ls -l .ssh/authorized_keys cp isu10 .ssh/id_rsa chmod 600 .ssh/id_rsa ls .ssh/id_rsa -l #疎通 ssh 34.84.152.xx -i .ssh/id_rsa -l hogehoge
再実行してfailなく完了。 やった後に気づいたけどphpとかrubyのインストールが時間食うので使わないやつは外したほうがよい。
$ ansible-playbook allinone.yaml -i inventory/hosts ... PLAY RECAP ******************************************************************************************************** 127.0.0.1 : ok=76 changed=72 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 Saturday 26 September 2020 22:33:40 +0000 (0:00:05.664) 1:05:45.373 **** =============================================================================== web-rust : Build Web Application Rust ------------------------------------------------------------------- 1215.58s langs : Install PHP -------------------------------------------------------------------------------------- 986.44s langs : Install Ruby v2.7.1 ------------------------------------------------------------------------------ 452.84s langs : Install perl v5.32.0 ----------------------------------------------------------------------------- 399.46s langs : Install python v3.8.5 ---------------------------------------------------------------------------- 256.83s web-perl : Install isuumo.perl Pacakges ------------------------------------------------------------------ 141.91s common : Install Package(Build) --------------------------------------------------------------------------- 74.59s web-bootstrap : Build frontend application ---------------------------------------------------------------- 51.67s web-ruby : Build Web Application ruby --------------------------------------------------------------------- 38.82s web-bootstrap : Install Package(MYSQL) -------------------------------------------------------------------- 34.19s web-bootstrap : Make initial data ------------------------------------------------------------------------- 31.82s web-bootstrap : Install frontend packages ----------------------------------------------------------------- 29.90s bench : Snapshot isucon10 --------------------------------------------------------------------------------- 25.66s web-php : Install isuumo.php Pacakges --------------------------------------------------------------------- 20.89s web-bootstrap : Export frontend application --------------------------------------------------------------- 20.45s langs : Install Rust -------------------------------------------------------------------------------------- 18.64s web-deno : Build Web Application deno --------------------------------------------------------------------- 18.59s web-go : Build Web Application Go ------------------------------------------------------------------------- 14.70s langs : Install Go 1.14.7 --------------------------------------------------------------------------------- 10.38s web-python : Install isuumo.python Pacakges ---------------------------------------------------------------- 9.95s
動作確認
インスタンスの外部IPをブラウザで開いてアプリの動作を確認(本番のような転送は不要)。
isuconユーザに切り替えてベンチ実行。
su isucon #passはisucon cd isuumo/bench $ ./bench --target-url http://localhost 2020/09/26 23:13:24 bench.go:86: === initialize === 2020/09/26 23:13:27 bench.go:98: === verify === 2020/09/26 23:13:28 bench.go:108: === validation === 2020/09/26 23:14:12 load.go:181: 負荷レベルが上昇しました。 2020/09/26 23:14:26 fails.go:105: [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:367 message("POST /api/estate/nazotte: リクエストに失敗しました") [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136 code(error timeout) *url.Error("Post \"http://localhost/api/estate/nazotte\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)") *http.httpError("context deadline exceeded (Client.Timeout exceeded while awaiting headers)") [CallStack] [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136 [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:361 [scenario.estateNazotteSearchScenario] /home/isucon/isuumo/bench/scenario/estateNazotteSearchScenario.go:214 [scenario.runEstateNazotteSearchWorker] /home/isucon/isuumo/bench/scenario/load.go:100 [runtime.goexit] /home/isucon/local/go/src/runtime/asm_amd64.s:1373 2020/09/26 23:14:28 bench.go:110: 最終的な負荷レベル: 1 {"pass":true,"score":406,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":1}],"reason":"OK","language":"go"}
成功。もう少しベンチを実行してブレを確認。
2020/09/26 23:17:05 bench.go:110: 最終的な負荷レベル: 1 {"pass":true,"score":389,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":5}],"reason":"OK","language":"go"} {"pass":true,"score":398,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":6}],"reason":"OK","language":"go"} {"pass":true,"score":389,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":3}],"reason":"OK","language":"go"}
大体400前後になった。本番の初回実行が456だったので少し低いが、WSL2上に構築して初期で1310になったときよりはまあ近い感じになったかも。 ベンチとアプリを一緒のサーバに入れてしまってる分は性能が落ちていると思われる。ネットワーク的にも影響あるかもしれない。