IsuconでGoの勉強をしてみる
今回初めてisuconに出ることになり、色々調べてみるとほとんどのチームがGoを使うことがわかった。初心者としては情報が多い方が良さそうなので使ったことはないがGoで挑戦することを決意。
どうやってGoを学ぼうか迷っていたが、Goの書き方やら環境構築やらを行うのがめんどうなので、いっそのことisuconの過去問を眺めながらGoについて学ぶことにした。
筆者のスペック
- 大学生
- バックエンドを中心に一年位インターン
- Go未経験
- Isucon未経験
環境構築
今回導入するツールは
- alp(nginxログ監視ツール)
- pt-query-digest
- pprof
まずは環境構築や、問題を解くためのツールの導入などをおこなう。
以下の記事を参考にしてmultipassで環境構築を行なった。
今回解くのはisuconの練習問題であるprivate-isu
今回はgoで練習したいのでマニュアルを参考にして以下のコマンドを実行
$ sudo systemctl stop isu-ruby
$ sudo systemctl disable isu-ruby
$ sudo systemctl start isu-go
$ sudo systemctl enable isu-go
multipassでIPアドレスを調べて、https://{IPアドレス}
にアクセスし、以下のようなサイトが表示されれば成功
ベンチマークを回してみる。
{"pass":true,"score":4055,"success":3914,"fail":0,"messages":[]}
問題なく実行できた
各種ツールの導入
まず初めにvscodeからアクセスできるようにsshキー登録する。
まずローカル側で鍵の作成。
cd ~/.ssh
ssh-keygen -t rsa -f id_rsa_isu
公開鍵をコピー
cat id_rsa_isu.pub
multipassインスタンスに~/.ssh/authorized_keysを作成し、先ほどのコピーを追加
cd ~/.ssh
touch authorized_keys
chmod 600 authorized_keys
vi authorized_keys
ローカルから接続できることを確認
ssh isucon@***.***.***.*** -i ~/.ssh/id_rsa_isu
あとはvscodeからリモート接続できれば完了
alp
alpが一番有名そうなので今回はこちらを使用してみる。
以下のコマンドでダウンロード
wget https://github.com/tkuchiki/alp/releases/download/v1.0.9/alp_linux_arm64.tar.gz
tar -zxvf alp_linux_arm64.tar.gz
sudo install alp /usr/local/bin/alp
その後、以下を実行して動作確認
alp --version
次にnginxでログを出力するように設定
sudo vi /etc/nginx/nginx.conf
以下の設定をhttpの中に追加
##
log_format ltsv "time:$time_local"
"\thost:$remote_addr"
"\tforwardedfor:$http_x_forwarded_for"
"\treq:$request"
"\tmethod:$request_method"
"\turi:$request_uri"
"\tstatus:$status"
"\tsize:$body_bytes_sent"
"\treferer:$http_referer"
"\tua:$http_user_agent"
"\treqtime:$request_time"
"\truntime:$upstream_http_x_runtime"
"\tapptime:$upstream_response_time"
"\tcache:$upstream_http_x_cache"
"\tvhost:$host";
access_log /var/log/nginx/access.log ltsv;
pt-query-digest
次はsqlのログを取得するためのツールを導入
/etc/mysql/conf.d/my.cnfでmysqlの設定が行えるらしいがなぜかそのファイル自体がなかったので以下のコマンドで探してみる
isucon@private-isu:~$ sudo find / -name my.cnf
find: ‘/proc/89099’: No such file or directory
find: ‘/proc/89100’: No such file or directory
find: ‘/proc/89101’: No such file or directory
/tmp/private-isu/ansible_old/roles/portal/files/mysql/my.cnf
/var/lib/dpkg/alternatives/my.cnf
/etc/alternatives/my.cnf
/etc/mysql/my.cnf
/home/isucon/private_isu.git/ansible_old/roles/portal/files/mysql/my.cnf
/etc/mysql/my.cnfにあるらしいので開いてみる
sudo vi /etc/mysql/my.cnf
#
# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
#
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html
#
# * IMPORTANT: Additional settings that can override those from this file!
# The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
この内容をみるとMySQLの設定は/etc/mysql/conf.d/や/etc/mysql/mysql.conf.d/ディレクトリ内の.cnfファイルによって追加または上書きできるようになっている。
よって、以下のように新しくファイルを作成して設定を書き込む。
sudo vi /etc/mysql/conf.d/custom-my.cnf
[mysqld]
character-set-server=utf8mb4
slow_query_log=ON
long_query_time=0.001
slow_query_log_file=/var/log/mysql/my-slow.log
[mysql]
default-character-set=utf8mb4
[client]
default-character-set=utf8mb4
ログを出力するファイルに書き込み権限を与える
sudo mkdir -p /var/log/mysql
sudo chown mysql:mysql /var/log/mysql
再起動
sudo systemctl restart mysql
その後percona-toolkitをインストール(この中にpt-query-digestが含まれる)
sudo apt install -y percona-toolkit
その後にbenchを回した後に以下のコマンドを実行
sudo pt-query-digest /var/log/mysql/my-slow.log > ~/pt-query-digest.txt
以下のようなファイルが作成されれば成功
# 1.8s user time, 390ms system time, 40.54M rss, 47.60M vsz
# Current date: Sat Oct 12 00:48:34 2024
# Hostname: private-isu
# Files: /var/log/mysql/my-slow.log
# Overall: 25.73k total, 27 unique, 0.55 QPS, 0.04x concurrency __________
# Time range: 2024-10-11T02:32:18 to 2024-10-11T15:36:18
# Attribute total min max avg 95% stddev median
# ============ ======= ======= ======= ======= ======= ======= =======
# Exec time 1886s 1ms 1s 73ms 155ms 51ms 56ms
# Lock time 82ms 0 13ms 3us 1us 129us 0
# Rows sent 3.96M 0 9.77k 161.26 6.98 1.17k 0.99
# Rows examine 2.07G 0 97.68k 84.50k 97.04k 32.68k 97.04k
# Query size 14.46M 10 1.17M 589.02 80.10 21.26k 65.89
# Profile
# Rank Query ID Response time Calls R/Call V/M It
# ==== ============================= =============== ===== ====== ===== ==
# 1 0x624863D30DAC59FA16849282... 1192.1396 63.2% 10026 0.1189 0.01 SELECT comments
# 2 0x422390B42D4DD86C7539A5F4... 516.1626 27.4% 10960 0.0471 0.01 SELECT comments
# 3 0x100EC8B5C400F34381F9D7F7... 116.6155 6.2% 934 0.1249 0.01 SELECT comments
# MISC 0xMISC 61.2725 3.2% 3815 0.0161 0.0 <24 ITEMS>
# Query 1: 0.21 QPS, 0.03x concurrency, ID 0x624863D30DAC59FA16849282195BE09F at byte 4566541
# This item is included in the report because it matches --limit.
...
pprofの導入
手順概要
- Goアプリケーションに
pprof
を組み込む pprof
サーバーを有効にする- プロファイルデータを取得
- ローカルでプロファイルデータを分析
- グラフ化して視覚的に分析
1. Goアプリケーションにpprof
を組み込む
Goアプリケーションでpprof
を利用するには、net/http/pprof
パッケージをインポートして、プロファイリング用のエンドポイントを有効にする必要があります。main.go
やアプリケーションのエントリーポイントで以下のようにコードを追加します。
例: pprof
のインポートと設定
import (
_ "net/http/pprof"
"log"
"net/http"
)
func main() {
// pprof用のHTTPサーバーを起動 (ポート6060)
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// アプリケーションの他のコード
}
- この設定により、
http://localhost:6060/debug/pprof/
にアクセスすることで、pprof
のプロファイリング機能が利用可能になります。 -
go func()
内で別ゴルーチンを使ってpprof
サーバーをバックグラウンドで起動しています。 - Go初心者ならではかもしれないが、goはコンパイル言語なので、以下のコマンドでbuildしないと変更が反映されないことに注意(アプリの再起動も)
go build -o app
sudo systemctl stop isu-go
sudo systemctl restart isu-go
2. pprof
サーバーを有効にする
pprof
が正しく起動しているか確認します。アプリケーションが実行中のサーバーにアクセスし、pprof
のエンドポイントが利用可能かどうかを確認します。
curl http://localhost:6060/debug/pprof/
- 正常に動作している場合、
pprof
のエンドポイントの一覧が表示される。
3. プロファイルデータを取得
pprof
のエンドポイントからプロファイルデータを収集します。たとえば、CPUプロファイルを60秒間収集する場合、次のコマンドを実行。
CPUプロファイルの取得例
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=60
このコマンドを実行すると、60秒間のプロファイルデータが収集され、pprof
のインタラクティブな解析環境が起動します。
4. ローカルでプロファイルデータを分析
取得したプロファイルデータをローカルでさらに分析することも可能です。プロファイルデータをファイルとして保存し、後から解析できるらしい。
プロファイルデータの保存と解析
# プロファイルデータを保存
curl -o profile.pb.gz http://localhost:6060/debug/pprof/profile?seconds=60
# 保存したプロファイルデータを解析
go tool pprof profile.pb.gz
これにより、pprof
の解析インターフェースが起動し、データを対話的に分析できる。
ただ、このprofile.pb.gzがあれば解析はできるので、以下ではこのファイルをローカルに落としてweb上で解析してみる
5. グラフ化して視覚的に分析
pprof
の解析インターフェースでGraphviz
を利用して、プロファイルデータを視覚的にグラフ化できる。まず、Graphviz
をインストール。
Graphviz
のインストール(macOSの場合)
brew install graphviz
グラフの表示
pprof
のインターフェース内で以下のコマンドを実行して、グラフをWebブラウザで表示。
go tool pprof profile.pb.gz
(pprof) web
これにより、ブラウザでグラフ化されたプロファイルデータを見ることができる。また、SVGやPNG形式でグラフを生成することも可能
まとめ
次回はこの環境を用いてGoの勉強をしながらパフォーマンス改善を行う