フォーム読み込み中
dagger とは Go や Javascript, Python 等のプログラミング言語で CI/CD パイプラインをコードとして開発する事ができるエンジンです。SDK が用意されており、少ない記述でどこでも実行可能な CI/CD を構築する事ができます。可搬性が高い為、例えば GitHub Actions のデバッグをローカルで実施したり、GitHub Actions から Azure Pipeline へ移行するといったような事を簡単に行えるようになります。本記事ではこの dagger についてご紹介します。
本記事では以下のような環境を利用し、dagger を用いて node.js プロジェクトのテスト、ビルド、コンテナ化と GitHub Container Registry への登録までを行います。
まずは dagger を実行する golang のプロジェクトを用意しましょう。
# Shell
go mod init dagger-study
touch main.go
次に main.go へ以下ソースコードを貼り付けます。コードの解説は次項へ記載します。なお 11 行目の `registryPath` はご自身が利用する GitHub Container Registry のパスへ置き換えを行います。
// main.go
package main
import (
"context"
"fmt"
"os"
"dagger.io/dagger"
)
var (
registryPath string = "*** container registry path ***"
hostDir string = "./app"
workDir string = "/app"
)
func main() {
ctx := context.Background()
client, err := dagger.Connect(context.Background(), dagger.WithLogOutput(os.Stdout))
if err != nil {
fmt.Println(err)
}
defer client.Close()
source := client.Host().Directory(hostDir)
fmt.Println("===== Testing with Dagger =====")
testContainer := client.Container().From("node:16")
testContainer = testContainer.WithMountedDirectory(hostDir, source).WithWorkdir(workDir)
testContainer = testContainer.WithExec([]string{"npm", "install"}).WithExec([]string{"npm", "test"})
errorMessage, err := testContainer.Stderr(ctx)
if err != nil {
fmt.Println("> Test failed")
fmt.Println(errorMessage)
}
fmt.Println("===== Building with Dagger =====")
buildContainer := client.Container().Build(source, dagger.ContainerBuildOpts{})
published, err := buildContainer.Publish(ctx, registryPath, dagger.ContainerPublishOpts{
PlatformVariants: []*dagger.Container{buildContainer},
})
if err != nil {
fmt.Println("> Build failed")
fmt.Println(err)
}
fmt.Printf("Published : docker pull %s\n", published)
}
コードの解説です。dagger.Connect により dagger Engine へ接続します。
// main.go
ctx := context.Background()
client, err := dagger.Connect(context.Background(), dagger.WithLogOutput(os.Stdout))
if err != nil {
fmt.Println(err)
}
defer client.Close()
次のコードでは今回コンテナ化する Node.js アプリケーションのパスを指定して、取得しています。
// main.go
source := client.Host().Directory(hostDir)
以下のコードでは今回用意した Node.js アプリケーションのテストを行なっています。Dockerfile でも代替可能ですが、今回は dagger で行なっています。今回は簡単なテストのため、 dagger で記述するメリットはあまりありませんが、もっと複雑なテスト等を行う際には dagger で行うメリットを感じる事ができると思います。
なお、最後の行で `testContainer.Stderr(ctx)` にてエラーメッセージの取得を行なっています。 テストコマンドの実行は `WithExec` メソッドにて行われていますがこの処理上でエラーが発生しても後続の処理はそのまま進んでしまいます。その為、明示的にエラーの取得を行い、 err が nil でなければ処理を止める為に記述しています。
// main.go
testContainer := client.Container().From("node:16")
testContainer = testContainer.WithMountedDirectory(hostDir, source).WithWorkdir(workDir)
testContainer = testContainer.WithExec([]string{"npm", "install"}).WithExec([]string{"npm", "test"})
errorMessage, err := testContainer.Stderr(ctx)
最後に以下の処理ではテストの終わったアプリケーションのコンテナビルドを行なっています。ここで勘の良い方はお気付きかもしれませんが、Github Container Registry に対する認証関連のパラメーターや処理は記述されていません。認証の必要なレジストリへコンテナを push 、または pull する際の処理方法を次に記載致します。
// main.go
buildContainer := client.Container().Build(source, dagger.ContainerBuildOpts{})
published, err := buildContainer.Publish(ctx, registryPath, dagger.ContainerPublishOpts{
PlatformVariants: []*dagger.Container{buildContainer},
})
GitHub Actions を例にご紹介します。以下 YAML ファイルの通り、事前に docker login を dagger の外で実施しています。 dagger にはレジストリへの認証関連の処理は組み込まれていない為、GitHub Actions でもそれ以外の CI ツール上でも事前に docker login を実行しておく必要があります。
※ 以下ファイルは GitHub 上で GitHub Actions の設定ファイルに対して投入してください。
# .github/workflows/build.yml
name: build by dagger
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io --username ${{ github.actor }} --password-stdin
- uses: actions/setup-go@v3
with:
go-version: 1.19
- run: go run ./main.go
これで golang における dagger を使った一連のファイルを作成できました。コマンドラインに戻り、 以下を実行してプロジェクトを完成させましょう。
# Shell
go mod tidy
次に今回コンテナ化するサンプルアプリケーションを用意します。
本検証で利用する node.js プロジェクトを用意します。dagger の検証とは直接関係はないので、読み飛ばして頂いても問題ありません。
# Shell
cat << EOF > .eslintrc.yml
env:
browser: true
commonjs: true
es2021: true
extends: eslint:recommended
overrides: []
parserOptions:
ecmaVersion: latest
rules: {}
EOF
cat << EOF > Dockerfile
FROM node:alpine
COPY . /app
WORKDIR /app
RUN npm install
CMD ["npm", "start"]
EOF
cat << EOF > package.json
{
"name": "app",
"scripts": {
"test": "eslint ./server.js",
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2",
"eslint": "^8.36.0"
}
}
EOF
cat << EOF > server.js
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(3000, () => {
console.log("Example app listening on port 3000");
});
EOF
これにてアプリケーション側も用意ができました。
完成したプロジェクト全体を GitHub へ格納しましょう。GitHub Actions の YAML ファイル上に "main" ブランチへ "push" があれば、と自動的に動作するように指定している為、"push" が完了すると今回作成した dagger が実行され、GitHub Container Registry へビルドされたコンテナが格納されます。
# Shell
git add .
git commit -m "initial commit"
git push
これにて本サンプルは完了となります。 通常であれば GitHub Actions 上のフローは都度 GitHub Actions を実行しなければテストを行う事ができませんが、dagger を用いれば事前にローカルで大部分をテストしておくことが可能です。また、GitHub Actions から他の CI ツールへ移行する際にもほとんど書き直す必要はなく、それでいて golang 上で複雑なテストやビルドを表現する事が可能です。是非、活用ください。
また、本記事は dagger において主に CI 部分を自動化しておりますが、当社ではその後の CD の自動化・高度化として CNAP という Kubernetes のマネージドサービスを公開しております。もし宜しければ以下のリンク記事もご確認ください。
※ Dagger and the Dagger logo are trademarks or registered trademarks of Blocklayer, Inc. in the United States and/or other countries. Blocklayer, Inc. and other parties may also have trademark rights in other terms used herein.
条件に該当するページがございません