SRE兼スクラムマスターのブログ

チーム開発が好きなエンジニアブログです。検証した技術やチームの取り組みを書いていきます。

成熟度 のギャップがあるとチームが辛くなる

この度、1年ぶりくらいに スクラムマスター として仕事をする機会ができたので 事前準備としてチームにヒアリングして気づいたことをまとめておく.

背景

今回は新しく スクラム開発を始めるチームではなく, 既にスクラム開発を行っているチームにジョインすることになった. 背景として, スクラムイベントが停滞していて建設的な議論ができないとのことだ.

なんだか…炎上はしていないけど, 煙がたちはじめた所に参加しているような気がする.

やったこと

チームのヒアリング

現状を把握したいので, プロダクトオーナー, デベロッパー, スクラムマスターにそれぞれ話を聞くことにした. 端的に表現すると, お互いへの期待値に大きなズレがあることが分かった.

チームの成熟度を可視化

「お互いへの期待値に大きなズレがある」ことは分かったが, 「なぜ, ズレがあるのか?」を考えたときに, いくつか仮説を立てた.

  • ふりかえりで検査と適応が機能していない
  • お互いの信頼関係ができていない
  • フレームワーク通りにやることに固執している...etc

しかし, これら仮説が有意だと判断するには観察時間も少ないので, 現時点での判断は諦めることにした. そこで現時点で見える範囲を分析をすると, スクラムチームのスキルや体験について気になることがあった. 役割毎に, スクラム開発経験と成功体験の有無に差があったのだ.

役割 スクラム開発経験 補足
プロダクトオーナー 有り 経験が長い, 上手くいったパターンを体験している
デベロッパ 無し 初めてスクラム開発する. まずは型から入っている
スクラムマスター 有り 経験が短い, 上手くいったパターンを体験している

それをもとに立てた仮説は「成功したときのチーム像を追いかけているのでは?」ということだ. ※実際に聞いたところ, 事実だった

そこで思い出したのが過去に@ryuzeeさんがtweetしたこちらの内容だ.

もとの記事が2018年なので, 現在のスクラムと状況が異なる部分もあるかもしれないが, こちらを使って自分たちの考える役割毎の成熟度を可視化してもらった.

役割 成熟度 補足
プロダクトオーナー 4 4よりの3らしい
デベロッパ 1
スクラムマスター 3 最近,は3に近いらしい

この結果から得た気づきは各役割が相手に対して同じ成熟度でのやりとりを求めていたのだ. そのことから, 理想と現実のギャップを埋めるアクションを考えるきっかけが出来たので, そのための場づくりを準備をしようと思う.

まとめ

観察し対話することで, 気づき を促し, アクションに繋げられることを考えると, 可視化しながらの対話の重要性を改めて感じる機会になった. 今回は 成熟度という共通の指標を用いたが, 他にも可視化の方法があるだろうし, 成熟度から起こしたアクションを経過観察して効果が無ければ「ふりかえり」で別の方法を考えればよいので, たくさん試していこうと思う.

ちなみに, 先日 「スクラムマスターのためのファシリテーションの学び方について集まってみる会」に参加させてい頂いた. そこで スクラムマスター のファシリテーションについて考える機会があったので, この行動が スクラムマスター としてコーチングかファシリテーションかと考えたときに 「コーチング: 3, ファシリテーション: 7」 くらいの割合だと思った. スクラムマスターがチームにファシリテーションを行う場合は, コーチングの要素が含まれているのが, 多いのかな?と感じた出来事だった. ※司会進行とかに徹しているファシリテーションも見たことがあったので...

KEDA v2 よる イベント駆動オートスケール 環境を構築する

Kubernetes を基盤としたプロジェクトで実際に利用している、KEDA というコンポーネントのメジャーバージョンが上がったので追加機能についてまとめる.

KEDAとは

KEDA (Kubernetes Event-driven Autoscaling)は, kubernetes clusterに追加できる単一目的の軽量コンポーネントで,CNCFのSandboxプロジェクトにホストされているkubernetes ベースのイベント駆動オートスケーラーである. 宣言的に定義されたイベントに応じて, Kubernetes の任意のコンテナーのスケーリングを促進できる. Horizo​​ntal Pod Autoscaler などの kubernetesコンポーネントと連携して動作し,上書きや重複なしに機能拡張できる.

keda.sh

KEDA を使うと嬉しいこと

Kubernetes Horizo​​ntal Pod Autoscaler では CPU , Memory , Metrics API をトリガーに自動スケーリングが可能である. しかし, 多くの点で, それは症状の治療に似ており, 実際の原因ではないことがある. 例えばメッセージングの仕組みを利用していた場合に, 大量のメッセージによって最終的にCPU使用率が上昇するが, この場合本当にスケーリングしたい条件は, キューに滞留しているメッセージの数であったりする. これらの場合, KEDA を用いればシンプルに スケーリング環境を構築できる.

v2の追加機能

追加されたスケール対象

  • StatefulSet
  • Custom Resource
  • ScaledJobs

DeploymentやJobが主な対象だったが、サブリソースとしての Custom Resource や Jobスケール専用の ScaledJobs が追加されたり 対象毎の特性に応じたプロパティ設定が可能となった.

追加されたスケーラー

  • CPU
  • Memory
  • External Push
  • Metrics API
  • Azure Log Analytics
  • IBM MQ

Kubernetes Horizo​​ntal Pod Autoscaler でも取得が可能だった CPU , Memory も追加されているのが印象的である. Kubernetesの複雑さの一部を取り除くために, Kubernetes API を使用しなくても対処できるようにするためだと推測している.

事前準備

localに kubernetes 環境を構築する。 今回は minikube を使って cluster の構築を行う. kubernetes.io

インストール

$ kubectl apply -f https://github.com/kedacore/keda/releases/download/v2.0.0/keda-2.0.0.yaml

namespace/keda created
customresourcedefinition.apiextensions.k8s.io/scaledjobs.keda.sh created
customresourcedefinition.apiextensions.k8s.io/scaledobjects.keda.sh created
customresourcedefinition.apiextensions.k8s.io/triggerauthentications.keda.sh created
serviceaccount/keda-operator created
clusterrole.rbac.authorization.k8s.io/keda-external-metrics-reader created
clusterrole.rbac.authorization.k8s.io/keda-operator created
rolebinding.rbac.authorization.k8s.io/keda-auth-reader created
clusterrolebinding.rbac.authorization.k8s.io/keda-hpa-controller-external-metrics created
clusterrolebinding.rbac.authorization.k8s.io/keda-operator created
clusterrolebinding.rbac.authorization.k8s.io/keda:system:auth-delegator created
service/keda-metrics-apiserver created
deployment.apps/keda-metrics-apiserver created
deployment.apps/keda-operator created
apiservice.apiregistration.k8s.io/v1beta1.external.metrics.k8s.io created
  • NamespaceDeployment など KEDA のリソースが作成されている.
$ kubectl get all -n keda
NAME                                          READY   STATUS    RESTARTS   AGE
pod/keda-metrics-apiserver-5bffbfbd68-ftg6q   1/1     Running   0          3m12s
pod/keda-operator-7b98595dc7-b4rnb            1/1     Running   0          3m12s

NAME                             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE  
service/keda-metrics-apiserver   ClusterIP   10.97.130.94   <none>        443/TCP,80/TCP   3m12s

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/keda-metrics-apiserver   1/1     1            1           3m12s
deployment.apps/keda-operator            1/1     1            1           3m12s

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.apps/keda-metrics-apiserver-5bffbfbd68   1         1         1       3m12s
replicaset.apps/keda-operator-7b98595dc7            1         1         1       3m12s

イベント駆動スケールを試す

せっかくなので KEDA v2から追加された Metrics API を試してみる 今回は自分で定義したAPIから Metrics を取得する.

サンプルはこちら

github.com

  • KEDA の トリガー対象とmetrics用のアプリケーションをデプロイする.
$ make deploy-app
or 
$ kubectl apply -f ./deploy/demo/app/

namespace/demo created
service/components-mock created
deployment.apps/components-mock created
deployment.apps/nginx created

demo Namespace にmetrics用のアプリケーションcomponents-mockとスケール対象のnginxがデプロイされる.

$ kubectl get all -n demo
NAME                                   READY   STATUS    RESTARTS   AGE
pod/components-mock-6b58c499dd-x6bf7   1/1     Running   0          95s
pod/nginx-6799fc88d8-p9jp5             1/1     Running   0          95s

NAME                      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/components-mock   ClusterIP   10.99.64.141   <none>        3003/TCP   95s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/components-mock   1/1     1            1           95s
deployment.apps/nginx             1/1     1            1           95s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.apps/components-mock-6b58c499dd   1         1         1       95s
replicaset.apps/nginx-6799fc88d8             1         1         1       95s
  • KEDA の ScaledObject をデプロイする

今回デプロイする ScaledObject閾値2 に設定している.

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: http-scaledobject
  namespace: demo
  labels:
    deploymentName: nginx
spec:
  maxReplicaCount: 5
  scaleTargetRef:
    name: nginx
  triggers:
    - type: metrics-api
      metadata:
        targetValue: '2'  # 閾値
        url: "http://components-mock.demo.svc.cluster.local:3003/components"  # 対象のREST API
        valueLocation: "components.worker.tasks"  # responseのjson構造

なお, サンプルではレスポンスのデフォルトが 0のため ScaledObject がトリガーされnginx0に Scale Inする.

$ make deploy-scaledobject
or 
$ kubectl apply -f ./deploy/demo/scaledobject/metrics-api.yaml
scaledobject.keda.sh/http-scaledobject created

demo Namespace を確認すると 0に Scale In したことが分かる.

$ kubectl get po -n demo
NAME                               READY   STATUS    RESTARTS   AGE
components-mock-6b58c499dd-x6bf7   1/1     Running   0          7m16s
  • 値を変更して, Scale Outさせる /deploy/demo/app/component.yaml を変更する
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: components-mock
  name: components-mock
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: components-mock
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: components-mock
    spec:
      containers:
      - name: components-mock
        image: cabi99/components-mock:0.0.1
        env:
        - name: WORKER_TASKS
          value: "0"   # ここの値を「2」に変更する
        imagePullPolicy: Always
        ports:
        - containerPort: 3003

変更後に再デプロイする.

$ make deploy-app
or 
$ kubectl apply -f ./deploy/demo/app/

namespace/demo unchanged
service/components-mock unchanged
deployment.apps/components-mock configured
deployment.apps/nginx unchanged

nginx が Scale Outしていることが確認できる.

$ kubectl get po -n demo -w

NAME                               READY   STATUS              RESTARTS   AGE
components-mock-6c494cd68b-4tkmv   1/1     Running             0          32s
nginx-6799fc88d8-22q9l             0/1     ContainerCreating   0          4s 
nginx-6799fc88d8-22q9l             1/1     Running             0          5s

なお、今回は閾値を2にしているので、WORKER_TASKS を 10 に変更してデプロイすれば, maxReplicaCount で定義した値までScale Outする.

$ kubectl get po -n demo -w

NAME                               READY   STATUS    RESTARTS   AGE  
components-mock-6d66575d55-r8vxh   1/1     Running   0          2m17s
nginx-6799fc88d8-22q9l             1/1     Running   0          4m11s
nginx-6799fc88d8-95r6v             1/1     Running   0          2m4s 
nginx-6799fc88d8-jkdqw             1/1     Running   0          2m4s 
nginx-6799fc88d8-wkmq4             1/1     Running   0          2m4s
nginx-6799fc88d8-xvctr             1/1     Running   0          109s

まとめ

KEDA v2を触ってみたが, v1との使い勝手に大きな差はなかった. しかし, 今回のバージョンアップで CPU と メモリ が追加により, Kubernetes Horizo​​ntal Pod Autoscaler と共存する形でなく, 自動スケーリング のリソースは全て KEDA に統一することで シンプル性を担保できる. また, Metrics API を利用すれば, APIが提供されているサービスをトリガーにすることが容易であったり, SQLクエリに基づいてワークフローを自動スケーリングも可能になるので拡張性の高い環境構築に活かせると感じた.

「インタビューしあう」 を体験してきた振り返り

はじめに

「インタビューしあう」のアドベントカレンダー に淡い期待を持ってtweetしたら gaoryu さんに反応していただき, 「インタビューしあう」を体験できたので振り返っておく.

ちなみに読んだブログはこちらのインタビューブログだ. インタビュイーとインタビュアーに別れてお互いをインタビューしたら何か発見があるんじゃないかと思ったので参加させていただいた.

note.com

相互インタビュー所感

普段からブログ等を参考にさせてもらっていたが,実際に話すのは初めてでせっかくの機会なので お互いに「ファシリテーションについて」をテーマにさせていただいた.

内容はインタビュアーがテーマについて聞きたいことを聞いていく形式でお互い15分ほどの持ち時間で交代して行う形式.

自分自身に対してあまり問いかけないような質問であったり, なんとなく軸はもっているけど, 言語化したことがない質問だったりと 1人ではたどり着けないような発想が2人だとたどり着けてかつ言語化することで整理されるのでとても有意義な時間だったと思う.

ふりかえり

ここではインタビュー後に振り返った質問に対してまとめておく. 頭の中を整理してあらためて問いに回答していく.

インタビュアーのとき

Q1.話すとき/聞くとき何を気にして話して/聞いていた?

A1.相手がどのような部分に対して, 思い入れが強いのかを意識した. 声のトーンや表情を観察して感情がスパイクしていると感じた時は掘り下げて質問して言語化しようと考えていた.

Q2.インタビュー中に相手の態度や自分の心境とか気になったことあるかな?あった場合は,それは何があったから気になったのかな?

A2.質問に対して回答が連鎖して話が進むと, そのテーマに対して熱意が強いことが伝わるので自分と興味の部分が一致した場合に話が盛り上がりやすい.

Q3.インタビュー前後で何か変わった?(相手の印象やテーマに対する気分)変わった場合、それは何があったから変わったのかな?

A3.質問の意図を考えて回答してくれてたと分かり, インタビューは2人で作り上げていくものだと感じた. これまでは自分がいかに引き出すかだけを考えていたが, 2人でコラボレーションして作る場は学びも多く有意義なものだった.

インタビュイーのとき

Q1.話すとき/聞くとき何を気にして話して/聞いていた?

A1.相手の質問に対して, 「事実」と「感情」を分けて答えるようにした. また,質問に対してこれまで1人で考えたことがあったかを振り返った.

Q2.インタビュー中に相手の態度や自分の心境とか気になったことあるかな?あった場合は,それは何があったから気になったのかな?

A2.引き出そうと意識して,自分の実体験を交えながら質問してくれてることが伝わった. 自分への質問を色々考えてくれていると感じると答える方も信頼して話しやすくなる.

Q3.インタビュー前後で何か変わった?(相手の印象やテーマに対する気分)変わった場合、それは何があったから変わったのかな?

A3.良い意味でしたたかさを持っているというフィードバックをもらえた. これまで1人で考えてきたことを言語化できて頭の中が整理できた.

最後に

お互いにインタビューすることは初めての経験だが,意図的にファシリテーターを立てなくとも場が進んでいくのが新鮮だった. ある程度, 役割が明確になると人はその形に順応できることがわかった. 役割が明確なだけでHowの部分を自分で考えるので, 対話や場に対して思考する良いきっかけになると感じた. 通常のインタビューでは機会が少ないかもしれない, お互いへのフィードバックも今回はとても貴重だった. インタビュアー/インタビュイー双方の視点で考える機会があるのでインタビューやテーマに対して良い気づきになることがおおい.

いろいろ書いたが, シンプルに楽しい時間が過ごせたので相互インタビューに参加してよかったと思う. 機会があればいろんな人とインタビュー形式で話してみるのも面白そう.

VSCode の Remote Development を使って開発者体験を上げる

はじめに

Remote Developmentとは

Remote Developmentは, VSCode からServerやWindows Subsystem for Linux (WSL), Containerなどの VSCode を実行しているホスト以とは別の環境に接続し, Server上のワークスペースを開発環境のように扱うことができる. これにより環境構築済みのServerへVSCodeから直接アクセスすることができ,開発者間での環境を簡単に統一できたり, ローカル環境に影響を与えず, 開発環境を構築/削除が容易にできる.

code.visualstudio.com

試してみる

今回はMacを使い, SSHで他のサーバに接続する手順を説明する.

拡張機能インストール

はじめに, Remote Development拡張機能をインストールする. VSCodeRemote Developmentを検索し,インストールする. Remote Development をインストールすると,Remote WSL, Remote SSH, Remote Containers のようにそれぞれに接続するための 拡張機能が自動的にインストールされる. とくにRemote WSL はWSLを利用しているWindowsユーザーにとっては便利機能だと思う.

f:id:JunichiMitsunaga:20201130232217p:plain
Remote Development Extension

SSH接続

サーバへの接続はSSHを用いて行われるため,鍵認証方式でサーバとSSH接続できるようにセットアップしておく必要がある. 今回は, Microsoft Azure 上に仮想マシンを作成してそこに接続する.

docs.microsoft.com

VSCode の左側のRemote Development タブを開くと接続先一覧が表示される.すでにsshで接続するために,~/.ssh/configなどがある場合は読み込まれて設定している接続先一覧が表示される.

configファイルを持っていない場合は,No SSH hosts...のような表示が出ているので,ギアクリックしてSSHのconfigファイルを作成する.

f:id:JunichiMitsunaga:20201201001208p:plain
Remote SSH Extension 1

config ファイルを以下のformatで作成する.

# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host host1
    HostName xxx.xxx.xxx.xxx (IPアドレス, DNSなど接続先)
    Port 22
    User hoge (ログインするユーザ名)
    IdentityFile ~/.ssh/sample_rsa (鍵認証に使う鍵ファイルを記述)

configファイルを設定するとconfig のhostが表示されるので選択して接続する.

f:id:JunichiMitsunaga:20201201001501p:plain
Remote SSH Extension 2

f:id:JunichiMitsunaga:20201201110835p:plain
Remote SSH Extension 3

「Continue」を選択する.

f:id:JunichiMitsunaga:20201201111058p:plain
Remote SSH Extension 4

接続が成功したら,「Open Folder」から接続先のフォルダを選択して操作できるようになる.

f:id:JunichiMitsunaga:20201201111250p:plain
Remote SSH Extension 5

Container 接続

接続するために, nginx のimageをpullして起動しておく.

$docker run --name remote-sample -d -p 8080:80 nginx
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
c164ead158ca        nginx               "/docker-entrypoint.…"   15 seconds ago      Up 13 seconds       0.0.0.0:8080->80/tcp   remote-sample

nginx が起動したのを確認して, Remote Development タブを開くと, Containers に nginxが表示される. リアルタイムで表示されなかったが,VSCodeを再起動したら表示された.

f:id:JunichiMitsunaga:20201130235455p:plain
Remote Containers Extension 1

nginx を選択して, 「Attach Container」を選ぶと nginx containerに接続して, container内部の情報をVSCodeで閲覧して修正できる. 添付画像は container 接続後に /etc/nginx 配下のfileを確認している.

f:id:JunichiMitsunaga:20201130235915p:plain
Remote Containers Extension 2

他にもチュートリアル用のContainerが用意されているので,そちらで各開発言語が含まれているContainerに接続できる.

code.visualstudio.com

終わりに

別環境をローカル環境のように扱うことができ,かつ使い慣れたエディタで開発できることは非常に便利になった. 環境構築されたContainerやServerを使うことで初期設定のコストが削減される. またローカル特有の問題に影響を受けることも少ない.

Flux v2 を使った GitOps チュートリアル Flux v1 との比較

Weaveworks社が提唱する kubernetes のベストプラクティスで , Single Source of Truth として Git を採用し,Kubernetes リソースなどを継続的にデリバリーしていく GitOps がある。 今回はそんな GitOpsを実現するツールである Flux のv2が出てきたのでチュートリアルを試し、 v1との比較を行っていく。

なお、 GitOps の説明や過去にv1を検証した内容は以下の記事等に紹介がある。

Get started

今回は Flux v2の動作を確認するために「Get started」の内容に沿って実施していく。 toolkit.fluxcd.io

事前準備

localに kubernetes 環境を構築する。 今回は minikube を使って cluster の構築を行う。 kubernetes.io

$ kubectl version --short
Client Version: v1.19.3
Server Version: v1.19.2

次に Flux を操作するためのクライアントをインストールする。 v1の時は fluxctl だったが、v2からは flux に変更されている。

$curl -s https://toolkit.fluxcd.io/install.sh | sudo bash
$flux --version
flux version 0.2.3

最後にチュートリアルで使う GitHub リポジトリをフォークしておく。 github.com

セットアップ

flux を使って, Flux に対応した環境か確認する。

$ flux check --pre
► checking prerequisites
✔ kubectl 1.19.4 >=1.18.0
✔ Kubernetes 1.19.2 >=1.16.0
✔ prerequisites checks passed

環境の問題はなさそうなので,環境変数GITHUB_TOKEN GITHUB_USER GITHUB_REPOを設定して flux bootstrapコマンドを実行する。

$ export GITHUB_TOKEN=<your-token>
$ export GITHUB_USER=<your-username>
$ export GITHUB_REPO=<repository-name>

$ flux bootstrap github \
    --owner=${GITHUB_USER} \
    --repository=${GITHUB_REPO} \
    --branch=main \
    --personal \
    --path=clusters/staging
► connecting to github.com
✔ repository cloned
✚ generating manifests
✔ components are up to date
► installing components in flux-system namespace

namespace/flux-system created
networkpolicy.networking.k8s.io/allow-scraping created
networkpolicy.networking.k8s.io/allow-webhooks created
networkpolicy.networking.k8s.io/deny-ingress created
role.rbac.authorization.k8s.io/crd-controller-flux-system created
rolebinding.rbac.authorization.k8s.io/crd-controller-flux-system created
clusterrolebinding.rbac.authorization.k8s.io/cluster-reconciler-flux-system created
customresourcedefinition.apiextensions.k8s.io/buckets.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/gitrepositories.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/helmcharts.source.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/helmrepositories.source.toolkit.fluxcd.io created
service/source-controller created
deployment.apps/source-controller created
customresourcedefinition.apiextensions.k8s.io/kustomizations.kustomize.toolkit.fluxcd.io created
deployment.apps/kustomize-controller created
customresourcedefinition.apiextensions.k8s.io/helmreleases.helm.toolkit.fluxcd.io created
deployment.apps/helm-controller created
customresourcedefinition.apiextensions.k8s.io/alerts.notification.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/providers.notification.toolkit.fluxcd.io created
customresourcedefinition.apiextensions.k8s.io/receivers.notification.toolkit.fluxcd.io created
service/notification-controller created
service/webhook-receiver created
deployment.apps/notification-controller created
Waiting for deployment "source-controller" rollout to finish: 0 of 1 updated replicas are available...
deployment "source-controller" successfully rolled out
Waiting for deployment "kustomize-controller" rollout to finish: 0 of 1 updated replicas are available...
deployment "kustomize-controller" successfully rolled out
deployment "helm-controller" successfully rolled out
Waiting for deployment "notification-controller" rollout to finish: 0 of 1 updated replicas are available...
deployment "notification-controller" successfully rolled out
✔ install completed
► configuring deploy key
✔ deploy key configured
► generating sync manifests
✔ sync manifests pushed
► applying sync manifests
◎ waiting for cluster sync
✔ bootstrap finished

$ kubectl get all -n flux-system

NAME                                           READY   STATUS    RESTARTS   AGE
pod/helm-controller-7fc55767cc-bfzck           1/1     Running   0          4m51s
pod/kustomize-controller-6b85bf79f9-t6qs4      1/1     Running   0          4m51s
pod/notification-controller-7bb8667967-6xqdb   1/1     Running   0          4m48s
pod/source-controller-6cdb6c8889-hpsxx         1/1     Running   0          4m53s

NAME                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/notification-controller   ClusterIP   10.108.140.72    <none>        80/TCP    4m50s
service/source-controller         ClusterIP   10.104.224.8     <none>        80/TCP    4m53s
service/webhook-receiver          ClusterIP   10.103.160.216   <none>        80/TCP    4m48s

NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/helm-controller           1/1     1            1           4m51s
deployment.apps/kustomize-controller      1/1     1            1           4m53s
deployment.apps/notification-controller   1/1     1            1           4m48s
deployment.apps/source-controller         1/1     1            1           4m53s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/helm-controller-7fc55767cc           1         1         1       4m51s
replicaset.apps/kustomize-controller-6b85bf79f9      1         1         1       4m52s
replicaset.apps/notification-controller-7bb8667967   1         1         1       4m48s
replicaset.apps/source-controller-6cdb6c8889         1         1         1       4m53s

bootstrap コマンドで flux-system のnamespaceが作成されて, GitOpsを実施するための kustomize , helm のcontrollerが作成されている。 この他にもv2では監視機能が強化されたこともあって notificationのcontrollerが作成されている。 Flux v1flux-agentmemcacheのみだったのに対して役割に応じてcontrollerが増えているようだ。 Flux v2 の全体構成は公式に掲載されている図が非常に分かりやすい。

           Flux v2 構成図 https://toolkit.fluxcd.io/diagrams/gitops-toolkit.png

           Flux v2より引用

なお、bootstrapで対象にしたrepository で管理されているmanifestがclusterに展開されていることが分かる。

$ flux get kustomizations --all-namespaces
NAMESPACE       NAME            REVISION                                        SUSPENDED       READY   MESSAGE
flux-system     apps            main/32112c9d1a3f89fdd35f91fe3ae5d0b8096923b1   False           True    Applied revision: main/32112c9d1a3f89fdd35f91fe3ae5d0b8096923b1
flux-system     flux-system     main/32112c9d1a3f89fdd35f91fe3ae5d0b8096923b1   False           True    Applied revision: main/32112c9d1a3f89fdd35f91fe3ae5d0b8096923b1
flux-system     infrastructure  main/32112c9d1a3f89fdd35f91fe3ae5d0b8096923b1   False           True    Applied revision: main/32112c9d1a3f89fdd35f91fe3ae5d0b8096923b1


$ flux get helmreleases --all-namespaces
NAMESPACE       NAME    REVISION        SUSPENDED       READY   MESSAGE
nginx           nginx   5.6.14          False           True    release reconciliation succeeded
podinfo         podinfo 5.0.3           False           True    release reconciliation succeeded
redis           redis   11.3.4          False           True    release reconciliation succeeded

GitOps実践

初期状態の確認

まず, bootstrapでデプロイされた初期のappにアクセスするためにport-forwardする。

$ kubectl -n podinfo port-forward svc/podinfo 9898:9898

Forwarding from 127.0.0.1:9898 -> 9898
Forwarding from [::1]:9898 -> 9898
Handling connection for 9898
Handling connection for 9898

ブラウザからlocalhost:9898にアクセスすると、下記のようなサンプルアプリケーション画面が表示される。なお、初期状態ではv5.0.3と表示される。

サンプルアプリケーション画面初期状態
flux v2 gitops before

マニフェストの修正

GitOps対象のリポジトリ flux2-kustomize-helm-example の中にあるマニフェスト flux2-kustomize-helm-example/workloads/apps/staging/podinfo-values.yamlを修正する。chart.spec.version: ">=1.0.0-alpha"chart.spec.version: "5.0.0"に変更し、repositoryにpushしておく。

podinfoを確認すると変更されたことが確認できる

$ flux get helmreleases --all-namespaces
NAMESPACE       NAME    REVISION        SUSPENDED       READY   MESSAGE
nginx           nginx   5.6.14          False           True    release reconciliation succeeded  
podinfo         podinfo 5.0.0           False           True    release reconciliation succeeded  
redis           redis   11.3.4          False           True    release reconciliation succeeded

再度、ブラウザから確認する

$ kubectl -n podinfo port-forward svc/podinfo 9898:9898
Forwarding from 127.0.0.1:9898 -> 9898
Forwarding from [::1]:9898 -> 9898

versionが 5.0.0に変更していることが分かる これによりgitの結果を正とした GitOpsが実現できている

f:id:JunichiMitsunaga:20201118094212p:plain
flux v2 gitops after

モニタリングを設定

Flux v1では flux agentの監視が困難であり、同期の失敗を検知/修正するまでに時間がかかっていたが、 Flux v2 になってからは Prometheusによる監視と Grafanaダッシュボードが提供された。チュートリアルでは扱っていないが、有用な仕組みなので試してみる。

flux2リポジトリにあるmanifestから作成する

$ flux create source git monitoring \
>   --interval=30m \
>   --url=https://github.com/fluxcd/flux2 \
>   --branch=main
✚ generating GitRepository source
► applying GitRepository source
✔ GitRepository source created
◎ waiting for GitRepository source reconciliation
✔ GitRepository source reconciliation completed
✔ fetched revision: main/37fb0f632b7acd192a314388c8f6bab5a6376764

$ flux create kustomization monitoring \
  --interval=1h \
  --prune=true \
  --source=monitoring \
  --path="./manifests/monitoring" \
  --health-check="Deployment/prometheus.flux-system" \
  --health-check="Deployment/grafana.flux-system"

✚ generating Kustomization
► applying Kustomization
✔ Kustomization created
◎ waiting for Kustomization reconciliation
✔ Kustomization monitoring is ready
✔ applied revision main/37fb0f632b7acd192a314388c8f6bab5a6376764

kubectl -n flux-system port-forward svc/grafana 3000:3000

ブラウザでダッシュボードにアクセスすると GitOps 状態が確認できる。

Control planeのダッシュボード

http://localhost:3000/d/gitops-toolkit-control-plane/gitops-toolkit-control-plane

f:id:JunichiMitsunaga:20201118100432p:plain
control

Cluster reconciliationのダッシュボード

http://localhost:3000/d/flux-cluster/flux-cluster-stats

f:id:JunichiMitsunaga:20201118100354p:plain
cluster

v1との比較

導入のしやすさ

v2になってbootstrap コマンドが追加されたことによって、コマンド1つで設定できるため、v1と比較してnamespaceの準備等がなくなり、簡単に設定できるようになった。

監視

Flux v1 のlogを監視して、errorが発生した場合にトラブルシューティングするなど、泥臭いことをやっていた(少なくとも自分の環境では)。 Flux v2PrometheusGrafanaが提供されたので、metricsの取得やダッシュボードによる可視化が可能になり、保守性が高まった。 また、slackへの通知などの機能も提供されたので、障害発生から検知までの時間短縮に繋がる。

構成

Flux v1はflux agentが1つだったことに対して、Flux v2では役割毎にcontrol planeが分かれた。個人的には役割が明確になったため、トラブルシューティングが簡単になり、かつ自分たちの環境にマッチした最小構成を構築するのが容易になったと感じた。

まとめ

Flux v2チュートリアルを実施してみたが、Flux v1の時よりも導入は容易になったと感じる。 CLIに関しての変更はv1ユーザーにとっては破壊的変更なため、v2へ移行するには時間がかかりそうだ。しかし、移行した場合は、監視や通知といったこれまで苦戦していた部分が提供されているので、時間をかけてしっかり移行していきたいと思う。

移行の手順は用意されているのでcluserの移行だけなら簡単そう。 pipelineやその他の仕組みでFlux CLIを使っている場合は、考慮が必要そう。

toolkit.fluxcd.io

「ONE TEAM」を目指したが、コラボレーションは簡単じゃない

はじめに

2019年にラグビーワールドカップで「ONE TEAM」という言葉が流行り、ラグビーだけでなく仕事場でも「ONE TEAM」という言葉を使ってチーム作りしている現場もあると思います。 私の現場も「ONE TEAM」というキーワードが出てきてみんな賛同して目指すことになりました。しかし、いざ目指してみるとチーム間でコラボレーションするのがすごく大変だったので解消するためにやったことを残しておきます。

「ONE TEAM」を目指してどうなっていたのか?

「ONE TEAM」を目指そうと話が出たときは、プロジェクト全員が乗り気でかつ自分たちなら達成できると考えていたと思います。しかし、「ONE TEAM」というのはノープランで目指すべきものではないです。

ここではノープランで「ONE TEAM」を目指した結果

  • チーム間で上下関係ができた
  • サイロ化した

の2つの事象が発生しました。

チーム間で上下関係ができた

私たちのプロジェクトはクロスファンクショナルなチームもいれば専門性の高いチームも存在しています。 そんな中、スクラム開発しているチームのスプリントレビュー時に、専門性の高いチームのメンバーが参加すると、 ときどきちゃぶ台返しのような事態も発生します。

サイロ化した

「チーム間で上下関係ができた」ことも原因の1つになると思うのですが、こうして各チームでのサイロ化が急速に進みました。 このころになると、「設計時に困ったら呼んでください」といっても、あまり呼ばれなくなったりとチーム間の交流は少なくなりました。

なぜ「ONE TEAM」にならないのか?

「上下関係ができる」、「サイロ化が進む」で「ONE TEAM」を目指す前は良いペースで進んでいたプロジェクトが徐々に失速しました。 そこでなぜ失敗したかをふりかえっていきます。

なぜ上下関係ができたか

まずはなぜ上下関係ができたかを考えてみます。 個人的には上下関係ができたのはスプリントレビュー時のちゃぶ台返しや「設計時に困ったら呼んでください」などの「困ったら呼んでください」といった発言なのかなぁっと思います。ちゃぶ台を返した時点で建設的なレビュー者ではなく、チームにとってはちゃぶ台を返したメンバーがフェーズゲート(承認者)のように見えていたと思います。そこで、なぜちゃぶ台を返すかをヒアリングしたところ、チームによって達成すべきミッションが異なるからということが分かりました。達成するミッションが異なるので各チームでプロダクトに対する優先順位も異なるのでリリースできる・できないの判断基準が異なったのです。

これ以外にも細かい部分では「困ったら呼んでください」という声かけも「呼んでください」という表現は呼ぶ側と呼ばれる側で心理的に対等にならないとも感じました。

なぜサイロ化したのか

サイロ化したのは「上下関係ができた」ことも原因の1つだと考えていますが、チーム間でコラボレーションする方法を知らなかったことが根本原因だと感じています。プロジェクトでチームが増えてこれまでと同じやり方でうまくいかないのは明白なのに、コラボレーションする仕組みを作ることをしなかったのが問題だと思います。

取り組んだこと

なぜ失敗したかをふりかえり

  • ミッションの違いによる問題
  • 上下関係を感じてしまう心理的な問題

の2つの観点で問題があると考え、これらの問題を解決するための仕組みを作りました。

ミッションの違いによる問題を解決するために

ちゃぶ台返しが良く発生していたのはSREチームとスクラムチームでした。 そこでシステムのSLO(Service-level objective)*1を策定しました。 SLOという明確な基準値を設けることにより各チーム間で基準値内の場合はお互いの自由を許容するというルールにしました。

上下関係を感じてしまう心理的な問題

ミッションの問題と違って明確な数値を設定できるものではないのでコミュニケーションの機会を増やすようにしました。 具体的には

  • 各チームのタスクボードを共有する時間を作る
  • 合同でlearning sessionを実施する
  • 心理的安全性を実現するためにスキルを習得する

の3つに取り組んでいます。

まとめ

ノープランやスキルない「ONE TEAM」は実現しない ミッションが異なるチームでコラボレーションするには?

  • 共通言語を作る
    • 自分たちの場合はSLOを作成する

心理的な問題を解消するには?

  • スキルを身につける
  • コミュニケーションの機会を増やす
    • 合同でlearning sessionを実施する
    • 各チームのタスクボードを共有する時間を作る

すぐ取り組めるものから、エンジニアリングが必要なものもありますが、私たちのプロジェクトは仕組みを導入してから以前よりも雰囲気が良くなりました。 チーム間でギクシャクしているなと感じることがあったら、ぜひ立ち止まってふりかえってみてください。その時のアクションプランの参考になれば幸いです。

参考

スキルやサイロ化について学習したときの書籍の一部です。 ここでは紹介しませんが、ファシリテーション系の書籍で得た知識も大変役に立ちました。SLOについてはもともと知っており、SRE本に詳しく書かれています。

amzn.to

amzn.to

*1:サービルレベル目標(service level objective)であり、SLIで計測されるサービスレベルのターゲット値、あるいはターゲットの範囲を指します。

チームサポートするにあたって心掛けていること

はじめに

社内で新しいチームにをサポートしていくことになりました。 チームリーダーの方やマネージャーと「1 on 1」を実施して、抱えている問題や 自分が協力できそうなことを話し合い、7月から本格的にサポートしていくことになりましたが、自戒の念を込めてサポート時に心掛けようとすることをいくつかまとめておこうと思います。

心掛けていること

この内容がすべてではないですが、チームサポートするにあたって、とくに初期段階でおろそかにしないように気をつけたいことになります。

サポートに徹することを明確にする

自分がチームサポートする時に、「チームが、良い方向に進められるように全力を尽くします。しかし、実際に良いチームを作るのは皆さん自身です。」ということを伝えるようにしています。チームメンバー自身に 良いチームを作る意思がなかったり、サポートに参加する自分がすべてを決定するようでは、最終的に自己組織化されたチームが生まれにくいからです。 自己組織化するために必要なチームの対話や、メンバー間のコラボレーションについては、知識やスキルが必要になるので実践の方法やアクティビティについてアウトプットするようにしています。

チームの目標を統一する

チームの目標について共通認識をもつようなプラクティスを実施するようにしています。なぜならば、これまでのチームサポートの経験では、 「目標が人によってバラバラである」 「目標が明確でも達成するための道筋は不明瞭である」 といった状態が多く、総じて同じ方向を向いてることが少ないです。

その場合は「インセプションデッキ」を使って目標、これからの進め方について議論し、理解し、合意するフェーズを設けています。 しかし「インセプションデッキ」は時間のかかるプラクティスで、チームによっては、そのようなイベントに抵抗がある場合も多いです。そういったときは、ふりかえりでも使われる「帆船」を使って自分たちの目標や立ち位置を明確にしていただいてます。

インセプションデッキ」や「帆船」の詳しいやり方は説明しませんので こちらの記事を参照してください。

takaking22.com

note.com

メンバーの価値観を可視化する

同じチームでコラボレーションしてくには、メンバー間で得意なことや 大切にしている価値観を開示していることが大切です。これを実現するために 自分がよく取り入れているのは「ドラッカー風エクササイズ」です。 実践することで 「何に対してモチベーションを感じるのか?」 「どういう部分で協力があると良いのか?」 という点で、メンバーに伝えることもできるし、言語化することで自分自身にも新しい気づきが生まれます。

ドラッカー風エクササイズ」詳しいやり方は説明しませんので こちらの記事を参照してください。

tech.pepabo.com

アジャイル」という言葉を使わない

これも経験談ですが、「アジャイル」という言葉を使わない方が割とうまくいくことが多かったです。これに関しては「アジャイル」のみならず、新しすぎる横文字なども多用しないようにしています。 その理由は、下記の2つではないかと推測しています。 「過去にアジャイル手法だけやって、うまくいかなかった」 「大きな変化を求められているようで抵抗感があった」 前者は、何度か見てきたことがあるのですが人は一度うまくできなったことに対してもう一度チャレンジするのは心理的にハードルが高いからです。 後者は、自分たちのこれまでのやり方が否定されているように感じるからだと思います。 私自身「アジャイルをやろう」というフレーズは、本質を見誤っているようで好きではないです。自分たちが働きやすく、成果を届けられるようにカイゼンを続けた結果「アジャイルな状態」になっていた!くらいがちょうどよいと考えています。

まとめ

久々に新しいチームのサポートをすることになったのでだいぶ緊張しているので サポート時に気をつけたいことまとめました。 まとめていて思ったのはフレームワークやプラクティスを適用する時は チームの成熟度に合わせることが重要ということです。 1つのチームでうまく言った方法が全てのチームでうまくいくと考えると大変なことになるので肝に銘じてやっていこうと思います。 自戒のために心がけをまとめてみましたが、「こういうのも大事だよ!」っていうのがあればコメント頂けると嬉しいです