🐈

Dagger Go SDKでCI/CDパイプラインを作成する

2022/11/19に公開

はじめに

今回はDagger Go SDKと AWS を利用して CI/CD パイプラインを構築したいと思います。
Daggerについては以下が参考になります

https://zenn.dev/jinwatanabe/books/fda82594b5a2e8

こちらは私が簡単に翻訳した日本語の公式マニュアルとなります。

https://speakerdeck.com/nagatomo/bao-su-5fen-dewakarudagger

こちらは秋に行われた Go conference の資料となります。
この資料がきっかけで Dagger というツールを知りました。まさかのこの数日後に Go バージョンが発表されるとは思いませんでした

環境

VSCode
Ubuntu 20.04 (WSL2)
Docker 20.10.12
docker-compose version v2.2.3
git version 2.25.1

Dagger で CI/CD パイプラインを作成する

こちらのリポジトリを今回は利用します

https://github.com/jinwatanabe/dagger_go_docker

このリポジトリではローカルにDockerdocker-composeの環境があればどなたでも Dagger Go SDK が利用できるような環境を提供しています

$ git cone https://github.com/jinwatanabe/dagger_go_docker
$ cd dagger_go_docker/
$ cd chapter2

まずローカルで CI/CD を作成してテストとビルドが動くかを確かめます
今回 CI/CD の実行対象にするリポジトリは以下のリポジトリを利用します

https://github.com/jinwatanabe/go_lambda_cicd

あとでこちらのリポジトリを利用します

まずは CI/CD パイプラインの構築を行います。main.goを以下に編集します

main.go
package main

import (
	"context"
	"fmt"
	"os"

	"dagger.io/dagger"
)

func main() {
	ctx := context.Background()

	// クライアントの用意、ログを出力するように追加で設定
	client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stdout))
	if err != nil {
		panic(err)
	}
	defer client.Close()

	// リポジトリをクローン(mainブランチの内容)
	src := client.
		Git("https://github.com/jinwatanabe/go_lambda_cicd").
		Branch("main").Tree()

	if err != nil {
		panic(err)
	}

    // CI/CDの実行環境をGoイメージを使うように設定(Goをテスト/ビルドするため)
    // 環境変数を設定する (今回は必要はないが環境変数を設定することもできる)
	golang := client.Container().From("golang:latest").WithEnvVariable("MESSAGE", "CI/CDが成功しました")

    // コンテナの/appにリポジトリの内容をコピー
    // カレントディレクトリを/app/srcに変更(srcにGoのコードがあるため)
	golang = golang.WithMountedDirectory("/app", src).WithWorkdir("/app/src")

    // コンテナの上でコマンドを実行
    // テストコマンド
    // ビルドコマンド
    // ビルド結果を表示(mainがあることを確認)
		// 環境変数を利用してを表示
	golang = golang.Exec(dagger.ContainerExecOpts{
		Args: []string{"sh", "-c", "go test"},
	}).
		Exec(dagger.ContainerExecOpts{
			Args: []string{"sh", "-c", "env CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags='-s -w' -o bin/main handler/main.go"},
		}).
		Exec(dagger.ContainerExecOpts{
			Args: []string{"sh", "-c", "ls -la bin"},
		}).
		Exec(dagger.ContainerExecOpts{
			Args: []string{"sh", "-c", "echo $MESSAGE"},
		})


    // CI/CDに失敗したらここでエラーがでて停止する
	if _, err := golang.ExitCode(ctx); err != nil {
		panic(err)
	}

	fmt.Println("Success!!")
}

コードに関してはコメントで説明させていただきました
実際に CI を動かしていきます

$ docker-compose up
# 別ターミナルを開く
$ docker exec -it dagger sh
$ go run main.go
#17 sh -c ls -la bin

#17 0.174 total 10248
#17 0.174 drwxr-xr-x 1 root root     4096 Nov 19 08:24 .
#17 0.174 drwxr-xr-x 1 root root     4096 Nov 19 08:02 ..
#17 0.174 -rwxr-xr-x 1 root root 10481664 Nov 19 08:24 main
#17 DONE 0.2s

このようなメッセージがでていれば成功です

Gitlab-runner で実行する

以下のファイルを用意しました

gitlab-ci.yml
(省略)

build:
  script:
    - echo "Start CI/CD"
    - docker-compose -f docker-compose.ci.yml up -d
    - echo "Start Docker Engine"
    - sleep 10
    - docker exec cicd sh -c "go run main.go"
    - docker-compose -f docker-compose.ci.yml down -v
    - echo "Success"
  only:
    - main

このファイルを gitlab-runner 上で動かすことで Dagger が実行できました

ポイントとしてはsleep 10Docker in DockerであるコンテナのDockerデーモンが起動するのを待っていることです。起動していない状態でgo run main.goをすると Go で利用しているライブラリttrpcでコンテナ起動ができないためエラーになってしまいます。

CodeBuild で実行する

はじめに CodeBuild 上で実行するスクリプトを作成します
buildspec.ymlというファイルを作成して以下の内容を追加します

buildspec.yml
version: 0.2

env:
  variables:
    GO_VERSION: 1.19.3
phases:
  install:
    commands:
      - apt-get update
      - apt-get install wget
      - wget https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz
      - tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
      - export PATH="/usr/local/go/bin:$PATH" && export GOPATH="$HOME/go" && export PATH="$GOPATH/bin:$PATH"
  build:
    commands:
      - cd chapter2
      - go run main.go

次に GitHub にリポジトリを作成します。

次に AWS コンソールを開きます
「codebuild」を検索してクリックします

「ビルドプロジェクトを作成する」をクリック

以下の設定をします

項目
プロジェクト名 dagger-ci
ソースプロバイダ GitHub
リポジトリ GitHub アカウントのリポジトリ
GitHub リポジトリ 作成したリポジトリを選択
オペレーティングシステム Ubuntu
イメージ 最新のものを選択
イメージのバージョン 最新のものを選択
特権付与

「ビルドプロジェクトを作成する」をクリック

作成出来たら「ビルドを開始」をクリック

以下のようになれば成功です

おまけ: CodeBuild 上で Go コンテナを起動してそのうえで Dagger を起動する

現在エラーを解決が発生して解決できておりません

buildspec.ymlを以下で作成しました

buildspec.yml
version: 0.2
phases:
  build:
    commands:
      - cd chapter2
      - docker-compose up -d
      - sleep 10
      - docker exec dagger sh -c "go run main.go"

現在ここでエラーが発生しています

現在調査中で進捗はこちらのスクラップで記載しています

CodeBuild 上で Docker in Docker のイメージからコンテナを作成した際にネットワークがつながらなくなるようです。

この方法を用いなくても問題なく利用することはできますが、気になるので調査を続けます。

もしわかる方がいればコメントいただきたいです。

おわりに

Dagger Go SDK を使って以下のようなメリット/デメリットを感じました

メリット

  • Go で CI/CD がかける
  • ローカルでパイプラインを実行できるのでマージして CI/CD 回して修正しての手間から解放される
  • ツールに依存する yml ファイルが少ないコマンドで終わるので楽
  • Go なので外部からモジュールインポートができる

デメリット

  • 今回は docker in docker でやったのでリソースを多く消費する
  • dagger でもコンテナを起動するためイメージ取得の時間が追加出かかる

私としては何度も CI/CD を回してエラーを解決する手間がなくなったのがとてもありがたいと思いました。

ちなみに CodeBuild で Dagger を実行するのですら何度もマージしたりと手間がかかりました。更にコマンドが本来ならあるのでより時間がかかります。。

更に発展させる場合は Go には強力な並列処理があるのでうまく利用することで CI/CD の時間を短縮することも可能です

https://developer.mamezou-tech.com/blogs/2022/10/28/dagger-go-sdk/

参考

GitHubで編集を提案

Discussion