こんにちは!
ポーラ・オルビスホールディングスのITプロダクト開発チームでスクラムマスターをしている川田です。
前回に引き続き、Four Keysの各指標についてNewRelicを利用して可視化することを目指します。前回の記事はこちら
です。
変更リードタイムとは
今回は、Four Keysで定義された4つの指標のうち「変更リードタイム」について可視化を行います。前回と同様、最初に変更リードタイムのパフォーマンスレベルの定義を確認します。
Elite | High | Medium | Low |
---|---|---|---|
1時間未満 | 1日~1週間 | 1か月~半年 | 半年以上 |
DORAの定義ではレベルの定義が非連続になっています。仮に計算結果が2つのレベルの間に位置する場合、今回は低いレベルになるような判定ロジックにします。
(例:リードタイムが1時間以上~1日未満の場合はHighとする)
変更リードタイムの定義は、Google Cloudのブログには以下のように記載されています。
For the primary application or service you work on, what is your lead time for changes (i.e., how long does it take to go from code committed to code successfully running in production)?
あなたが携わっている主要なアプリケーションやサービスについて、変更のリードタイム(コードをコミットしてから本番稼動するまでにかかる時間)はどのくらいですか?
つまり、必要なデータとしては
- コミットが行われた時刻
- コミットされた内容がブランチにマージされ、デプロイされた時刻
の2つで、各コミット単位でこれらの差分を計算し、全体の平均値を使ってパフォーマンスレベルを判定すれば良さそうです。
私たちのチームでは対象のブランチにプルリクがマージされたことをトリガーとしてデプロイを行っていることもあり、今回はブランチにマージされた時刻=デプロイ時刻として扱います。そうすることで、前回のデプロイ頻度で記録したデプロイイベントとデータを結合する必要がなくなるため全体の処理がシンプルになります。厳密にはデプロイ時間が差分として発生しますが、数分程度なので許容できる範囲内と判断しています。
前回と同様に本記事ではDORAの定義をそのまま利用しますが、実際のチームに適用する際にはチームの状況にあった定義をあらためて検討しましょう。
GitHub Actionsでコミット/マージ時刻を記録する
まずは、コミット/マージの時刻をNewRelicに送信する部分を実装します。GitHubで特定のブランチにマージされたことをトリガーに動くワークフローを作成し、前回と同様にNewRelicのカスタムイベントを利用してデータを送信します。また、あらかじめデータ送信先のNewRelicアカウントのアカウントIDとデータ送信のためのライセンスキーを払い出し、GitHubのシークレットに登録が必要な点もお忘れなく。
では実装内容について、パートごとに見ていきます。
まずはワークフローの最初からstepsの定義直前の設定部分です。
name: Send PullRequest event to New Relic
on:
pull_request:
types:
- closed
jobs:
send_event_to_newrelic:
runs-on: ubuntu-latest
if: github.event.pull_request.merged
steps:
...
on.pull_request.types
に closed
を指定することで、プルリクエストが閉じられたときに動くワークフローになります。これだけではマージせずにプルリクを閉じた場合にも動作してしまうため、jobs
内に if: github.event.pull_request.merged
の設定を記載してマージが行われたときのみ有効となるようにします。
続いて、各stepに移ります。まずはプルリクの情報を取得します。
- name: Get PR info
run: |
echo "pr_no=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
echo "pr_base_ref=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
pr_merged_at=$(date +%s -d "${{ github.event.pull_request.merged_at }}")
echo "pr_merged_at=$pr_merged_at" >> $GITHUB_ENV
プルリクに関する情報は github.pull_request.event
から取得できます。取得できる情報の詳細は公式ドキュメントを参照してください。
今回はプルリクがマージされた時間を利用するため、github.pull_request.event.merged_at
を利用します。merged_atはISO8601形式の文字列になっているので、後ほど計算しやすいようにdateコマンドでUNIX時間に変換し、環境変数 $GITHUB_ENV
に入れておきます。また、一緒にプルリクの番号とマージ先のブランチも取得しておきます。
続いて、プルリクに含まれるすべてのコミットの時刻を取得します。
- name: Get all commit timestamps
run: |
dates=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits" \
| jq -c '[.[] | .commit.committer.date ]')
echo "dates=$dates" >> $GITHUB_ENV
プルリクに含まれるコミットの情報は、List commits on a pull request API を利用して取得します。1コミットの情報を表すJSONオブジェクトのリストが返却されるため、そこからコミットの時刻(commit.committer.date
)を取得し、リスト化して環境変数に入れておきます。
最後に、ここまでに集めたデータをNewRelicに送ります。
- name: Send commit timestamps to new relic
run: |
echo '${{ env.dates }}' | jq -c '.[]' | while read date; do
var=$(echo "$date" | tr -d '\\"')
timestamp=$(date +%s -d $var)
curl -i \
-X POST 'https://insights-collector.newrelic.com/v1/accounts/${{ secrets.NEW_RELIC_ACCOUNT_ID }}/events' \
-H "X-Insert-Key:${{ secrets.NEW_RELIC_LICENSE_KEY }}" \
-H "Content-Type: application/json" \
-d \
"[
{
\"eventType\": \"Commit\",
\"pr_no\": ${{ env.pr_no }},
\"pr_base_ref\": \"${{ env.pr_base_ref }}\",
\"pr_merged_at\": ${{ env.pr_merged_at }},
\"commit_at\": $timestamp
}
]"
done
eventType
に指定するカスタムイベントの名称は Commit
としています。各コミットの情報ごとに、前ステップで取得した時刻をUNIX時間に変換してから他の情報と一緒にNewRelicに送ります。
今回求めたい時刻の差分についてはこのステップで計算しても良いのですが、なるべくデータは生の状態で保持しておき、利用するNewRelic側で自由に利用・加工できるような方針としています。
NewRelicでパフォーマンスレベルを判定し可視化する
必要なデータがNewRelicに集まったので、いよいよパフォーマンスレベルを判定した結果を可視化する部分を実装します。
まずは、NewRelicのクエリビルダーでデータを確認してみます。直近1か月に本番環境向けのリリースブランチへマージされたコミットの情報について、以下のNRQLで取得します。WHERE句の条件は環境に合わせて修正してください。
SELECT *
FROM Commit
WHERE pr_base_ref = ‘release-prd’
SINCE 1 month ago
結果は以下の通り、ちゃんと記録されています!
続いてプルリクがマージされた時刻とコミットされた時刻の差分を求め、その平均値を計算します。四則演算で差分を求めたのち、NRQLの average関数 を用いて平均値を計算します。あわせて、convert関数 により単位を秒から時間に変換してみます。
SELECT convert(average(pr_merged_at - commit_at), 'second', 'hour')
FROM Commit
WHERE pr_base_ref = 'release-prd'
SINCE 1 month ago
こちらのクエリを実行すると、ばっちり計算結果が表示されました!
あとはレベルに応じた基準と比較する部分を実装して完成です。最終的には以下のようなクエリとなりました。
SELECT
if (count(*) = 0 OR convert(average(pr_merged_at - commit_at), 'second', 'hour') <= 1,
'Elite',
if(convert(average(pr_merged_at - commit_at), 'second', 'day') <= 7,
'High',
if(convert(average(pr_merged_at - commit_at), 'second', 'month') <= 6, 'Medium', 'Low')
)
)
AS '' // 結果に表示される関数名を消すため
FROM Commit
WHERE pr_base_ref = 'release-prd'
SINCE 1 month ago```
こちらを実行すると、無事レベルが表示されました!
さいごに
Four Keysの可視化の2回目として、今回は変更リードタイムをNewRelic上で可視化してみました。参考になれば幸いです。
次回は変更障害率の可視化にトライします!