ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

PipeCD で Google Cloud に対して Terraform Apply する

前回は Quick Start として Kubernetes の canary-example を動かしてみた。

blog.chaspy.me

今回は wait-approval の example を参考に Terraform で、gcs を backend に、cloud storage 上に object を作る。

github.com

KeyPair 作成

Service Account といった secret key を安全に git 上に commit するには暗号化する必要がある。

PipeCD では WebUI 上で Encryption する方法が提供されている。

pipecd.dev

基本的にはこのドキュメントのまんまで、Key Pair を作成し、Piped を deploy する際に --set-file flag で Key Paid を格納、piped の configuration でそのファイル名を指定してやることで有効化される。

$ openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private-key 
..+++
.............................................+++
$ openssl pkey -in private-key -pubout -out public-key

Piped のデプロイ

今回も Piped は local の kind 上で動かしている。Control Plane は別の検証環境を使っている。

このように helm で deploy している。前のステップで作成した KeyPair を格納している。

$ helm upgrade -i dev-piped pipecd/piped --version=v0.20.2 --namespace=pipecd --set-file config.data=piped.yaml --set-file secret.secretManagementKeyPair.publicKey.data=./public-key --set-file secret.secretManagementKeyPair.privateKey.data=private-key

piped の configuration piped.yaml は以下。

apiVersion: pipecd.dev/v1beta1
kind: Piped
spec:
  projectID: pipecd
  pipedID: 111111111-2222-3333-4444-5555555555
  pipedKeyData: secret=
  apiAddress: apiaddress:443
  webAddress: https://webendpoint
  syncInterval: 1m

  git:
    sshKeyData: secret==

  repositories:
    - repoId: chaspy-test
      remote: git@github.com:pipe-cd/chaspy-dev.git
      branch: main

  chartRepositories:
    - name: pipecd
      address: https://charts.pipecd.dev

  cloudProviders:
    - name: kubernetes-default
      type: KUBERNETES
    - name: terraform-dev
      type: TERRAFORM
      config:
        vars:
          - "project=pipecd-dev"
    - name: cloudrun-dev
      type: CLOUDRUN
      config:
        project: pipecd-dev
        region: asia-northeast1
    - name: lambda-dev
      type: LAMBDA
      config:
        region: ap-northeast-1
        profile: default
    - name: ecs-dev
      type: ECS
      config:
        region: ap-northeast-1
        profile: default

  secretManagement:
    type: KEY_PAIR
    config:
      privateKeyFile: /etc/piped-secret/secret-management-private-key
      publicKeyFile: /etc/piped-secret/secret-management-public-key

(pipedKeyData と sshKeyData は set file で渡すのが良い。このように base64 にして直接記入しても良い)

cloudProvider の Terraform は少なくとも有効にしておくこと。

Application の作成

Web UI で Application を作成する。

こんな感じで作成すれば良い。 f:id:take_she12:20211109155521p:plain

GCP Project と Service Account の作成

pipecd-dev という Project を作り、service account を作成します。Role にはいったんプロジェクトオーナーをつけました。(本当に運用するなら最小権限にしましょう)

Service Account Key を作成したら、json ファイルをダウンロードしましょう。

Service Account の暗号化

Service Account key の json file を WebUI の Application の右側から暗号化しましょう。

Application の右の3点リーダーから Encrypt Secret を選択

f:id:take_she12:20211109160157p:plain

Service Account を貼り付けて Encrypt

f:id:take_she12:20211109160159p:plain

Encrypted secret が表示されます。

f:id:take_she12:20211109160202p:plain

Encrypted Secret を .pipe.yaml に commit

https://github.com/pipe-cd/chaspy-dev/blob/main/terraform/wait-approval/.pipe.yaml#L14

このように encrypted secret の value に貼って push しましょう。

.pipe.yaml の内容

apiVersion: pipecd.dev/v1beta1
kind: TerraformApp
spec:
  input:
    terraformVersion: 0.12.26
  pipeline:
    stages:
      - name: TERRAFORM_PLAN
      - name: WAIT_APPROVAL
      - name: TERRAFORM_APPLY
  encryption:
    encryptedSecrets:
      serviceAccount: (encrypted secret)==
    decryptionTargets:
      - .credentials/service-account.json

この yaml は読んでそのままで、terraformVersion, stages, 暗号化された servicceAccount が記載されています。TerraformApp の Configuration のドキュメントは以下です。

pipecd.dev

Pipeline (stage) に設定可能な値は以下に記載されています。

pipecd.dev

今回は読んでそのまま、Plan を実行し、approval を待って、人間が approve したら apply する、という pipeline が組まれています。

Pipeline の実行

それではいよいよ実行していきましょう。

正しく設定が行われていれば、Application は Deploying の status になります。

f:id:take_she12:20211109160823p:plain

Deployments タブから移動すると、Approval を待っている状態であることがわかります。

f:id:take_she12:20211109160926p:plain

Plan 結果が画面下部に記載されています。

f:id:take_she12:20211109161013p:plain

Wait Approval をクリックすると Approve ができます。

f:id:take_she12:20211109161049p:plain

Apply 成功しました。

f:id:take_she12:20211109161129p:plain

まとめと感想

PipeCD を使って Terraform の Plan と Approval, Apply を一通り実行してみました。

Terraform の場合、state に対して適用するかどうかの 0/1 になるので、ANALYSIS のような metrics 結果との組み合わせも、Canary Deployment みたいなことの実施も PipeCD レイヤーで持つのは難しいように感じます。何かしらの healthcheck endpoint なり metric が外部にあり、それが ready であれば apply, そうでない限り apply する、みたいなことは可能かもしれません。

競合になるのは Terraform Cloud あるいは自前の CI/CD と GitHub PullRequest による Deploy flow だと思います。Terraform に限って言えば(お金はかかるものの)Terraform Cloud がユーザの権限管理含めてリッチでしょうし。Plan による CI を実施して PR での Approve を実現できる点では GitHub Flow も遜色ありません。tfnotify https://github.com/mercari/tfnotify や tfcmt https://github.com/suzuki-shunsuke/tfcmt を組み合わせれば GitHub や (tfnotify の場合)slack への通知も可能です。

また、PipeCD は CD しかサポートしない点も GitHub Flow に比べてのデメリットになるかもしれません。tfsec や tflint など、CI での検査を組み込む場合は、自前で設定する必要があるものの、GitHub Flow にメリットがありそうです。

一方、すでに PipeCD Control Plane がある環境で、かつ Plan と Apply が実行できればいい、という状況であれば PipeCD は非常に楽で強力な選択肢になると思います。複雑になりがちなデプロイ設定も yaml で stage を書くだけですし、通知設定も yaml を書くのみです。どのみち必要になる Terraform の state backend の設定を終えて Application の箱と piped をデプロイすれば安全なデプロイパイプラインを簡単に組むことができます。

PipeCD という、複数の Cloud Propvider をサポートする共通のデプロイコンポーネントを採用するメリットは、今後他の Provider を同時に使った場合のシナジーであったり、マルチテナントを採用し、各開発チームごとに任意の Deploy Pipeline を自律的に設定可能な点があると思います。(これは Terraform provider に限りませんが)Many repo で細かく Terraform repository / state を切る場合は、この準備の楽さはメリットになるかもしれません。

引き続き他の Provider のデプロイを試していきます。

PipeCD の Quick Start を kind 上で動かす

pipecd.dev

これを動かす。

PipeCD とは

汎用的なデプロイツール。Kubernetes だけでなく、Cloud Run, Lambda, Terraform, ECS をサポートしている。

Control Plane としての PipeCD と、エージェント的に動く Piped の2つのコンポーネントからなる。

WebUI を提供しているのは Control Plane。

エージェントである Piped が実際のデプロイを行う。

デプロイパイプラインは Git Repository に保存し、Piped がそれを参照し、その指示通りにデプロイを行う。

用語や概念は Concepts ページに掲載されている。

pipecd.dev

Architecture

今回は Control Plane, piped ともに local の kind cluster に行う。

適宜公式ドキュメントを参照しながら自分の理解を整理していく。

Prerequisite

筆者の動作環境は macOS Big Sur version 11.3.1

helm

helm.sh

v3.7.1

$ helm version
version.BuildInfo{Version:"v3.7.1", GitCommit:"1d11fcb5d3f3bf00dbe6fe31b8412839a96b3dc4", GitTreeState:"clean", GoVersion:"go1.17.2"}

kubectl

kubernetes.io

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.3", GitCommit:"ca643a4d1f7bfe34773c74f79527be4afd95bf39", GitTreeState:"clean", BuildDate:"2021-07-15T21:04:39Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.1", GitCommit:"5e58841cce77d4bc13713ad2b91fa0d961e69192", GitTreeState:"clean", BuildDate:"2021-05-21T23:01:33Z", GoVersion:"go1.16.4", Compiler:"gc", Platform:"linux/amd64"}

kind

kind.sigs.k8s.io

$ kind version
kind v0.11.1 go1.16.4 darwin/amd64

クラスタを作成しておく。

$ kind create cluster --name pipecd-quickstart 
Creating cluster "pipecd-quickstart" ...
 ✓ Ensuring node image (kindest/node:v1.21.1) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-pipecd-quickstart"
You can now use your cluster with:

kubectl cluster-info --context kind-pipecd-quickstart

Not sure what to do next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/

Install

1. Cloning pipe-cd/manifests repository

pipe-cd/manifests を clone しておく。

2. Installing control plane

Control Plane を install.

helm -n pipecd install pipecd ./manifests/pipecd --dependency-update --create-namespace \
    --values ./quickstart/control-plane-values.yaml

NAME: pipecd
LAST DEPLOYED: Sat Oct 30 20:10:09 2021
NAMESPACE: pipecd
STATUS: deployed
REVISION: 1
TEST SUITE: None

pod が deploy される。

$ kb get po -n pipecd
NAME                              READY   STATUS    RESTARTS   AGE
pipecd-cache-6fbf867d56-ln6wn     1/1     Running   1          16h
pipecd-gateway-5ccd5cb8f8-z2fpp   1/1     Running   27         16h
pipecd-minio-6ffb69ff5-zvbv9      1/1     Running   1          16h
pipecd-mysql-55bc4f9495-rlphc     1/1     Running   1          16h
pipecd-ops-59d6b9bc9d-kv4bn       1/1     Running   12         16h
pipecd-server-77fcbc96f4-gjdmm    1/1     Running   11         16h

3. Accessing the PipeCD web

PipeCD の Web UI にアクセスする。

$ kubectl -n pipecd port-forward svc/pipecd 8080

http://localhost:8080 にアクセスし、quikcstart project に hello-pipecd の user name / password でログインする。

なおこの値は以下でプリセットされている。

github.com

4. Adding an environment

次に Environment。これは単なる Logical Group で、以降 Piped を設定するために必要なので作る。

5. Installing a piped

次にエージェントである Piped をインストールしていく。まずは Control Plane 側で piped の id と key を発行する。

id と key は控えておく。

.env につっこむ。

export PIPED_ID="3a23d2d6-e0ce-4659-98ea-721e07055cc3"
export PIPED_KEY="1m62gdfvmw2tvbujroqcnav6m8h6f2aqnj9m2t3ajarm1288vg"

これで Control Plane 側に箱ができたので、この id と key を使って local cluster に Piped をデプロイしていく。

piped-values.yaml はこんな感じになる。

args:
  insecure: true

config:
  data: |
    apiVersion: pipecd.dev/v1beta1
    kind: Piped
    spec:
      projectID: quickstart
      pipedID: 3a23d2d6-e0ce-4659-98ea-721e07055cc3
      pipedKeyFile: /etc/piped-secret/piped-key
      apiAddress: pipecd:8080
      webAddress: http://pipecd:8080
      syncInterval: 1m
      repositories:
        - repoId: examples
          remote: https://github.com/chaspy/examples.git
          branch: master

repositories には Deploy Pipeline が配置されている git repository を指定する。

pipe-cd/examples を fork しておく。

github.com

piped を helm でデプロイする。

$ helm -n pipecd install piped ./manifests/piped \
  --values ./quickstart/piped-values.yaml \
  --set secret.pipedKey.data=$PIPED_KEY

NAME: piped
LAST DEPLOYED: Sun Oct 31 12:23:24 2021
NAMESPACE: pipecd
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Now, the installed piped is connecting to .

piped がデプロイされました。

$ kb get po 
NAME                              READY   STATUS    RESTARTS   AGE
pipecd-cache-6fbf867d56-ln6wn     1/1     Running   1          16h
pipecd-gateway-5ccd5cb8f8-z2fpp   1/1     Running   27         16h
pipecd-minio-6ffb69ff5-zvbv9      1/1     Running   1          16h
pipecd-mysql-55bc4f9495-rlphc     1/1     Running   1          16h
pipecd-ops-59d6b9bc9d-kv4bn       1/1     Running   12         16h
pipecd-server-77fcbc96f4-gjdmm    1/1     Running   11         16h
piped-58575956f7-jqxwr            1/1     Running   1          73s

6. Configuring a kubernetes application

次に Application を設定していきます。Application とはデプロイ対象のことです。例えば Kubernetes であればデプロイ対象の manifest - Deployment や Service に加えて、それをどのようにデプロイするかの Strategy や Notification などが記述できる KubernetesApp という Custom Resource を .pipe.yaml という名前で git repository に配置します。

公式の Example 通り、canary example を使います。

github.com

f:id:take_she12:20211031123036p:plain

しばらく待つと Sync されます。

f:id:take_she12:20211031123424p:plain

初回は Application の状態が表示されません。

f:id:take_she12:20211031123502p:plain

こちらももうしばらく待つとちゃんと表示されます。

f:id:take_she12:20211031123917p:plain

7. Let’s deploy!

それでは Deployment の対象の Image のバージョンを変更してみます。

v0.7.0 から v0.8.0 に変更します。こういう PR になります。

github.com

PR をマージしたあと、Web UI を眺めていると...

新しい Deployment が作られました。

f:id:take_she12:20211031124449p:plain

一瞬 OutOfSync の Status になりますが、すぐに戻りました。

f:id:take_she12:20211031124454p:plain

Deploy 完了です。

f:id:take_she12:20211031124633p:plain

改めて今回の KubernetesApp の定義を見てみます。

github.com

# Deploy progressively with canary strategy.
apiVersion: pipecd.dev/v1beta1
kind: KubernetesApp
spec:
  pipeline:
    stages:
      # Deploy the workloads of CANARY variant. In this case, the number of
      # workload replicas of CANARY variant is 10% of the replicas number of PRIMARY variant.
      - name: K8S_CANARY_ROLLOUT
        with:
          replicas: 10%
      # Wait 10 seconds before going to the next stage.
      - name: WAIT
        with:
          duration: 10s
      # Update the workload of PRIMARY variant to the new version.
      - name: K8S_PRIMARY_ROLLOUT
      # Destroy all workloads of CANARY variant.
      - name: K8S_CANARY_CLEAN

KubernetesApp の Configuration の説明は以下のページにあります。

pipecd.dev

pipecd.dev

この定義によると、まず 10% の Replicas を新しいバージョンに切り替えて、10秒待ったあとに、Primary の Deployment =残りを切り替えて、最後に Canary 用の Deployment を削除します。

Primary や Canary という概念の説明、そして各ステージで実行できる操作については K8S-[PRIMARY|CANARY]_* は公式ドキュメントに記載されています。

まとめ

今回動かしたものをまとめるとこんな感じになる。

  1. PipeCD の Control Plane 上で用意した Components は Environment, Piped, Application の3つ。
    • Environment は単なる Logical Group
    • Piped は Control Plane 上で登録したあと、ID と Key を実際にエージェントとして動く Piped に認識させて通信させる
    • Application は Git Repository 上にあるリソース定義とデプロイ設定が書かれたものを参照する。今回は KubernetesApp というものがデプロイ定義になる。
  2. エージェントとして動く Piped は Git Repository 上のデプロイ定義および manifests file の変更を Watch し、GitOps 的に対象リソース(Application)をDeploy する
    • 今回の例だと Deployment の image version を変更した
    • Canary 設定に則って新規バージョンを Deploy した

※実際に Control Plane として動いている Components の詳細は割愛した。また、それらが "PipeCD Components" を動かしている。

ArgoRollouts / ArgoCD との比較

今回の例、Kubernetes Deployment を Canary 的にデプロイするというケースにおいてのみいえば、両者に大きな差はない。

用語を見ても ArgoRollouts とできることは大差なさそうである。(Analythis の詳細や Integration には差があるかもしれない)

UI も ArgoCD と結構似ている。

argoproj.github.io

argo-cd.readthedocs.io

Argo Rollouts が Kubernetes Native な Resource として Replica Set を操作することに対して、PipeCD はあくまで Deployment を操作して Canary Deploy を実現している。

PipeCD の特徴

この quickstart をなぞっただけでわかることは少ないが、Multi Tenacy と Kubernetes 以外のリソースも GitOps による Deploy ができる点が大きな特徴のように見える。

Overview にあるところの Multi-provider & Multi-Tenancy だ。

pipecd.dev

社内の共通デプロイ基盤として作られた経緯もあり、おそらく社内で1つ、あるいは求められる信頼性やある程度の大きさの組織単位で Control Plane を構築、運用し、あとは各チームや運用主体の単位に Piped を Install して自律的にデプロイの設定を入れていく。

CI と CD の分離はもちろんのこと、CD を Piped というエージェントを入れて行うことで、デプロイの設定も各開発チームで自律的にできるような世界を目指しているように見える。

Kubernetes のデプロイだけでいえば ArgoCD & ArgoRollouts(Canary Release)、FluxCD があるし、Kubernetes や ECS などのよりメタなデプロイツールとしては Waypoint がある。今後も他のツールを使ったり、PipeCD の他の Cloud Provider に対するデプロイも試していきたい。

【WSA 研】Site Reliability Engineering における重要領域とパフォーマンス指標の提案

ずいぶん公開に時間が経ってしまった。6/4 か。。。特に出せなかった理由はない。より「ちゃんとした」形で出したいと思っていたが、そんな日はこないので当時のまま公開する。

WSA 研に初参加し、はじめて自分の身の回りの仕事、SRE の関心対象に対して計測を行なった。当時は本当に手探りであったが、この時泥臭くデータを取り、考察したことが2ヶ月後の今に確実に繋がっている。貴重な機会をくれた WSA研のメンバーに感謝したい。

当日は参加者からたくさんフィードバックをもらえた。その時いただいた意見は今に活きており、より実務に生かすことができている。

修士卒業以来久しぶりに「研究」っぽいことをしたが新鮮で楽しかった。ビジネスと研究、行ったり来たりするのいいかもな、と思った。


本資料は第8回WebSystemArchitecture研究会の予稿です。

以下が当日使ったスライドです。

背景

Site Reliability Engineering(以下、SRE)はインフラ・サービス運用をソフトウェアで制御することで、スケーラビリティや信頼性を担保する役割である。また、SRE は DevOps を実装しているという見方もあり*1、サービスのデリバリーを高速に繰り返すことにも関係が深い。加えて、システムの信頼性のためにはクラウドを含めたインフラの管理や設計にも関わるなど、その業務範囲は多岐に広がる。そしてこれらはビジネス・組織の規模や性質、組織内の分担によっても大きく異なる。そのため、多くの企業で SRE をどう採用し、どのように活かすのかの解答を探すのが難しい。*2

加えて、ソフトウェア・プロダクトを開発して利益を得る企業のプロダクト開発組織は、通常ビジネス KPI を定め、その改善のために開発を行うが、SRE の場合そういった指標を持って改善を行う"プロダクト開発的なアプローチ"はまだ一般的ではない。ただし、DevOps 領域においては書籍 「Lean と DevOps」の科学」*3でのキーメトリクスとして「デプロイの頻度」「変更のリードタイム」「MTTR」「変更失敗率」が紹介されており、エリート企業はこのどれもが優れていることが報告されている。

本発表では、前半で、筆者が所属する企業*4での経験を元に、SRE の業務領域のうち、重要な領域とその領域ごとの関係性について考察する。これによって、同様な規模の組織における SRE の活動方針のモデルを定義する。後半では前半で定義した領域のうち、2つに着目し、「Lean と DevOps の科学」の紹介内容を拡張しながら、プロダクト開発的なアプローチを SRE 業務全体に適用できるパフォーマンス指標とその測定方法を提案する。

SRE が扱う5つの領域

SRE が扱う領域は企業によって異なる。本研究では、筆者が所属する Quipper Ltd. が提供するサービス*5のように、クラウド上での Web サービスを提供する企業の SRE が関わる領域を考察していく。

クラウド上でサービスを構築していることから、今回、AWS Well-Architected*6 Framework を参考にした。この資料によると、Five Pillars として「Operational Excellence」「Security」「Reliability」「Performance Efficiency」「Cost Optimization」があげられている。AWS Well-Architected はクラウドアーキテクトが、スケーラブルで信頼性のあるクラウドインフラを構築するためのガイドである。

「Lean と DevOps の科学」と「AWS Well-Architected」の両者を参考にしつつ、筆者が所属する環境では以下の5つを重要領域だと捉えた。

  • Reliability
  • Developer Productivity
  • Platform
  • Security
  • Cost

1つずつ内容を説明する。

Reliability

その名の通り、Site(Service)の信頼性である。AWS Well-Architected でいう「Reliability」「Performance Efficiency」が関連する。

Developer Productivity

開発者生産性である。DevOps が関連する。

Platform

Kubernetes などに代表される、Application Platform を開発者に提供している。

Security

AWS Well-Architected にも存在する。また、インフラだけに閉じず、Security Incident は一度起こすと一気にビジネスが破綻しかねない重要な要素である。

Cost

AWS Well-Architected にも存在する。クラウドに限らず、コンピューティングリソースを使うために Cost は発生する。

領域の関係性

これら5つの領域はそれぞれが密接に関わっている。

以下、その関係性について説明する。

Platform の特性

Developer のための Application Platform は、それ自体がその上のレイヤーの他の全ての分野を支える関係性にある。

Trade-off

  • Reliability(可用性)をあげようとするとコストがあがる。99.9% という数字で表される Availability の桁を1つあげるには非常に大きな労力がかかる。*7
  • Security を高めようとすると、利便性, Developer Productivity を損ねる可能性がある

提案指標と測定方法

今回は5つの領域のうち、「Lean と DevOps の科学」に関連が深い"Developer Productivity" と、SRE の本丸である"Reliability" の2つの領域に絞って指標を提案する。

Reliability

Site "Reliability" Engineering を考える上で、サービスの安定稼働は第一のミッションであり、いかに他の指標を満たそうともサービスの信頼性を落としては意味がない。

しかし、サービスの可用性を指標にはしない。サービスレベルはビジネス要求によって異なる。また、サービスレベルを高めるとコストも高まる。サービスレベル目標(Service Level Objectives) として、信頼性と機能開発のアジリティのバランスをとるためのツールでしかないので、この数値自体を目標値にするのは本質的ではない。

そこで今回は、「Lean と DevOps の科学」でも紹介されていた、"MTTR / Mean time to recovery" を採用した。これを小さくするために、障害を局所化したり、すぐに復旧できるようにする必要がある。*8

MTTR 測定の自動化は非常に難しい。"障害"の判定基準は企業により異なるからである。仮に可用性(Uptime)を採用したとしても、それだけでは全てのシステム障害をカバーすることはできない。そのため、障害と判定されたインシデントの報告フローに MTTR を項目として組み込んで手動で計測する。

なお、MTTR を指標とすべきかどうかは懐疑的な面がある。その一番の理由は障害の発生数が非常に少なく、指標として追いかけるには母数が少なく、ばらつきが大きくなってしまうことが予想されるからである。*9しかし、重要指標であることには変わりなく、数が少ないことは手動でのトラッキングも可能であることから採用した。

Developer Productivity

以下の4つを指標としてあげた。それぞれの指標は開発環境と本番環境両方で区別して計測する。

  • デプロイ回数
  • デプロイ時間
  • CI 安定性
  • 変更失敗率

CI 安定性以外は「Lean と DevOps の科学」であげられた基準と同じあるいは似た指標である。また、「デプロイ時間」は該当ブランチにマージしてから、実際にコードが環境に適用されるまでの時間である。「Lean と DevOps の科学」における「リードタイム」は「コードのコミットから本番稼働までの所要時間」であるが、レビューの時間を含むと変数が多くなってしまうため、マージから本番稼働までの所要時間、とした。

CI 安定性は「デプロイ時間」に内包される(CI が失敗すれば Rerun を行う必要があるので、デプロイ時間は長くなる)が、より分析をしやすくするために CI 安定性(CI 成功率)も指標に掲げた。

これらの指標は基本的に CI サービスから取得する。CI サービスがそのような metrics を提供していればそれを利用すれば良い。もし提供していない場合でも、CI の Job / Workflow で時間を計測し、何らかの時系列ストレージに情報を送ることで取得できる。Monitoring SaaS*10では Custom Metrics がサポートされているので、こちらをストレージ兼可視化システムとして採用することができる。

変更失敗率は、コード適用後、何かしらの"不具合"が発生した件数である。これは"不具合"の定義に依存してしまうため、計測が難しい。そこで、原則不具合があった場合は即 Revert をして、その後 HOTFIX を適用する運用とし、本番環境に対応するブランチにおける Revert commit の数を計測する。

指標のまとめ

領域ごとの指標と測定方法をまとめた。

領域 指標 測定方法
Reliability MTTR 障害発生時に、障害報告フロー内の必須項目として手動で計測
Developer Productivity デプロイ回数 CI サービスの metrics
Developer Productivity デプロイ時間 CI サービスの metrics
Developer Productivity CI 安定性 CI サービスの metrics
Developer Productivity 変更失敗率 本番環境デプロイに対応するブランチの Revert commit の数

計測結果

MTTR

以下、筆者が所属する企業で計測した MTTR を複数の観点で図にした。

f:id:take_she12:20210604042715p:plain

これは TTR の散布図である。2019年から2020年前半にかけて、1000分以上停止しているインシデントが3件存在している。それ以降はそのようなインシデントはなく、減少傾向にある。

f:id:take_she12:20210604042720p:plain

これは半年ごとの MTTR の平均である。2019年後半に MTTR は増大し、その後減少傾向にある。

f:id:take_she12:20210604042723p:plain

これは横軸を TTR としたヒストグラムである。Incident Response in SRE Figure2 で紹介されているグラフと近い形を示しており、少数の長時間解決しないインシデントと、多数の短時間で復旧したインシデントがあることがわかる。

デプロイ回数

以下、筆者が所属する企業における、Production に Deploy した回数である。

f:id:take_she12:20210604044419p:plain

なお、筆者が所属する企業特有の事情があるので説明する。筆者が所属する企業は monorepo*11 を採用しており、かつ 1つの Database を複数アプリケーションで共有するDistributed Monolithとなっている。この monorepo で master branch に merge された場合、この Distributed Monolith を構成する全サービスがデプロイされる。今回プロットしたデプロイ回数は Distibuted Monolith へのデプロイ回数である。Microservice 化は徐々に進んでおり、その場合別のブランチへのマージを契機に microservice へのデプロイが行われる。

デプロイ回数が減少傾向にあるのは、microservice 化が進んでいるからだと考えられる。

デプロイ時間

f:id:take_she12:20210616035334p:plain

これは master branch の CI Workflow の median である。(Duration Period は 7 Days)なお測定開始は2021年2月である。

CI 安定性

f:id:take_she12:20210604050634p:plain

これは master branch の CI Workflow の成功率である。(Duration Period は 7 Days)なお測定開始は2021年2月である。

変更失敗率

f:id:take_she12:20210604051018p:plain

これは master branch を base branch とする、すべての merge 済み PR の数と、"Revert" という Title が含まれている PR の数である。

f:id:take_she12:20210604051022p:plain

これは master branch を base branch とする、すべての merge 済み PR の数に対する "Revert" という Title が含まれている PR の比率である。

まず、2020年 1st half に merge 済み PR が増えている理由は、本番環境に対する Kubernetes manifest の変更が行われる場合、develop branch を経由する必然性がないため、HOTFIXとして直接 PR 作成されたことがある。

次に、2021年 1st half に Revert PR が増えている理由として、Argo Rollouts*12による Canary Release を採用している過程で、Canary Strategy を1度有効にして Promotion し、終わったあとそれを Revert する動きが多くあることがあげられる。これは Canary Strategy は通常有効にしておらず、Kubernetes Deployment と同様に Rolling Update を行うが、重要なリリースのみ Canary を行っているためである。

このような事情より、Revert PR は必ずしも変更失敗を示していないため、別の方法での変更失敗率の計測が必要である。

まとめと今後の展望

今回、SRE が関わる領域において分類し、Reliability と Developer Productivity 領域において、「Lean と DevOps の科学」を参考にしながらパフォーマンス指標を提案し、計測を行なった。

MTTR に関しては自動的に計測するツーリングが必要だと考える。今回はデータソースとして Postmortem を用いたが、Postmortem を書く基準が定まっていなかったり、Incident によっては発生時間、検知時間、復旧時間が記載されていないものもあった。MTTR 自体が有効な指標かどうかはまだ判断が難しいが、計測することによって、Incident Response の型化が期待できる。

Developer Productivity 領域の指標は、いずれも monorepo の master branch のみを対象であった。そのため、原則 Weekly Release であり、それに対してどれだけ HOTFIX が行われたか、というものを示すに止まった。今後は microservice 群にも範囲を拡大し、より広い範囲でのデプロイ回数の計測を行いたい。変更失敗率に関しては、何らかのルールとプロセスの追加によって計測可能にするアプローチが必要だと考える。(例えば PR に label をつけるなど)

今後、このようにあらゆるものを計測して可視化するアプローチが、SRE だけに止まらず、様々な領域で有効になっていくと考えられる。そのため、計測して可視化する一般的なアーキテクチャやデザインパターンについても探求したい。*13

また、今回提案した領域は飽くまで筆者が所属する規模の組織での話であることから、組織やビジネスの規模に対して SRE Practice をどのような順番で適用すべきかの Trail map のようなものを定義できれば、今後新規に SRE を採用する企業にとって大きな貢献になるだろう。

謝辞

本発表で話した内容は以前複数の会社の SRE Member と会話したことが元になっている。その場に参加いただいた @katainaka0503 , @kazoooto , @taisho6339 に心から感謝する。

筆者が所属する Quipper Ltd. の SRE Team およびすべての関係者に心から感謝する。

*1:https://www.ahsan.io/2019-05-26-sre-implements-devops/#:~:text=Site%20Reliability%20Engineering%20(SRE)%20and,team%20building%20and%20running%20software.

*2:そのような理由で、各社の取り組みを共有するSRE Loungeというコミュニティも存在する

*3:https://book.impress.co.jp/books/1118101029

*4:プロダクト2つ、開発者数100名程度

*5:https://studysapuri.jp

*6:https://aws.amazon.com/architecture/well-architected/?wa-lens-whitepapers.sort-by=item.additionalFields.sortDate&wa-lens-whitepapers.sort-order=desc

*7:そのバランスを取るツールが Service Level Objectives である

*8:なお、応用として障害発生時の MTTR に Number of the affected user をかけることも指標になりうるが、今回は運用が複雑になるため採用を見送った。

*9:Incident Metrics in SREでも MTTR を指標として採用すべきではない "these statistics(MTTR or MTTM) are poorly suited for decision making or trend analysis in the context of production incidents." とされている

*10:Datadog, Mackerel 等

*11:https://en.wikipedia.org/wiki/Monorepo

*12:https://argoproj.github.io/argo-rollouts/

*13:筆者が Cloud Native Days Spring 2021 Online で発表した Metric-Driven Decision Making with Custom Prometheus Exporter はそのパターンの1つと言えるだろう

ソフトウェアエンジニアリングと認知

職業ソフトウェアエンジニアを7年+ やってきて、いわゆる新卒、ジュニアからキャリアをスタートし、今は Site Reliability Engineering 領域で Lead をしている。

こうした中で、自分自身のことを振り返ると、ソフトウェアエンジニアリングとは自己認知との戦いではないか、と思う。

自分自身、自分の認知の歪みでパフォーマンスが著しく出なかった経験が何度もあった。そして認知の矯正によってパフォーマンスが急に出るようになった経験、その両方がある。

昨日たまたま、エンジニアリングと認知について話す機会があったのでついでに記録しておく。

ソフトウェアエンジニアリングに対する認知

ソフトウェアエンジニアリング、システムプログラミング、なんでもいいが、こういったものを取り扱ってビジネス価値を発揮している我々にとって、機械の上で動く、なんらかの入力を通じて動く何某は当然目に見えないし、ソースコードを丹念に追わないと動作原理は理解できない。

そういった「理解できない」こと。「わからないこと」と呼ぼう。あるいは「問題」と呼ぼう。新規開発だろうが既存プロダクトの保守だろうが、大きく我々は「問題」を解決している。その問題解決のためにはこれら「わからないこと」と正しく向き合う必要がある。

「わからないことと正しく向き合う」ということは、対象に対して正しく認知することに他ならない。

問題を正しく認知するとはどういうことか。そのために、どのようなアプローチが有効か。

  • エラーメッセージを正しく読みとり、その指示に従う
  • 自分がした行動と結果を記録し、その因果を捉える
  • 問題の再現させ、発生条件を狭める
  • 内部の仕組みを理解するために、該当するソースコードを読む

正しく認知できてないとはどういうことか。誤ったアプローチとはどういうことか。

  • google で検索し、検索結果に出る対象コマンドをコピペで打ってみる
  • 対象のソフトウェアの参考書を引っ張り出して0から読み直す
  • 同僚に「うまくいきません!」と助けを求める

上記は別に 100% 間違っているわけではない。たまたまそれで解決することもある。しかし、その行動は問題の対象に正しく向き合っておらず、有効性の低いアプローチだと思うだろう。

文字にすればびっくりするが、ここまで極端でないにせよ、後者のアプローチのようなことを取っていたことが僕にはあった。

人間は何らかの外的要因、例えば精神的な安定性であったり、周囲に対する心理的安全性であったりで驚くほど簡単に認知能力が下がり、パフォーマンスが出なくなる。

ここで言いたいのは、

  • 認知能力がソフトウェアエンジニアリングにもっとも重要であり、
  • その認知能力は外的要因で簡単に狂ってしまうものであり、
  • 自身の認知能力を認知する、メタ認知こそがジュニアソフトウェアエンジニアにとって最初に超えるべき壁である

ということだ。ある程度経験を積んできた僕たちは、こういったことを伝え続けるとともに、認知能力を下げない環境づくりに力を割かないといけないと思う。「経験を積むしかない」ことはある意味事実であることは認めるが、それ以上にできることがあると思う。

チームメンバーに対する認知

ジュニアソフトウェアエンジニアの育成、という文脈からもわかるように、(おそらく圧倒的多数を占める)チームでのソフトウェア開発について取り上げる。複数人で協業してソフトウェアを扱う仕事をするにあたって、前述したソフトウェアだけでなく、人間というチームメンバーとも向き合う必要がある。

"正しさ"は環境によって異なる。チームが醸成する価値観によって異なる。ある問題に対してどうアプローチを取るかに対して、絶対的な正解があるとはいえない。だからこそ、もっとも正しいらしいものを、時にはチームで議論をして合意を得ながら、時には個人の責任感を持って、意思決定していく。

チームメンバーはいろんなコミュニケーションの方法を使ってメンバーと協業していくだろう。その代表的な方法として我々は言葉を使う。「こうしたほうがいい」「この方法だとこの面でデメリットがある」「ここの考慮事項を考えておいたほうがいい」様々だ。

しかし、「こうしたほうがいい」という言葉1つにとっても、これを「正しく認知する」ことが求められる。コードレビューの文脈でもよくあるが、コードへの指摘が個人攻撃だと捉えることは十分にありえる。誰もが正しく認知できるわけではないのだ。こうしたほうがいい -> お前は間違っている -> お前のコードは最悪だ -> お前はソフトウェアエンジニアの適性がない あえて極端な例を書いたが、こういう状況は起きうる。これが良い悪いではない、起きうるということを言っている。

ここで言いたいのは、

  • チーム開発が必要な場面において、チームメンバーとの協業の過程でメンバーの言動を正しく認知することが重要だが、
  • その認知能力は外的要因で簡単に狂ってしまうものであり、
  • 自身の認知能力を認知する、メタ認知こそがジュニアソフトウェアエンジニアにとって最初に超えるべき壁である

後ろ2つをあえて同じことを書いた。対エンジニアリングだけでなく、対人間に対しても同じことが言える。

だからこそ、正しく他者の言動を正しく認知するためには相手の言動のその背景を理解する必要があるし、自分が誤解してそうだと思えばその意図を確認する必要がある。

Tech Lead, Senior / Stuff Engineer 相当の Role の人間に高いドキュメンテーション能力や、言語化能力、コミュニケーション能力が求められるのは、大きなインパクトを、多くの人間が関わる中で、より正しく遂行する必要があるからだと思う。

自分にとってのブレイクスルー: 分からないことを受け入れること

自分自身、自分の認知の歪みでパフォーマンスが著しく出なかった経験が何度もあった。そして認知の矯正によってパフォーマンスが急に出るようになった経験、その両方がある。

7年も職業 Software Engineer をやっていると、それなりに挫折も経験したし、良い意味でのブレイクスルーも何度も経験した。

一番直近のブレイクスルーは、直属のマネージャからかけられた「(あの Senior Software Engineer だって)わからないんです。」という言葉だった。

この頃、得体のしれない「技術力」が足りない、という漠然とした課題感があった。しかし、何がどう足りなくて、どういった状態が足りていて、そのために何をすればいいのか、そのすべてが分からない状態だった。

1on1 で、技術力があると思うひとは誰だと思いますか?という問いにいくつかの人をあげた。それに対して、直近あった、難易度が高い問題に対して、そういったクラスの人たちが集まって取り組んでいた事例をあげながら、「この人だって最初から分かってるわけではないんです。この人でも分からないことはあるんです」と言ったことを言ってくれた。

それと同時に、自分がこれまでしてきたことに対して「これまでこういうことをしてきましたよね。だから、もうできるんです。分かってるんですよ。」とも言ってくれた。

これを受けて、分からなくてもいいんだと、分からないことは普通で、当たり前で、そこからどうするかが大事ということと、 自分も分かっていることだってあって、それは肯定して良い、ということを感じることができた。

今思えば、この時、無意識下にあった「分からないことに対する漠然とした不安」が自分の認知能力を奪っていたんだと思う。

この後、テクニカルに難易度が高く、インパクトの大きい仕事をいくつか完遂し、自信を持つことができ、Software Enginering のスキルがあがることで、もともと得意だったコミュニケーションスキルを活かして Team / Project Lead をやれている。周囲から見てもきっとそうだと思うが、自分から見ても、パフォーマンスは大きく変わり、成長できたと思う。

おわりに

当時の自分に、あるいはこれから業界に入ってくるエンジニアに対して、自分ができることは、こういった認知能力の大切さを伝え続けることと、それを阻害しない環境づくりをしつづけることだと思う。

こういうことが書けるようになり、ここまで成長するまで見守ってくれた、手を差し伸べてくれた、前職と現職の全ての方に、心から感謝します。特に、現職の SRE Team にいた・いる上司、同僚が、挑戦と失敗を辛抱強く見守ってくれたおかげです。

業界への恩返しとして、こういうことをやっているので、よかったらどうぞ。

Roomba で掃除が完了したら IFTTT 経由で Pixe.la で草を生やす

かくかくしかじか部屋を綺麗にする意識が高まり、Roomba e5 を買いました。

で、かくかくしかじかそういう話を社内 LT でして、この時健康やお金の話も一緒にしていて、定量的に評価して目標値を決めるっていう SRE 的なアプローチを、部屋の綺麗さでやれたらいいよねって話題があって

f:id:take_she12:20210322185938p:plain:w300

まぁ結局 Clean map 機能は e5 では使えなかったのでどのみち面積はわからないんですが、床には物を置かないという前提で、スケジュール実行していれば、毎日実行はしてくれるので、その結果をとりあえず可視化してみようと思い、pixe.la を試してみました。

pixe.la

ご存知の方も多いと思います。GitHub の草を API 経由ではやしてくれるサービスです。

たくさん事例があって楽しいですね。

github.com

Roomba が掃除完了したら IFTTT 経由で草を生やす

さて本題。とても簡単かつこちらの記事で紹介されているものとほぼ同じです。

t.co

アカウント作成

README にある通りですが

curl -X POST https://pixe.la/v1/users -d '{"token":"secret", "username":"chaspy", "agreeTermsOfService":"yes", "notMinor":"yes"}'
{"message":"Success. Let's visit https://pixe.la/@chaspy , it is your profile page!","isSuccess":true}

Graph 作成

curl -X POST https://pixe.la/v1/users/chaspy/graphs -H 'X-USER-TOKEN:secret' -d '{"id":"roomba","name":"roomba","unit":"commit","type":"int","color":"shibafu"}'
{"message":"Success.","isSuccess":true}

Webhook 作成

curl -X POST https://pixe.la/v1/users/chaspy/webhooks -H 'X-USER-TOKEN:secret' -d '{"graphID":"roomba","type":"increment"}'
{"message":"Success.","isSuccess":true,"webhookHash":"1234567890123456789012345678901234567890"}

草を生やす

上で返ってきた webhookHash を使います。

curl -X POST https://pixe.la/v1/users/chaspy/webhooks/1234567890123456789012345678901234567890
{"message":"Success.","isSuccess":true}

これまでで草を生やす準備は完了。

IFTTT で iRobot 連携する

iRobot アカウントでログインします。

ifttt.com

IFTTT で掃除が完了したら Webhook を発行する

実際に作ったのはこれなんですが、

ifttt.com

If は iRobot で JobComplete Then で上記の webhook の設定をします。

f:id:take_she12:20210322191708p:plain:w300

これで、実際に roomba をリモートで動かしてみて、完了したら IFTTT が実行されました。

f:id:take_she12:20210322192128p:plain:w300

ちゃんと草も生えた!

pixe.la

今後

  • 生やした草を表示する Static site をホストする
  • Withings の体重計とも連動できそうなので、体重増減を草生やす
  • Withings の体重増減のグラフもパブリックにしたいので調べる
  • ジムに行ったら、運動したら草生やす

などなど、Pixe.la 使い倒していこうと思います!最高のサービスを a-know さんありがとうございました!

Cloud Native 読書会第5回 NGINX Ingress Controller に参加した

した。

zenn.dev

コードを読むことは重要である。これはここ数年思っていることである。そしてある程度読めるようになった。コード、読めば読めるし、書けば書けるな、というところまではなってきた。しかし、解決したい課題がない状態でコードを読むということはあまり経験がない。しかし優秀なソフトウェアエンジニアはみんなきっとそうしているのであろう。そういうものが体験できるかな、と思い参加した。あと Nginx Ingress Controller は(あんまり関わってないけど)弊社でも動いているので意味は大きい。

主催の Wantedly 南さんや田中さんとはもう何度も話している間なので、安心して参加できた。白金台のオフィスに遊びにいくみたいな気持ちで。自己紹介を軽くして、entry point からさっそく読んでいく。そしてさすが南さん、これが早い。このスピードで読んでいくのね。もちろん手元に Clone はしていたが、自分でコードジャンプや grep をしながら全体の話についていくのは無理だと早々に判断したので、手を止めて聞きに徹した。

そうすると面白くて、他人がコードを読むときの思考をトレースできて貴重な体験だった。コメントをちゃんと読んでるなあ、とか、メインの処理のあたりをつけて、それ以外は読み飛ばしているなあ、とか、ここがこうしているという仮説を立てて、その検証をするために別のコードを追っているなあ、とか。これは実はあまりできない経験なのではなかろうか。

今回参加者のほぼ全員(多分)が Nginx Ingress は使っていて、動作概要は知っていたようで、この前提がないと厳しいだろうなと感じたし、逆に前提がみんなあるからこそこのスピードで読み終えられたなと思った。エントリポイントで config を読み込んで nginx を起動している部分や、config の変更を検知して再起動をしている部分、そしてコアの部分には lua のコードが多く出てきて、nginx ingress を深く読み特には lua を読めないといけないのね、と(おそらく参加者全員が)驚いた。

みんなの感想を聞いてて思ったのは、基本挙動は知っているメンバーが揃っているとはいえ、Ingressyaml がだいたいこんなんだとして、Nginx Ingress Controller が Ingress を読んで、nginx.conf を生成して、nginx として動いているってのが基本的な動作ですよね。だいたいこういう流れになっていると(いったん)想像するとして、読んでいきましょう、でコードの該当部分をマッピングしていく、みたいにできると便利そうだと思った。どうにもテキストベースの GitHubパーマリンクと図をあらわすツールみたいなのをいい感じにあわせるのが難しいが。

あと今回は南さんの画面共有だったけど、VSCode LiveShare とかで見ていくとどんな感じかなーとも気になった。今日は〜の何行目!みたいなことを言ってたけど、そういうのも共有楽になったりするかもしれない。あとかめねこさんが言ってたブックマーク のエクステンションも使えばなおさら便利そう。一方 GitHub もコードジャンプできるし十分使えるので一長一短かも。自分は Go は Vim か Goland で読んでジャンプできるようにしています。

次回は ArgoCD。弊社でもバリバリ使っているし、使ってる人も多いので参加者も増えるんじゃないかな、と期待。次回は少し予習をしてきて、よりみんなをワクワクさせられるような場になるように貢献できたらいいなと思う。

南さん、田中さん、参加されたみなさんありがとうございました!

CircleCI の Insights API の結果を Prometheus 形式で Export する OSS 作った

作った。

github.com

シリーズものです。Prometheus Exporter としては7作目。

経緯

解決したい課題

所属している会社では monorepo を採用しており、大量の Build と Test と Deploy とその他いろいろの Job が超巨大ワークフローとして走る。

しかしまぁこれが遅い。改善を続けてきているがまだ遅い。そしてそれがどう遅い、何が遅いということが見えやすい状況とはいえない。

あと Flaky Test がぼちぼちあって、それによって脳死 Rerun が状態化している。よくない。

テストやビルドなんかは基本的にサービスを開発している Developer が詳しく、SRE が直接改善していくことはできないではないが、効果的とはいえない。

SRE としてはサービスの Observability と同じ構図で、Insights を得られる指標を提供し、その活用方法を組織にインストールして、それを Developer に活用してもらうということを CI でも取りたい。

CircleCI Insights では不足か

最初は多分うちの monorepo が裏のデータが大きすぎて GUI がうんともすんとも開かないみたいな状態で使い物にならなかったが、最近は十分活用できるレベルになっている。

API ではここで表示している値を取ってきてるだけなので、単に取り回しの問題で、Datadog に送ったほうがよりカスタマイズ性があがるという理由である。

サマリを把握する分には CircleCI Insights の GUI で十分である。

これまでやってきたこと

これまでも Insights API が出る前から Job や Job 内の Step ごとにかかった時間を Push で Datadog に送るなどしていた。

しかし活用が難しい。なぜか。それは前述したように、Sparse metrics になるから。

Push として metric を送る場合、それは Event 的なデータとなる。継続的な"状態"を示すデータではない。これは解析しづらい。データ量が不足するということと、データ量自体が得られるかどうかの頻度が metric によるからである。適当な間隔で Aggregation / Rollup したりなんたりの処理が必要で面倒である。

そういうわけで値自体はとれていたが活用には至っていなかった。

差分検知に関する問題

これは今回の件と関係するようで関係しないんだが、弊社では Build / Test の時間を短縮するために独自の差分検知の仕組みを使っている。

これは monorepo 内の各サービス(ディレクトリがトップレベルで切られている)の sha1Kubernetes の configmap に保存しており(ここに置くのが良いのかというのはある)ここと差があれば差分ありと判定されビルドやテストが走る。差分なしと判定されればビルトやテストはスキップされる。

...んだが、CircleCI の Workflow は Dynamic に構成することはできない。条件によってこのジョブを走る、走らせない、みたいなことができない。そのため、ジョブ自体は走るがすぐに halt するということをしている。

これによって何が問題かというと、前述する metric がスキップしたものとそうでないものが区別できない点にある。これは Insights の情報も同じである。

その点で GitHub Actions の Path filter は優位にあり、CircleCI も現状開発中のようだ。

どうするのか

大きく2点の対応が必要。

  1. ビルドやテストなど、傾向を追いたいものは Nightly で定期的に実行し、統計情報を安定的に取得する
  2. よりコントローラブルにするために Insights API の結果を Prometheus 形式で Export して Datadog に送る

今回作った OSS は 2 を解決するものであり、1 は別途行う必要がある。

CircleCI Insights は結構イケていて、統計情報を提供してくれる点が大きい。これまで自前でやっていた"Duration Second" という事実を Event 的に送っても活用は難しい。CircleCI Insights に統計情報の計算はお任せして、その推移を追って活用することができる。

統計情報というのは API のページを見てもらえばわかるが一定の Time Window における Job / Workflow の Success Rate や Duration Second の P95, median, min, max などのことである。

circleci.com

結果

いい感じ。

  • Workflow ごとの Success Rate

f:id:take_she12:20210217064247p:plain

  • Workflow ごとの Duration metrics, median

f:id:take_she12:20210217064253p:plain

  • Job ごとの Success Rate

f:id:take_she12:20210217064300p:plain

  • Job ごとの Duration metrics, median

f:id:take_she12:20210217064306p:plain

「やだ、、、私のテスト/ビルド、遅すぎ?」みたいなことがすぐわかって便利ですね。

まだこれらは差分検知の影響もあって確からしい値じゃないものもあるのですが、それでもそれなりに事実に近い結果は出ていそうです。

わかっていない点

CircleCI API の Rate Limit

rate exceeded のエラーをよくもらう。(後述するが API call 数が多いので)

これ結局いくつが上限なのかわかってない。

実際に動かすときは30分に1度実行するようにしていて、それだと大丈夫そう。

CircleCI Insights の計測間隔

metric を見る限り、last-7-days の time window にすると、24時間は同じ値を返し続けているように見える。その瞬間から24時間前を常に出しているわけではなく、1日に1度バッチ的にリフレッシュしているようだ。

last-90-days にした場合、GUI 上はグラフが週に一度しか変わってないように見えるので、もしかして API でも 7 days に一度しかこの値はリフレッシュされないのか?という点を気にしている。

f:id:take_she12:20210217064947p:plain

毎回瞬間ごとに計算する必要はないと思うが、せめて 24時間に一度は Time window が伸びても計算しなおしてほしい。表示が重くなるから GUI 上だけそうなっているのかどうかはわかっていない。

改善したい点

OSS のほうの話です。

Repository / Branch の持たせ方

現状はそれぞれ環境変数で持たせていて怒りのループぶんまわしを決めているので無駄がすごく多い。

例えば repo が "chaspy/hoge,chaspy/huga" で、branch が"develop,release,master" だとして、chaspy/hoge はこの3つの branch が存在するとして、chaspy/huga に release branch がなかったとしても API call してしまう。

この辺は yaml みたいな構造データで設定をいれるべきなんだろうなあと思っている。

repo: 
  - name: chaspy/hoge
    - branch
      - develop
      - master
  - name: chaspy/huga
    - branch
      - develop
      - release
      - master

Go の CircleCI SDK がない

これがあるがメンテされておらず、Ingiths API はもちろんサポートされてない。v1 まで。

github.com

v2 用のライブラリ書いてもいいかなーと思ったけどいったんパスした。このリポジトリ内でまず外部利用可能な感じにできたらしようと思う。

いまはなんかパッケの名前がそれっぽいがこれそうじゃなくて API call をラップした一連の処理群で切っている。

github.com

学んだこと

JSON-to-Go 便利

API Response 格納する構造体定義するのばりめんどいやんと思ったけどレスポンスペーストするだけでできてめっちゃ便利。

json.Unmarshal 便利

response を構造体につっこむの楽で便利。

               err = json.Unmarshal(body, &wfJobsInsight)
                if err != nil {
                    return []WorkflowJobsInsightWithRepo{}, fmt.Errorf("failed to parse response body. body %v, err %w", string(body), err)
                }

wfJobsInsight がその構造体。body は response body。

おわりに

AWS は go-sdk を使うだけって感じだったけど http response をそのまま扱ったので勉強になった。

うまく改善につなげられますように。

おまけ

いろいろ書いてデータ眺めて観察してわかった知見をまとめて話そうと思うよ。