isucon/isucon9-qualifyを、ローカル環境にDockerを使って構築します。MySQLやGoはインストールすること無くDockerだけで動かします。アプリケーションにはRubyを利用します。(Rubyもインストールする必要はありません)
完成品は下記です。
https://github.com/s2terminal/isucon9-qualify-docker
環境
- ASUS Chromebook Flip C434TA (Core i5モデル)
- ChromeOS Crostini (Linux) Debian 9.11
- Docker 19.03.4
構築手順
まずgit clone
します。
$ git clone git@github.com:isucon/isucon9-qualify.git
$ cd isucon9-qualify
下準備
リポジトリに含まれていない初期画像ファイルのダウンロードが必要なのですが、1.5GBあるので先に落としておきます。
$ wget https://github.com/isucon/isucon9-qualify/releases/download/v2/bench1.zip
$ wget https://github.com/isucon/isucon9-qualify/releases/download/v2/initial.zip
ダウンロードできたら、解凍してそれぞれ所定のディレクトリに移動します。
$ unzip bench1.zip
$ unzip initial.zip
$ mv v3_initial_data webapp/public/upload
$ mv v3_bench1 initial-data/images
初期データの準備をします。中ではperlのDockerコンテナを使ってSQLを生成するなどしているようです。
$ cd initial-data
$ make
Ruby環境準備
Rubyアプリケーションを実行する準備をします。webapp/ruby
ディレクトリにDockerfileを配置します。
FROM ruby:2.7.1
WORKDIR /app
RUN apt-get update \
&& apt-get install -y \
mariadb-client \
less
COPY ./Gemfile ./
COPY ./Gemfile.lock ./
RUN bundle install
CMD /bin/sh -c "while sleep 1000; do :; done"
mariadb-client
は必須です。他にless
だけ入れていますが、追加で欲しい物があれば随時入れておきます。ISUCON用ということは中に入ってなんやかんやしたいと思うので、キャッシュを消したりはせずに置いておきます。
CMD /bin/sh -c "while sleep 1000; do :; done"
で、コンテナが立ち上がりっぱなしになるようにしています。
プロジェクトルートにdocker-compose.yml
を配置します。
version: '3'
services:
ruby:
build: ./webapp/ruby
volumes:
- .:/app
ports:
- 127.0.0.1:5555:5555
- 127.0.0.1:7000:7000
- 127.0.0.1:8000:8000
environment:
- MYSQL_HOST=db
- MYSQL_USER=root
- MYSQL_PASS=secret
MySQL環境準備
データベースの準備をします。DBだけDockerを使ってISUCON9の環境構築した - teru_0x01.logを参考に、webapp/sql/conf/my.cnf
を作成します。
$ mkdir webapp/sql/conf
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
secure-file-priv= NULL
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
slow_query_log = 1
long_query_time = 5
[client]
default-character-set = utf8mb4
# Custom config should go here
!includedir /etc/mysql/conf.d/
docker-compose.yml
に下記を追記します。
db:
image: mysql:5.7
volumes:
- ./webapp/sql:/docker-entrypoint-initdb.d
- ./webapp/sql/conf:/etc/mysql/conf.d
- ./datadir:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_HOST=127.0.0.1
- MYSQL_USER=root
- MYSQL_PASS=secret
ports:
- 127.0.0.1:3306:3306
アプリケーションの起動
Rubyコンテナに入って下記実行します。
$ docker-compose exec ruby /bin/bash
初回起動時やDBをリセットした時などは、DBの初期化処理を実行します。
# ./webapp/sql/init.sh
isucon9-qualifyのプロビジョニングファイルのサービス定義ディレクトリを参考に、アプリケーションを起動します。
# cd webapp/ruby && bundle exec rackup --port 8000 --host 0.0.0.0
http://localhost:8000/ にアクセスして、ISUCARIが表示されたら成功です。
もし下記のようなエラー画面が表示された場合はRubyのエラーログを確認します。(自分の場合はDBの接続文字列が間違っていました)
git管理する場合は、MySQLのデータマウントに使うディレクトリを.gitignore
に入れておきます。ついでに私はtmp/
ディレクトリにダウンロード済ファイルを放り込んでいましたので、お好みで。
datadir/
tmp/
ベンチマーカーのビルド
ベンチマーカーの実行バイナリはリポジトリに含まれておらず、Goでビルドする必要があります。ビルド用のコンテナを準備するためにdocker-compose.yml
に追記します。
golang:
image: golang:1.14.6
volumes:
- .:/app
working_dir: /app
command: /bin/sh -c "while sleep 1000; do :; done"
ベンチマーカーのMakefile
はプロジェクトルートにありますので、下記コマンドでビルドします。
$ docker-compose exec golang make
他にもGoに依存している所があるかなと思ってdocker-compose.yml
に記述して立ち上げておきましたが、このベンチマーカーのビルドの1回しか使いませんでした。1回$ docker run
するだけで良かったかもしれません。
ベンチマーク実行
rackを起動した状態で、Rubyのコンテナ内で$ ./bin/benchmarker
すればベンチマークを実行できます。しかし今回使った環境(Chromebook C434TA Core i5 Crostini + Docker)では重すぎるのか、スコア記録前の/initialize
へのPOSTでタイムアウトしてしまいスコアが付きませんでした。
調べてみると、init.sh
のSQLを流すのに1分以上かかっているのが原因のようです。
$ time ./webapp/sql/init.sh
(中略)
real 1m2.555s
user 0m1.872s
sys 0m0.536s
ISUCON9予選マニュアルには POST /initialize が20秒以内と記載されており、ベンチマーカーの実装も20秒でタイムアウトするようになっているようです。
これは恐らくルール違反なのですが、init.sh
のSQLを先に手動で流した後、SQL実行をスキップするように修正してからベンチマークを動かすことで、やっとスコアが付きました。
export LANG="C.UTF-8"
cd $CURRENT_DIR
-cat 01_schema.sql 02_categories.sql initial.sql | mysql --defaults-file=/dev/null -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER $MYSQL_DBNAME
+# cat 01_schema.sql 02_categories.sql initial.sql | mysql --defaults-file=/dev/null -h $MYSQL_HOST -P $MYSQL_PORT -u $MYSQL_USER $MYSQL_DBNAME
# ./bin/benchmarker
(中略)
{"pass":true,"score":810,"campaign":0,"language":"ruby","messages":[]}
スコアは810点でした。Macbook Proだと3,000ぐらい出るようですが、Chromebook上のLinux仮想環境と、その上にDockerを載せているという二重のオーバーヘッドがパフォーマンスを下げているのでしょうか。
参考
完成品は下記です。
https://github.com/s2terminal/isucon9-qualify-docker/pull/1