こんにちは。プラットフォーム開発部 のthorです。
以前の記事で、Four keys(海外では、指標を提唱した組織名からDORA Four Key metricsと呼ばれる事が多いです)についてご紹介しました。このメトリクスを測定するためのオープンソースのプロジェクトにFour Keysプロジェクトがあります。このプロジェクトを使用するには以下の要件を満たす必要があります。
実行環境
- Google Cloud
対応データソース
- ArgoCD
- Circle CI
- Cloud Build
- GitHub
- GitLab
- PagerDuty
- Tekton
2022年5月時点のロードマップを見ると、GitHub ActionsやJIRAも将来的には対応予定ですが、2023年5月時点では対応していません。また、実行環境がGoogle Cloudのみと、まだ使うための条件が厳しい状態です。
そこで、本記事ではDockerコンテナ上で動作するPostgreSQLでデータを集計しGrafanaで可視化する方法をご紹介します。(ただし、データは手入力で行う必要があります) Four keysの意味については、以下の以前の記事を参照してください。
miraitranslate-tech.hatenablog.jp
Dockerコンテナの準備
作業用のディレクトリを作成し、以下のdocker-compose.yml
ファイルを作成します。
version: '3' services: grafana: image: grafana/grafana:v8.2.6 ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_PASSWORD=admin # 初回ログイン時に変更します volumes: - grafana-data:/var/lib/grafana depends_on: - db db: image: postgres:15.3 container_name: postgres ports: - 5432:5432 volumes: - db-store:/var/lib/postgresql/data - ./script:/docker-entrypoint-initdb.d # DB初期化用のSQLファイルをマウント environment: - POSTGRES_PASSWORD=${PGPASSWORD} volumes: grafana-data: db-store:
.env
ファイルを作成し以下を設定します。(password部分は適宜変更します)
PGPASSWORD='password'
DB初期化用のSQLファイルの作成
script
ディレクトリを作成し、そこに4keys.sql
ファイルを作成して、以下の内容を定義します。Four Keysプロジェクトのコードをベースに、月に数回とかデプロイ頻度の少ないプロジェクトでの集計結果が分かりやすいように作成しています。
-- デプロイログ CREATE TABLE deployments ( deploy_id text, -- デプロイのID。バージョン等。 ex) 1.5.7 time_created timestamp, -- デプロイした日時 success boolean -- デプロイ後障害が発生していなければtrue ); -- インシデントログ CREATE TABLE incidents ( incident_id text, -- インシデントのID。RedmineのチケットIDやJIRAのISSUE ID等。 ex) ISSUE-1234 time_created timestamp, -- 障害を検知した日時 deploy_id_resolved text -- 障害を復旧させたデプロイのdeploy_id ); -- 変更ログ CREATE TABLE changes ( change_id text, -- 変更ID。基本はcommit hash。 time_created timestamp, -- 障害を検知した日時 deploy_id text -- 変更をデプロイしたdeploy_id ); -- 三月前から今日までの日付 CREATE VIEW days_from_1st_of_3_months_prior AS SELECT generate_series as day FROM GENERATE_SERIES(date_trunc('month', current_date + interval '-3 months'), current_date, '1 days') ; -- 日毎のデプロイ CREATE VIEW daily_deployment AS SELECT days_from_1st_of_3_months_prior.day AS day, -- 日付 COUNT(deploy_id) AS day_deployed -- 1日に何回デプロイしたか FROM days_from_1st_of_3_months_prior LEFT JOIN (SELECT date_trunc('day', time_created) AS day, deploy_id FROM deployments) deployments ON deployments.day = days_from_1st_of_3_months_prior.day GROUP BY days_from_1st_of_3_months_prior.day ; -- 週毎のデプロイ CREATE VIEW weekly_deployment AS SELECT date_trunc('week', days_from_1st_of_3_months_prior.day) AS week, -- 週の開始日 MAX(CASE WHEN deployments.day is not null THEN 1 ELSE 0 END) AS week_deployed, -- 週に1回以上デプロイしたか COUNT(distinct deployments.day) AS days_deployed -- 週に何日デプロイしたか FROM days_from_1st_of_3_months_prior LEFT JOIN (SELECT date_trunc('day', time_created) AS day, deploy_id FROM deployments) deployments ON deployments.day = days_from_1st_of_3_months_prior.day WHERE days_from_1st_of_3_months_prior.day > date_trunc('week', current_date + interval '-12 weeks') GROUP BY week ; -- 月毎のデプロイ CREATE VIEW monthly_deployment AS SELECT date_trunc('month', days_from_1st_of_3_months_prior.day) AS month, -- 月の開始日 COUNT(deployments.day_deployed > 0 OR NULL) AS days_deployed -- 月に何日デプロイしたか FROM days_from_1st_of_3_months_prior LEFT JOIN (SELECT date_trunc('day', day) AS day, day_deployed FROM daily_deployment) deployments ON deployments.day = days_from_1st_of_3_months_prior.day GROUP BY month ; -- 週毎のデプロイ頻度(中央値) CREATE VIEW median_weekly_deployment_frequency AS SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY days_deployed) AS median_days_deployed_in_week, -- 週に何日デプロイしたかの中央値 percentile_cont(0.5) WITHIN GROUP (ORDER BY week_deployed) AS median_weekly_deployed -- 週に1回以上デプロイしたかの中央値 FROM weekly_deployment ; -- 30日毎のデプロイ頻度 CREATE VIEW median_30days_deployment_frequency AS WITH last_three_30days AS (SELECT generate_series(current_date + interval '-89 days', current_date, '30 days') AS bin_start) SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY deploy_count) AS median_30days_deployment -- 30日に何回デプロイしたかの中央値 FROM (SELECT COUNT(day_deployed > 0 OR NULL) AS deploy_count FROM last_three_30days LEFT JOIN (SELECT date_bin('30 days', day, current_date + interval '-89 days') AS bin, day_deployed FROM daily_deployment) AS d ON last_three_30days.bin_start = d.bin GROUP BY bin_start) AS thirty_days_delploys ; -- デプロイ頻度のバケット CREATE VIEW deployment_frequency_bucket AS SELECT CASE WHEN daily THEN 'Daily' WHEN weekly THEN 'Weekly' WHEN monthly THEN 'Monthly' ELSE 'Yearly' END as deployment_frequency FROM (SELECT median_days_deployed_in_week >= 3 AS daily, median_weekly_deployed >= 1 AS weekly FROM median_weekly_deployment_frequency) AS median_weekly, (SELECT median_30days_deployment >= 1 AS monthly FROM median_30days_deployment_frequency) AS median_30days ; -- 変更のリードタイム CREATE VIEW median_time_to_changes AS SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY time_to_change) AS median_time_to_change -- 変更のリードタイムの中央値 FROM (SELECT deployments.deploy_id, date_trunc('day', deployments.time_created) AS day, deployments.time_created - changes.time_created AS time_to_change FROM deployments, changes WHERE deployments.deploy_id = changes.deploy_id AND deployments.time_created BETWEEN date_trunc('day', now()) + '-3 months' + '1 days' AND date_trunc('day', now())) AS time_to_changes ; -- リードタイムのバケット CREATE VIEW time_to_changes_bucket AS SELECT CASE WHEN median_time_to_change < interval '24 hours' THEN 'One day' WHEN median_time_to_change < interval '1 week' THEN 'One week' WHEN median_time_to_change < interval '1 month' THEN 'One month' WHEN median_time_to_change < interval '6 month' THEN 'Six month' ELSE 'One year' END AS lead_time_to_change FROM median_time_to_changes ; -- 変更障害率 CREATE VIEW change_failure_rate AS SELECT count (success = false OR NULL) / cast( count (deploy_id) AS float ) AS rate -- 変更障害率 FROM deployments WHERE time_created BETWEEN current_date + interval '-3 months' AND current_date ; -- 変更障害率バケット CREATE VIEW change_failure_rate_bucket AS SELECT CASE WHEN rate < 0.15 THEN '0-15%' WHEN rate < 0.46 THEN '16-45%' ELSE CAST(rate AS TEXT) || '%' END AS change_failure_rate_bucket FROM change_failure_rate ; -- サービス復元 CREATE VIEW restore_services AS SELECT incidents.incident_id AS incident_id, incidents.time_created AS time_failed, deployments.time_created AS time_resolved FROM deployments, incidents WHERE deployments.deploy_id = incidents.deploy_id_resolved AND deployments.time_created BETWEEN current_date + interval '-3 months' AND current_date ; -- サービス復元時間 CREATE VIEW median_time_to_restore_service AS SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY time_to_resolve) AS med_time_to_resolve FROM (SELECT date_trunc('hour', time_resolved - time_failed) AS time_to_resolve FROM restore_services) AS time_to_restore_services ; -- サービス復元時間バケット CREATE VIEW time_to_restore_service_bucket AS SELECT CASE WHEN med_time_to_resolve < interval '24 hours' THEN 'One day' WHEN med_time_to_resolve < interval '1 week' THEN 'One week' WHEN med_time_to_resolve < interval '1 month' THEN 'One month' WHEN med_time_to_resolve < interval '6 months' THEN 'Six month' ELSE 'One year' END AS time_to_restore_services_bucket FROM (SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY time_to_resolve) AS med_time_to_resolve FROM (SELECT date_trunc('hour', time_resolved - time_failed) AS time_to_resolve FROM restore_services) AS time_to_restore_services ) AS p_resolve ;
Dockerコンテナの起動
以下のコマンドを実行し、コンテナを起動します。
docker-compose up -d
変更・デプロイ・インシデントの履歴の登録
まず、以下のようなコミットの履歴があるとします。
各バージョンのデプロイが以下のようになっていたとします。
バージョン | デプロイ日 | 障害発生の有無 |
---|---|---|
1.0.0 | 1/30 | なし |
1.1.0 | 2/10 | なし |
1.2.0 | 2/22 | あり |
1.2.1 | 2/26 | なし |
1.3.0 | 3/15 | なし |
1.4.0 | 4/20 | なし |
ここでバージョンの1.2.0
はデプロイ後の2月23日に障害が発見されたとします。
上記のような場合、以下のようなCSVファイルを作成します。
まず、コミット履歴のchanges.csv
ファイルを作成します。
change_id,time_created,deploy_id 28f11953,2023-02-05 12:21:10 +0900,1.1.0 762ed56b,2023-02-07 17:35:23 +0900,1.1.0 c38e5fa1,2023-02-11 10:11:55 +0900,1.2.0 ed2cbc88,2023-02-15 13:27:05 +0900,1.2.0 3eefae6c,2023-02-18 14:48:39 +0900,1.2.0 8eff3ab9,2023-02-24 17:59:18 +0900,1.2.1 1c243e69,2023-03-01 09:27:27 +0900,1.3.0 a70ce5b4,2023-03-11 15:11:44 +0900,1.3.0 96343c50,2023-03-12 16:33:13 +0900,1.3.0 1de889de,2023-03-13 17:25:39 +0900,1.3.0 b1d011ba,2023-03-22 10:03:56 +0900,1.4.0 90e0d745,2023-04-09 18:43:15 +0900,1.4.0 25386e01,2023-04-18 11:55:01 +0900,1.4.0
以下のようにコマンドを実行すると、1.0.0
と1.1.0
のバージョン間の全コミットの履歴を取得できます。これを各バージョンで実行して追記していきます。
git log 1.0.0..1.1.0 --date=iso --pretty=format:"%h,%cd,1.1.0" >> changes.csv
次に、デプロイ履歴のdeploy.csv
ファイルを作成します。これは、その組織でのデプロイ作業の記録から作成します。このsuccessは、最初はt
ですが、障害が発見された場合はf
に更新する必要があります。
deploy_id,time_created,success 1.1.0,2023-02-10 20:55:18 +0900,t 1.2.0,2023-02-22 21:18:37 +0900,f 1.2.1,2023-02-26 20:35:25 +0900,t 1.3.0,2023-03-15 22:05:12 +0900,t 1.4.0,2023-04-20 21:19:09 +0900,t
最後に障害の履歴のincidents.csv
ファイルを作成します。これはチケットの作成日時から作成します。
ticket_id,time_created,deploy_id_resolved ISSUE-1234,2023-02-23 12:55:18 +0900,1.2.1
ファイルの作成が完了したら、以下のようにDBにcopyコマンドを実行して登録します。
source ./.env export PGPASSWORD psql -h localhost -U postgres -c "\copy deployments from ./deployments.csv delimiter ',' header" psql -h localhost -U postgres -c "\copy incidents from ./incidents.csv delimiter ',' header" psql -h localhost -U postgres -c "\copy changes from ./changes.csv delimiter ',' header"
Grafanaで可視化
ログインとDB接続設定
http://localhost:3000
をブラウザで開きます。
初期パスワードは
- username:
admin
- password:
admin
になっています。ログイン後にパスワードの変更を求められるので変更します。
ログイン後、左のメニューでConnect data
を選択します。
Connect dataの検索部分でPostgreSQL
と入力し、検索結果のPostgreSQLをクリックします。
Create a PostgreSQL data source
をクリックします。
data source画面で以下のように接続情報を入力し、Save & test
ボタンをクリックします。
バケットの可視化
Home画面に戻り、右上のAdd
メニューでVisualization
を選択します。
左下で
- Table:
deployment_frequency_bucket
- Column:
deployment_frequency
を選択し、右側で
- Stat
- Title:
デプロイ頻度バケット
- Fields:
deployment_frequency
を選択し、Run query
ボタンをクリックします。
更に、右側の下部の Value mappings
, Thresholds
を以下のように設定します。色の設定はチームの目標に合わせて変更します。
設定が完了したら、右上のApply
ボタンをクリックします。
同様にリードタイム、変更障害率、サービス復元時間についてもバケットの設定をします。
以上で、Four keysの結果を可視化できました。
詳細データを可視化
時系列データを可視化
次に、時間によって変化する指標を可視化します。ここでは例として、月毎のデプロイをグラフ化します。
左下で、テーブル、カラムを選択した後、Run query
ボタンをクリックします。
右上で、
- Bar chart
- Title:
月毎のデプロイ
としてApply
ボタンをクリックします。
ヒストグラム情報を可視化
最後に、変更のリードタイムの分布を可視化します。
左下のクエリの入力ペインでcode
をクリックし、以下のクエリを入力しRun query
ボタンをクリックします。
SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY lead_time) FROM (SELECT deployments.deploy_id, extract(epoch FROM (deployments.time_created - changes.time_created)) / (60 * 60 * 24) AS lead_time FROM deployments, changes WHERE deployments.deploy_id = changes.deploy_id AND deployments.time_created > date_trunc('month', current_date + interval '-3 months')) AS lead_times GROUP BY lead_times.deploy_id
右側で以下のように入力します。
- Histogram
- Title:
リードタイムの分布
- Bucket size:
1
更に、右下で
- Display name:
日数
としてApply
ボタンをクリックします。
ダッシュボードの完成
Home画面に戻り、パネルの位置や大きさを調整したら、完成です。
まとめ
このようにFour keysの指標を可視化してみると、チームの成長が分かり生産性の向上に取り組むモチベーションアップに繋がるのではないかと思います。
みらい翻訳では、一緒に開発を進めてくれるエンジニアを募集しています!
ご興味のある方は、ぜひ下記リンクよりご応募・お問い合わせをお待ちしております。