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