Dockerに割り当てるリソースを制限して、よりリアルにローカルでISUCON10の予選問題を実行する

2020年9月12日に開催されたISUCON10の予選の問題が公開されました。予選のときとは異なり、公開版は全ての要素(チューニング対象のAPIサーバ、MySQL、ベンチマーカー)がDockerコンテナ化されているので、READMEに書いてある順序でmakeを実行すればベンチマークを実行して点数を出すことができます。

実行するのに必要なツール類は下記です。

  • Docker (Compose含む)
  • pip3
  • go
  • make

github.com

実行してみる

手元の環境では偶然TCP1323が使われていてAPIサーバにつながらない事象がありましたが、それ以外はすんなりと点数を出すところまでいきました。

ベンチマーカーの結果を見ると1500点前後が出ています。これは上位20%のスコアで、実際の予選環境での初期スコアが400前後だったことを考えると、かなり高くなってしまうことがわかります。 最近のISUCONのベンチマーカーは負荷を適応的に上げていく機構が盛り込まれていることがよくありますが、負荷レベルが9まで上がっていることも確認できます。

2020/09/19 02:23:50 bench.go:110: 最終的な負荷レベル: 9
{"pass":true,"score":1560,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":55}],"reason":"OK","language":"go"}

公式の講評にもこのように書かれているとおり、本来はDBがネックになる問題なのですが、DBが複数のコアを使っているので性能が上がってしまっていることがわかります。

DB の CPU が100%に張り付くケースが多いです。しかも1台分のスペックはそこまで強くありません ISUCON10 予選問題の解説と講評 : ISUCON公式Blog

$ docker stats
CONTAINER ID        NAME                          CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
419297d07565        docker-compose_api-server_1   46.38%              36.68MiB / 25GiB    0.14%               209MB / 202MB       0B / 0B             23
5cf91f529276        docker-compose_mysql_1        343.88%             256.6MiB / 25GiB    1.00%               58.4MB / 196MB      0B / 0B             38

f:id:translucens:20200919023832p:plain:w480
初期状態では全てのコアが使われて性能問題を札束で解決している状態になる

Docker Composeにリソース制限の設定を追加する

Dockerコンテナに割り当てるリソースを少なくしてリアリティのあるスコアを目指します。

配布されているDocker ComposeのYAMLはv3形式なので、 resources によるリソースの制限はSwarmモード専用なのかと思いつつ調べていたら、 --compatibility フラグを付けることでv2の動作を再現できることが分かりました。

webapp/docker-compose/go.yamlmysqlapi-server にそれぞれ下記を追記します。

    deploy:
      resources:
        limits:
          cpus: 1
          memory: 2048M
        reservations:
          cpus: 1
          memory: 128M

webapp/Makefile に測定対象のコンテナを起動するためのコマンドが記載されているので、 --compatibility フラグを追記して make isuumo/go を実行します。

ストレージのIOPS性能は高いままになってしまいますが、今回はIOがネックになっているようには感じられなかったのでそのままにします。

リソース制限後のベンチ実行、結果

リソースの制限が効いた状態で再度ベンチマーカーを走らせると、DBが1コアを使い切るという実際の状況を再現できました。 またスコアも実際の初期スコアに近い391、負荷レベルも1のままになりました。

CONTAINER ID        NAME                          CPU %               MEM USAGE / LIMIT   MEM %               NET I/O             BLOCK I/O           PIDS
31b849a0bd49        docker-compose_api-server_1   27.50%              17.33MiB / 2GiB     0.85%               41.4MB / 70MB       0B / 0B             21
4e5ec3c8d814        docker-compose_mysql_1        101.30%             255.7MiB / 2GiB     12.49%              32.5MB / 39.7MB     0B / 0B             39

2020/09/19 02:19:59 bench.go:110: 最終的な負荷レベル: 1
{"pass":true,"score":391,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":5}],"reason":"OK","language":"go"}

予選当日は下記のようにテーブル追加作戦が失敗してしまったので、どこでベンチマーカーが通らなくなってしまったのか検証してみたいと思います。

featureのTABLE追加はレスポンス不正の不具合が取れず断念 ISUCON10に参加した #isucon - つばくろぐ @takamii228

2020年9月22日追記 上記の不具合は追加テーブル用のデータを作成するときに、初期データではなくベンチマーク実行後のデータから加工したので、ベンチマーカーを再実行すると一意制約に違反してデータが追加できなくなっていたのが原因でした(´・ω・`)