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

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

ポータブルな 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を行う場合は複数プロジェクトで共有できます
  • ローカルでテストできるのが非常に便利でした
  • サンプルが増えてきたら、採用して利用したいですね

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

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クエリに基づいてワークフローを自動スケーリングも可能になるので拡張性の高い環境構築に活かせると感じた.

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

GitOps Days 2020 参加レポート 2日目

はじめに

2020/5/21(木)開催のGitOps Days 2020のDay2に参加してきましたので、 まとめていきたいと思います!

※1日目と同様に発表者ではなく、ただ聞くだけです。。。

1日目の参加レポートはこちらです。 cabi99.hatenablog.com

印象深かったセッションなど

ここでは印象深かったセッション(など)についてまとめていきます。 Day 1ではGitOpsを実際に導入するための具体的な技術をチームに定着させる方法やGitOpsの今後について説明するなど、 1つ1つのセッションが濃ゆい内容になります。

11:00 – 11:57 am PT: Flux and Helm: Intro and How to teach your teams – Stefan Prodan and Leigh Capili

@stefanprodanさん@capileighさんが従来のCI/CDパイプラインの課題を明確にし、GitOpsに変更する利点と具体的なデプロイの流れを説明しデモを見せてくれました。

従来のCI/CDの課題

従来のCI/CDではビルドとデリバリーを自動化できていましたが ここでの問題提起は監視や復旧といったオペレーション観点となっています。 CI/CDを導入しているチームに共通して抱えている課題のように感じます。

トレーサビリティ

  • クラスターで実行されているアプリのバージョンを確認するか?
  • 複数ビルドを並行して実行するとどうなるのか?

セキュリティ

  • クラスター認証の管理方法をどうするのか?
  • 複数クラスターをターゲットにする方法は?

ディザスターリカバリ

  • インフラ依存関係に対処する方法は?

GitOpsに変更する利点

全体的にGitで宣言的に管理できるようになります。 これにより従来のようにrebuildする機会も減り、Gitのマニフェストを 前バージョンに戻すだけで切り戻しができるようになります。 OPAのようなポリシー適応はまだ実現していないので興味深いです。

  • インフラストラクチャの変更についてPRsとレビューで協力できる
  • 静的分析(OPA)でポリシーを適用できる
  • rollbackとrevertが簡単
  • 平均回復時間を短縮できる

GitOps準備

GitOps実現にあたって必要なものを説明しています。

必須

オペレーション

  • リポジトリ変更を監視し、通知する
  • 設定ドリフトを検出して修正する
  • 設定とドリフトに関するアラート
  • コミッターのバリデーションを行う

flux, helmを利用したGitOpsデモ

デモについては省略します。

fluxの使い方はこちらを参考にしてください。 cabi99.hatenablog.com

所感

2日目は1日目に説明した概念について具体的にデモをする場面が多いです。 これから導入する人や動作のイメージがわかない人向けのような気もしました。 2日間を通してGitOpsは概念であり、実現の方法が1つでないこと、そしてセキュリティに対して利点が多いこと。 これらが参加する前の自分になかった視点なのでとても参考になりました。 また、これらの新しい知識をプロダクトチームにoutputできるのでよりチームのDevOpsが促進することを楽しみにしています。 とても有益なイベントでしたので次回開催もぜひ参加したいと思います。

GitOps Days 2020 参加レポート 1日目

はじめに

2020/5/20(水)開催のGitOps Days 2020に参加してきましたので、 まとめていきたいと思います!

※発表者ではなく、ただ聞くだけです。。。

www.gitopsdays.com

GitOps Daysは、私が知っている限りではGitOpsに関するはじめての大きめイベントです。 はじめてGitOpsについて学びたい人や、GitOpsの利点を理解するようにチームを説得する人向けのようです。 現在のプロダクトでGitOpsを導入しているので世界的にGitOpsがどのように浸透しているのか?今後の展望はどうなっていくのか?などに興味を持ち参加しました。

GitOpsとは?

まずGitOpsとは何ぞや?という疑問もあると思うので説明します。 GitOpsはWeaveworks社が提唱した、Kubernetesの運用ベストプラクティスであり、 宣言的インフラストラクチャとアプリケーションの「Single source of Truth」としてGitを利用し、システム実行環境やアプリケーションの変更を行う方法です。

簡単に述べるとgitのcodeと環境面が同期して常にcodeの状態が正になる方法です。 メリットとして、codeですべて管理できる、環境構築が容易になる、セキュリティが向上するなどが挙げられます。

過去にGitOpsを実現するfluxを検証したことがあるので興味がある方はご覧ください。 cabi99.hatenablog.com

印象深かったセッションなど

ここでは印象深かったセッション(など)についてまとめていきます。 Day 1ではGitOpsがどのようなものなのか?概念やメリットを説明しています。 他にも具体的なプラクティスを紹介し、導入企業のエンジニアが事例を紹介しています。

10:00 – 10:27 am PT: GitOps Practitioner Highlight – Palo Alto Networks – Javeria Khan

Javeria KhanさんがGitOpsプラクティスを紹介してくれました。

利用する技術以外にもメリットやオンボーディング時の注意点が興味深かいです。

GitOpsの利点

  1. 可逆性
  2. 監査証跡
  3. 透明性
  4. 反復的なタスクを自動化する
  5. 開発および運用チーム間のコラボレーション

5番以外の利点は認識としてありました。 GitOpsにより環境が同期するため、コード管理時にDevelopとOperationを意識する。 これによりDevとOpsのようにチームが完全分業体制から共通の概念を持つ one teamとして機能しやすいのだと解釈しました。

Layer別の役割と技術

GitOps導入で技術の統一は必須でなく、それぞれの用途に合う仕組みを採用します。 また、環境なのかアプリケーションなのかでオーナシップを持つメンバーが変化します。

インフラ層(IaC)

ステークホルダー

  • SRE
  • Infra Engineers
  • Management

プロビジョニング(例)

  • Terraform → Atlantis
  • Salt → Scheduled states

設定情報(例)

Puppet → code manage Flux, Argo → cluster controllers + config repos

アプリ層

ステークホルダー

  • Developers
  • DevOps, Test Engineers

アプリケーションTemplating, Deployments

  • Helm, Kustomize
  • Flux, Argo: cluster controllers + app manifest
  • Flagger: Blue/Green

オンボーディングで心掛けること

自分たちのチームもGitOpsを導入しているのですが、 オンボーディング時の心掛けはかなり同意です!

  • Dev

    • 時間を先行投資して、仕組みと文化を定着させること
  • Management

    • 個人への依存をなくすこと

12:40 – 1:10 pm PT: GitOps for Cost Efficiency, Compliance, Velocity, Security, Resilience, and more! – Cornelia Davis

@cdavisafcさんによるコスト効率、コンプライアンス、速度などなど良い点をてんこ盛りに説明して頂いてます。

GoogleDevOps Elite performance reportでは、セキュリティについてあまり言及されていないので、ここでGitOpsだよ!と述べています。 システムの可用性、セキュリティを支えるために、必要な要素がGitOpsによりどのようにカイゼンするか?明確になるセッションでした。

GitOpsを支える5つの要素

  • アプリケーションチームの生産性
  • アプリケーションチームの自主性
  • 安全なネットワークの確立
  • 再現性
  • 冗長性

ここは動画が公開されてもう一度視聴してからまとめようと思います。。。

1:10 – 1:17 pm PT: Security and GitOps – Maya Kaczorowski

パズルがアイスクリームと同じくらい大好きな @MayaKaczorowski さんによる GitOpsがもたらすセキュリティの利点の説明です。

個人的には従来のCIOpsのようなpush型デプロイからpull型デプロイへ変更されるため kubernetes clusterのip制御が無くなるので、セキュリティホールのリスクが減る認識でした。 このセッションでは、それ以外にもセキュリティの利点があることを学べました。

セキュリティ利点

管理

さまざまな仕組みが「〇〇 as Code」で実現するため、 バージョニングや不変なものをSingleSourceに集約でき、全体を管理しやすくなる。

監視

デプロイ方法が決まるため、監視しやすい

セキュリティアップデート

デプロイが単純化されるため、セキュリティアップデートに集中する時間が取れる。

軽くまとめてみる?

gitログ、PRコメント、およびレビューポリシーにより、責任を共有しセキュリティの脆弱性の解決に対して協力体制が作れるようです。 同じ指標が持てるようになれば、同じ方向に向かって協力できるようになるのが良いです。 なんだかエンジニアリングでプロジェクトをデザインしている感じが好きです。

GitOps = IaC + Config as Code + Policy as code + Policy as code + thing as code

所感

Day 1は主にGitOpsの抽象的な部分を説明が多かったです。 GitOps自体は哲学であり、それを実現する仕組みは1つでなく、目的に併せて変化するものだと感じました。 実際に導入事例の紹介でも企業によって「GitOps = 〇〇 + △△」の定義が異なることも 多く、各企業が自分たちのモデルにあったものを探して適応している印象です。 もしかすると組織のDevOps習熟度で、この辺は変化していきそうな気がします。

※英語を学習中なので、所々説明している内容と異なるかもしれませんがご了承ください。 むしろ、これはこういうことだよとかフィードバックがあると嬉しいです。