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

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

【備忘録】Github ActionsでEB CLIを使いたい

GitHub Actionsを使っていて、少しハマったのでメモを残しておきます。

やりたいこと

  • Github Actions を使って ElasticBeanstalk へデプロイする

困ったこと

やったこと

Github ActionsにEB コマンドをインストール

pipで手動インストールします。

...
      - name: SetUp python
        uses: actions/setup-python@v2
        with:
          python-version: '3.x'

      - name: Install awsebcli
        run: |
          pip install awsebcli
          echo "~/.local/bin" >> $GITHUB_PATH
...

eb cliはOIDCとの親和性もOK。

EB CLIの初期化

EB CLI自体はインストール可能になりましたが、eb init を実行しない限り環境設定がされていないため、エラーが出てまだebコマンドは使えません。 eb init コマンドはインタラクティブな設定方法であるため、自動化環境上での実行は難しいです。

eb init実行時には、コマンドを実行したディレクトリで .elasticbeanstalk/config.yml が生成されます。これをあらかじめ用意しておくことで、eb init を実行しなくても eb cli を初期化できます。

config.yml

branch-defaults:
  default:
    environment: test-environment
environment-defaults:
  test-environment:
    branch: null
    repository: null
global:
  application_name: test-application
  default_platform: XXXXX
  default_region: ap-northeast-1
  profile: null
  sc: null

これでebコマンドが実行できるようになりました。 GitHub Actions OIDC を利用したデプロイは最終的に下記のようになりました。

name: eb-deploy

on: [ workflow_dispatch ]

env:
  AWS_ROLE_ARN: "XXXXX"
  AWS_REGION : "XXXXX"
  ELASTIC_BEANSTALK_NAME: "XXXXX"
  APPLICATION_NAME: "XXXXX"

permissions:
  id-token: write
  contents: read

jobs:
  aws-deploy:
    runs-on: ubuntu-latest
    steps:          
      - name: Checkout
        uses: actions/checkout@v2

      - name: SetUp python
        uses: actions/setup-python@v2
        with:
          python-version: '3.x'

      - name: Install awsebcli
        run: |
          pip install awsebcli
          echo "~/.local/bin" >> $GITHUB_PATH

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-region: ${{ env.AWS_REGION }}
          role-to-assume: ${{ env.AWS_ROLE_ARN }}
          role-session-name: preDeploySession

      - name: Deploy
        env:
          F_TIME: "${{ steps.current-time.outputs.formattedTime }}"
        run: |
          eb deploy ${{ env.ELASTIC_BEANSTALK_NAME }} --timeout 30
          aws elasticbeanstalk describe-environments --environment-names ${{ env.ELASTIC_BEANSTALK_NAME }}

ポータブルな CI/CDツール Dagger を触ってみる

Daggerとは

docs.dagger.io

DaggerはCI/CDのためのポータブルな開発ツールです。

公式サイトによると以下の利点が得られます。

  • 開発環境とCI環境を統合するので、CIのデバッグが迅速に行える

  • CIツールのロックインを防ぐことができる

  • CI/CDパイプラインを一度定義すれば、ツールの依存が少なく様々な環境で利用できる

  • CUE言語を使って記載できるため、YAML, JSON地獄から解放される(個人的にはそこまでYAMLは嫌いじゃないけど)

Quick Startをやってみる

インストール

> brew install dagger/tap/dagger

> type dagger
dagger is /usr/local/bin/dagger

公式のサンプルアプリで試してみます。

> git clone https://github.com/dagger/dagger

Cloning into 'dagger'...
remote: Enumerating objects: 24542, done.
remote: Counting objects: 100% (38/38), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 24542 (delta 17), reused 20 (delta 11), pack-reused 24504
Receiving objects: 100% (24542/24542), 10.86 MiB | 5.56 MiB/s, done.
Resolving deltas: 100% (13452/13452), done.

> cd dagger

> git checkout v0.2.7

> cd pkg/universe.dagger.io/examples/todoapp

構造を確認します。 todoapp.cue が拡張子的にCUE言語のファイルなのでdaggerの設定っぽいです。 それ以外のファイルは特段気になるなものは無さそうです。

> ls
README.md       package.json    public          src             todoapp.cue     yarn.lock

> tree
.
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.js
│   ├── components
│   │   ├── FilterButton.js
│   │   ├── Form.js
│   │   └── Todo.js
│   ├── index.css
│   └── index.js
├── todoapp.cue
└── yarn.lock

コマンドを実行してみます。

※dagger は BuildKitで実行されるようなので、dockerが動く環境が必要になります。

> dagger do build
[✔] client.filesystem."./".read
[✔] actions.deps
[✔] actions.build.run.script
[✔] actions.test.script
[✔] actions.test
[✔] actions.build.run
[✔] actions.build.contents
[✔] client.filesystem."./_build".write

実行ログに表示されているように client.filesystem."./_build".write ビルド結果が出力されたようなので open _build/index.htmlを実行するとアプリが起動します。

dagger do buildの役割はなんとなくわかりました。 todoapp.cueを確認すれば定義されているので詳細を知りたい場合は、そちらを確認すれば良さそうです。

簡単な例を作成してみる

なんとなく動きはわかったので、関連ある言語でもっと簡単な例で試してみます。 Go言語 でテストとビルドをやってみます。またGitHub Actionsに組み込んでみます。

ちなみに試したコードはこちらになります。

https://github.com/JunichiMitsunaga/dagger_example

Goプロジェクトの実装

hello/main.go

package main

import (
    "fmt"
    "os"

    "github.com/JunichiMitsunaga/dagger_example/hello/greeting"
)

func main() {
    name := os.Getenv("NAME")
    if name == "" {
        name = "Hoge"
    }
    fmt.Printf(greeting.Greeting(name))
}

hello/greeting/greeting.go

package greeting

import "fmt"

func Greeting(name string) string {
    return fmt.Sprintf("Hi %s!", name)
}

hello/greeting/greeting_test.go

package greeting

import "testing"

func TestGreeting(t *testing.T) {
    name := "Dagger Test"
    expect := "Hi Dagger Test!"
    value := Greeting(name)

    if expect != value {
        t.Fatalf("Hello(%s) = '%s', expected '%s'", name, value, expect)
    }
}

テストのdaggerコマンドを定義する

先ほどの dagger do 〇〇〇〇部分を定義していきます。 定義するためのファイルはCUE言語で書く必要がありますが、CUEに対して詳しいわけではないのでここでは説明しません。

hello.cue

package main

import (
    "dagger.io/dagger"
    "universe.dagger.io/go"
)

dagger.#Plan & {
    client: filesystem: "./hello": read: contents: dagger.#FS

    actions: {
        test: go.#Test & {
            source:  client.filesystem."./hello".read.contents
            package: "./..."
        }

        build: go.#Build & {
            source: client.filesystem."./hello".read.contents
        }
    }

}
  • Daggerで記述するファイルは常に dagger.#Plan で始める必要があります
  • clientファイルシステムとやりとりができて、ファイルを読み込んだり、結果をファイルに書き込んだりすることができます
    • サンプルでは、./hello ディレクトリを読み込むよう指定しています。。dagger.#FS はファイルであることを示す型です
  • actions では実際に実行する操作を定義することができます
    • 今回はgoプロジェクトのテストとビルドを実施するアクションを定義しました。正しく定義されていればdagger do --helpコマンドで表示されます
> dagger do --help
Usage: 
  dagger do  [flags]

Options


Available Actions:
 test  
 build 

ローカルでdaggerコマンドを試してみる

さっそく daggerの良さであるローカル実行を試してみます。従来のCIでは環境に反映してからでないと試せないので便利です。

テストの実行

> dagger project init
Project initialized! To install dagger packages, run `dagger project update`

> dagger project update
Project updated

> dagger do test
[✔] actions.test
[✔] client.filesystem."./hello".read

ビルドの実行

> dagger do build
[✔] actions.build.container
[✔] client.filesystem."./hello".read
[✔] actions.build.container.export

どちらも正常に動いているようです。

GitHub Actionsに組み込む

ローカルでのテストはうまくいったので、 GitHub Actionsに組み込みます。

.github/workflows/ci.yaml

name: test

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Test
        uses: dagger/dagger-for-github@v2
        with:
          version: 0.2
          cmds: |
            project init
            project update
            do test

      - name: Build
        uses: dagger/dagger-for-github@v2
        with:
          version: 0.2
          cmds: |
            do build

dagger用の Taskが用意されているので、実行自体は簡単です。

main branchにpushすれば トリガーされます。

dagger ci

テストとビルドが動いていていい感じになりました。

まとめ

  • Daggerを使ってテストをビルドを実行する簡単なCIを構築しました
  • CUE言語に依存しているだけで、一度定義すればCIツールへの依存度は下げられそうです。同じようなCIを行う場合は複数プロジェクトで共有できます
  • ローカルでテストできるのが非常に便利でした
  • サンプルが増えてきたら、採用して利用したいですね

G検定 合格体験記

先日、G検定 2022#1に合格したので、そのときに勉強や感じたことを書いていきます。 これからG検定を受けようと思う人の参考になればと思います。

G検定とは?(概要)

G検定は、AIからディープラーニングまでの知識問題、倫理問題や法律問題など、浅く広く問われます。 実装部分や、複雑な計算問題を問われることはなく、概念的の理解やそれぞれの出来事に対して背景の理解が求められます。 また、試験中にネットでの検索が認められているところもG検定の特徴だといえます。

受験の動機と受験前の状況

受験の動機

ちょうど1年ほど前から仕事の環境で、徐々にAIやデータ活用などのキーワードが増えており、 実際には何か具体的な動きがあったわけではないですが、いつか自分も携わりたいとコツコツ勉強していました。 しかし、そのままだと「データ使ってこういうのやりたい」と提案しても、 あまり説得力がないのと一緒にやってくれるメンバーがいた場合にサポートしたい思いもあり受験しました。

受験前の状況

  • Webアプリケーション開発のエンジニア
  • AIの知識は、ほとんど無し
  • 個人でPythonのサンプルコードを写経していたくらい

G検定受験のために始めたこと

動機の部分でも述べたように、1年前からカメのような速度でいろんな書籍からCodingの勉強は 取り組んでいました。しかし、G検定では知識問題、倫理問題や法律問題などが問われるため、改めて学習する必要があります。 ここではそのときに利用した書籍を紹介します。

書籍

人工知能は人間を超えるか ディープラーニングの先にあるもの』

はじめにG検定を受けようかなって考えているけど、「AIってなに?」「機械学習と違うの?」みたいな状況の人は、JDLAの理事長、兼、東大教授の松尾先生の執筆されたこちらの本を入門書にするとよいと思います。 数年前の本なので、少々情報が古い部分や最新の内容が書かれていないなどはありますが、AIの概要を理解できますので、AI周りの土地勘がつきます。

ディープラーニングG検定公式テキスト』

AI周りの土地勘が分かってきたら、G検定に向けて学習を進めます。 G検定の公式テキストで、全体的に知識を広く浅くインプットできた印象です。 個人的にはオライリー本で、一部の内容については深く学習していたのですが、忘れていた部分も多かったので復習にはぴったりでした。

『最短突破 ディープラーニングG検定(ジェネラリスト) 問題集』

ある程度、知識もインプットできたら次は問題を解いていきます。 検索していくと、黒本などが見つかりましたが、レビューで解説がしっかりしているという点に 惹かれてこちらの問題集を解きました。 実際の使い心地は、正解の選択肢以外に対しても説明が丁寧になされており、問題に慣れていくとともに、解説ページで他の知識も補強できました。

web模擬試験

界隈では有名なweb模擬を使用させていただきました。 メールアドレスを登録するとメールにリンクが載せられてくるので、そのリンクから使用することができます。

G検定(AIの検定)模擬テスト – Study-AI

私の使用方法を記述しておきます。

  • 直前対策225題 無料版

こちらは本番試験の3日前から2回解きました。 1回目は知識の獲得と本番試験の雰囲気をつかむことを目的にして解き。 2回目は時間配分も把握したいので、まとめた学習内容で検索しながら解きました。 どの模擬試験でもいいと思いますが、本番試験のその辺の感覚を掴んでおくことは重要だと思います。 知識、用意したもの、検索スピードなどによって戦略を考えた方が良いと思います。

こちらは本番試験の前日に1回解きました。 その名の通り、本流の模擬試験では対応しきれていないシラバスで追加されたキーワード関連の問題を解くことができます。 問題集では正解率90%ほどを維持できていましたが、実際に模擬試験を解くと正解率60%くらいまで落ちていたので、自身の対策があまかった部分の補強になりました。

学習期間

今回G検定を受けようと考え、学習するのにかかった期間は2ヵ月くらいになります。 「AIってなんだろう?」から始める人は、もう少し期間があった方が良いかもしれません。 1日当たりの学習時間は、集中できる日は長めにしたりできない日もありましたが、平均するとだいたい 1.0 ~ 2.0h くらいを確保していました。

内訳はこんな感じです。 学習するテキストも、問題集も「機械学習」と「ディープラーニング」周りの内容が多めになっているので、だいたい時間がかかるのはそのあたりだと思います。 その他に、自分の場合だと「法律」関連がなかなか頭に入ってこなくて苦戦しました。

1ヵ月目

2ヵ月目

  • 『最短突破 ディープラーニングG検定(ジェネラリスト) 問題集』で問題を解く × 3
  • 問題の解説をまとめる
  • web模擬試験を受ける × 2
  • 模擬試験の解説をまとめる

感想メモ

実際に受験してみて、「適切でないものを選択する」など、背景や意味を理解していないと答えられない問題が多いと感じました。これから受験する人は「選択肢だから何度か問題集解けばよい」などという考えを捨てて、ひとつひとつの用語や手法の理解を深めていくのが近道だと思います。でなければ、実際のプロジェクトで活用するのも難しいです。

最後に今回の受験で個人的に対策が甘かった部分を挙げておきます。

  • 音声処理分野について

問題集だけで学習していたら、かなりあぶなかったです。音声処理分野の音韻、音素、メル尺度などのキーワードは、あまり出てこなかった気がします。この辺りはweb模擬でかなりカバーできました。

  • 法律・時事分野について

この辺りは、まとめた内容に助けられました。エンジニアの方ですと、普段触れることのないような用語が多いです。事前にまとめた内容を自分なりに解釈して、素早く検索できるかにかかっている気がします。

Software Bill of Materials ( SBOM ) について知る

皆さんは Software Bill of Materials 通称:SBOM というものをご存知でしょうか? 私自身は、チームメンバーがDevelopers Summit 2022 で聞いた話を聞いて初めてその存在を知りました。 昨今 DevSecOps という言葉があるように、DevOps のなかで Security も担保してソフトウェアを作っていくことが重要だとされています。 DevSecOps を聞いたときは、Securityテストとかも自動化した方が良いなぁなど、テストを自動化することくらいしか、Actionを起こせていませんでした。 しかし、 SBOM の存在を知り、ぜひ自分達でも取り組んでみたいと思い、調べたので残しておこうと思います。

SBOMとは?(概要)

Software Bill of Materials (ソフトウェア部品表) は、ソフトウェアの一部に含まれるコンポーネントのリストです。 ソフトウェアを構築する際に、ほとんどの場合、 OSSおよび商用ソフトウェア コンポーネントを組み立てることになります。 このときに、コンポーネント同士の依存関係や利用しているコンポーネントを把握するのは非常に困難になります。 SBOM を利用することで、コンポーネント同士の依存関係を素早く把握できたり、コンポーネントが最新の状態であることを確認し、新しい脆弱性に迅速に対応できるようになります。

SBOM に含まれるもの

SBOMがソフトウェア部品表だとわかったけど、実際にはどんなものが含まれているのか?具体的には以下の通りです。

SBOMのフォーマット

上記で述べているSBOMの記載情報ですが、これらを記載するにあたっていくつかのフォーマットが存在するようです。

Software Package Data Exchange (SPDX)

Linux Foundationが運営するプロジェクトで、SBOMに記載されている情報の共有・利用方法を標準化することを目的としています。 SPDXには、コンポーネントの出所、ライセンス、セキュリティなどに関連するデータが含まれています。

f:id:JunichiMitsunaga:20220307174836p:plain
SPDXドキュメントに含まれるデータ概要
              SPDX websiteより引用

なお、この仕様はISO/IEC 5962:2021 として公開され、セキュリティ、ライセンス コンプライアンス、およびその他のソフトウェア サプライチェーン成果物に関する国際オープン標準として認められています。

MicrosoftVMwareなどは、すでにSPDXを使用してポリシーやツールでソフトウェア部品表 (SBOM) 情報をやりとりし、グローバル ソフトウェア サプライチェーン全体でSPDXに準拠した安全な開発を行えるようになっているようです。

Software Identification (SWID) Tags

ソフトウェア製品の構成要素を識別するための標準化されたXMLフォーマットです。 SWIDタグには4つのタイプがあり、ソフトウェア開発ライフサイクル(SDLC)の異なるポイントで使用されます。

  • Corpus Tags

インストール前の状態のソフトウェアコンポーネント(プレインストールソフトウェア)を識別し説明するためのタグです。

  • Primary Tags

インストール後のソフトウェア製品を識別し説明するためのタグです。

  • Patch Tags

パッチを識別し説明するためのタグです。さらにパッチタグには、他の製品やパッチとの関係についての関連情報を含めることができます。

  • Supplemental Tags

SWIDフォーマットでは、タグ作成者のみがコーパスタグ、プライマリータグ、パッチタグを修正することができます。補足タグは、ソフトウェアユーザーやソフトウェア管理ツールに、ライセンスキーなど、関連情報を追加する機能を提供します。

CycloneDX

OWASPコミュニティに起源を持つCycloneDXコアワーキンググループによって アプリケーションのセキュリティコンテキストとサプライチェーンコンポーネント分析での使用のために設計された軽量のソフトウェア部品表(SBOM)規格です。 アプリケーションを構成するソフトウェアコンポーネントに関する重要な情報を提供することで、SPDXやSWIDなどのSBOMフォーマットと同様の目的を達成することを目的としています。

f:id:JunichiMitsunaga:20220307182107p:plain
CycloneDXのオブジェクト構造

              CycloneDX websiteより引用

SBOM Example

今回は Nodejs のプロジェクトから CycloneDX形式のSBOMを作成する方法を見つけたので、試しに出してみようと思います。 github.com

適当なNodejsのプロジェクトにCycloneDXのモジュールをインストールします。

$ npm install -g @cyclonedx/bom

ヘルプを表示すると詳細な使い方がわかります。

$ cyclonedx-node -h
Usage: cyclonedx-node [options] [path]

Creates CycloneDX Software Bill of Materials (SBOM) from Node.js projects

Arguments:
  path                        Path to analyze

Options:
  -v, --version               output the version number
  -d, --include-dev           Include devDependencies (default: false)
  -l, --include-license-text  Include full license text (default: false)
  -o, --output <output>       Write BOM to file (default: "bom.xml")
  -t, --type <type>           Project type (default: "library")
  -ns, --no-serial-number     Do not include BOM serial number
  -h, --help                  display help for command

今回はSBOMを json形式で出力してみます。

$ cyclonedx-node -o bom.json

bom.jsonが出力され、中身を確認すると BOMのメタ情報の部分が作成されました。 今回はサンプルにしたアプリケーションの Dependenciesがなかったため、components部分は空になっていますが アプリによっては表示されるようです。

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.3",
  "serialNumber": "urn:uuid:6d9779f3-30dc-4b43-99f9-3d1cc02f2585",
  "version": 1,
  "metadata": {
    "timestamp": "2022-03-07T09:47:42.247Z",
    "tools": [
      {
        "vendor": "CycloneDX",
        "name": "Node.js module",
        "version": "3.5.0"
      }
    ],
    "component": {
      "type": "library",
      "bom-ref": "pkg:npm/example-contentful-theExampleApp-js@0.0.0",
      "name": "example-contentful-theExampleApp-js",
      "version": "0.0.0",
      "description": "[![CircleCI](https://img.shields.io/circleci/project/github/contentful/the-example-app.nodejs.svg)](https://circleci.com/gh/contentful/the-example-app.nodejs)",
      "purl": "pkg:npm/example-contentful-theExampleApp-js@0.0.0"
    }
  },
  "components": []
}

SBOM の活用事例

調べてみると非常に便利そうではあるのですが、実際に活用事例も見つかりました。 国内外で活用されているようで、幾つかリンクを載せておきます。

経済産業省の取組み

qiita.com

米国のNTIAにおけるNTIA Software Component Transparencyの取組み

www.ntia.doc.gov

今後やること

今回はSBOM の概念と簡単なツールを使って作成してみました。 この他にも、さまざまなツールもあるようなので、システムの品質を高めるためにも有用なものだと感じます。 特にCLIで実行できるパターンは CIなどに組み込んで自動化に組み込んでいこうと思います。

参考情報

デブサミでSBOMに出会った発表 speakerdeck.com

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

この度、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の部分を自分で考えるので, 対話や場に対して思考する良いきっかけになると感じた. 通常のインタビューでは機会が少ないかもしれない, お互いへのフィードバックも今回はとても貴重だった. インタビュアー/インタビュイー双方の視点で考える機会があるのでインタビューやテーマに対して良い気づきになることがおおい.

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