びくんびくんしながらコードを書く。

いしきひくい系エンジニアのらくがき帳

さくらのクラウドにisucon10予選問題環境を構築する

こんにちは、この度ISUCONに初参加することになり、Memberで過去問を説いてみようということで色々やっています。
そんな中、さくらのクラウドでかんたんに過去問構築できるぞ!というお話を聞き、やってみました。

やってみると意外と詰まるところがあったので、つまりポイントなど交えながら構築記事を書いてみようと思います。

今回は、isucon10の予選問題の構築記事です。

前置きとかその他の情報

過去問集は下記にまとまっています。

isucon.net

ISUCON11の予選問題はさくらのナレッジで公開されていますので、11の予選環境はこちらで組めると思います。

ISUCON過去問題の環境を「さくらのクラウド」で構築する | さくらのナレッジ

さくらのクラウドでISUCON10予選問題の環境を構築する

1. さくらのクラウドのアカウント作成などの前処理

まずはアカウントを作らなければ始まりません。
アカウント作成の中でも電話通話認証による本人確認があるため携帯電話などで電話通話できる状態でないとアカウント開設できないので注意しましょう。
※ちょうどauの通信障害が起きていて、これができずソフトバンクのサブ回線を使って認証しました。
※auの障害の対応お疲れさまでした。

次に必要なのがクレジットカード登録です。
これはアカウント登録できないわけではありませんが、さくらのクラウド(IaaS)でサーバ作成ができないので注意しましょう。

2. サーバ作成

無事アカウント作成、クレカ登録ができたらサーバの作成を行っていきます。
コントロールパネルのさくらのクラウド(IaaS)をクリックします。

リージョン選択

作りたいリージョンがある場合は左上にあるリージョンのプルダウンから選択します。
※今回は石狩第1ゾーンで作成をします。(現在は第2ゾーンになっているので変更します)

サーバを追加する

リージョン選択が終わったらサーバの追加を行います。
右上にある追加をクリックします。

サーバのスペック、その他を入力します。
今回はcloud-initを利用して作成します。

ISUCON10, 11についてはmatsuuさんがgithubにまとめてくださっています。
ありがとうございます。

github.com

READMEに注意点が書いてあるので確認しましょう。

- Ubuntu 18.04 LTSを用意してください。
- ストレージは8GBでは不足します。16GBあれば問題ないと思います。
- Memoryは1GBだと構築中に不足します。2GB以上あれば問題ないと思います。
- CPUコア数は少なくても問題ないですが、多ければ構築が早く完了するはずです。

cloud-init-isucon/isucon10q at main · matsuu/cloud-init-isucon · GitHub

上記の注意点を確認し、スペックを下記としました。

  • サーバプラン
    • CPU: 通常プラン 4コア
    • メモリ: 4GB
  • ディスク
    • 新規ディスクを作成
    • ディスクプラン: SSDプラン
    • ディスクソース: アーカイブ
    • アーカイブ選択: Ubuntu Server 18.04.5 LTS 64bit (cloudimg)
    • ディスクサイズ: 20GB
    • 準仮想化モードを使う(Virtio): チェック
  • NIC
    • インターネットに接続
    • 準仮想化モードを使う(Virtio): チェック
  • cloud-init設定
    • 作成後すぐに起動: チェック
    • 公開鍵: github.comのUsernameから取得
      • Username: {{ ご自身のgithubのuser nameを入力 }}
      • github.comにアカウントがない場合、公開鍵の直接入力でも良いと思います。
    • ホスト名: isucon10q
      • ご自身のわかりやすい名前をつけましょう。
      • 今回はISUCON10-qualifyの環境を作るため上記名前にしています。
    • タイムゾーン: Asia/Tokyo
    • ローケル: ja_JP.utf-8
    • 追加ユーザパラメータ
  • シンプル監視
    • シンプル監視を有効にする:未チェック
  • サーバの情報
    • 名前: isucon10q
      • ご自身のわかりやすい名前をつけましょう。
    • 説明
      • 必要があれば入力
    • タグ
      • 必要があれば入力
    • アイコン:ubuntuを選択
      • 必要があれば選択
  • 作成数: 2
    • ベンチサーバと分けるために2台作成します。
    • ISUCON10-qualifyでは3台+ベンチサーバだったかと思うので近い環境で練習したい場合は4台作成すると良いと思います。
    • その分料金はプラスになるのでお財布と相談しましょう。

必要項目を入力したら最下部の作成ボタンをクリックします。

決定したスペックをスクリーンショットで下記にまとめています。
注意が必要そうな場所は赤く囲っています。
スクリーンショットが長大なため、折りたたんであります。

入力できたら作成をクリックします。

サーバ設定スクリーンショット

作成をクリックすると進捗ダイアログが出てきます。 ※台数分長くなるため、4台構成の場合はもっとたくさん出てきます。

内部の設定

cloud-initを利用すると、サーバ起動後、内部でansibleが実行されます。

まれに失敗するため、成功したかどうかを確認します。
※isucon環境はいくつかの言語のインストール、ビルドが走るため、起動後すぐは環境構築が終わっていません。

作成されたサーバのNICE項目がグローバルのIPとなります。 構築確認をしたいサーバのNICを確認し、ssh接続しましょう。

なお、初期構築では ubuntu ユーザでログインできます。

構築状況を確認するには下記のlogを確認します。
/var/log/cloud-init-output.log

$ tail -f /var/log/cloud-init-output.log

サーバが立ち上がってすぐの場合、ansibleが実行中なので、下記のような中途半端なログイなっているかと思います。

すべて終わるまで待ちましょう。

ansibleが途中でコケてしまった場合のリカバリ

一番簡単なのはサーバを削除して再度作成することですが、2台作成するうちの1台がコケてしまったような場合はサーバ内でansibleを再実行することでリトライすることができます。

cloud-initの中を覗いてみるとansibleファイルがどこに配置されているか確認できます。
isucon10qの場合は下記です。
/root/isucon10-qualify/provisioning/ansible

リカバリを行う場合、下記commandでリトライします。
※あくまでもisucon10-qualify用です。

$ sudo su -
$ cd /root/isucon10-qualify/provisioning/ansible
$ PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ansible-playbook -i allinone, --connection=local allinone.yaml

成功している場合、下記の様なログになります。

ISUCON10 qualifyでは初期はGoでアプリが動いています。 こちらも合わせて確認しましょう。

3. サーバ間通信ができるようにする

サーバ内の設定まで終わったら今度はサーバ間で疎通が取れるようにします。
現在のままでは、外部通信による通信のみですので、内部からの疎通ができるようにします。

スイッチの追加

左メニューのネットワークからスイッチを選択します。 次に右上の追加をクリックします。

スペックは下記通りです。

  • 名前: isucon10q-switch
  • 説明
    • 任意で入力
  • タグ
    • 任意で入力
  • アイコン
    • 任意に選択
  • ルータ: いいえ

入力したら最下部の作成をクリックします。

確認ダイアログと進捗ダイアログが出てきますが両方肯定系のボタンをクリックしましょう。

スイッチ一覧に作成したスイッチが表示されたら、サーバに戻ります。

サーバへ新しいNICを追加

サーバ一覧へ戻ったら新しいNICを追加する準備を行います。
NICの追加はサーバが停止のときにしかできないため、コンソールから一括停止を行います。

下記図のように止めたいサーバのチェックボックスにチェックを入れ、右上の電源操作からシャットダウンを選択します。

確認画面へ繊維するため、問題なければ右上のシャットダウンをクリックします。

確認いダイアログが出るため、実行をクリックします。

進捗ダイアログは閉じてしまっても問題ありません。

停止状態になったらNICを追加したいサーバの右側にある▼ボタンをクリックし、プルダウンから詳細をクリックするか、1台選択した状態で右上の詳細をクリックします。

下記図のように、NICタブを選択肢、追加ボタンをクリックします。

本当に追加しますか?という操作確認ダイアログが出るため、追加をクリックします。
下記図のように新しくNICが増えますが、まだこの状態ではスイッチに割り当てができていません。 新しく追加されたNICの右側の▼をクリックし、「接続を編集」を選択します。

編集ダイアログが表示されるので、下記のように設定し更新ボタンをクリックします。

  • NIC: スイッチに接続
  • スイッチに接続: 先程作成したスイッチ名を選択

進捗ダイアログが表示され、終了するとNICをの一覧で新しく追加したNICをに先程作ったスイッチが接続されていることがわかります。

NICの追加が必要なすべてのサーバに同様の設定を入れ込みます。

サーバのローカルIPの設定

スイッチの接続が行えたらサーバを起動します。 サーバ一覧でシャットダウンのときと同様起動を行ってください。

ssh接続し、内部のNICのが増えていることを確認します。

さくらのナレッジでprivate-isuの構築箇所で下記の設定箇所が言及されていますが、そのままコピペだとうまくいきません。
追加されたNICの名前が異なるためです。
きちんと追加されたNICを確認して設定しましょう。

$ ifconfig -a
## こっちがはじめから付いてたNIC ※IPなどはマスクしています。
ens3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet ***  netmask ***  broadcast ***
        inet6 ***  prefixlen 64  scopeid 0x20<link>
        ether 9c:a3:ba:27:3a:0c  txqueuelen 1000  (Ethernet)
        RX packets 3655  bytes 239922 (239.9 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 179  bytes 23508 (23.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

## 今回新しく追加したNIC
ens4: flags=4098<BROADCAST,MULTICAST>  mtu 1500
        ether 9c:a3:ba:24:a5:9b  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 100  bytes 7480 (7.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 100  bytes 7480 (7.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens4が新たに追加されたことが確認できます。
ubuntuでは、netplanを利用してネットワークの設定を行います。
下記ディレクトリにファイルが存在することを確認してください。

$ ls -lah /etc/netplan
total 12K
drwxr-xr-x  2 root root 4.0K  75 22:36 .
drwxr-xr-x 98 root root 4.0K  75 22:58 ..
-rw-r--r--  1 root root  481  75 22:36 50-cloud-init.yaml

存在するファイルを変更します。

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        # もとからある設定
        ens3:
            dhcp4: true
            match:
                macaddress: 9c:a3:ba:27:3a:0c
            set-name: ens3
        # 新たに追加した設定
        ens4:
            addresses:
                - 192.168.0.41/24
            dhcp4: 'no'
            dhcp6: 'no'
    version: 2

ファイルの変更を行ったら下記コマンドで設定反映を行います。

$ sudo netplan apply

2台目も同様に設定変更を行います。
※ローカルIPはかぶらないように注意しましょう。

2台目から1台目へ疎通確認を行います。

同様に1台目から2台目へ疎通確認を行います。

問題ありませんでした。

4. benchmarkを実行して見る。

ベンチサーバとアプリサーバができたので、アプリサーバに対してベンチサーバからbenchmarkを実行します。

今回は1台目をアプリサーバ、2台目をbenchmark用サーバと見立てます。

isuconユーザへスイッチし、下記ディレクトリへ移動します。

$ sudo su - isucon
$ cd ~/isuumo/bench

ディレクトリ内のREADMEにbenchmarkの回し方が記載されています。 実際にアプリサーバへbenchmarkをかけます。

$ pwd
/home/isucon/isuumo/bench
$ ./bench --target-url http://198.168.0.1


2022/07/06 00:47:04 bench.go:78: === initialize ===
2022/07/06 00:47:07 bench.go:90: === verify ===
2022/07/06 00:47:07 bench.go:100: === validation ===
2022/07/06 00:47:17 load.go:181: 負荷レベルが上昇しました。
2022/07/06 00:47:25 load.go:181: 負荷レベルが上昇しました。
2022/07/06 00:47:32 load.go:181: 負荷レベルが上昇しました。
2022/07/06 00:47:36 load.go:181: 負荷レベルが上昇しました。
2022/07/06 00:47:37 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://192.168.0.1/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
・
・
・
2022/07/06 00:48:07 bench.go:102: 最終的な負荷レベル: 9
{"pass":true,"score":1562,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":57}],"reason":"OK","language":"go"}

結果は上記のようになりました。
これで一通りの設定は完了です。
チューニングができる体制になったのでチューニングを行っていきましょう。