CodePipelineでリリースにかかった時間を計測する方法
背景
DevOpsの一環でリリースにかかっている時間を計測したいこと、あると思います。こういった改善系のタスクに着手する際、現状のパフォーマンス(今回でいえばリリースにかかってる時間)を計測することは、改善系のタスクの第一歩なので意識していきたいですね。
自分の開発しているアプリケーションではリリース作業でCodePipelineを使っていることが多いです。リリースの全体の流れとしては、mainマージなどをフックにしてCodePipelineが走り、開始、進行中とstatusが変わるたびにSlackでその様子が通知され、承認フローを挟んで完了状態になって終了、という流れになっています。
やったこと
ここでstatusが完了状態になった場合は、deploy_time: xxx[sec]
のようににそのリリースに何秒かかったかがわかるようにしました。
どうやってやったか
リリースの進捗をSlackにポストするのは、EventBridgeで発火するLambdaの中でやっています。そのLambdaの中でCodePipelineの実行状態を呼び出すlist-pipeline-executionsを使いました。
呼び出すと実行状況が以下のような形式で返ってきます。pipelineExecutionSummariesのstartTimeとlastUpdateTimeにPipeline開始時間と最終更新時刻が入っています。statusが完了状態の場合にstartTimeとlastUpdateTimeを取得できればリリースにかかった時間を計算できそうです。
{
'pipelineExecutionSummaries': [
{
'pipelineExecutionId': 'string',
'status': 'Cancelled'|'InProgress'|'Stopped'|'Stopping'|'Succeeded'|'Superseded'|'Failed',
'startTime': datetime(2015, 1, 1),
'lastUpdateTime': datetime(2015, 1, 1),
'sourceRevisions': [
{
'actionName': 'string',
'revisionId': 'string',
'revisionSummary': 'string',
'revisionUrl': 'string'
},
],
'trigger': {
'triggerType': 'CreatePipeline'|'StartPipelineExecution'|'PollForSourceChanges'|'Webhook'|'CloudWatchEvent'|'PutActionRevision',
'triggerDetail': 'string'
},
'stopTrigger': {
'reason': 'string'
}
},
],
'nextToken': 'string'
}
ここでLambdaに対してListPipelineExecutionというポリシーが必要です。要するにCodePipelineの実行状況をLambdが読み取るためには適切なREAD権限が必要です。なのでLambdaに紐づいているIAMロールにこのポリシーをattachする必要があります。
あとはLambdaのスクリプトでlist-pipeline-executionsのAPIを使えばPipelineの開始時間と終了時間が取得できるので、deploy時間を計算してあげるだけです。雰囲気のコードを置いておきます。
import boto3
import slackweb
import os
SLACK_WEBHOOK_URL = os.environ['SLACK_WEBHOOK_URL']
codepipeline = boto3.client('codepipeline', region_name='ap-northeast-1')
def post_to_slack(text):
slack = slackweb.Slack(url=SLACK_WEBHOOK_URL)
attachment = {
title: 'CodePipeline Status'
text: text
}
slack.notify(attachments=[attachment],
link_names=1,
channel='#release-channel',
username='CodePipeline',
icon_emoji=':robot_face:')
def lambda_handler(event, context):
detail = event['detail']
state = detail['state']
pipeline = detail['pipeline']
if state == 'SUCCEEDED':
try:
res = codepipeline.list_pipeline_executions(pipelineName=pipeline)
summary = res['pipelineExecutionSummaries'][-1]
deploy_time = summary['lastUpdateTime'] - summary['startTime']
deploy_time_seconds = int(deploy_time.total_seconds())
except Exception as e:
print('Error occured in reading deploy_time.')
print(e)
result = f"成功 (deploy_time: {deploy_time_seconds}[sec])"
elif state == 'STARTED':
result = '開始'
elif state == 'CANCELED':
result = 'キャンセル'
elif state == 'SUPERSEDED':
result = '置き換え完了'
elif state == 'FAILED':
result = '失敗'
text = "Pipeline: %s\n%s" % (pipeline, result)
post_to_slack(text)
今後の展望
一旦はリリースごとのdeploy時間が出せるようになったので、今後はこのデータをどこかに集計して「月次でx%改善した!」みたいな改善サイクルが回せるといいなと思います。S3とかにcsv形式で保存するのが一番簡単そうですね。
Discussion