packerを使用して、VirtualMachineで動くVagrant用のBoxファイルを作成した

昨日(日付の上では、一昨日)にあったGo Conference 2013 autumnに行ってきました。

野良Hackathonがあったので、久々のGo慣らしも兼ねてpackerで1つ環境用意してやるかといった感じでやってました。
勿論、イメージ作ってる待ち時間の間にpackerのソースも読んでました
正直、ドキュメント読んで(?)ってなったところもあったので読んだとかそんな感じです。

Google I/O以来に会った@ymotongpooさんにLTやらね?って誘われた。
然しながら、Goのネタが今手元に無かったので、LT時刻までにpackerで環境用意できたらやるって言ったけど、出来なかったのでLT諦めた。
後で、すごい誤った。(完成したのは、LT終了して暫くしてからだった)
@ymotongpooさん、もし見てたら次回のGoConではLTやれるように準備しておきます。

という訳で、代わり(?)に表題の内容をやります。

最終ゴール


以下のコマンドでVirtualBox上で動作するCentOS6.4(64bit)が起動してログインできるようにする

1
$vagrant up

但し、Vagrant側では、特にアプリケーションのインストールを実施せず、packer側で以下の2点を満たすこと

  • 最小インストールしたパッケージを全て最新化すること
  • Gitをインストールすること

前提


前提がないとお話にならないので、以下が入ってることにします。

  • Vagrant 1.3.4
  • VirtualBox 4.2.16
  • Go 1.1.2

こいつらが入ってない場合は、入れて下さい。

あと、実行環境はMacでやってますが、Linuxも似たような感じでできると思います。
Windowsは知らないですが多分できるんじゃないかなぁと思います。

packerのインストール


packerをインストールします。
公式の手順に従えば1発でインストールできます。

1
2
$brew tap homebrew/binary
$brew install packer

OSインストール用のkickstartファイルを用意する


kickstartは、RHEL系(CentOSなど)OS のインストール&セットアップが自動化できることを指します。
で、一応OSインストールした時の構成がOS側で記録されていまして、「/root/anaconda-ks.cfg」がkickstartファイルになります。
他にも、system-config-kickstart.noarchをインストールすることでGUIで設定ファイルを用意できるようになっています。

以下のような感じで最小構成インストールファイルを用意します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
install
cdrom
lang en_US.UTF-8
keyboard us
network --bootproto=dhcp
rootpw --iscrypted $1$FB.fOroc$b2.YcN30BjrYxlUbECUxO1
firewall --enabled --service=ssh
authconfig --enableshadow --passalgo=sha512
selinux --disabled
timezone UTC
bootloader --location=mbr

text
skipx
zerombr

clearpart --all --initlabel
autopart

auth  --useshadow  --enablemd5
firstboot --disabled
reboot

%packages --nobase
@core
%end

%post
/usr/bin/yum -y install sudo
/usr/sbin/groupadd moonstruckdrops
/usr/sbin/useradd moonstruckdrops -g moonstruckdrops -G wheel
echo "moonstruckdrops"|passwd --stdin moonstruckdrops
echo "moonstruckdrops        ALL=(ALL)       NOPASSWD: ALL" >> /etc/sudoers.d/moonstruckdrops
chmod 0440 /etc/sudoers.d/moonstruckdrops
%end

最小インストールを実施した後にsudoをインストールし、ログインユーザーを新規に追加しています。

因みに、rootパスワードは以下のコマンドで生成します。
生成された文字列をrootpwに記述します。

1
$openssl passwd -1

jsonファイルを作成する


細かい解説は抜きにして、以下のような感じで作成します。
大体、ドキュメントに書いてあるので直感的に分かるかと思います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
    "builders": [
        {
            "type": "virtualbox",
            "vm_name": "centos_box",
            "guest_os_type": "RedHat_64",
            "disk_size": "20000",
            "format": "ovf",
            "hard_drive_interface":"sata",
            "boot_wait": "5s",
            "iso_url": "http://ftp.iij.ad.jp/pub/linux/centos/6.4/isos/x86_64/CentOS-6.4-x86_64-minimal.iso",
            "iso_checksum_type": "md5",
            "iso_checksum": "4a5fa01c81cc300f4729136e28ebe600",
            "ssh_username":"root",
            "ssh_password":"vagrant",
            "ssh_port": 22,
            "shutdown_command": "shutdown -h now",
            "guest_additions_path": "VBoxGuestAdditions_.iso",
            "virtualbox_version_file": ".vbox_version",
            "vboxmanage":[
                ["modifyvm", "", "--memory", "1024"],
                ["modifyvm", "", "--cpus", "2"]
            ],
            "http_directory": "./builders/",
            "boot_command": [
                "<tab> text ks=http://:/ks.cfg<enter><wait>"
            ]
        }
    ],
    "provisioners": [{
        "type": "shell",
        "inline": [
            "sleep 30",
            "sudo yum -y update",
            "sudo yum -y install git"
            ]
    }],
    "post-processors": [{
        "type": "vagrant",
        "output": "centos.box"
    }]
}

ビルドしてイメージを作成する


作成したjsonに誤りがないことを確認する

以下のコマンドを使用して確認する

1
$packer validate

以下の実行結果になれば、エラーなく記述できている

1
Template validated successfully.

エラーだと、こんな感じでどこが誤っているか教えてくれる

1
Failed to parse template: Error in line 14, char 8: invalid character '"' after object key:value pair
ビルドを行う

うまくいけば、期待した結果の仮想マシンができている

1
$packer build -only=virtualbox centos.json

vagrantを使ってインストールしたOSを起動する


やることは非常に簡単です。

まず、初期化します。

1
$vagrant init

vagrantの設定ファイルを編集します。

  • 「config.vm.box_url = “your_boxfile_path”」に生成したファイルのパスを記述
  • 「vb.gui = true」 を有効化(コメント削除)

最後に起動します。

1
$vagrant up

うまくいけば、最終ゴールの内容で起動します。

嵌ったビルドエラー


ビルドを行うと以下のようなビルドエラーに遭遇します。
エラーになると、以下のようなエラーになります。

1
2
3
4
5
==> virtualbox: Waiting for SSH to become available...
==> virtualbox: Timeout waiting for SSH.
==> virtualbox: Unregistering and deleting virtual machine...
==> virtualbox: Deleting output directory...
Build 'virtualbox' errored: Build was halted.

解決方法ですが、「”ssh_wait_timeout”」の値を長めに設定する、もしくはjsonに記述しない(デフォルトは20min設定のため)こと
少し考えれば理解できた話なんですが、そもそもこのsshはどこで使用するかというとOSインストール完了後、即ちprovisionersで利用します。
そのため、インストールしたOSに接続するためsshが必要になるということです。(ゴールの場合だと、updateとgitインストール)
こういうことから、OSインストールが終わるまでssh接続できないからsshがタイムアウトしていたということです。

そもそも、公式のドキュメントに「”ssh_wait_timeout”:”30s”」と記述してあり、sshがどこで使われるのか言及していないことも嵌る原因だと思います。

感想というか思うところ


ドキュメントもかなり充実してるんで、BOXファイルを作成するまでは割りとすんなりいけるかなぁと思います。
一部、嵌まりどころもありますが・・・。

一方で、仕組み上仕方ない部分もありますが、kickstartやその他OS自動インストールの知識がないと使いづらい印象を受けました。
尤も、こういうツールに手を出す人は、知識持ちの人かもしれませんが・・・

「packer + Vagrant + Chef」を組み合わせれば、かなりプラットフォーム及び開発環境系の自動化ができるなぁと思いました。

しかし、これがGoで作られているのがすごいよなぁ・・・。
Goマジイケてると思います。