kavo’s diary

備忘録

ISUCON練習日記2021-02-26 ISUCON10予選

ISUCON10予選問題で練習した。色々忘れないようにメモ。

generated columns追加

とりあえず入れてみる popularity_desc INTEGER AS (-popularity) NOT NULL,

2021/02/22 19:35:29 bench.go:102: verify failed
{"pass":false,"score":0,"messages":[{"text":"GET /api/estate/:id: レスポンスが不正です","count":5},{"text":"GET /api/estate/low_priced: レスポンスが不正です","count":1},{"text":"GET /api/estate/search: レスポンスが不正です","count":5},{"text":"GET /api/recommended_estate/:id: レスポンスが不正です","count":5},{"text":"POST /api/estate/nazotte: レスポンスが不正です","count":5},{"text":"登録済み物件の詳細取得に失敗しました","count":1}],"reason":"スコアが0点を下回りました","language":"go"}

単に入れただけだとダメ。アプリログにエラー出てる。

[19:52:02 isucon@ix ~]$ sudo systemctl status isuumo.go.service
● isuumo.go.service - isuumo.go
   Loaded: loaded (/etc/systemd/system/isuumo.go.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-02-22 19:35:22 UTC; 16min ago
  Process: 25939 ExecStop=/bin/kill -s QUIT $MAINPID (code=exited, status=0/SUCCESS)
 Main PID: 25940 (isuumo)
    Tasks: 5 (limit: 2333)
   CGroup: /system.slice/isuumo.go.service
           └─25940 /home/isucon/isuumo/webapp/go/isuumo

Feb 22 19:35:29 ix isuumo[25940]: {"time":"2021-02-22T19:35:29.141867316Z","level":"ERROR","prefix":"echo","file":"main.go","line":"733","message":"Database Execution error : missing destination name popularity_desc in *main.Estate"}

733行目見ると代入失敗っぽい。struct定義にPopularityDesc int64 db:"popularity_desc" json:"-"``を追加する。

   var estate Estate
    err = db.Get(&estate, "SELECT * FROM estate WHERE id = ?", id)
    if err != nil {
        if err == sql.ErrNoRows {
            c.Echo().Logger.Infof("getEstateDetail estate id %v not found", id)
            return c.NoContent(http.StatusNotFound)
        }
        c.Echo().Logger.Errorf("Database Execution error : %v", err)
        return c.NoContent(http.StatusInternalServerError)
    }

SQLコメント

GoでSQLにトレーシングコメントを埋め込んで実行する | おそらくはそれさえも平凡な日々

これやってみたいなと思ったけど、sqlx使ってるコードに大してあまり書き換えずにやる方法あるのかな?分からなかった。記事はdatabase/sql、ISUCON10予選問題はgithub.com/jmoiron/sqlx。

generated columns使って5.7でも有効なインデックスを作成

インデックス追加

,INDEX popularity_desc_id(popularity_desc,id)

追加前後のexplain f:id:kavohtn:20210225010359j:plain

これをやったあとは"score":607くらいになって、スロークエリの上位は椅子と不動産に条件色々くっつけるやつと位置情報の3種になっている。

]$ cat ~/slowq-state.log |sort
/*[11] L1031 */SELECT * FROM estate WHERE latitude <= 36.53227509710664 AND latitude >= 35.58900608516611 AND longitude <= 137.92007832283616 AND longitude >= 137.34442150965003 ORDER BY popularity_desc ASC, id ASC\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE color = 'ピンク' AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE depth >= 80 AND depth < 110 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE height < 80 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE height >= 110 AND height < 150 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE height >= 150 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE kind = 'エルゴノミクス' AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE price < 3000 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE price >= 15000 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE price >= 9000 AND price < 12000 AND stock > 0\G
/*[3] L586 */SELECT COUNT(*) FROM chair WHERE width >= 110 AND width < 150 AND stock > 0\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE door_height < 80\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE door_height >= 150\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE door_height >= 80 AND door_height < 110\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE door_width < 80\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE door_width >= 110 AND door_width < 150\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE door_width >= 150\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE rent >= 150000\G
/*[7] L909 */SELECT COUNT(*) FROM estate WHERE rent >= 50000 AND rent < 100000\G

hub createコマンドが動かなくなった件

現在パスワード認証だとダメらしい。 Unable to authorize with my GitHub password (Unathorized/Not Found) · Issue #2655 · github/hub · GitHub

ISUCON練習日記2021-02-22 ISUCON10予選

ISUCON10予選問題で練習した。色々忘れないようにメモ。

流れ

  • アプリ立てる
  • ベンチ流す
  • ベンチスクリプトで各データが自動で取れるようにしていく
    • nginx
    • app measure
    • slow query
    • netdata

他に自動化したい部分

  • app log
  • index
  • explain

インスタンス複製

  • スナップショット取得
  • 同様のインスタンスを作成
  • オプション:静的IP付与

これも自動化したい。

最新のGoインストール

最新版のGo言語をLinuxにインストールする手順をやった上で、パスが変わってなかったので以下をやる。

sudo rm /usr/bin/go 
export PATH=$PATH:/usr/local/go/bin
go version

でもsnapを使うほうが楽かも

sudo snap install --channel=1.16/stable --classic go
snap info go

slack通知でファイルを飛ばす

Slackのfiles.upload APIを使ってファイルを簡単に連携する - バナーナナブログ

Slack API | Slack

OAuth & PermissionsでBot Token Scopesにfile:write権限を追加したけどうまく行かず、User Token Scopesの方に追加したらいけた。

curl -F file=@/home/isucon/pt.log -F channels=#notify_stats_result \
 -H "Authorization: Bearer xoxp-hogehoge" https://slack.com/api/files.upload

MySQLWorkbenchで表示してみる

isucon10参加記 | カオスの坩堝

・クライアント上にツールインストール

・サーバ上で次のコマンド実行

sudo grep bind /etc/mysql/ -rni

sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
 bind_addressをコメントアウト

sudo mysql -e "grant all privileges on isuumo.* to isucon@'%' identified by 'isucon' with grant option;"

sudo systemctl restart mysql.service

インデックス追加

スロークエリで出てくる SELECT * FROM chair WHERE stock > 0 ORDER BY price ASC, id ASC LIMIT 20 用に INDEX stock_price_id (stock, price, id)を貼った場合、さらにINDEX price_id (price, id)を貼った場合の実行計画。

mysql> mysql> explain SELECT * FROM chair WHERE stock > 0 ORDER BY price ASC, id ASC LIMIT 20;
+----+-------------+-------+------------+------+----------------+------+---------+------+-------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys  | key  | key_len | ref  | rows  | filtered | Extra                       |
+----+-------------+-------+------------+------+----------------+------+---------+------+-------+----------+-----------------------------+
|  1 | SIMPLE      | chair | NULL       | ALL  | stock_price_id | NULL | NULL    | NULL | 29281 |    50.00 | Using where; Using filesort |
+----+-------------+-------+------------+------+----------------+------+---------+------+-------+----------+-----------------------------+

$ sudo mysql -e "use isuumo; explain SELECT * FROM chair WHERE stock > 0 ORDER BY price ASC, id ASC LIMIT 20;"
+----+-------------+-------+------------+-------+----------------+----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys  | key      | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+----------------+----------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | chair | NULL       | index | stock_price_id | price_id | 8       | NULL |   40 |    50.00 | Using where |
+----+-------------+-------+------------+-------+----------------+----------+---------+------+------+----------+-------------+

Using filesortはクイックソートのことらしい。 漢(オトコ)のコンピュータ道: Using filesort

ISUCON練習準備メモ

ISUCONの練習準備

チーム内部向けに、久しぶりにISUCON練習やるとき、諸々の操作を思い出すためのメモ。とりあえず上から順にやっていけば感覚が蘇る・・・はず。

クラウドコンソール
ツール
  • RLoginでログイン
  • VSCodeでRemote Development接続
  • Slack
よく使うaliasを思い出す
0 #ベンチ一式実行
br #vi bash profile
so #source bash profile
sli #サービスリスト
sre #アプリ再起動
アプリ・ベンチ・ログ
  • ブラウザでアプリにアクセス
  • ベンチマークを実行、slackで結果確認
  • ログ出てるか確認
  • ログ分析結果が出てるか確認

メモ

スコアを追っていけるダッシュボード的なのが欲しい

ISUCONのススメの商業版・同人版の違い

サークルFrattyが技術書典9で頒布した「Webサービスチューニングコンテスト ISUCONのススメ」ですが、商業版の出版が決まりました。同人版から内容が増強されており、いくつか違いがあるため本記事にてまとめます。

同人版・商業版の差異は次の表の通りです。

同人版 商業版
販売プラットフォーム 技術書典オンラインマーケット 各種プラットフォーム
媒体 電子版のみ 電子版/印刷版
価格 500円(税込) 電子版1,600円(税別) 印刷版 2,000円(税別) ※1
発売日 2020/09/12 2021/02/05
内容 詳細はこちら 左記にリソースモニタリング、N+1問題・処理並列化・サーバ間リソース分散のトピックを追加
  • ※1 それぞれのプラットフォームのセール等により価格が変化する場合があります。

(参考)目次の差分 f:id:kavohtn:20210202191905j:plain

技術書典9で技術同人誌を出した記録(時間推移、やったこと、ツール選定等)

2020/09/12-22に開催された技術書典9で、チームFrattyとしてWebサービスチューニングコンテスト ISUCONのススメ:Frattyという本を出した。 そのときの時間推移とやったこと、使ったツールや選定理由の記録。

大体3人で1ヵ月で79ページの本を作成・出版した。

技術書典終了後に記念に作った物理本:

タイムライン

  • 6/27 チームのLINEグループで技術書典でISUCONの本出してみない?と言ってみる
  • 7/3 打ち合わせ1 本のネタ出し
  • 7/10 技術書典9の日程(9/12-22)発表、改めてチーム3人で本を出そうと決める
  • 7/18 打ち合わせ2 本のネタ出し
  • 7/25 リポジトリ作成
  • 8/1 技術書典9参加フォーム公開
  • 8/2 打ち合わせ3 チーム名検討、アイコン検討、参加申し込み
  • 8/8 打ち合わせ4 ゆるい目次書き出し
  • 8/10 18:20 技術書典9参加申込審査結果[参加]メール通知
  • 8/11執筆本格開始~
  • 8/21 チームアイコン決定
  • 8/22 打ち合わせ5 目次リファクタ
  • 8/30 打ち合わせ6 タイトル決定
  • 9/4-11 追い込みレビュー
  • 9/8 夜 暫定版入稿
  • 9/9 23:36 暫定版 販売審査[承認]メール通知
  • 9/11 夜 完成版入稿
  • 9/12 0:18 完成版 販売審査[承認]メール通知
  • 9/12 10:00 技術書典9販売開始
  • 9/22 技術書典9終了

データ

  • 執筆期間:8/11~9/11の約1ヵ月
    • A5 表紙込79ページ
    • PDF
    • 電子版のみ
    • 表紙は表表紙のみ(電子版なので)
  • Gitリポジトリ
    • 消化issue:116
    • コミット数: 257
    • ビルド数: 270
    • PR数: 40

コミットグラフ:

f:id:kavohtn:20201011194516p:plain

進め方

f:id:kavohtn:20201011195613p:plain

  • discord打ち合わせでissueを棚卸しする
  • 執筆はPRであげてレビューしてもらう(ただ、実際にはPR上でレビューするというより、先にマージしてから読むことが多かった)

苦労した点

  • 正解がないもの
    • 本タイトル、チーム名、アイコン、表紙デザインなど、なかなか決まらなかった
  • 権利関係
    • 重要だが調べるのが大変
  • 環境整備・インストール
    • ビルド環境、校正ツールあたりでかなりエラー対処に時間食ったが、あまり頑張らず素直に動いてくれるものだけで良かった気がする
  • ハマったこと
    • .gitignoreのpdf除外設定の無効化をミスっていてpdfがpushできていなかった
    • VSCode上でID: constimage1 にマッチする画像が見つかりませんでした。とかエラーメッセージが出てるけどビルドしたら表示されてた

使ったツール・サービス、選択理由など

全体

技術検証

  • Google Cloud Platform
    • バウチャーがあったのと、使い慣れていたので。

文章執筆

  • Re:VIEW Template TechBooster
    • Re:VIEW Starterというテンプレートもあったが、併用はできないことが分かり、どちらを使うか迷った。Re:VIEW Starterは綺麗な装飾が魅力だったが、結局ベースバージョンが新しいRe:VIEW Templateを選んだ。
  • VSCode
    • .reのリアルタイムプレビューができる
    • prhでlintしてくれる
  • GitHub プライベートリポジトリ
    • 今は複数人で使ってもタダ。太っ腹。
  • GitHub Actions
    • 最初はRe:VIEWのビルド環境をWindowsに作ろうとしたがまずdokcerが動かずダメだった。結局はActionsでなんとかなった。pushするたびにビルドしてくれ、2分くらい経つとダウンロードできるようになる。脱稿まで270回(x2分)ビルドしたが無料枠(2000分/月)で収まった。
  • Google docs音声入力
    • 最初のドラフトとか、とりあえず喋って原文を作るのに良かった。口に出すことで整理される効用もある。

校正

prh、redpen、JustRightProなどlintツールを色々試したが、インストールの苦労のわりにそこまで効果があった感じはなかった。後半の方になるとそんな些末なことより・・・という感じになっていく。とりあえずVSCodeにprh extention for VSCodeはさっと入るのでそれくらい入れておけばいい気がする。

表紙作成

Google spreadsheetはフォント追加ができないので、Powerpointで全部やった。PDF出力も可能。画像がはみ出てても削ってくれる。

図作成

ベクタ画像が欲しくて結構苦労した。 最終的なやり方としては、作成途中では仮のpngをおいてしまい、最終工程で一気にepsを作る。画像編集の段階では編集/共有のしやすさからgoogle slideで作り、pptxでエクスポート後、パワポで該当の図形だけを選択→図として保存(png/emf)→Metafile to EPS Converterでeps作成。

今回は使わなかったがvscode拡張で編集できるdraw.io統合もある。

内容議論

広報

  • Twitter
    • チーム用アカウントを作って本の宣伝をした

アンケートフォーム

アイコン

  • hatchful

感想

  • 技術書典運営様のサポートが凄い。新規サークル向けにYouTubeで解説動画を連載したり、チャットサポートがあったり。チャットサポートレスが爆速かつ親身。ありがとうございます。
  • ISUCON運営様が快く許諾を下さったので本が出せました。ありがとうございます。

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予選より基本スペックは以下の通り。

  • 問題用 (3台)
    • CPU: 1 Core (AMD EPYC 7352)
    • Memory: 2 GiB
    • IO throughput: 100 Mbps
    • IOPS limit: 200 (Read / Write)
    • Interface: 1 Gbps
  • ベンチマーカ用 (1台)
    • CPU: 1 Core (AMD EPYC 7352)
    • Memory: 16 GiB
    • IO throughput: 100 Mbps
    • IOPS limit: 200 (Read / Write)
    • Interface: 100 Mbps

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をブラウザで開いてアプリの動作を確認(本番のような転送は不要)。 f:id:kavohtn:20200920045956p:plain

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になったときよりはまあ近い感じになったかも。 ベンチとアプリを一緒のサーバに入れてしまってる分は性能が落ちていると思われる。ネットワーク的にも影響あるかもしれない。

ISUCON10予選問題をUbuntu18.04@WSL2(Windows 10 Home)で動かすまでのメモ、トラブルシュート集

ISUCON10予選問題をUbuntu18@WSL2(Windows 10 Home)で動かしたときのメモ。末尾にトラブルシュートもまとめた。

環境

大体は2020/09/19の最新バージョン。

  • ホストOS側(Windows)
    • Windows 10 Home 2004 19041.508
    • WSL2
    • Docker desktop 2.3.0.5 (48029)
  • ゲストOS側(Ubuntu)
    • Ubuntu 18.04.5 LTS
    • Docker-compose 1.27.3
    • Go 1.15.2

ソース

github.com

ゴール

WSL2のUbuntu上でdocker-composeでアプリを立ち上げ、ブラウザでWebアプリ画面を確認し、ベンチマーカーでスコアを出す。

手順

基本的にGitHub - isucon/isucon10-qualify: ISUCON10予選の「問題の起動方法」を中心に色々設定を挟む形になっている。

WSL2インストール

WSL2はWindows 10 バージョン2004に含まれる。 Windows 10 バージョン2004の更新配布は段階的に行われているらしく、私のPCではまだ来ていなかった。 ただ、手動インストールは可能で、少し不安はあったがやってみた(結果問題なく動作した)。 大体以下を参考にすればOK。Ubuntuは本番と同じ18.04を選ぶ。

WSL2導入|WinアップデートからWSL2を既定にするまでのスクショ - Qiita

Install Docker Desktop

Windows 10 Home で WSL 2 + Docker を使う - Qiita

docker-composeをインストール

Install Docker Compose | Docker Documentationを参考にインストール。

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.27.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

再起動しないとパスが通らなかった。PowerShellからシャットダウン(自動再起動する)。

PS> wsl -t Ubuntu-18.04

またコンソールを開く。

$ docker-compose --version
docker-compose version 1.27.3, build 4092ae5d

クローン、諸々インストール、パス設定

$ git clone https://github.com/isucon/isucon10-qualify.git
$ sudo apt install -y gcc make python3-pip
$ pip3 install -r requirements.txt

golangはaptでは入れない。aptだと1.10が入ってしまうが、使用するライブラリwaytが1.13以上を必要とするため、最新版を手動で入れる。

$ wget https://golang.org/dl/go1.15.2.linux-amd64.tar.gz
$ sudo tar -xvf go1.15.2.linux-amd64.tar.gz
$ sudo mv go /usr/local

wgetが進まない場合、後述の # ホストNWとゲストNWのMTU値が一致していないとダウンロードがタイムアウトすることがある 参照。

Goの環境変数を設定する。.profileにexportを追加する。

$ vi ~/.profile
export GOROOT=/usr/local/go
export GOPATH=~
export PATH=$GOROOT/bin:/usr/local/go/bin:$GOPATH/go/bin:$PATH
$ source ~/.profile

$ go version
go version go1.15.2 linux/amd64

ライブラリインストール。

$ go get github.com/orisano/wayt
$ wayt
wayt: subcommand is required:
Available SubCommands:
...

sudoでgo,waytが実行できるようにsudoパスを設定(makeで必要)

$ sudo visudo

Defaults secure_pathにパスを書き足す。 /home/koのところは自分の設定に合わせる。

Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/ko/bin:/usr/local/go/bin"

動作確認

$ sudo go version
$ sudo wayt
wayt: subcommand is required:
Available SubCommands:
...

docker-composeのためにPATHに docker-credential-desktop.exeのあるディレクトリを追加し、~/.docker/config.jsonを一度削除しておく(これをしないと認識してくれないことがある)。

echo 'export PATH="$PATH:/mnt/c/Program Files/Docker/Docker/resources/bin:/mnt/c/ProgramData/DockerDesktop/version-bin"' >> ~/.profile
tail ~/.profile
source ~/.profile

rm ~/.docker/config.json

初期データの生成

$ cd isucon10-qualify/initial-data
$ sudo make

エラーなく終わればOK。

  • Windows側でdocker desktopがrunningになっていること。タスクトレイアイコンから状態が分かる。
  • Ubuntu側で3306ポートを使っていないこと。既存のmysqlがあったりする場合停止する。
  • ERROR: error pulling image configuration: unexpected EOFが出て止まった場合、とりあえずもう何回か試すとよい。

問題サーバーを立ち上げ

$ cd isucon10-qualify/webapp/
make isuumo/go
(そのままログtail状態へ)

アプリをブラウザで確認

Microsoft Storeから起動してもう1つ窓を開く。

f:id:kavohtn:20200920050014p:plain

IPを確認する。

$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1454
inet xxx.xxx.xxx.xxx netmask 255.255.240.0 broadcast 172.yyy.yyy.yyy
...

PowerShellで先程確認したxxx.xxx.xxx.xxxへポートフォワーディング を行う。Connection resetと出ることがあるがその後も転送は続いている。この手順は ISUCON10 予選マニュアル · GitHubによる。

PS C:\WINDOWS\system32> ssh localhost -L localhost:8080:xxx.xxx.xxx.xxx:80 -N
kex_exchange_identification: read: Connection reset
PS C:\WINDOWS\system32>

ブラウザで http://localhost:8080/ にアクセスし、ISUUMOの画面が見えればOK。 f:id:kavohtn:20200920045956p:plain

ベンチマークを実行

ビルドして実行し、スコアが出れば完了。

$ cd ~/isucon10-qualify/bench
$ sudo make
$ ./bench
...
2020/09/20 02:32:04 bench.go:110: 最終的な負荷レベル: 8
{"pass":true,"score":1310,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":40}],"reason":"OK","language":"go"}

トラブルシュート

docker desktopが起動していないとdocker-composeがFileNotFoundErrorで終了する

docker desktopを起動してもう一度やってみる。docker desktopはタスクトレイアイコンから状態が分かる。

$ sudo make
...
docker-compose -f ../webapp/docker-compose/go.yaml down -v
Traceback (most recent call last):
  File "urllib3/connectionpool.py", line 677, in urlopen
  File "urllib3/connectionpool.py", line 392, in _make_request
  File "http/client.py", line 1252, in request
  File "http/client.py", line 1298, in _send_request
  File "http/client.py", line 1247, in endheaders
  File "http/client.py", line 1026, in _send_output
  File "http/client.py", line 966, in send
  File "docker/transport/unixconn.py", line 43, in connect
FileNotFoundError: [Errno 2] No such file or directory
...

参考: During startup check that docker is running · Issue #774 · jupyterhub/repo2docker · GitHub

docker-composeがdocker.credentials.errors.InitializationErrorで終了する

PATHに docker-credential-desktop.exeのあるディレクトリを追加し、また、それでも上手く行かない場合は~/.docker/config.jsonを一度削除する。

$ sudo make
...
Traceback (most recent call last):
  File "bin/docker-compose", line 3, in <module>
  File "compose/cli/main.py", line 67, in main
  File "compose/cli/main.py", line 126, in perform_command
  File "compose/cli/main.py", line 1070, in up
  File "compose/cli/main.py", line 1066, in up
  File "compose/project.py", line 615, in up
  File "compose/service.py", line 356, in ensure_image_exists
  File "compose/service.py", line 1267, in pull
  File "compose/progress_stream.py", line 99, in get_digest_from_pull
  File "compose/service.py", line 1234, in _do_pull
  File "docker/api/image.py", line 396, in pull
  File "docker/auth.py", line 48, in get_config_header
  File "docker/auth.py", line 324, in resolve_authconfig
  File "docker/auth.py", line 235, in resolve_authconfig
  File "docker/auth.py", line 262, in _resolve_authconfig_credstore
  File "docker/auth.py", line 287, in _get_store_instance
  File "docker/credentials/store.py", line 25, in __init__
docker.credentials.errors.InitializationError: docker-credential-desktop.exe not installed or not available in PATH
[7239] Failed to execute script docker-compose
Makefile:22: recipe for target 'verification_data' failed
make: *** [verification_data] Error 255

参考: WSL2上のdocker-composeで認証エラー - roy-n-roy メモ `docker-compose up` fails in WSL 2 environment · Issue #7495 · docker/compose · GitHub

ホストNWとゲストNWのMTU値が一致していないとダウンロードがタイムアウトすることがある

ホスト側でPowerShellでMTUを確認。この場合1454。

PS C:\WINDOWS\system32> netsh interface ipv4 show subinterface

   MTU  MediaSenseState  受信バイト   送信バイト  インターフェイス
------  ---------------  -----------  ----------  -----------------
...
  1454                1  159585932    7359736  ブロードバンド接続
...

ゲスト側でMTUを確認。元がいくつだったか取りそこねた。ゲスト側も1454に合わせて、何かwgetしてみる。ダウンロードが成功すればOK.

$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1xxx
...

$ sudo ip link set eth0 mtu 1454

$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1454
...

$ wget https://golang.org/dl/go1.15.2.linux-amd64.tar.gz

参考: WSL2 fails to make HTTPS connection if Windows is using VPN · Issue #4698 · microsoft/WSL · GitHub

waytのコマンドが通らない

バイナリが存在するか、PATHにそのフォルダが入っているか、.profileに書いたパスは正確か、.profileを読み込んだか、sudo実行時はsudoにPATH等が継承されているかを確認する。

$ find / -name wayt 2>/dev/null
/home/ko/src/github.com/orisano/wayt
/home/ko/go/src/github.com/orisano/wayt
/home/ko/bin/wayt

$ env|grep GO

$ cat ~/.profile

$ source ~/.profile

$ sudo visudo

$ wayt

$ sudo wayt

go get github.com/orisano/waytが上手く行かない

-u -vをつけてログを出してみる。TLS handshake timeoutしてるようなら上記のMTUのトラブルシュートを試す。

$ go get -u -v  github.com/orisano/wayt

...
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc": https fetch: Get "https://google.golang.org/grpc?go-get=1": net/http: TLS handshake timeout
...

../../go/src/github.com/orisano/wayt/http.go:45:14: undefined: http.NewRequestWithContextが出てるようなら使っているgoのバージョンが古いので上げる。