DUICUO

Docker の創設者による新製品、Dagger は良いのでしょうか?

まず、私の個人的な意見を述べさせてください。Daggerは、特に開発者にとって非常に便利ですが、CUE言語の知識が必要となるため、参入障壁はある程度あります。しかし、まだDevOpsにおいて破壊的な製品ではありません。

Dockerの創業者ソロモン・ハイクス氏は先日、新製品「Dagger」のリリースを発表しました。これは、開発者がDevOpsプロセスで直面するいくつかの問題を解決するために設計された新しいDevOpsプラットフォームです。DaggerはすでにRedpoint Venturesが主導するシリーズAラウンドで2,000万ドルの資金調達を完了しており、GitHubの元CEOナット・ファイアマン氏、Red Hatの元CTOブライアン・スティーブンス氏、Redditの元CEOエラン・パオ氏といった著名人が参加しています。

Daggerは、DevOps開発者がCI/CDパイプラインをCUEの宣言型モデルとして記述するのに役立ちます。開発者はこれに基づいて、パイプラインを記述し、純粋なコードで実装されたパイプラインの様々な部分を接続できます。

インストール

macOS を使用しており、Homebrew がインストールされている場合は、次のコマンドを使用してワンクリックで Dagger をインストールできます。

 brew install dagger / tap / dagger

上記のコマンドは、Dagger を /opt/homebrew/bin ディレクトリにインストールします。

 タイプダガー
ダガー/ opt / homebrew / bin / ダガーです

Homebrew または別のシステムがインストールされていない場合、または特定のバージョンの Dagger をインストールする場合は、次のコマンドを使用してインストールできます。

 curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.2.4 sh

./bin/dagger バージョン
ダガー0.2.4 ( GIT_SHA ) ダーウィン/ arm64

Dagger はタスクを実行するために Docker を使用するため、実際に使用するには Docker Engine をインストールして実行する必要があります。

ここで、公式の todo サンプル アプリケーションを使用して、Dagger を使用して CI/CD パイプラインを実行する方法を説明します。

まず、サンプル アプリケーション コードを取得します。

 git クローンhttps://github.com/dagger/dagger
CD ダガー
git チェックアウトv0.2.4

サンプル アプリケーション コードのルート ディレクトリに移動し、`dagger do build` コマンドを実行して CI/CD パイプラインを実行します。

 cd pkg / universe .dagger .io / examples / todoapp
ダガービルド

タスクを初めて実行する際は、キャッシュがないため、すべての依存関係をインストールする必要があります。そのため、このサンプルアプリケーションのテストビルドは完了するまでに時間がかかります。

 [] クライアント.filesystem . "./" .read 0.4 
[] アクション.deps 170.8
[] アクション.test .script 0.2
[] アクション.build .run .script 0.2
[] アクション.test 4.4
[] アクション.build .run 49.6
[] アクション.build .contents 0.1
[] クライアント.filesystem . "./_build" .write

上記の結果は、dagger-buildkitd というローカル コンテナーで実行されるビルド コマンドの実行結果を示しています。

これは、Dagger が Docker の実行エンジンである BuildKit 内でタスクを実行することを証明しています。

これは静的アプリケーションなので、最終的に生成されたファイルをブラウザで開くことができます。ここでは、最終的なビルド結果をホストマシンの `_build` ディレクトリにコピーするように定義しています。`open _build/index.html` コマンドを実行すると、アプリケーションをプレビューできます。

これで、特定のアプリケーション依存関係をインストールする必要がなくなりました。Daggerがこれらの中間ステップをすべて管理します。Daggerを使えば、アプリケーションの結果を確認するために毎回コードをコミットしてプッシュする必要はありません。さらに、各アクションはキャッシュされるため、後続の実行が非常に高速になります。

たとえば、todoapp ディレクトリで、src/components/Form.js の 25 行目を編集し、その内容を「今日何をする必要がありますか?」に変更してファイルを保存し、ローカルで再度ビルド コマンドを実行します。

 ダガービルド
[] クライアント.filesystem . "./" .read 0.2
[] アクション.deps 1.8
[] アクション.test .script 0.2
[] アクション.build .run .script 0.2
[] アクション.test 0.0
[] アクション.build .run 0.0
[] アクション.build .contents 0.0
[] クライアント.filesystem . "./_build" .write

パイプライン全体の実行時間が大幅に短縮され、結果が正しく出力されていることがわかります。ブラウザを更新して、結果が変わったかどうかを確認してください。

パイプラインの定義

Daggerはパイプラインの定義にCUE言語を使用するため、まずこの言語を理解する必要があります。CUE言語の基本的な使い方については、先ほど紹介したCUE言語の使い方を参照してください。

ここでのサンプル アプリケーションのパイプラインの定義は次のとおりです。

 パッケージtodoapp
輸入
「dagger.io/dagger」
dagger.io/dagger/core
「universe.dagger.io/alpine」
「universe.dagger.io/bash」
「universe.dagger.io/docker
「universe.dagger.io/netlify」

ダガー. #Plan & {
_nodeModulesMount : "/src/node_modules" : {
宛先: "/src/node_modules
タイプ: 「キャッシュ」
内容: core . #CacheDir & {
id : "todoapp-modules-cache"
}

}
クライアント {
ファイルシステム: {
"。/" 読む {
内容: ダガー #FS
除外: [
「README.md」
"_建てる"
「todoapp.cue
「node_modules」
]
}
./_build : 書き込み: 内容: アクション.build.contents.output
}
環境: {
APP_NAME : 文字列
NETLIFY_TEAM : 文字列
NETLIFY_TOKEN : ダガー #秘密
}
}
アクション: {
deps : docker . #Build & {
手順: [
アルパイン #ビルド& {
パッケージ: {
バッシュ: { }
{ }
git : { }
}
}
docker . #コピー& {
コンテンツ: クライアント.ファイルシステム. "./" .read .contents
宛先: "/src"
}
bash . #実行& {
作業ディレクトリ: "/src
マウント: {
「/cache/yarn」 : {
保存先: "/cache/yarn
タイプ: 「キャッシュ」
内容: core . #CacheDir & {
ID : "todoapp-yarn-cache"
}
}
_nodeModulesマウント
}
スクリプト: 内容: # """
yarn config でキャッシュフォルダ /cache/yarn を設定します
糸のインストール
「」 #
}
]
}
テスト: bash . #実行& {
入力: deps.output
作業ディレクトリ: "/src
マウント: _nodeModulesMount
スクリプト: 内容: # """
糸走行テスト
「」 #
}
建てる {
実行: bash . #実行& {
入力: テスト.出力
マウント: _nodeModulesMount
作業ディレクトリ: "/src
スクリプト: 内容: # """
糸ランビルド
「」 #
}
内容: core . #Subdir & {
入力: run.output.rootfs
パス: "/src/build"
}
}
デプロイ: netlify #展開する {
コンテンツ: ビルド.contents.output
サイト: client.env.APP_NAME
トークン: クライアント.env .NETLIFY_TOKEN
チーム: クライアント.env.NETLIFY_TEAM
}
}
}

上記のCUEファイルからわかるように、Daggerのパイプラインは#Planから始まります。#Plan内では、以下の処理が可能です。

クライアントのファイル システムと対話します。

  • ファイルを読み取るには、通常、現在のディレクトリを表すドット (.) を使用します。
  • ファイルに書き込みます。ビルド出力は通常、_build ディレクトリにあります。

上記で定義された NETLIFY_TOKEN などの環境変数を読み取ります。

テスト、ビルド、デプロイなどのアクションを宣言します。アクションの名前は任意です。

上記で定義したパイプラインの全体的なアーキテクチャを以下に示します。クライアント部分はクライアント関連のやり取りを定義し、アクション部分はパイプラインのアクションを定義します。

 ダガー. #Plan & {
クライアント {
ファイルシステム: {
// ...
}
環境: {
// ...
}
}
アクション: {
deps : docker . #Build & {
// ...
}
テスト: bash . #実行& {
// ...
}
建てる {
実行: bash . #実行& {
// ...
}
内容: core . #Subdir & {
// ...
}
}
デプロイ: netlify #展開する {
// ...
}
}
}

先ほど `dagger do build` コマンドを実行すると、次の出力が生成されました。

 [] クライアント.filesystem . "./" .read 0.2 
[] アクション.deps 1.8
[] アクション.test .script 0.2
[] アクション.build .run .script 0.2
[] アクション.test 0.0
[] アクション.build .run 0.0
[] アクション.build .contents 0.0
[] クライアント.filesystem . "./_build" .write

`build` アクションのみを実行したため、`deploy` 関連の情報は表示されませんでした。`dagger do` の後にアクション名を指定することで、特定のアクションを実行できます。`build` アクションの入力は `test.output` なので、`test` も実行されます。同様に、`test` アクションの入力は `deps` の出力なので、このアクションも実行されます。

各アクションは基本的に、事前にインポートされたパッケージを使用して定義されます。例えば、`build` アクションの実行フローは、以下のコードに示すように、`bash.#Run` を使用して定義されています。

 建てる {
実行: bash . #実行& {
入力: テスト.出力
マウント: _nodeModulesMount
作業ディレクトリ: "/src
スクリプト: 内容: # """
糸ランビルド
「」 #
}
内容: core . #Subdir & {
入力: run.output.rootfs
パス: "/src/build"
}
}

そのため、`test` アクションの出力は、`input: test.output` を介してこの操作の入力として使用されます。次に、`mounts` を介してマウントディレクトリを指定し、キャッシュされた `nodemodules` ディレクトリの使用を許可します。`workdir` は作業ディレクトリを `/src` として指定し、`script` は実行するコマンドを `yarn run build` として指定します。全体的な定義構造は実際には `base.#Run` によって定義されており、これはパッケージ `universe.dagger.io/bash` を通じて理解できます。

開発者エクスペリエンスを向上させるため、DaggerはDagger Universeと呼ばれるツールキットライブラリをリリースしました。これにより、開発者は独自のDagger構成を柔軟にインポートできるようになります。前述のパイプラインの多くは、このツールキットで定義されています。

クライアントとのやり取り

`dagger.#Plan` で定義できるプロパティや操作を確認するには、インポートされたパッケージ `dagger.io/dagger` のコード(https://github.com/dagger/dagger/blob/main/pkg/dagger.io/dagger/plan.cue にあります)を参照してください。このファイルでは、`client` を介したクライアント側インタラクションの有効化など、`#Plan` のすべてのプロパティが定義されています。

ファイルシステムへのアクセス

client.filesystem を使用してファイル システム アクセスを定義できます。

 ダガー. #Plan & {
クライアント: ファイルシステム: {
"。" 読む {
// ローカルディレクトリをダガーとしてロードします。#FS
内容: ダガー #FS
除外: [ "node_modules" ]
}
"config.yaml" : 書き込み: {
// CUE値をYAML文字列に変換する
内容: yaml .Marshal ( actions .pull .output .config )
}
}
アクション: {
コピー: docker . #コピー& {
内容: クライアント.ファイルシステム. "." .read .contents
}
// ...
}
}

ローカルソケットを取得します:

 ダガー. #Plan & {
クライアント: ネットワーク: "unix:///var/run/docker.sock" : 接続: dagger . #ソケット
アクション: {
画像: alpine . #Build & {
パッケージ: "docker-cli" : { }
}
実行: docker . #実行& {
入力: 画像.出力
マウント: docker : {
宛先: "/var/run/docker.sock"
内容: client .network . "unix:///var/run/docker.sock" .connect
}
指示 {
名前: 「docker」
引数: [ "情報" ]
}
}
}
}

環境変数

環境変数は、ホスト マシンから文字列またはシークレットとして読み取ることができます。タイプを指定するだけで済みます。

 ダガー. #Plan & {
クライアント: 環境: {
REGISTRY_USER : 文字列
REGISTRY_TOKEN : dagger . #シークレット
}
アクション: pull : docker . #Pull & {
ソース: "registry.example.com/image"
認証: {
ユーザー: クライアント.env.REGISTRY_USER
シークレット: クライアント.env.REGISTRY_TOKEN
}
}
}

コマンドを実行

場合によっては、クライアントで定義できるローカル コマンドを実行する必要があります。

 ダガー. #Plan & {
クライアント: コマンド: {
os : {
名前: "uname"
引数: [ "-s" ]
}
アーチ: {
名前: "uname"
引数: [ "-m" ]
}
}

アクション: ビルド: go . #ビルド& {
os : クライアント.コマンド.os .標準出力
arch : クライアント.commands .arch .stdout
// ...
}
}

プラットフォーム情報を取得する

プラットフォーム情報は client.platform を通じて取得できます。

 ダガー. #Plan & {
クライアント _
アクション: ビルド: go . #ビルド& {
os : クライアント.プラットフォーム.os
アーキテクチャ: client.platform.arch
// ...
}
}

ビルドイメージ

同様に、次のコードに示すように、Dagger を使用してコンテナ イメージをビルドすることもできます。

 パッケージメイン
輸入
「dagger.io/dagger」
「universe.dagger.io/docker」

ダガー. #Plan & {
クライアント: ファイルシステム: "./src" : 読み取り: 内容: dagger . #FS
アクション: ビルド: docker . #Dockerfile & {
// コンテキストを構築する
ソース: クライアント. ファイルシステム. " ./src".read.contents
// デフォルトでは、Dockerfile はコンテキスト内で検索されます。ここでは直接宣言します。
Dockerfile : 内容: # """
Python:3.9から
コピー . /app
pip install -r /app/requirements.txt を実行します。
コマンド python /app/app.py
「」 #
}
}

ここでは universe.dagger.io/docker パッケージをインポートしたため、ビルドアクションは docker.#Dockerfile 定義を使用します。まず、client.filesystem を介して ./src ディレクトリの内容を読み取り、次にビルドアクションでビルドコンテキストを指定し、次に dockerfile.contents を介して使用する Dockerfile を直接定義します。もちろん、./src ディレクトリに Dockerfile ファイルを直接配置することもできます。

Dockerfile を直接使用するだけでなく、次のコードに示すように、CUE でイメージを直接ビルドすることもできます。これにより、上記と同じ結果が得られます。

 パッケージメイン

輸入
「dagger.io/dagger」
「universe.dagger.io/docker」


ダガー. #Plan & {
クライアント: ファイルシステム: "./src" : 読み取り: 内容: dagger . #FS

アクション: ビルド: docker . #ビルド& {
手順: [
docker . #Pull & {
ソース: "python:3.9"
}
docker . #コピー& {
内容: クライアント.ファイルシステム. "./src" .read .contents
宛先: "/app"
}
docker . #実行& {
指示 {
名前 「ピップ」
引数: [ "install" , "-r" , "/app/requirements.txt" ]
}
}
docker . #Set & {
設定: コマンド: [ "python" , "/app/app.py" ]
}
]
}
}

ここでは、docker.#Build という定義を使って設定します。steps はビルドステップを定義するために使用できます。docker.#Pull はベースイメージの指定に相当し、docker.#Copy はソースコードファイルのディレクトリをコピーします。docker.#Run はイメージのビルドコマンドを設定し、docker.#Set はイメージの起動コマンドを指定します。実際には、これは CUE を介して Dockerfile 宣言を実装することと同じです。

要約

Daggerはパイプラインの設定にCUE言語を使用するため、当然ながら学習曲線は長くなります。しかし、CUEに慣れてしまえば、Daggerパイプラインの設定は非常に簡単であることがわかります。パッケージ定義を見るだけで、使い方を理解できるはずです。

Daggerのマーケティングスローガンは「CI/CDパイプラインのためのポータブル開発キット」です。DevOpsエンジニアは、どこでも実行できる堅牢なCI/CDパイプラインを迅速に構築し、開発環境とCI環境を統合し、パイプラインのローカルテストとデバッグを可能にします。これは間違いなく、Daggerのこれまでの最大の強みです。しかし、DevOps分野において破壊的な製品と言えるでしょうか?少なくとも今のところ、破壊的な側面は見られません。もちろん、Daggerはまだ非常に初期段階にあり、今後、さらに驚くべき機能が登場するかもしれません。

参考資料

  • https://docs.dagger.io/
  • https://cuelang.org/docs/ をご覧ください。