CodePipelineでリリースにかかった時間を計測する方法

2023/08/19に公開

背景

DevOpsの一環でリリースにかかっている時間を計測したいこと、あると思います。こういった改善系のタスクに着手する際、現状のパフォーマンス(今回でいえばリリースにかかってる時間)を計測することは、改善系のタスクの第一歩なので意識していきたいですね。

自分の開発しているアプリケーションではリリース作業でCodePipelineを使っていることが多いです。リリースの全体の流れとしては、mainマージなどをフックにしてCodePipelineが走り、開始、進行中とstatusが変わるたびにSlackでその様子が通知され、承認フローを挟んで完了状態になって終了、という流れになっています。

やったこと

ここでstatusが完了状態になった場合は、deploy_time: xxx[sec] のようににそのリリースに何秒かかったかがわかるようにしました。

どうやってやったか

リリースの進捗をSlackにポストするのは、EventBridgeで発火するLambdaの中でやっています。そのLambdaの中でCodePipelineの実行状態を呼び出すlist-pipeline-executionsを使いました。
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codepipeline/client/list_pipeline_executions.html

呼び出すと実行状況が以下のような形式で返ってきます。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する必要があります。
https://docs.aws.amazon.com/codepipeline/latest/APIReference/API_ListPipelineExecutions.html

あとは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