Rustコンパイラをクラウドでビルドする備忘録(改良版)
概要
前回の改良版。 Rustコンパイラをいじっているとビルドの遅さのせいで作業に支障をきたすため、クラウドでビルドするようにしてみる。
EC2を立ち上げる
Ubuntu 16.04 (HVM) の c5.4xlarge を使うことにする(結局、手元とあわせたくなったので18.04に上げた)。使うインスタンスの強さは財布と相談して決めることにする。ディスクは結構必要なので上げておく。
何度も再起動して使いたいのでEIPを割り当てておく。(デフォルトのグローバルIPは再起動のたびに変わるっぽい)
高くて強いインスタンスを使っていくので、使わないときは落とし忘れないように気をつける(停止時はEBSやEIPだけ課金される)。このあたりも必要なら自動でシャットダウンする仕組みを考えるといいかもしれない。
IAMユーザーを作る
当該インスタンスを立ち上げられるだけの専用のユーザーを作る。以下のようなインラインポリシーを作る。 (i-
ではじまる部分に当該インスタンスのインスタンスIDを入れる)
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ec2:StartInstances", "Resource": "arn:aws:ec2:*:*:instance/i-0123456789abcdef0" } ] }
このユーザーのコンソールへのログインを不許可にしてクレデンシャルを発行する。手元のマシンで以下を実行してクレデンシャルを入れる。 (~/.aws/credentials
を直接編集してもOK)
$ aws configure --profile rust-builder
セットアップ
手元から rust-builder
という名前でSSHできるようにする。依存関係として以下を入れておく。
$ sudo apt update && sudo apt upgrade && sudo apt install build-essential python-minimal cmake curl git
自動シャットダウン
以下のファイルを /home/qnighy/shutdown-timer.sh
として置き、実行権限をつける。 (ユーザー名は適宜置き換えて) (ログイン状態のチェックのために w
の出力を雑にgrepしているので、そこの置き換えを忘れないように)
#!/usr/bin/env bash set -ue count=0 sleep=600 max_count=6 while true; do sleep "$sleep" date >&2 if w | grep -q qnighy; then echo "logged in; reset" >&2 count=0 else count="$((count + 1))" echo "not logged in; count=$count" >&2 if [[ $count -ge $max_count ]]; then echo "shutting down..." >&2 shutdown -h now fi fi done
また以下のsystemd設定ファイルを /etc/systemd/system/shutdown-timer.service
に置く。
[Unit] Description = shutdown timer [Service] ExecStart = /home/qnighy/shutdown-timer.sh Restart = always Type = simple [Install] WantedBy = multi-user.target
以下で有効化
sudo systemctl enable shutdown-timer sudo systemctl start shutdown-timer
これにより、10分に1回の頻度でログイン状態をチェックし、60分間非ログインだったらシャットダウンするようになる。
スクリプトの配置
Rustコンパイラを手元にcloneして、 x.py
があるのと同じ位置に以下の y.sh
を置いてexec permissionをつける。
#!/usr/bin/env bash set -ue # Remote side: # sudo apt update && sudo apt upgrade && sudo apt install build-essential python-minimal cmake curl git gdb ec2_profile=rust-builder ec2_region=ap-northeast-1 ec2_instance=i-0123456789abcdef0 builder_host=rust-builder local_host="$(hostname)" dirpath="$(realpath --relative-to "$HOME" "$(pwd)")" while ! aws --profile "$ec2_profile" --region "$ec2_region" ec2 start-instances --instance-ids "$ec2_instance" | grep -q running; do echo "waiting for EC2 wakeup..." >&2 sleep 1 done git submodule update --init --recursive rsync -avz --delete --exclude '.git' --exclude '/build' "$HOME/$dirpath/" "$builder_host:$local_host/$dirpath" ssh -t "$builder_host" "cd $local_host/$dirpath; $@"
やっていることは以下。
- まずEC2インスタンスを立ち上げる、replyに "start" が含まれるようになるまで待つ。
- rsyncで送って向こうでビルドを叩く。
- gitを全部転送したくないので
.git
は省く。ただしsubmoduleが必要なのでsubmodule updateを自動で叩く。 (x.pyは.gitがあるときはsubmodule updateを自動でやってくれる) build
も当然省く。手元のbuildと衝突しうるし、--delete
に巻き込まれないようにする意図もある。ただしbuildというディレクトリがソースツリーの中にあるので、/build
と指定する必要がある。- tmuxを立ち上げたりはしない。ビルドが短くなることが期待されているので途中で切れてもよい。むしろ自動アタッチとか考えるのが面倒。
ビルド
./remote.sh ./x.py test --stage 1
のように通常のビルドコマンドに ./remote.sh
をつけるとリモートでのビルドになる。