Help us understand the problem. What is going on with this article?

ISUCON9予選をDockerだけで動かす

isucon/isucon9-qualifyを、ローカル環境にDockerを使って構築します。MySQLやGoはインストールすること無くDockerだけで動かします。アプリケーションにはRubyを利用します。(Rubyもインストールする必要はありません)

完成品は下記です。
https://github.com/s2terminal/isucon9-qualify-docker

環境

構築手順

まず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を配置します。

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を配置します。

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
webapp/sql/conf/my.cnf
[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に下記を追記します。

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/ディレクトリにダウンロード済ファイルを放り込んでいましたので、お好みで。

.gitignore
datadir/
tmp/

ベンチマーカーのビルド

ベンチマーカーの実行バイナリはリポジトリに含まれておらず、Goでビルドする必要があります。ビルド用のコンテナを準備するためにdocker-compose.ymlに追記します。

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実行をスキップするように修正してからベンチマークを動かすことで、やっとスコアが付きました。

webapp/sql/init.sh
 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

suzuki_sh
Windowsでコンピュータの世界が広がります
https://www.s2terminal.com
finergy-a-tm
大阪府大阪市北区角田町8番1号 梅田阪急ビル オフィスタワー35F
https://finergy.a-tm.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした