ISUCONのデプロイスクリプトを組むにあたって、なるべく一撃でデプロイできるように準備したい。そう思った時に、Goのアプリケーションをビルドする際のバイナリ名やsystemdで動いているサービスの名前が毎回変わっていることを思い出したのでそこも機械的に判定できるといいな、と思って試行錯誤してみました。
アプリケーションの場所を見つける
webapp
というディレクトリの中に各言語のアプリケーション実装が配置されているというのがいつもの流れですが、大体 /home/isucon/webapp
にあると思いきや catatsuy/private-isu の場合は /home/isucon/private_isu/webapp
にあって、微妙に配置場所が変わることもあるかもしれません。
findコマンドを使って webapp
ディレクトリを見つけるというのをまずやっておきます。
$ find /home/isucon -maxdepth 3 -type d -name "webapp*" /home/isucon/webapp
その上で言語の名前のディレクトリが来ますが、Goの場合は go
という名前だったり golang
という名前であることがあります。
- isucon14/webapp/go at main · isucon/isucon14 · GitHub
- isucon10-final/webapp/golang at master · isucon/isucon10-final · GitHub
ビルドの際はこの両方のディレクトリを念の為試しておくと安心できます。
$ cd webapp/golang 2>/dev/null || cd webapp/go 2>/dev/null || exit 1; $ pwd /home/isucon/webapp/go
サービス名を特定する
例年のISUCONではsystemdを使って各言語の実装が動いていて、サービス名には isu
や go
のように言語名が含まれているがちです。
- ISUCON14の場合は
isuride-go.service
この前提を元にサービス名を特定することができます。
systemctl list-unit-files
を実行するとユニットファイルの一覧を取得できるので、その中で isu
と go
を grep しつつ、 enabled
という文字列が邪魔なので削るとサービス名だけを抽出できます。
$ systemctl list-unit-files | grep 'isu' | grep 'go' isuride-go.service enabled enabled $ systemctl list-unit-files | grep 'isu' | grep 'go' | awk '{print $1}' isuride-go.service
go build 時のバイナリの名前を特定する
アプリケーションをビルドする際、ISUCONの回ごとにバイナリの名前が違うものになっているがちです。
- ISUCON14の場合は
isuride
go build -o isuride
の -o
の引数をどうすればいいのかを探りましょう。
ISUCONでは systemd を使ってサービスのデーモン化をしているので、ユニットファイルの中身を確認することで実行しているバイナリが何かを特定することができます。
systemctl cat
コマンドを使うとユニットファイルの中身を出力できます。
$ systemctl cat isuride-go.service # /etc/systemd/system/isuride-go.service [Unit] Description=isuride-go After=syslog.target After=mysql.service Requires=mysql.service [Service] WorkingDirectory=/home/isucon/webapp/go EnvironmentFile=/home/isucon/env.sh User=isucon Group=isucon ExecStart=/home/isucon/webapp/go/isuride ExecStop=/bin/kill -s QUIT $MAINPID Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
ExecStart
に実行しているバイナリが指定されているのでここの最後の isuride
を抽出すれば完成です。
$ systemctl cat isuride-go.service | grep ExecStart ExecStart=/home/isucon/webapp/go/isuride $ systemctl cat isuride-go.service | grep ExecStart | sed -E 's|.*/([^/ ]+).*|\1|' isuride
Makefile の中ですべてを組み合わせる
ここまでわかってきたことを組み合わせることで、ISUCONの回ごとにMakefileをカスタマイズしなくとも同じ設定でwebappの中身をデプロイできるようになります(多分)。
前提: このデプロイ方式は手元のwebappの内容をrsyncでpushしています。
deploy-webapp: @for server in $(SERVERS); do \ WEBAPP_DIR=$$(ssh $$server 'find /home/isucon -maxdepth 3 -type d -name "webapp*" | head -n 1'); \ rsync -avz ./webapp/ $$server:$$WEBAPP_DIR; \ GO_SERVICE=$$(ssh $$server "systemctl list-unit-files | grep 'isu' | grep 'go' | awk '{print \$$1}'"); \ BUILD_OUTPUT=$$(ssh $$server "systemctl cat $$GO_SERVICE | grep ExecStart | sed -E 's|.*/([^/ ]+).*|\1|'"); \ ssh $$server "cd $$WEBAPP_DIR/go 2>/dev/null || cd $$WEBAPP_DIR/golang 2>/dev/null || exit 1; go build -o $$BUILD_OUTPUT"; \ ssh $$server "sudo systemctl restart $$GO_SERVICE"; \ ssh $$server "sudo systemctl status $$GO_SERVICE"; \ done