Dagger Learning
Dagger
Dockerの創始者であるSolomon Hykes氏らが中心となって開発している CI/CD 環境構築ツール
公式ページを読んでみて特徴などをざっくり読んでみる。
A PORTABLE DEVKIT FOR CI/CD PIPELINES
Build powerful CI/CD pipelines quickly, then run them anywhere.
- Give your developers parity between dev and CI environments
- Test and debug your pipelines locally
- Run the same pipeline on any CI environment without re-writes
- Developed in the open by the creators of Docker
ローカルとCI環境での差分をいい感じに吸収して、CI/CDのテストや開発がしやすくなる感じかな?
Escape YAML hell
Wire actions together using CUE, a powerful configuration language developed at Google. CUE has all the features you wish YAML had: string interpolation, templating, static type checking, a complete package system, and more. And best of all, it can natively import and export YAML and JSON, for maximum compatibility with your existing tooling.
設定などはYAMLではなくCUE言語なるもので記述していくらしい。
CUE言語はGoogleが開発した設定用の言語で型チェックや文字列の補間、パッケージシステムなどがあるとのこと。
またJSON/YAMLをネイティブでサポートしているらしい
Create actions with any programming language.
Creating a custom action is straightforward. First write the code in your favorite programming language, no proprietary framework required. Then write a small CUE file specifying how to run your code in a container, and how to connect it to other actions. This allows for language-agnostic composition: actions written in different languages can be seamlessly wired into the same pipeline.
好きな言語でカスタムアクションを作成できる🎉
カスタムアクションをプログラミングしたら、あとはコンテナ内での実行方法と他のアクションとの接続方法をCUEで指示すればok
すごい!
Choose and reuse actions from a large catalog.
Stop re-inventing the wheel every time a pipeline needs to be created or updated. Dagger ships with a large catalog of actions, and you can add custom actions to reuse later.
車輪の再発明をしなくてもいいように、カスタムアクションを追加して再利用がしやすいようになっているとのこと。
CI/CDはYAMLで記述するのがDRYにするのが面倒だったりするのでこれは嬉しい
Test and debug instantly, on your local machine.
App developers don’t have to wait several minutes to catch a typo, and neither should you. Dagger lets you develop, test and debug your pipeline locally, so you can get the pipeline completed quicker and move on to putting out other fires.
パイプラインのテストやデバッグがローカルマシンで出来る!
CIファイルを編集してgit push
して結果をさんざん待ってからtypoが発覚するみたいな悲しいことが無くなるの良さそう!
Run on any Docker-compatible runtime.
Real-world pipelines have to work on multiple platforms and they have to tie together fragmented tools. With Dagger, you can re-use pipelines on different platforms or build multi-platform pipelines, completely out-of-the-box.
マルチプラットフォームの差異をDockerが吸収してくれる?
今やDockerが動かない環境のほうが珍しいので良さそう
Daggerのドキュメントは以下
とりあえず getting started してみる
インストール
brewでインストールできる
$ brew install dagger/tap/dagger
インストールできたのでとりあえず helpやversion 見てみる
$ dagger help
A programmable deployment system
Usage:
dagger [command]
Available Commands:
do
help Help about any command
project Manage a Dagger project
version Print dagger version
Flags:
--experimental Enable experimental features
-h, --help help for dagger
--log-format string Log format (auto, plain, tty, json) (default "auto")
-l, --log-level string Log level (default "info")
Use "dagger [command] --help" for more information about a command.
$ dagger version
dagger 0.2.7 (18c19174) darwin/arm64
サンプルアプリでお試し
daggerのリポジトリにReactで作成されたTODOアプリがサンプルである
ドキュメントに従って dagger do build
を実行してみる
$ git clone https://github.com/dagger/dagger
$ cd dagger
$ git checkout v0.2.7
$ cd pkg/universe.dagger.io/examples/todoapp
$ dagger do build
[✔] actions.build.run.script 2.4s
[✔] actions.deps 2.3s
[✔] client.filesystem."./".read 4.4s
[✔] actions.test.script 2.8s
[✔] actions.test 2.5s
[✔] actions.build.run 6.7s
[✔] actions.build.contents 0.6s
[✔] client.filesystem."./_build".write 0.1s
_build/
以下に生成物が作成される
$ open _build/index.html
# TODOアプリがブラウザで開く
パイプラインの内容は todoapp.cue
に記述されている
dagger do build
を実行したときに実行されるパイプラインの定義は以下の箇所っぽい
build: {
run: bash.#Run & {
input: test.output
mounts: _nodeModulesMount
workdir: "/src"
script: contents: #"""
yarn run build
"""#
}
contents: core.#Subdir & {
input: run.output.rootfs
path: "/src/build"
}
}
ここで build run: bash.#Run
の inputに test.output
が指定されているが、これは test
アクションとの依存関係を表している?
dagger do build
を実行したときに test
アクションが実行されるのはここで指定しているから。
ビルドはdocker内で行われているのに最終的に _build
ディレクトリに出てくるのは以下の定義?
client: {
filesystem: {
...
"./_build": write: contents: actions.build.contents.output
}
ここでの ./_build
はホストマシンのパスで actions.build.contents.output
をマウントしている感じなのかな?
actions.build.contents.output
は以下で定義されている
build: {
...
contents: core.#Subdir & {
input: run.output.rootfs
path: "/src/build"
}
}
dagger do build --log-format=plain --no-cache
--log-format=plain --no-cache
をつけるとそれぞれ何やっているのかわかりやすい
12:07PM INF actions.build.run.script._write | computing
12:07PM INF actions.deps._dag."0"._dag."0"._op | computing
12:07PM INF client.filesystem."./".read | computing
12:07PM INF actions.deps._dag."2".script._write | computing
12:07PM INF actions.test.script._write | computing
12:07PM INF actions.deps._dag."2".script._write | completed duration=1.7s
12:07PM INF actions.build.run.script._write | completed duration=2.9s
12:07PM INF actions.test.script._write | completed duration=3.3s
12:07PM INF client.filesystem."./".read | completed duration=4s
12:07PM INF actions.deps._dag."0"._dag."0"._op | completed duration=4s
12:07PM INF actions.deps._dag."0"._dag."1"._exec | computing
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 0.640 fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/aarch64/APKINDEX.tar.gz
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.088 fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/community/aarch64/APKINDEX.tar.gz
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.716 (1/4) Installing ncurses-terminfo-base (6.3_p20211120-r0)
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.737 (2/4) Installing ncurses-libs (6.3_p20211120-r0)
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.764 (3/4) Installing readline (8.1.1-r0)
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.788 (4/4) Installing bash (5.1.16-r0)
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.818 Executing bash-5.1.16-r0.post-install
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.820 Executing busybox-1.34.1-r3.trigger
12:07PM INF actions.deps._dag."0"._dag."1"._exec | #8 1.823 OK: 8 MiB in 18 packages
12:07PM INF actions.deps._dag."0"._dag."1"._exec | completed duration=2.6s
12:07PM INF actions.deps._dag."0"._dag."2"._exec | computing
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.136 fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/aarch64/APKINDEX.tar.gz
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.354 fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/community/aarch64/APKINDEX.tar.gz
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.505 (1/8) Installing ca-certificates (20211220-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.534 (2/8) Installing nghttp2-libs (1.46.0-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.556 (3/8) Installing brotli-libs (1.0.9-r5)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.584 (4/8) Installing c-ares (1.18.1-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.604 (5/8) Installing libgcc (10.3.1_git20211027-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.626 (6/8) Installing libstdc++ (10.3.1_git20211027-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 1.660 (7/8) Installing nodejs (16.14.2-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 2.164 (8/8) Installing yarn (1.22.17-r0)
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 2.229 Executing busybox-1.34.1-r3.trigger
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 2.232 Executing ca-certificates-20211220-r0.trigger
12:07PM INF actions.deps._dag."0"._dag."2"._exec | #9 2.250 OK: 54 MiB in 26 packages
12:07PM INF actions.deps._dag."0"._dag."2"._exec | completed duration=2.8s
12:07PM INF actions.deps._dag."0"._dag."3"._exec | computing
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 0.702 fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/main/aarch64/APKINDEX.tar.gz
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 0.914 fetch https://dl-cdn.alpinelinux.org/alpine/v3.15/community/aarch64/APKINDEX.tar.gz
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 1.039 (1/4) Installing libcurl (7.80.0-r0)
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 1.070 (2/4) Installing expat (2.4.7-r0)
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 1.092 (3/4) Installing pcre2 (10.39-r0)
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 1.119 (4/4) Installing git (2.34.2-r0)
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 1.304 Executing busybox-1.34.1-r3.trigger
12:07PM INF actions.deps._dag."0"._dag."3"._exec | #10 1.307 OK: 66 MiB in 30 packages
12:07PM INF actions.deps._dag."0"._dag."3"._exec | completed duration=1.7s
12:07PM INF actions.deps._dag."1"._copy | computing
12:07PM INF actions.deps._dag."1"._copy | completed duration=900ms
12:07PM INF actions.deps._dag."2"._exec | computing
12:07PM INF actions.deps._dag."2"._exec | #12 2.402 yarn config v1.22.17
12:07PM INF actions.deps._dag."2"._exec | #12 2.418 success Set "cache-folder" to "/cache/yarn".
12:07PM INF actions.deps._dag."2"._exec | #12 2.418 Done in 0.02s.
12:07PM INF actions.deps._dag."2"._exec | #12 2.520 yarn install v1.22.17
12:07PM INF actions.deps._dag."2"._exec | #12 2.579 [1/4] Resolving packages...
12:07PM INF actions.deps._dag."2"._exec | #12 2.894 [2/4] Fetching packages...
12:07PM INF actions.deps._dag."2"._exec | #12 17.19 [3/4] Linking dependencies...
12:07PM INF actions.deps._dag."2"._exec | #12 17.19 warning " > @testing-library/user-event@7.2.1" has unmet peer dependency "@testing-library/dom@>=5".
12:07PM INF actions.deps._dag."2"._exec | #12 17.20 warning "react-scripts > @typescript-eslint/eslint-plugin > tsutils@3.17.1" has unmet peer dependency "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta".
12:07PM INF actions.deps._dag."2"._exec | #12 22.08 [4/4] Building fresh packages...
12:07PM INF actions.deps._dag."2"._exec | #12 22.84 Done in 20.32s.
12:07PM INF actions.deps._dag."2"._exec | completed duration=25.2s
...
コメントつけていってみる
package todoapp
import (
"dagger.io/dagger"
"dagger.io/dagger/core"
"universe.dagger.io/alpine"
"universe.dagger.io/bash"
"universe.dagger.io/docker"
"universe.dagger.io/netlify"
)
dagger.#Plan & {
// _始まりはPrivateField
_nodeModulesMount: "/src/node_modules": {
dest: "/src/node_modules"
type: "cache"
contents: core.#CacheDir & {
id: "todoapp-modules-cache"
}
}
// daggerを実行するホストマシン関連の設定?
client: {
filesystem: {
// Dockerコンテナに持ち込むファイルの定義?
"./": read: {
contents: dagger.#FS
exclude: [
"README.md",
"_build",
"todoapp.cue",
"node_modules",
]
}
// actions.build.contents.outputをホストマシンの _build/ に書き込む
"./_build": write: contents: actions.build.contents.output
}
env: {
APP_NAME: string
NETLIFY_TEAM: string
NETLIFY_TOKEN: dagger.#Secret
}
}
actions: {
deps: docker.#Build & {
steps: [
alpine.#Build & {
// Alpineにインストールするパッケージの指定
packages: {
bash: {}
yarn: {}
git: {}
}
},
// client.filesystem."./"で指定したファイルを /src にコピーしている?
docker.#Copy & {
contents: client.filesystem."./".read.contents
dest: "/src"
},
// yarn install の実行
bash.#Run & {
workdir: "/src"
mounts: {
"/cache/yarn": {
dest: "/cache/yarn"
type: "cache"
contents: core.#CacheDir & {
id: "todoapp-yarn-cache"
}
}
_nodeModulesMount
}
script: contents: #"""
yarn config set cache-folder /cache/yarn
yarn install
"""#
},
]
}
test: bash.#Run & {
input: deps.output
workdir: "/src"
mounts: _nodeModulesMount
script: contents: #"""
yarn run test
"""#
}
build: {
run: bash.#Run & {
input: test.output
mounts: _nodeModulesMount
workdir: "/src"
script: contents: #"""
yarn run build
"""#
}
contents: core.#Subdir & {
input: run.output.rootfs
path: "/src/build"
}
}
deploy: netlify.#Deploy & {
contents: build.contents.output
site: client.env.APP_NAME
token: client.env.NETLIFY_TOKEN
team: client.env.NETLIFY_TEAM
}
}
}