ヽ(´・肉・`)ノログ

How do we fighting without fighting?

Packerを使ってVagrantのBoxを作る方法を一つずつ説明する

Packer を使って Vagrant の Box を OS インストールから作成できたので,一つずつ説明する.

コードは github の niku/my-packer-and-vagrant-example にある.

読むと手に入るもの

packer build wheezy64.json

と実行するだけで 「chef-solo が実行可能な Vagrant 用 Box を OS インストールから全自動で作成してくれる環境」 の作り方.

問題にあたったときに,どういうところを参考にしたらよいか,がわかる.(ように書きたいと考えている)

読んでも手に入らないもの

  1. VirtualBox/Vagrant/Packer のインストール方法
  2. Vagrant Box 作成のベストプラクティス

2 については veewee/templates 以下を見るのがよい.今回もかなり参考にさせてもらった.

必要なソフト

ホスト OS は windows7 である.

今回は VirtualBox4.2.16 / Vagrant1.2.7 / Packer 0.2.3 で行なった. Packer は現在 (2013/08/20) 最新版が不安定なことがあるので,最新版で試してみて,うまくいかなければ 0.2.3 へダウングレードしてみるとよい. 僕が 0.3.x 系を試したときにはいくつか不安定な挙動をした.0.2.3 ではそのようなことはおきなかった.

2014-04-04現在の最新版であるVirtualBox4.3.10 / Vagrant1.5.1 / Packer 0.5.2で正常に動作した.

0. テンプレート解析に失敗する

以降のステップは全て my-packer-and-vagrant-template ディレクトリで作業している.

$ packer build wheezy64.json
Failed to parse template: open wheezy.json: no such file or directory

まだ何も用意していないので当然エラーになる.

最小限のテンプレートを用意する.

TEMPLATES には builders が必須 (required),VirtualBox Builder には

が必須 (required) と書いてあるので,準備する.

7171bf7417fb8464fa2aab8044a351a05eef6d3d のようになる.

packer0.5.0からtype名はvirtualboxではなくvirtualbox-isoになった.271a734d8953e89ab15ca24959bcf3194961e22c参照のこと

1. ブートメニューで止まる

$ packer build wheezy64.json

すると,Debian の ISO がダウンロードされて

01-stop-at-boot-menu.png

のようにブート画面が起動する.しかしそこからは何も進まない.

大抵の OS には,ファイルを用意しておくと自動的にインストールを進めてくれるような機能が備わっており, Packer ではそれを利用して OS の自動インストールを進められるようになっている.

その機能を利用するためには,最初に Boot Command というのものを入力しなければならないが,すぐに理解するのは難しい. そこで veewee のテンプレート を流用させてもらうことにする.リンク先を見てわかるように主要な OS のテンプレートは揃っている.

veewee の definition.rb を開くと,:boot_cmd_sequence という項目がある.その部分をコピーして

  1. JSON として利用するために ‘( シングルクォート ) を “( ダブルクォート ) に変更
  2. Packer のキーシュミレートにあわせて <Abc> を <abc> のように変更
  3. Packer の変数にあわせて %IP% を {{ .HTTPIP }} のように変更

の 3 つを行った.

Packer の Boot Command の詳細は VirtualBox Builder に記述がある.

965428a5ced59ace6ce772d8caee42cd86f34a11 のようになる.

2. ブートコマンド入力中に止まる

前回と同じように

$ packer build wheezy64.json -force

すると,ブート画面が起動する.(force は,作りかけのディレクトリを強制的に上書きするオプション)

10 秒くらい待つと画面が切り換わって,文字を入力しはじめるが,図のような状態で止まってしまう.

02-stop-at-boot-command.png

i が入力されておらず install ではなく nstall になってしまっているせいである.

Boot Command はキー入力をエミュレートしているので「画面が切り換わったら」という処理には対応していない.(画面を見ずにキーを入力しているのと同じ)

そこで,ブート画面起動 -> 10 秒くらい待つ -> Esc を押す -> install を入力するのあいだにウェイト処理を入れ,ブート画面起動 -> 10 秒くらい待つ -> Esc を押す -> 1 秒待つ -> install とする.

キー入力のウェイト制御は Boot Command の <wait> で行う.今回の自分の環境では起こらなかったが,もしブート画面が起動する前にキー入力が始まってしまう場合は boot_wait の時間を伸ばしてみるとよい.

差分は 6a29e4df31e804c93672ad8e5ea37ca4f0852884 のようになる.

3. コンフィグファイルがダウンロードできなくて止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,エラー画面になる.

03-fail-at-download-debconf-preconfiguration-file.png

OS のインストールをする際,いくつか設定が必要である.OS を自動的にインストールするためには,それらの設定を自動で行う必要がある.各 OS には,たいてい自動インストール用の設定を取得する方法が用意されている.

Packer では自動インストールが簡単にできるように,いくつかの仕組みを用意している.

Debian, Ubuntu, CentOS などではネットワーク越しに自動インストールの設定が取得できるようになっている. Packer は HTTP サーバーになる機能をもっていて,http_directory を指定すれば,その下にあるファイルを HTTP サーバー越しに取得できる.

Windows ではフロッピーディスクから自動インストールの設定が取得できるようになっている. Packer はフロッピーディスクをエミュレートする機能をもっていて,floppy_files を指定すれば,そのファイルをフロッピードライブ越しに取得できる.

今回は Debian をインストールしているので http_directory を指定して,そこに自動インストールの設定を置いた. 設定ファイルの書き方やサンプルは各 OS のマニュアルに用意されているはずなので,そこから探す. 今回の場合だと事前設定ファイルの内容 (wheezy 用) というページを参考に http://www.debian.org/releases/wheezy/example-preseed.txt から取得してきた.

4. ルートパスワード入力待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとルートパスワード入力画面になって止まる.

04-stop-at-input-root-password.png

ここからは事前設定ファイルの内容の説明,事前設定ファイルに書いてあるコメント,グーグル,勘の 4 つを駆使して作業を進める. ここには正解ルートがスラスラと書いてあるので,全然悩まなかったようにみえるだろうが,実際には多少悩みながら行きつ戻りつ作業していた. もしうまくいかなくても,コマンド一つで再チャレンジできるので,気楽に変更してみるとよい.

画面の内容を見ると,root password の入力が必要らしい.事前設定ファイルの説明を password で検索すると,B.4.5 にアカウント設定というのがあるので,これだろう.

今回は,Debian でよくある root でログインしない(かわりに各ユーザーの sudo 権限で操作する)という方式にしようと思うので

#d-i passwd/root-login boolean false

d-i passwd/root-login boolean false

とコメントアウトする.

5. ユーザー名 ( フルネーム ) 入力待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとユーザー名 ( フルネーム ) 入力画面になって止まる.

05-stop-at-input-user-full-name.png

“4. ルートパスワード入力待ちで止まる” と同様に,事前設定ファイルの説明 B.4.5 をみて

#d-i passwd/user-fullname string Debian User

d-i passwd/user-fullname string Vagrant User

とコメントアウトする.

後述するように Vagrant が期待する初期値 ( Convention over Configration を参照のこと ) はある.しかしフルネームは特に決まっていないようなので,適当に名付けてよい.

6. ユーザー名入力待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとユーザー名入力画面になって止まる.

06-stop-at-input-username.png

後で使う Vagrant では初期ユーザー名に指定がある ( Convention over Configration を参照のこと ) ので,それに従い vagrant と名付けることにする.

#d-i passwd/username string debian

d-i passwd/username string vagrant

にする.

7. パスワード入力待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとパスワード入力画面になって止まる.

07-stop-at-input-password.png

Vagrant では初期パスワードに指定がある ( Convention over Configration を参照のこと ) ので,それに従い vagrant と名付けることにする.

#d-i passwd/user-password password insecure

d-i passwd/user-password password vagrant

にする.

8. 再パスワード入力待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つと再パスワード入力画面になって止まる.

08-stop-at-input-password-verify.png

当然 “7. パスワード入力待ちで止まる” と同じものを設定しないとエラーになる ( はず.試してはいない…… ) ので vagrant と入力する.

#d-i passwd/user-password-again password insecure

d-i passwd/user-password-again password vagrant

にする.

9. パッケージ人気投票参加選択待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとパッケージ人気投票参加選択待ちで止まる.

09-stop-at-choose-to-participate-popularity-contest.png

(文字がぐちゃっとなるのが気になるが原因は調べていない.読めはするので今回は気にしないことにした)

人気投票は debian が,どのパッケージが人気か調べて インストール用 CD の 1 枚目に入れるパッケージを決めたりするのに使う. 週に 1 回の送信で,かつ匿名なので参加してもかまわないのだが,今回は参加しないことにする.

preseed.cfg を popularity で検索すると

#popularity-contest popularity-contest/participate boolean false

という,それらしいものが検索にひっかかる.

これをコメントアウトして

popularity-contest popularity-contest/participate boolean false

にする.

10. インストールするソフトウェアタスクの選択待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとインストールするソフトウェアタスクの選択待ちで止まる.

10-stop-at-choose-software-to-install.png

今回はミニマルを目指して,何も含めないで進めることにしているので.B.4. 事前設定ファイルの内容 (wheezy 用) - B.4.10. パッケージ選択 を参考にして,

#tasksel tasksel/first multiselect standard, web-server

tasksel tasksel/first multiselect

へ変更した.「何も含めない」という指定の方法は空欄でよいようだ.

( あらためて参考文献を読むと「standard タスクは常に含めるのをお勧めします」と書いてあったので,standardくらいは含めておいてもよかったなと,今は思っている )

11. ブートローダーをインストールするかの選択待ちで止まる

$ packer build wheezy64.json -force

すると,ブート画面が起動して,ブートコマンドが入力され,しばらく待つとブートローダーをインストールするかの選択待ちで止まる.

11-stop-at-choose-install-grub-boot-loader.png

ここでブートローダーを入れないを選択するのは,他の OS が既にインストールされており,共存させる場合である.今回は新規にまっさらな状態から作っているので,ブートローダーは必ずインストールする.

B.4. 事前設定ファイルの内容 (wheezy 用) - B.4.11. ブートローダのインストール を参考に

d-i grub-installer/only_debian boolean true

を追記した.

( 取得した初期設定ファイルにはこの記述がなかった.なぜだろう? )

だんだんサクサク進めていけている気がする.

12. ログインコンソールで止まる

“11. ブートローダーをインストールするかの選択待ちで止まる” が終わると,Packer による仮想 OS の作成がひとまず正常に完了する.やった!ヒュー!

さて,次に Packer によって作成した仮想 OS を Vagrant で起動できるようにしたい. そこでこのように Packer 設定ファイルの post-process に vagrant を指定する.

その状態で

$ packer build wheezy64.json -force

するとコンソール画面は以下のようになって止まる.

$ packer build wheezy64.json --force
virtualbox output will be in this color.

==> virtualbox: Downloading VirtualBox guest additions. Progress will be shown periodically.
==> virtualbox: Copying or downloading ISO. Progress will be reported periodically.
    virtualbox: Download progress: 0%
==> virtualbox: Starting HTTP server on port 8081
==> virtualbox: Creating virtual machine...
==> virtualbox: Creating hard drive...
==> virtualbox: Creating forwarded port mapping for SSH (host port 3213)
==> virtualbox: Starting the virtual machine...
==> virtualbox: Waiting 10s for boot...
==> virtualbox: Typing the boot command...
==> virtualbox: Waiting for SSH to become available...

その際 VirtualBox が起動しており,ログイン画面が出たままになる.

12-stop-at-login-console.png

待っても何もおきない.

この問題解決のヒントは,コンソール画面の最後の文章に書いてある. コンソール画面では VirturlBox で起動した OS へ SSH で接続できるようになるのを待っている. つまり,VirtualBox で起動した OS では SSH 接続を受け入れられる準備をしておかなければならない.

SSH 接続を受け入れるには,ssh のサーバー機能を動かしておく.Debian では openssh-server というものがそれに対応するので

#d-i pkgsel/include string openssh-server build-essential

d-i pkgsel/include string openssh-server

のように変更して,OS インストール時に openssh-server もインストールするよう変更する.

ここまでやって,再度

$ packer build wheezy64.json -force

してもコンソール画面は同じところで止まる.

実は SSH でログインするには

  1. ログインしたい側 ssh クライアント機能
  2. ログイン受け入れ側 ssh サーバー機能
  3. ログイン受け入れ側ユーザー名
  4. ログイン受け入れ側パスワード or 公開鍵

が必要なのだ.

今の状態を整理すると 1 は vagrant に組込まれている. 2 は先程用意した (openssh-server) . 3 は設定ファイルに書いてある ssh_username を利用する. しかし 4 については何も用意していない. そのために SSH ログインができない状態になっている.

そこでパスワードを

"ssh_password": "vagrant",

のように設定ファイルに記載してやる.

今までにくらべて,いくつかの変更をしないと直面している問題が解決しなかったので,やや長くなってしまった. 落ちついて読みなおすと,3 箇所しか変更していないので,つまっても何度か試してみてほしい.

ちなみにここまでやると以下のように packer_virtualbox_virtualbox.box が生成される.いぇーい.

$ packer build wheezy64.json --force
virtualbox output will be in this color.

==> virtualbox: Downloading VirtualBox guest additions. Progress will be shown periodically.
==> virtualbox: Copying or downloading ISO. Progress will be reported periodically.
==> virtualbox: Starting HTTP server on port 8081
==> virtualbox: Creating virtual machine...
==> virtualbox: Creating hard drive...
==> virtualbox: Creating forwarded port mapping for SSH (host port 3213)
==> virtualbox: Starting the virtual machine...
==> virtualbox: Waiting 10s for boot...
==> virtualbox: Typing the boot command...
==> virtualbox: Waiting for SSH to become available...
==> virtualbox: Connected to SSH!
==> virtualbox: Uploading VirtualBox version info (4.2.16)
==> virtualbox: Uploading VirtualBox guest additions ISO...
==> virtualbox: Halting the virtual machine...
==> virtualbox: Preparing to export machine...
    virtualbox: Deleting forwarded port mapping for SSH (host port 3213)
==> virtualbox: Exporting virtual machine...
==> virtualbox: Unregistering and deleting virtual machine...
==> virtualbox: Running post-processor: vagrant
==> virtualbox (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox (vagrant): Copying: output-virtualbox\packer-disk1.vmdk
    virtualbox (vagrant): Copying: output-virtualbox\packer.ovf
    virtualbox (vagrant): Renaming the OVF to box.ovf...
    virtualbox (vagrant): Compressing box...
Build 'virtualbox' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox: 'virtualbox' provider box: packer_virtualbox_virtualbox.box

13. vagrant up に失敗する

さて vagrant box が生成されたので起動してみよう.

Vagrant は

vagrant up

すると起動する.

$ vagrant up
A Vagrant environment is required to run this command. Run `vagrant init`
to set one up in this directory, or change to a directory with a
Vagrantfile and try again.

おや……

実はエラーメッセージの中にもあるように Vagrantfile というものを用意するか, vagrant init と実行して初期設定しなければならない.

そこで,言われた通りに

vagrant init

を行なうと Vagrantfile が生成される.

14. vagrant up に失敗する(2)

Vagrantfile を生成したので再度 vagrant up する.

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
There are errors in the configuration of this machine. Please fix
the following errors and try again:

vm:
* The box 'base' could not be found.

今度は「’base’ という名前の box が見つけられない」というエラーになる.

vagrant は box を利用する.その際,どの box を利用するかは Vagrantfile の中に書いてある.

今は

config.vm.box = "base"

となっている.

そこで vagrant は base という box を探すが,見つけられない. なので「’base’ という名前の box が見つけられない」というエラーになる.

これを解決するには vagrant に対して「base という名前でこの box ファイルを利用しますよー」と教えてやればいい.

そのコマンドは

vagrant box add [box名] [boxファイル名]

となる.

実行してみる

$ vagrant box add base packer_virtualbox_virtualbox.box
Downloading or copying the box...
Extracting box...
Successfully added box 'base' with provider 'virtualbox'!

うまくいったようだ.

今回はソースコードには何も手を加えていない.

15. vagrant で起動した仮想環境に SSH で接続できなくて失敗する

vagrant up すると,virtural box が GUI 起動して,ログイン画面が表示される.が,その後何もおきない.

数分待っていると GUI も終了し,コンソールもエラーで終わる.

コンソールのメッセージは以下のようになる

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'base'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Fixed port collision for 22 => 2222. Now on port 2200.
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2200 (adapter 1)
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] Failed to connect to VM!
Failed to connect to VM via SSH. Please verify the VM successfully booted
by looking at the VirtualBox GUI.

vagrant を起動した環境 ( 以下「ホスト環境」と呼ぶ ) と,vagrant で起動された仮想環境 ( 以下「VM環境」と呼ぶ ) の間は SSH で接続する.

Failed to connect to VM via SSH. Please verify the VM successfully booted

というメッセージからもわかるとおり,この SSH 接続がうまくできていない.

SSH 接続がうまくできていない原因は大きくわけると以下の 2 つになる.

  1. 仮想環境が SSH 接続を受けつけるようになっていない
  2. vagrant が仮想環境に入るための鍵穴 ( 公開鍵 ) を仮想環境に用意していない

そこで 2 を解決するために以下のようなシェルスクリプト (vagrant.sh) を追加し,vagrant が仮想環境に入るための鍵穴 ( 公開鍵 ) を取得するようにした. ( 1 は後程解決させる )

# Install vagrant keys
mkdir -pm 700 /home/vagrant/.ssh
curl -Lo /home/vagrant/.ssh/authorized_keys \
  'https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub'
chmod 0600 /home/vagrant/.ssh/authorized_keys
chown -R vagrant:vagrant /home/vagrant/.ssh

そして,そのシェルスクリプトを packer が仮想環境構築時に実行するよう設定ファイル (wheezy64.json) に追記した.

"provisioners": [{
  "type": "shell",
  "scripts": [
    "vagrant.sh"
  ]
}],

ここらへんややこしいが,ついてこれているだろうか.

このスクリプトが正しく動作すれば,SSH 接続の問題は解消するのだが,現状だとうまく動作しない. 以下 16 - 18 の手順で解消していこう.

16. curl コマンドがなくて失敗する

先程までは vagrant コマンドを実行していたが,再度 Packer のビルドからやり直してゆく.

$ ../packer/packer.exe build --force wheezy64.json
virtualbox output will be in this color.

==> virtualbox: Downloading VirtualBox guest additions. Progress will be shown periodically.
==> virtualbox: Copying or downloading ISO. Progress will be reported periodically.
==> virtualbox: Starting HTTP server on port 8081
==> virtualbox: Creating virtual machine...
==> virtualbox: Creating hard drive...
==> virtualbox: Creating forwarded port mapping for SSH (host port 3213)
==> virtualbox: Starting the virtual machine...
==> virtualbox: Waiting 10s for boot...
==> virtualbox: Typing the boot command...
==> virtualbox: Waiting for SSH to become available...
==> virtualbox: Connected to SSH!
==> virtualbox: Uploading VirtualBox version info (4.2.16)
==> virtualbox: Uploading VirtualBox guest additions ISO...
==> virtualbox: Provisioning with shell script: vagrant.sh
    virtualbox: /tmp/script.sh: line 3: curl: command not found
    virtualbox: chmod: cannot access `/home/vagrant/.ssh/authorized_keys': No such file or directory
==> virtualbox: Halting the virtual machine...
==> virtualbox: Preparing to export machine...
    virtualbox: Deleting forwarded port mapping for SSH (host port 3213)
==> virtualbox: Exporting virtual machine...
==> virtualbox: Unregistering and deleting virtual machine...
==> virtualbox: Running post-processor: vagrant
==> virtualbox (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox (vagrant): Copying: output-virtualbox\packer-disk1.vmdk
    virtualbox (vagrant): Copying: output-virtualbox\packer.ovf
    virtualbox (vagrant): Renaming the OVF to box.ovf...
    virtualbox (vagrant): Compressing box...
Build 'virtualbox' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox: 'virtualbox' provider box: packer_virtualbox_virtualbox.box
virtualbox: /tmp/script.sh: line 3: curl: command not found

となっているのがわかるだろうか.「curl コマンドがない」と言われている.

確かにインストールしていないので,ない.そこでインストールする.

curl のインストールはパッケージ (apt) 経由で行うのが簡単だ. パッケージ経由のインストールは以前 3 - 11 の手順でも利用していた preseed.cfg 経由で行なえる. 今回はこれを使う.

preseed.cfg の

d-i pkgsel/include string openssh-server

d-i pkgsel/include string openssh-server curl

とすることで,curl パッケージがインストールされ,curl コマンドが利用できるようになる.

17. provisioning で行なった仮想環境の変更が保存されない

コマンドを実行してみると,着々と進むので「うまくいったかな」と思ってしまう. しかし後述するように,実はうまくいってない.

$ packer build wheezy64.json --force
virtualbox output will be in this color.

==> virtualbox: Downloading VirtualBox guest additions. Progress will be shown periodically.
==> virtualbox: Copying or downloading ISO. Progress will be reported periodically.
==> virtualbox: Starting HTTP server on port 8081
==> virtualbox: Creating virtual machine...
==> virtualbox: Creating hard drive...
==> virtualbox: Creating forwarded port mapping for SSH (host port 3213)
==> virtualbox: Starting the virtual machine...
==> virtualbox: Waiting 10s for boot...
==> virtualbox: Typing the boot command...
==> virtualbox: Waiting for SSH to become available...
==> virtualbox: Connected to SSH!
==> virtualbox: Uploading VirtualBox version info (4.2.16)
==> virtualbox: Uploading VirtualBox guest additions ISO...
==> virtualbox: Provisioning with shell script: vagrant.sh
    virtualbox: % Total    % Received % Xferd  Average Speed   Time    Time      Time  Current
    virtualbox: Dload  Upload   Total   Spent    Left  Speed
    virtualbox: 0     0    0     0    0     0      0      0 --:--:-- --:--:-- -- 100   409  100   409    0     0    607      0 --:--:-- --:--:-- --:--:--   791
==> virtualbox: Halting the virtual machine...
==> virtualbox: Preparing to export machine...
    virtualbox: Deleting forwarded port mapping for SSH (host port 3213)
==> virtualbox: Exporting virtual machine...
==> virtualbox: Unregistering and deleting virtual machine...
==> virtualbox: Running post-processor: vagrant
==> virtualbox (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox (vagrant): Copying: output-virtualbox\packer-disk1.vmdk
    virtualbox (vagrant): Copying: output-virtualbox\packer.ovf
    virtualbox (vagrant): Renaming the OVF to box.ovf...
    virtualbox (vagrant): Compressing box...
Build 'virtualbox' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox: 'virtualbox' provider box: packer_virtualbox_virtualbox.box

$ vagrant box add base packer_virtualbox_virtualbox.box
Downloading or copying the box...
Extracting box...
Successfully added box 'base' with provider 'virtualbox'!

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'base'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Fixed port collision for 22 => 2222. Now on port 2200.
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2200 (adapter 1)
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] Failed to connect to VM!
Failed to connect to VM via SSH. Please verify the VM successfully booted
by looking at the VirtualBox GUI.

$ ssh vagrant@localhost -p 2200
The authenticity of host '[localhost]:2200 ([127.0.0.1]:2200)' can't be established.
RSA key fingerprint is 07:c3:14:6c:e2:e9:84:0b:5e:69:57:a5:e0:0b:6f:9b.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:2200' (RSA) to the list of known hosts.
vagrant@localhost's password:
Linux packer-virtualbox 3.2.0-4-amd64 #1 SMP Debian 3.2.46-1 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
vagrant@packer-virtualbox:~$ ls -ltra
total 20
-rw-r--r-- 1 vagrant vagrant  675 Aug 19 02:49 .profile
-rw-r--r-- 1 vagrant vagrant  220 Aug 19 02:49 .bash_logout
drwxr-xr-x 3 root    root    4096 Aug 19 02:49 ..
drwxr-xr-x 2 vagrant vagrant 4096 Aug 19 02:49 .
-rw-r--r-- 1 vagrant vagrant 3392 Aug 19 02:49 .bashrc

わかるだろうか. provisioning 中に curl でダウンロードしたはずの .ssh/authorized_keys が, 改めて仮想環境を立ち上げてみると存在していない.

ここは本当に原因を掴むのに苦労した.皆にはこんな思いをして欲しくないので大きく 3 回書いておく.

provisioning終了時のshutdownをgracefulに行わないと仮想環境にデータが残らない

provisioning終了時のshutdownをgracefulに行わないと仮想環境にデータが残らない

provisioning終了時のshutdownをgracefulに行わないと仮想環境にデータが残らない

普段皆さんが PC を使い終って電源を切る時にどうするだろうか? 各 OS に用意されているシャットダウンボタンをクリックしたり,コマンドラインから shutdown を実行するだろう. そうすると,OS は電源を切る準備をして,変更途中のものはディスクに書き込んで永続化した後,自ら電源を切る.

Packer も provisioning 終了時に一度仮想環境の電源を切る.その時にどうやって電源を切るか. Packer は何も設定されていないと「マシンの電源を切る」相当のことを仮想環境に対して行う. つまり,OS は変更途中のものをディスクに書き込んで永続化する時間がない.

そのため,再度仮想環境を立ち上げたときに,変更が保存されていないのだ.

詳しくは VirtualBox Builder の shutdown_command を参照してもらいたい.

By default this is an empty string, which tells Packer to just forcefully shut down the machine.

「デフォルトでは空文字になっている,つまり Packer は単にむりやりマシンを切る」と書いてある.

そうしないためには,その前の文に従えばいい.

The command to use to gracefully shut down the machine once all the provisioning is done.

「全てのプロビジョニングが終わったときにちゃんと (graceful に ) シャットダウンするコマンド」を設定すると書いてある.

つまり,ここにシャットダウンコマンドを書けば,無理矢理 ” ではない ” 方法でシャットダウンしてくれる.

そこで wheezy64.json へ

"shutdown_command": "sudo shutdown -h now"

というコマンドを追加する.

18 sudo コマンド実行時に password の入力を求められる画面で止まる

再度 packer build を実行してみる

$ packer build --force wheezy64.json
virtualbox output will be in this color.

==> virtualbox: Downloading VirtualBox guest additions. Progress will be shown periodically.
==> virtualbox: Copying or downloading ISO. Progress will be reported periodically.
==> virtualbox: Starting HTTP server on port 8081
==> virtualbox: Creating virtual machine...
==> virtualbox: Creating hard drive...
==> virtualbox: Creating forwarded port mapping for SSH (host port 3213)
==> virtualbox: Starting the virtual machine...
==> virtualbox: Waiting 10s for boot...
==> virtualbox: Typing the boot command...
==> virtualbox: Waiting for SSH to become available...
==> virtualbox: Connected to SSH!
==> virtualbox: Uploading VirtualBox version info (4.2.16)
==> virtualbox: Uploading VirtualBox guest additions ISO...
==> virtualbox: Provisioning with shell script: vagrant.sh
    virtualbox: % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
    virtualbox: Dload  Upload   Total   Spent    Left  Speed
100   409  100   409    0     0    651      0 --:--:-- --:--:-- --:--:--   853--:--:--     0
==> virtualbox: Gracefully halting virtual machine...
    virtualbox:
    virtualbox: We trust you have received the usual lecture from the local System
    virtualbox: Administrator. It usually boils down to these three things:
    virtualbox:
    virtualbox: #1) Respect the privacy of others.
    virtualbox: #2) Think before you type.
    virtualbox: #3) With great power comes great responsibility.
    virtualbox:

すると,メッセージが出てきて止まってしまう.

これは sudo コマンドを実行するときに出てくるメッセージで, ここでログインしているユーザー,今回だと vagrant のパスワードを入力すると,sudo の後につなげたコマンドが管理者権限で実行される.

管理者権限とは何か. そもそも linux は複数の異なるユーザーが同時にログインして作業することを前提にしている. その前提に立ってみると,電源を切ったり,再起動したり,システムの構成を変更することは,他のユーザーへの影響がとても大きい. ( 自分が作業中なのに他のユーザーに電源を切られたら……おそろしい >< )

そこで,そういったことをする場合には通常のユーザーではなく,管理者権限を持つユーザーという立場で作業することが求められる. sudo は「sudo 以降のコマンドを管理者権限として実行したいですよー」という宣言になる. 今回は「すぐに電源を切る shutdown -h now 」ということを,管理者権限で行ないたいと宣言している.

今回だと

sudo shutdown -h now

の実行時に出ているので,vagrant のパスワードを入力できれば,管理者権限で

shutdown -h now

が実行され,仮想環境の電源が切られる.

自動実行中にパスワード入力待ちで止まってしまうのは困るので,止まらないようにするには 2 つの方法がある.

  1. echo 'password' | sudo -S shutdown -h now のように sudo 実行時に vagrant のパスワードを与えるようにする. see pass password to su/sudo/ssh
  2. そもそも vagrant の sudo 実行時にパスワードを確認しない

今回は vagrant ユーザーの操作を無条件で信じる,つまり vagrant ユーザーが sudo したものは全て許可するので 2 ですすめる.

Debian の場合 sudo の設定は /etc/sudoers に書くか, /etc/sudoers.d/ 以下にファイルを置くとよい. 今回は sudores.d 以下に vagrant というファイルを用意して,その中に vagrant の sudo 条件を記述するようにした.

sudores.d 以下に有効な設定ファイルを置く場合は

  1. 権限が root であること
  2. root 権限の人のみが読み込めること

という制約があるので,注意する.それに対応したのが以下のようなスクリプトになる.

# Set up sudo
echo 'vagrant ALL=NOPASSWD:ALL' > vagrant
echo 'vagrant' | sudo -S chmod 440 vagrant
echo 'vagrant' | sudo -S chown root:root vagrant
echo 'vagrant' | sudo -S mv vagrant /etc/sudoers.d/

これを base.sh として保存する.

また,そのスクリプトを実行するように wheezy64.json へ追記する.

"provisioners": [{
  "type": "shell",
  "scripts": [
    "base.sh",
    "vagrant.sh"
  ]
}],

ここまでやると

packer build --force wheezy64.json

というコマンドで,仮想環境構築ができ,その仮想環境に vagrant が SSH で接続できるようになる.

つまり「15. vagrant で起動した仮想環境に SSH で接続できなくて失敗する」からの課題は一通り解決し, 「Packer で仮想環境を構築する」という部分は達成できたことになる.

ウォオォォォォ!

(各自ひととおり自分なりに喜びを表わしてもらいたい)

……ふぅ.

さて,次に「packer で環境構築した仮想環境で chef-solo を実行する」というステップに入ろう. vagrant から chef-solo を実行するには Vagrantfile に Chef Solo Provisioner を記述すればよい. そこで Vagrantfile に以下のコードを追記する.

config.vm.provision :chef_solo do |chef|
end

19. chef がないというエラーが出て止まる

前回までで packer の処理はうまくいったようなので,次に vagrant を実行してみる.

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] The cookbook path 'c:/Users/niku/my-packer-and-vagrant-example                                                                                                                                         /cookbooks' doesn't exist. Ignoring...
[default] Fixed port collision for 22 => 2222. Now on port 2200.
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2200 (adapter 1)
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] The guest additions on this VM do not match the installed version of
VirtualBox! In most cases this is fine, but in rare cases it can
cause things such as shared folders to not work properly. If you see
shared folder errors, please update the guest additions within the
virtual machine and reload your VM.

Guest Additions Version: 4.1.18
VirtualBox Version: 4.2
[default] Mounting shared folders...
[default] -- /vagrant
[default] Running provisioner: chef_solo...
The chef binary (either `chef-solo` or `chef-client`) was not found on
the VM and is required for chef provisioning. Please verify that chef
is installed and that the binary is available on the PATH.

chef_solo を実行しようとしたのだが,chef binary が見つからないと言われている.

あっ,はい.まだインストールしていませんでした……

もう一度 packer の処理に戻って,chef のインストールを仮想環境構築に組込む.

chef のインストール方法は公式サイトの Install Chef をみるとよい.

今回は

した.

そうすると画面右側に Quick Install Instructions というのが表れて, 簡単にインストールするためのコマンド

curl -L https://www.opscode.com/chef/install.sh | sudo bash

が書いてあるので,このコマンドを利用する.

chef.sh という名前で以下のコードを保存して

# install Omnibus Chef Client
curl -L https://www.opscode.com/chef/install.sh | sudo bash

packer 実行時に仮想環境内から起動するよう wheezy64.json に追記する

"type": "shell",
"scripts": [
  "base.sh",
  "chef.sh",
  "vagrant.sh"
]

20. cookbook がないというエラーが出て止まる

まず前回修正した内容を有効化するために packer を実行する.

packer build --force wheezy64.json

packer は正常終了するので,次に vagrant を起動する.

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'base'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] The cookbook path 'c:/Users/niku/my-packer-and-vagrant-example/cookbooks' doesn't exist. Ignoring...
[default] Fixed port collision for 22 => 2222. Now on port 2200.
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2200 (adapter 1)
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] The guest additions on this VM do not match the installed version of
VirtualBox! In most cases this is fine, but in rare cases it can
cause things such as shared folders to not work properly. If you see
shared folder errors, please update the guest additions within the
virtual machine and reload your VM.

Guest Additions Version: 4.1.18
VirtualBox Version: 4.2
[default] Mounting shared folders...
[default] -- /vagrant
[default] Running provisioner: chef_solo...
Generating chef JSON and uploading...
[default] Warning: Chef solo run list is empty. This may not be what you want.
Running chef-solo...
stdin: is not a tty
[2013-08-19T22:03:26-04:00] INFO: Forking chef instance to converge...
[2013-08-19T22:03:26-04:00] INFO: *** Chef 11.6.0 ***
[2013-08-19T22:03:27-04:00] INFO: Run List is []
[2013-08-19T22:03:27-04:00] INFO: Run List expands to []
[2013-08-19T22:03:27-04:00] INFO: Starting Chef Run for packer-virtualbox.vagrantup.com
[2013-08-19T22:03:27-04:00] INFO: Running start handlers
[2013-08-19T22:03:27-04:00] INFO: Start handlers complete.
[2013-08-19T22:03:27-04:00] FATAL: No cookbook found in ["/tmp/vagrant-chef-1/cookbooks/cookbooks"], make sure cookbook_path is set correctly.
[2013-08-19T22:03:27-04:00] ERROR: Running exception handlers
[2013-08-19T22:03:27-04:00] ERROR: Exception handlers complete
[2013-08-19T22:03:27-04:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
[2013-08-19T22:03:27-04:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.

ご覧の通り,うまくいっていない.

No cookbook found in ["/tmp/vagrant-chef-1/cookbooks/cookbooks"], make sure cookbook_path is set correctly.

クックブックが見当らないと言われている.

確かに,まだクックブックを登録していない.そこで登録する.

クックブックは何でもよい.ここでは例として,今後もよく使うであろう build-essential を入れてみる.

まず,クックブックを置くパスがないので用意する.これは 1 度だけ行えばよい.

mkdir -p chef-recipes/cookbooks

次にそのパスへ移動する.

cd chef-recipes/cookbooks

そしてクックブックを用意する.

git clone https://github.com/opscode-cookbooks/build-essential.git

上の3つを行ない,再度 vagrant up すると,無事に vagrant が起動する. すなわち「packer から vagrant の chef-solo provision 対応の仮想環境を,コマンド一つで作成でき」たことになる.

ふー.この記事はこれでおしまい. 1 ヶ月半くらいかけてチマチマ書いて,ちょうど 1000 行にわたる記事になった.読んでくれた人もおつかれさまでした.