この記事は、 HashiCorp Japan Advent Calendar 2025 10 日目の記事です。
はじめに
みなさん、どんな環境でゴールデンイメージ(AMI など)を焼いていますか?
そして、「待ち時間」 に疲れていませんか?
AMI 作成の過程を考えてみましょう。
イメージ作成をキックする。
インスタンスが立ち上がるのを数分待つ。
SSH がつながるのを待つ。
プロビジョニングが走り出す...
「あ、シェルスクリプトの変数名間違えた」
エラーで終了。インスタンス削除。
...修正して、もう一回ビルド。また数分待つ。
うまくいっても、イメージビルドの時間がかかって・・・
この「試行錯誤ループ」があまりに遅いため、開発効率が落ちてしまう。これはイメージ作成あるあるです。
更に、独自で CI/CD でイメージビルドが自動で回せるように作っても、
中身の確認には、結構手間取るものです。
今回は、このループを 「数分」から「数秒」に短縮 し、 Packer で効率よくデバッグしていくベーシックな手法を紹介します。
今回の主役:null builder
Packer には、いわゆるイメージビルドに使われるような AWS (amazon-ebs) や Azure (azure-arm) 用のビルダーの他に、null builder というプラグインが存在します。
これは何?
一言で言えば、「何もビルドしない(Artifact を作らない)」 ビルダーです。
通常の Packer は「サーバーを立ち上げ→設定し→イメージ(AMI など)として固める」までが仕事ですが、null ビルダーは以下の仕事しかしません。
- 指定されたホスト(IP アドレス)に SSH する
- Provisioner を実行する
- そのまま終了する(イメージ作成処理をスキップ)
なぜ嬉しいの?
「AMI を作らないなら意味がないのでは?」と思うかもしれませんが、開発フェーズ ではこれが最強の武器になります。料理に例えてみると・・・
Packer の使い分けイメージ
- 普通の Packer: 「冷凍食品(AMI)」を作る工場。完成・梱包までやるので時間がかかる。
- null ビルダー: シェフの「味見」。レシピ(Provisioning コード)が美味しいか(正しいか)だけを即座に確認する。
これを使って、「ローカルで起動したコンテナ」 に対して Packer を実行すれば、AWS への通信待ち時間ゼロで、プロビジョニングスクリプトの検証が可能になります。
3分クッキング:爆速検証環境を作る
では、実際にやってみましょう。AWS のクレデンシャルは一切不要です。
今回は、企業環境などでも導入しやすい Podman を使ってコンテナを立てますが、SSH がつながれば Docker でも VM でも何でも構いません。
1. 検証用ターゲット(コンテナ)の準備
まず、Packer が SSH するための「捨てサーバー」をローカルに立てます。SSH サーバーが入っている軽量なイメージを使います。
ここでは Podman を使っていきます。
# Podman の初期設定がまだ無い方はこちら
brew install podman
podman machine init
podman machine start
Podman が使えるようになったら、以下のコマンドだけで準備が終わります。
# ポート 2222 で SSH を受け付けるコンテナを起動
# ユーザー: root / パスワード: root
podman run -d -p 2222:22 --name packer-test rastasheep/ubuntu-sshd:18.04
これだけで、一瞬で「実験用サーバー」が手に入りました。
2. Packer コードの作成
次に、このローカルコンテナに対して実行する Packer コードを書きます。
ここでのポイントは source "null" "..." ブロックです。
以下のファイルを debug.pkr.hcl のような名前で用意します。
# packer ブロックは最小構成のため不要
#ここがpoint
source "null" "debug-target" {
communicator = "ssh"
ssh_host = "127.0.0.1" # ローカルのコンテナに向ける
ssh_port = 2222 # さっき開けたポート
ssh_username = "root"
ssh_password = "root"
}
build {
sources = ["source.null.debug-target"]
# 検証したいプロビジョニング処理
# ここが複雑な Ansible や ShellScript になっても、高速にテストできます
provisioner "shell" {
inline = [
"echo '=== Start Provisioning ==='",
"apt-get update",
"apt-get install -y curl",
"echo '複雑なインストール処理が完了しました!'",
"curl --version"
]
}
}
3. いざ、実行
まず、 packer init . を実行して準備します。
ただ、 null buidler は core plugin なので特に何も起こりません。
packer init .
最小構成のためこんな感じのログがでますが、全く問題ありません。
No plugins requirement found, make sure you reference a Packer config
containing a packer.required_plugins block. See
https://www.packer.io/docs/templates/hcl_templates/blocks/packer
for more info.
では、実行です。心の準備はいいですか? コマンドを叩いてください。
packer build .
実行結果:
null.debug-target: output will be in this color.
==> null.debug-target: Using SSH communicator to connect: 127.0.0.1
==> null.debug-target: Waiting for SSH to become available...
==> null.debug-target: Connected to SSH!
==> null.debug-target: Provisioning with shell script: /var/folders/13/f62404gn3px5qcff7q0g034m0000gn/T/packer-shell2029297147
==> null.debug-target: === Start Provisioning ===
==> null.debug-target: Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
==> null.debug-target: Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [102 kB]
(中略)
==> null.debug-target: Setting up curl (7.58.0-2ubuntu3.24) ...
==> null.debug-target: Processing triggers for libc-bin (2.27-3ubuntu1) ...
==> null.debug-target: 複雑なインストール処理が完了しました!
==> null.debug-target: curl 7.58.0 (x86_64-pc-linux-gnu) libcurl/7.58.0 OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3
==> null.debug-target: Release-Date: 2018-01-24
==> null.debug-target: Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
==> null.debug-target: Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL
Build 'null.debug-target' finished after 31 seconds 661 milliseconds.
==> Wait completed after 31 seconds 662 milliseconds
==> Builds finished. The artifacts of successful builds are:
--> null.debug-target: Did not export anything. This is the null builder
どうでしょう?
コマンドを叩いてから、あっという間に完了まで駆け抜けていきますよね?
AMI 作成などの後処理もないため、検証が終われば一瞬で完了します。
コンテナ環境によって、AMI 作成時はインスタンス起動を含めた時間がかかりますが、そういった待ち時間もローカルなので不要となり、
null builder によって、実際にイメージの Build が走らない事で、イメージ作成の繰り返し試行の時間を大幅に削ることができるようになります。
応用編:-debug で「止めて」直す
これだけでも便利ですが、さらに便利なのが Packer のデバッグモードです。
packer build -debug .
このオプションを付けて実行すると、各ステップの実行前後に Packer が一時停止し、Enter キーを押すまで待機してくれます。
==> null.debug-target: Pausing before the next provisioner . Press enter to continue.
この「待機中」の間、コンテナはまだ生きています。
つまり、別のターミナルから podman exec でコンテナに入り、状態を確認可能です。
- プロビジョニングが失敗した!
-
-debugで失敗する直前まで進めて一時停止。 - 別画面で
podman exec -it packer-test bash。 - コンテナ内でコマンドを手動実行し、何が悪いか調査する。
- 原因がわかったらコードを直す。
このフローは、どこでビルドしてもデバッグ困難なイメージのビルドにおいて、「可視化されたデバッグ」 を実現し、デバッグを容易にしてくれます。
まとめ:使い分けの戦略
最後に、このテクニックを実際のワークフローにどう組み込むかです。
| フェーズ | 使用するビルダー | ターゲット | 目的 |
|---|---|---|---|
| 開発・デバッグ | null |
ローカルコンテナ (Podman 等) | ロジックの検証。高速イテレーション。 |
| 本番ビルド | amazon-ebs |
クラウド (AWS / Azure) | AMI 作成。OS 固有の問題がないかの最終確認。 |
「ロジック(Ansible / Shell)の正しさ」は null ビルダーで高速に担保し、「OS やクラウド固有の挙動」だけを最後に本物のビルダーで確認する。
これで、開発中のビルド待ち時間の長いコーヒーブレイクとはお別れです。
ぜひ、お手元の setup.sh や Playbook の検証に、null ビルダーを活用してみてください!