この記事の内容
Dockerfileのデバッグは非常に面倒です。ビルドの途中でエラーが出たら、Dockerfileを変更して再実行して確認することである程度デバッグできますが、規模の大きいDockerfileになると時間が掛かります。
Buildgはこうした問題を解決するために、Dockerfileのデバッグを対話的に行うことができるツールです。
対象者
- Dockerの経験がある程度あり、Dockerfileを書いた経験がある人
- Dockerfileでイメージをビルドするときにデバッグが上手くできずに苦労した経験がある人
検証環境
- Ubuntu 20.04
- Docker 23.0.1
Buildgを使わない場合のデバッグ方法の例
Dockerfileでイメージビルドするときにエラーが出たときに、以下の手順でコンテナを調査することができます。
-
docker ps -a
でビルドに失敗したコンテナを見つけ、docker commit
してそのコンテナのイメージを保存する。 - 保存したイメージからコンテナを作成して、そのコンテナにログインして中身を調べる。
この方法でもある程度調査できますが、不要なイメージが溜まっていくのが個人的にストレスだったり、地味に手順1が面倒だなあと思っていました。
そこで、見つけたのがBuildgでした。
Buildgとは?
Githubから引用すると、「BuildkitをベースにしたDockerfileをインタラクティブにデバッグするツール」とあります。
buildg is a tool to interactively debug Dockerfile based on BuildKit.
Buildkitについては深入りしませんが、興味がある方はこちらの記事が分かりやすかったです。
Buildgでできることを紹介すると、以下の通りです。
- ソースレベルの検査
- ブレークポイントとステップ実行
- 独自のデバッグ ツールを使用したステップ上の対話型シェル
- BuildKit ベース (マージされていないパッチあり)
- ルートレスをサポート
参考:ktock/buildg
個人的に嬉しい機能が、「ブレークポイントとステップ実行」です。Dockerfileの行番号を指定して、その行までビルドするということができます。ブレイクポイントまでビルドした状態でコンテナを起動して中身を確認できます。
Buildgのセットアップ
今回は記事投稿時に最新であるv0.4.1
をインストールします。以下のスクリプトを適当な場所に配置して実行すればセットアップできます。今回はバイナリをインストールして利用します。make
を利用してバイナリをビルドすることもできます。インストール手順はこちらで最新の情報を確認してください。
#! /bin/bash
# 実行パス取得
readonly CURRENT=$(cd $(dirname $0);pwd)
# Buildgのバージョン
BUILDG_VERSION="v0.4.1"
# バイナリーのインストール
wget -P ${CURRENT} https://github.com/ktock/buildg/releases/download/${BUILDG_VERSION}/buildg-full-${BUILDG_VERSION}-linux-amd64.tar.gz
# 解凍
tar -zxvf buildg-full-${BUILDG_VERSION}-linux-amd64.tar.gz
# バイナリをコピー
cp ${CURRENT}/bin/* /usr/local/bin
Buildgのデモ
利用するソース
ファイルの配置は以下を想定しています。
.
├── Dockerfile
├── hello.c
└── world.c
# hello.cのコンパイル
FROM ubuntu:20.04 AS hello
RUN apt-get update && \
apt-get install -y gcc
# ソースをホストからコピー
COPY hello.c /
# "hello"という名前の実行ファイルを作成
RUN gcc -o /hello /hello.c
# world.cのコンパイル
FROM ubuntu:20.04 AS world
RUN apt-get update && \
apt-get install -y gcc
# ソースをホストからコピー
COPY world.c /
# "world"という名前の実行ファイルを作成
RUN gcc -o /world /world.c
FROM ubuntu:20.04
COPY --from=hello /hello /hello
COPY --from=world /world /world
CMD /hello ; /world
#include <stdio.h>
int main () {
printf("Hello");
return 0;
}
#include <stdio.h>
int main () {
printf("World!");
return 0;
}
デバッグを開始
コマンドリファレンスは以下を参考にしてください。
Dockerfileを配置したディレクトリで以下を実行します。
buildg debug .
Proxy環境下で実行する場合、--build-arg
で構築時の変数を指定できます。(ただし、DockerfileにProxyを利用する設定の追記が必要。)
buildg debug --build-arg http_proxy="proxy-url" --build-arg no_proxy="no-proxy-list" .
ブレイクポイントの指定
break
でブレイクポイントを指定します。continue
でブレイクポイントまでビルドします。
(buildg) break 25
(buildg) continue
ブレイクポイントまでビルドした状態のコンテナに入る
ビルドした部分までの状態でコンテナ内を調べたい場合は、exec
を実行します。コンテ内でコマンドを実行できることが確認できました。
(buildg) exec
# cat hello.c
#include <stdio.h>
int main () {
printf("Hello");
return 0;
}
# ./hello
Hello
コンテナ、およびデバッグから抜ける
コンテナから抜ける場合は、exit
を実行し、デバッグを終了したい場合もexit
を実行します。
# exit
(buildg) exit
#6 DONE 10.8s
#7 [hello 3/4] COPY hello.c /
#7 DONE 0.0s
#8 [world 3/4] COPY world.c /
#8 DONE 0.0s
#9 [world 4/4] RUN gcc -o /world /world.c
#9 ...
#10 [hello 4/4] RUN gcc -o /hello /hello.c
#10 CANCELED
#9 [world 4/4] RUN gcc -o /world /world.c
#9 CANCELED
まとめ
Buildgを活用することで、Dockerfileのデバッグが対話的に行えることが分かりました。
buildで失敗したときに気軽にコンテナ内を調査できそうですね。