こんにちは。AWS事業本部コンサルティング部に所属している今泉(@bun76235104)です。
まだ熟成していない技術を触るのって楽しい反面、ドキュメントが少なくて苦労する時ってありますよね?
私は先日DaggerというCI/CDに役立つツールがNode.js SDKをリリースしていたことに気づき、試してみました!
Daggerというサービス自体がどんなものか気になる方は、こちらの記事をご参照ください。
その後、「ちょっと実践的な使い方もしてみたいな」ということで、既存のDockerfileからコンテナイメージをビルドしてECRにプッシュ
してみることにしました。
現時点でバージョンがv0.3.0
(2022年1月1日執筆時点)ということもあり、まだまだ発展途上です。
何点か詰まった点があるため、記事を書いてみることにしました。
今回は既存のDockerfileからコンテナイメージをビルドするまでに詰まった点
について記載します。
具体的には以下のエラーの解消方法と調査方法についてです。
[
{
"message": "failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15",
"locations": [
{
"line": 5,
"column": 9
}
],
"path": [
"container",
"build"
]
}
]
先に結論(エラーの原因と解決方法)
以下のようなディレクトリ構造で、Dockerfileからイメージをビルドしようとしていました。
.
├── app
│ ├── Dockerfile #このDockerfileを利用したい
│ ├── app.py
│ └── requirements.txt
├── cicd.ts
├── package-lock.json
├── package.json
├── sample_env
└── tsconfig.json
cicd.ts
import Client, { connect } from "@dagger.io/dagger";
import * as path from "path";
// initialize Dagger client
connect(async (client: Client) => {
try {
// appディレクトリ配下のDockerfileからビルド
const builder = client.container().build(path.join(process.cwd(), "app"));
// ここでエラーが発生する
const id = await builder.id();
console.log(id);
} catch (e) {
console.log("Error!!");
const json = JSON.stringify(e);
console.log(json);
}
});
原因は上記ハイライト部分です。
以下画像は私が利用するエディタの型情報のヒントです。
引数として渡す context
という変数名から「これはDockerのビルドコンテキストのことだな。今回app
直下でDockerfileからビルドしたいからパスを渡そう」と誤認してしまいました。
型として文字列を受け取れるため、コンパイル時点ではこちらが間違いと気づかなかったというわけです。
実際のソースコードを見ればわかるのですが、ここで文字列を渡す場合はDirectoryIDを引き渡す必要があり、パスを表す文字列ではありません。
build(
context: DirectoryID | Directory,
opts?: ContainerBuildOpts
): Container {
return new Container({
queryTree: [
...this._queryTree,
{
operation: "build",
args: { context, ...opts },
},
],
host: this.clientHost,
sessionToken: this.sessionToken,
})
}
// 略
ということで以下のように、Daggerで定義されているDirectoryクラスのインスタンスを利用することで無事解決しました。
connect(async (client: Client) => {
try {
const buildDir = client.host().directory(path.join(process.cwd(), "app"));
const builder = client.container().build(buildDir);
await builder.publish(
`${process.env.PUBLISH_TARGET}:${process.env.PUBLISH_TAG}`
);
} catch (e) {
console.log(JSON.stringify(e));
process.exit(1);
}
});
おまけ:調査する時のTips
エラーメッセージからヒントを探す
まだ以下のコードでエラーを出してしまった時です。
cicd.ts
import Client, { connect } from "@dagger.io/dagger";
import * as path from "path";
// initialize Dagger client
connect(async (client: Client) => {
try {
// appディレクトリ配下のDockerfileからビルド
const builder = client.container().build(path.join(process.cwd(), "app"));
// ここでエラーが発生する
const id = await builder.id();
console.log(id);
} catch (e) {
console.log("Error!!");
const json = JSON.stringify(e);
console.log(json);
}
});
この時はエラーメッセージのハンドリングもしておらず、以下のようにちょっと見にくい形で表示されていました。
cause: ClientError: failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15: {"response":{"data":null,"errors":[{"message":"failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15","locations":[{"line":5,"column":9}],"path":["container","build"]}],"status":200,"headers":{}},"request":{"query":"\n {\n container {\n \n build (context: \"/path-to-app/dagger_nodejs/ecr_python/app\") {\n \n id }}\n }\n "}}
ちょっと嫌になったので\n
となっている箇所を取り除き、以下のように見やすい形に整形してみました。
[
{
"message": "failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15",
"locations": [
{
"line": 5,
"column": 9
}
],
"path": [
"container",
"build"
]
}
]
この時点ではDirectoryIDというものを認識していないので、*core.directoryIDPayload bytes
というメッセージをみてもさっぱりわかりませんが、path: ['container', 'build']
というメッセージから、ビルドが原因であろうことはわかりました。
そこでエディタの定義ジャンプ機能を使って、ソースコードを確認したところ上記のようなミスに気づいたという経緯です。
例えばVSCodeの場合、調べたいメソッドの上でF12
を押下することでジャンプできます。(参考: 【Windows版】VS Code キーボードショートカット一覧 (オススメ付き) - Qiita)
この状態でF12を押下することで、以下のようにソースコードにジャンプできます。
「エラーをよくみる」「ソースコードを見る」という基本をおざなりにしたらダメだなと思いました(子どものような感想)。