ISUCON9予選環境の再現手順

ISUCON9の予選問題を解くにあたり、本番と同じ環境を構築する手順が書かれた記事が見当たらなかったため備忘録として記述。練習でやる際の一助になれば。

構築さえできればよいという人は、webapp用サーバ3台・bench用サーバ1台・dev用サーバ1台それぞれに[Ubuntu 18.04 LTS](http://ftp.jaist.ac.jp/pub/Linux/ubuntu-releases/bionic/)をインストールし下記コピペ用コマンド一覧を`bash`に流し込んでください。

**注：**再現と銘打っていますが、厳密には以下の点が異なるため注意。理由は後述します。

- アプリケーションのWebサーバのデフォルトリッスンポートを`443`から`80`に変更。
- ベンチ測定時のDB初期化におけるタイムアウト時間を20秒から120秒に延長。

## コピペ用コマンド一覧

プロビジョニング用スクリプトは[Ansible](https://docs.ansible.com/)を使ったものが[公式リポジトリで提供されている](https://github.com/isucon/isucon9-qualify/tree/master/provisioning)ため本来は1サーバ上でのみ実行すればよいのですが、練習時は高々5台のみ設定すればよいため、プロビジョニングを早く済ませるために各サーバローカルで実行する前提でコマンドを列挙します。

### webapp用

```bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y software-properties-common
sudo apt-add-repository -y --update ppa:ansible/ansible
sudo apt update
sudo apt install -y ansible
sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
sudo apt install -y build-essential unzip
cd
git clone https://github.com/isucon/isucon9-qualify.git
cd isucon9-qualify/initial-data/
sudo make
cd ~/isucon9-qualify/webapp/public/
wget https://github.com/isucon/isucon9-qualify/releases/download/v2/initial.zip
unzip initial.zip
rm -rf upload
mv v3_initial_data upload
cd ~/isucon9-qualify/provisioning/
sed -i -e 's/\[webapp\]/\[webapp\]\nlocalhost/' inventory/hosts
sed -i -e 's/443 ssl/80/' roles/webapp.nginx/files/etc/nginx/sites-available/isucari.conf
sed -i -e 's/443 ssl/80/' roles/webapp.php/files/etc/nginx/sites-available/isucari.php.conf
sed -i -e 's/:app/:app -t 120/' roles/webapp.python/files/etc/systemd/system/isucari.python.service
sudo ansible-playbook webapp.yml -c local -i inventory/hosts
```

### bench用

```bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y software-properties-common
sudo apt-add-repository -y --update ppa:ansible/ansible
sudo apt update
sudo apt install -y ansible
sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
sudo apt install -y build-essential unzip
cd
git clone https://github.com/isucon/isucon9-qualify.git
cd isucon9-qualify/initial-data/
sudo make
wget https://github.com/isucon/isucon9-qualify/releases/download/v2/bench1.zip
unzip bench1.zip
rm -rf images
mv v3_bench1 images
cd ~/isucon9-qualify/provisioning/
sed -i -e 's/\[bench\]/\[bench\]\nlocalhost/' inventory/hosts
sed -i -e 's/20\*time\.Second/120\*time\.Second/' ~/isucon9-qualify/bench/scenario/scenario.go
sudo ansible-playbook bench.yml -c local -i inventory/hosts
```

### dev用

```bash
sudo apt update
sudo apt upgrade -y
sudo apt install -y software-properties-common
sudo apt-add-repository -y --update ppa:ansible/ansible
sudo apt update
sudo apt install -y ansible
cd ~/isucon9-qualify/provisioning/
sed -i -e 's/\[dev\]/\[dev\]\nlocalhost/' inventory/hosts
sudo ansible-playbook dev.yml -c local -i inventory/hosts
```

## デプロイ手順詳細

何をやっているのかきちんと理解したい人向けに、各コマンドの意図を記述します。練習さえできればよいという人は以下を読む必要はありません。

### Ubuntu 18.04 TLSのインストール

[Ubuntu 18.04 LTS](http://ftp.jaist.ac.jp/pub/Linux/ubuntu-releases/bionic/)をローカル/クラウドの仮想マシン5台にインストール。OpenSSHを除き追加パッケージはインストールしない。

### 初期パッケージの更新

```bash
sudo apt update
sudo apt upgrade -y
```

### Ansibleのインストール

[Ansibleのドキュメント](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-ubuntu)に従い最新版の`ansible`をインストール。プロビジョニングに使用されます。

```bash
sudo apt install -y software-properties-common
sudo apt-add-repository -y --update ppa:ansible/ansible
sudo apt update
sudo apt install -y ansible
```

### Dockerのインストール

[Dockerのドキュメント](https://docs.docker.com/engine/install/ubuntu/)に従い最新版をインストール。初期データの生成に利用されます。

```bash
sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
```

### 初期データの生成・ダウンロード

プロビジョニング前にGitリポジトリに登録されていない初期データを生成ないしダウンロードする必要があります。

まず、`make`及び`unzip`コマンドを利用するためそれぞれパッケージをインストールします。

```bash
sudo apt install -y build-essential unzip
```

次に、[ISUCON9の予選リポジトリ](https://github.com/isucon/isucon9-qualify)をcloneし、初期データを生成します。

```bash
cd
git clone https://github.com/isucon/isucon9-qualify.git
cd isucon9-qualify/initial-data/
sudo make
```

bench用サーバの場合は、そのままベンチマーク用のデータをダウンロード・展開します。

```bash
wget https://github.com/isucon/isucon9-qualify/releases/download/v2/bench1.zip
unzip bench1.zip
rm -rf images
mv v3_bench1 images
```

webapp用サーバの場合は、アプリの公開ディレクトリに移動し同様に初期データをダウンロード、展開します。

```bash
cd ~/isucon9-qualify/webapp/public/
wget https://github.com/isucon/isucon9-qualify/releases/download/v2/initial.zip
unzip initial.zip
rm -rf upload
mv v3_initial_data upload
```

### アプリケーションサーバのデプロイ

`localhost`を対象としたアプリケーションサーバのデプロイ準備をします。

```bash
cd ~/isucon9-qualify/provisioning/
sed -i -e 's/\[webapp\]/\[webapp\]\nlocalhost/' inventory/hosts
```

#### Webサーバのリッスンポートの変更

アプリケーションのWebサーバについて、デフォルトではポートが`443`になっているのですが、付属されている証明書の期限が切れているため`80`に変更します。

```bash
sed -i -e 's/443 ssl/80/' roles/webapp.nginx/files/etc/nginx/sites-available/isucari.conf
sed -i -e 's/443 ssl/80/' roles/webapp.php/files/etc/nginx/sites-available/isucari.php.conf
```

#### タイムアウト設定の変更

環境によってはDBの初期化に時間がかかり（筆者環境では約1分）Webサーバでタイムアウトしてしまう場合があります。具体的には、Pythonを使用する場合[Gunicornがデフォルト30秒でタイムアウトしてしまう](https://docs.gunicorn.org/en/latest/settings.html?highlight=timeout#timeout)ため、デーモン起動時に`-t`オプションを与えて対応しています。

```bash
sed -i -e 's/:app/:app -t 120/' roles/webapp.python/files/etc/systemd/system/isucari.python.service
```

#### 不要言語のスキップ

使用する言語が決まっている場合は使用しない言語のインストールをスキップしてもOKです。下記はPythonで開発する場合の設定ファイル編集の例。なお、`PATH`を書き出す`.bash_profile`の生成がGoのインストールスクリプトに含まれているため、`golang`は省かないことをおすすめします（わかる人はインストールスクリプトを書き換えてください）。

```bash
sed -i -r 's/(.*(perl|php|ruby|nodejs))$/#&/g' webapp.yml
```

#### デプロイの実行

諸々の設定が終わったら`ansible-pleybook`を用いてデプロイします。

```bash
sudo ansible-playbook webapp.yml -c local -i inventory/hosts
```

### ベンチマーク測定サーバのデプロイ

`localhost`を対象にベンチマーク測定サーバをデプロイします。環境によってはDBの初期化でタイムアウトが発生するため、DB初期化のタイムアウト時間を120秒に延長しています。

```bash
cd ~/isucon9-qualify/provisioning/
sed -i -e 's/\[bench\]/\[bench\]\nlocalhost/' inventory/hosts
sed -i -e 's/20\*time\.Second/120\*time\.Second/' ~/isucon9-qualify/bench/scenario/scenario.go
sudo ansible-playbook bench.yml -c local -i inventory/hosts
```

### 外部サービスサーバのデプロイ

`localhost`を対象に外部サービスサーバをデプロイします。

```bash
cd ~/isucon9-qualify/provisioning/
sed -i -e 's/\[dev\]/\[dev\]\nlocalhost/' inventory/hosts
sudo ansible-playbook dev.yml -c local -i inventory/hosts
```