サーバー上のシステムをチームで開発する場合、開発環境の構築をどう共有するかは課題になると思いますが、いくつかの既存のやり方には問題があります
今までの開発環境構築のやり方
README.md などに手順を書く方法
これには次の問題が発生することがあります
- 本番環境(ex. Ubuntu)と開発環境(ex. Mac)で土台となる OS が違う
- 手順書の更新漏れがある
- 手順書の再現性の検証がおろそかになる
- 手順書の更新に対して、既存の開発環境をアップデートしないメンバーが出てくる
Vagrant を使う方法
これは上記のいくつかを解消しますが、まだ問題が残ります
- 引き続き残る問題
- 手順書の更新に対して、既存の開発環境をアップデートしないメンバーが出てくる
- 新たに発生した問題
-
vagrant up
に時間がかかる(ex. 30 分以上とか)
vagrant up
によって時間がかかる問題は、apt-get
や gem install
に失敗してやり直したりするリスク(時にはライブラリのインストールサーバーがしばらく不通になることもあると思います)も含めて、それなりにチーム全体でみると無駄な時間を作ってしまいます
開発環境構築済みの Vagrant Box
を S3
にホスティングするやり方
開発環境構築済みの Vagrant Box
を作る
これによって、vagrant up
に時間がかかる問題は解消します
- ライブラリのインストールに時間がかかったり、ライブラリをホストするサーバー依存で完了しないといったことがなくなります
-
Vagrant Box
のダウンロードがほとんどの時間を占めるので、ホスティング先や回線速度にもよりますが、5 ~ 10 分程度で終わるのではないでしょうか - ファイルサイズは、自分の感覚ではだいたい 300 MB ~ 1.2 GB 程度に収まることが多いのではと思います
Vagrant Box
を manifest.json
と一緒に S3
にホスティングする
これによって、既存の開発環境をアップデートしないメンバーが出てくるという問題は解消します
- 新しいバージョンの
Vagrant Box
をアップロードすると、vagrant up 時にバージョンアップのチェックが自動実行されるので、古い場合には警告が表示されたり、バージョンアップするにはコマンド 1 発で出来るようになります -
Vagrantfile
にて、バージョン制約もつけることができます
Vagrant Box
の生成と S3
へのアップロードを行うために、Packer
を使う
Packer
による Vagrant Box
の生成
Packer
を使うと、テンプレートファイルを用意することで AMI
や Vagrant Box
をある程度透過的に、作ることができます
- 例えば、
AWS
のAuto Scaling
機能を使う際に、インスタンスのセットアップをすぐに行うとか外部依存しないで終わらせるために、AMI
を使うことは多いと思います - その
AMI
構築をスクリプトで管理・実施することになるので、いわゆる、Infrastructure as a Code を実現することができます
すでに AMI
構築に Packer
を使っている場合には、その構築手順を流用して Vagrant Box
を作ることができます。これには、次のようなメリットがあります
- 本番環境の更新箇所と開発環境の更新箇所(= 要するにプロビジョニング)を共通化できる
- ソースコード上の位置的にも近接しているので、本番環境の構築スクリプト更新時に、開発環境の構築スクリプトの更新漏れが発見しやすい
まだ Packer
を使っていない場合でも、後述するように、テンプレートファイルによる記述と実行の簡単さやプラグインによる拡張性の高さを理由に、Packer
を使う方法をおすすめします
Packer
による Vagrant Box
の S3
へのアップロード
Packer
はサードパーティ製のプラグインを使って、機能を拡張することができます
-
packer-post-processor-vagrant-s3 もそのひとつで、
Vagrant Box
のS3
へのアップロードとそのバージョン管理をするためのmanifest ファイル
の管理を自動化してくれます -
post-processor
というのは、Packer
が参照するテンプレートファイル内のブロックの名前のひとつです - ここでは、
Vagrant Box
を生成したメイン処理の後に、実施されるプラグインであるという意味になります
具体例をもとに、手順をまとめる
Vagrant Box
それ自体を作成する作業と、そのプロビジョニング(= 必要なライブラリをインストールするなど)をする作業の 2 つに大別されます
前提
本番環境は Ubuntu trusty
の AMI で動かしているとして、それに合わせる前提で考えます
また、Packer
と Vagrant
はすでにインストール済みで操作はわかっている前提で書きます
- これらの導入をまとめたチュートリアルはすでに多くあると思いますのでそれらを参照ください
ubuntu-trusty-server-cloudimg-amd64 の OVA を作成する
Vagrant Box
(≒ VirtualBox Virtual Machines
)は、ISO から作るか、OVF/OVA から作るかという 2 つの方法があります
- Qiita などには
ISO
から作るチュートリアルが多くあるのですが、スクラッチから作りたいわけではないならば、OVF/OVA
から作るのがおすすめです - 本番環境と同じような環境を作りたいだけなので、公式に提供されているものから作った方が安全だと思うためです
- 公式ドキュメントやチュートリアルサイトにも同様の趣旨の記載があります
- また、テンプレートファイル内に読んで理解しづらい(人によるでしょうが)ISO 生成の設定を書くのは避けたいと個人的には思っています
ubuntu 上で実行するなら次の手順で作ることができます
$ BOX_URL=https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
$ curl $BOX_URL | tar --delete Vagrantfile > ubuntu.ova
mac 上では tar のオプションが違うので愚直な次の方法で作ることができました
$ BOX_URL=https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
$ curl $BOX_URL > ubuntu.box
$ tar xvf ubuntu.box
$ rm Vagrantfile
$ tar cvf ubuntu_trusty.ova box.ovf box-disk1.vmdk
Packer
のテンプレートファイルを作成する
これ以後、秘匿情報は ***
で隠してます
それぞれのブロックの意味
-
variables
では、ファイル中で変数として参照するための定義を書いてます -
builders
では、VirtualBox VM
を生成するための手順を書いています -
provisioners
では、ライブラリのインストールなど、そのプロジェクト固有の設定をするための手順を書いています -
post-processors
では、生成したVagrant Box
をS3
にアップロードするための手順を書いています
アップロード先について
-
Vagrant Box
とmanifest.json
は S3 の***-vagrant-box/vagrant
ディレクトリに配置されます
注意する点について
-
builders
のvboxmanage
のmemory
を2048
にしています - この値はプロジェクトによって必要最低限のメモリ使用量が良く、多ければマシンが重くなり、少なければプロジェクトが動きません
- 例えば、
1024
の場合、自分のプロジェクトではOOM Kill
されました
{
"variables": {
"version": "0.0.2",
"aws_access_key": "***",
"aws_secret_key": "***"
},
"builders": [
{
"type": "virtualbox-ovf",
"guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
"headless": "true",
"shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now",
"source_path": "ubuntu_trusty.ova",
"ssh_password": "vagrant",
"ssh_username": "vagrant",
"ssh_wait_timeout": "20m",
"vboxmanage": [
["modifyvm", "{{ .Name }}", "--memory", "2048"]
],
"virtualbox_version_file": ".vbox_version",
"vm_name": "mtburn-base"
}
],
"provisioners": [
{
"source": "files",
"destination": "/tmp/files",
"type": "file"
},
{
"scripts": [
"scripts/ubuntu-trusty/basic.sh",
"scripts/ubuntu-trusty/packages.sh",
"scripts/ubuntu-trusty/user.sh",
"scripts/ubuntu-trusty/perl.sh",
"scripts/ubuntu-trusty/td-agent.sh",
"scripts/ubuntu-trusty/mysql-server.sh",
"scripts/ubuntu-trusty/redis-server.sh"
],
"environment_vars": "MTBURN_ROLE=local",
"type": "shell"
}
],
"post-processors": [
[
{
"type": "vagrant",
"output": "/tmp/ubuntu-trusty-server-cloudimg-amd64.box"
},
{
"type": "vagrant-s3",
"region": "ap-northeast-1",
"bucket": "***-vagrant-box",
"manifest": "vagrant/manifest.json",
"box_name": "mtburn-ubuntu-trusty-server-cloudimg-amd64",
"box_dir": "vagrant/boxes",
"version": "{{ user `version` }}",
"access_key": "{{ user `aws_access_key` }}",
"secret_key": "{{ user `aws_secret_key` }}"
}
]
]
}
packer build
します
- S3 のアップロードは回線状況などによって失敗する場合があります
- そのため、足元にも生成した box を置くようにしてます
- その場合には手動アップロードすることになりますが、手間がかかるので手順を後述します
$ packer build local.json
...
==> virtualbox-ovf: Running post-processor: vagrant
==> virtualbox-ovf (vagrant): Creating Vagrant box for 'virtualbox' provider
virtualbox-ovf (vagrant): Copying from artifact: output-virtualbox-ovf/mtburn-base-disk1.vmdk
virtualbox-ovf (vagrant): Copying from artifact: output-virtualbox-ovf/mtburn-base.ovf
virtualbox-ovf (vagrant): Renaming the OVF to box.ovf...
virtualbox-ovf (vagrant): Compressing: Vagrantfile
virtualbox-ovf (vagrant): Compressing: box.ovf
virtualbox-ovf (vagrant): Compressing: metadata.json
virtualbox-ovf (vagrant): Compressing: mtburn-base-disk1.vmdk
==> virtualbox-ovf: Running post-processor: vagrant-s3
==> virtualbox-ovf (vagrant-s3): Preparing to upload box for 'virtualbox' provider to S3 bucket '***-vagrant-box'
virtualbox-ovf (vagrant-s3): Box to upload: /tmp/ubuntu-trusty-server-cloudimg-amd64.box (1105662889 bytes)
virtualbox-ovf (vagrant-s3): Fetching latest manifest
virtualbox-ovf (vagrant-s3): Generating checksum
virtualbox-ovf (vagrant-s3): Checksum is 4f44e9727719bb2b4780da33d40c27095eeffefcb4ad2ab68917bedecde87531
virtualbox-ovf (vagrant-s3): Adding virtualbox 0.0.2 box to manifest
virtualbox-ovf (vagrant-s3): Uploading box to S3: vagrant/boxes/0.0.2/ubuntu-trusty-server-cloudimg-amd64.box
virtualbox-ovf (vagrant-s3): File size > 100MB. Initiating multipart upload
virtualbox-ovf (vagrant-s3): Uploading the manifest: vagrant/manifest.json
Build 'virtualbox-ovf' finished.
上記の手順によって生成された成果物を確認する
Vagrant Box
と manifest.json
が S3
上の所定の位置にアップロードされているはずです
- 次の
manifest.json
は、理解のために、すでに0.0.1
というバージョンをアップロードしたあとに、0.0.2
という新しいバージョンをアップロードした場合のものです - これによって、これ以後新規に
vagrant up
すると、0.0.2
のVagrant Box
をダウンロードして構築するようになります - また、すでに
0.0.1
を構築済みの環境でvagrant up
した場合も、vagrant box update
と打てば、0.0.2
のVagrant Box
をダウンロードしてアップデートしてくれます
{
"name": "mtburn-ubuntu-trusty-server-cloudimg-amd64",
"versions": [
{
"version": "0.0.1",
"providers": [
{
"name": "virtualbox",
"url": "https://s3-ap-northeast-1.amazonaws.com/***-vagrant-box/vagrant/boxes/0.0.1/ubuntu-trusty-server-cloudimg-amd64.box",
"checksum_type": "sha256",
"checksum": "d4cb23e3502c4717dff626ede4838e70ec26f92a495679ab57137f96fb7caade"
}
]
},
{
"version": "0.0.2",
"providers": [
{
"name": "virtualbox",
"url": "https://s3-ap-northeast-1.amazonaws.com/***-vagrant-box/vagrant/boxes/0.0.2/ubuntu-trusty-server-cloudimg-amd64.box",
"checksum_type": "sha256",
"checksum": "4f44e9727719bb2b4780da33d40c27095eeffefcb4ad2ab68917bedecde87531"
}
]
}
]
}
上記の手順によって生成された成果物を利用する
Vagrantfile
を用意する
local_initfile
では supervisord
などプロセス管理デーモンの起動など最低限の処理や、他の開発メンバーの任意に任される(= 不要なら削除・編集してもらって構わない)ような /etc/hosts
の編集などを記述しています
-
Vagrantfile
とlocal_initfile
は、プロジェクトのレポジトリに含めて管理・共有されます
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "mtburn-ubuntu-trusty-server-cloudimg-amd64"
config.vm.box_url = "https://s3-ap-northeast-1.amazonaws.com/***-vagrant-box/vagrant/manifest.json"
config.vm.provision "shell", path: "Packer/scripts/ubuntu-trusty/local_initfile"
config.vm.network "private_network", ip: "172.20.20.10"
end
Vagrant Box
のバージョン管理
古いバージョンを使っている場合(すでに S3
に 0.0.2
がありながら、手元はまだ 0.0.1
など)に、vagrant up
すると次のような警告が出るようになります
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Checking if box 'mtburn-ubuntu-trusty-server-cloudimg-amd64' is up to date...
==> default: A newer version of the box 'mtburn-ubuntu-trusty-server-cloudimg-amd64' is available! You currently
==> default: have version '0.0.1'. The latest is version '0.0.2'. Run
==> default: `vagrant box update` to update.
==> default: VirtualBox VM is already running.
警告にある通り、vagrant box update
を実行すれば最新に更新されます
$ vagrant box update
==> default: Checking for updates to 'mtburn-ubuntu-trusty-server-cloudimg-amd64'
default: Latest installed version: 0.0.1
default: Version constraints:
default: Provider: virtualbox
==> default: Updating 'mtburn-ubuntu-trusty-server-cloudimg-amd64' with provider 'virtualbox' from version
==> default: '0.0.1' to '0.0.2'...
==> default: Loading metadata for box 'https://s3-ap-northeast-1.amazonaws.com/***-vagrant-box/vagrant/manifest.json'
==> default: Adding box 'mtburn-ubuntu-trusty-server-cloudimg-amd64' (v0.0.2) for provider: virtualbox
...
補足
Vagrant Box
の手動アップロード
生成した Vagrant Box
を S3
にアップロードする post-processors
処理は回線状況によっては失敗する場合があります
- その場合、手動で
***-vagrant-packer/vagrant
以下にmanifest.json
とboxes/[version]/ubuntu-trusty-server-cloudimg-amd64.box
をアップロードしたい場合もあると思います
box はそのままアップロードすれば良いですが、manifest.json
はすでに S3
でホスティングされている内容に append する形式で新しいバージョンの情報を付け加える作業が必要になります
- この際の
sha256
のchecksum
はopenssl dgst -sha256 ubuntu-trusty-server-cloudimg-amd64.box
というコマンドで算出できます
また、metadata
の Content-type
が application/json
になっていないといけない点に注意してください
AMI
を生成するテンプレートファイルの post-processors
処理内で Vagrant Box
を生成する
例えば、すでに AMI
を生成するために Packer
を使っている場合、そのテンプレートファイルは次のような感じかと思います
{
"variables": {
"aws_access_key": "***",
"aws_secret_key": "***"
},
"builders": [{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"region": "ap-northeast-1",
"source_ami": "ami-e74b60e6",
"instance_type": "t2.micro",
"ssh_username": "ubuntu",
"ami_name": "app {{timestamp}}"
}],
}
https://www.packer.io/intro/getting-started/vagrant.html を読むと、次のように post-processors
で type: "vagrant"
を指定すれば、Vagrant Box
が生成されると書いてあります
{
"variables": {
"aws_access_key": "***",
"aws_secret_key": "***"
},
"builders": [{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"region": "ap-northeast-1",
"source_ami": "ami-e74b60e6",
"instance_type": "t2.micro",
"ssh_username": "ubuntu",
"ami_name": "app {{timestamp}}"
}],
"post-processors": [{
"type": "vagrant",
"output": "./Packer/box/ubuntu-trusty-14.04-amd64-server-for-local.box",
"keep_input_artifact": true
}]
}
これをビルドしてみると、確かに box ができます
- ただし、
Vagrant Box
のprovider
がvirtualbox
ではなくaws
になっています - そのため、
.vmdk
が含まれず、vagrant-aws plugin を使う前提の box が生成されるだけです -
AMI
から生成されるので当たり前の結果といえばそうです
$ packer build Packer/app-and-local.json
amazon-ebs output will be in this color.
==> amazon-ebs: Inspecting the source AMI...
==> amazon-ebs: Creating temporary keypair: packer 54cdf714-446a-eef6-6415-8f7b2913bcf5
==> amazon-ebs: Creating temporary security group for this instance...
==> amazon-ebs: Authorizing SSH access on the temporary security group...
==> amazon-ebs: Launching a source AWS instance...
amazon-ebs: Instance ID: i-06b6ab1f
==> amazon-ebs: Waiting for instance (i-06b6ab1f) to become ready...
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Stopping the source instance...
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating the AMI: app 1422784276
amazon-ebs: AMI: ami-36e7ff37
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Deleting temporary security group...
==> amazon-ebs: Deleting temporary keypair...
==> amazon-ebs: Running post-processor: vagrant
==> amazon-ebs (vagrant): Creating Vagrant box for 'aws' provider
amazon-ebs (vagrant): Compressing: Vagrantfile
amazon-ebs (vagrant): Compressing: metadata.json
Build 'amazon-ebs' finished.
==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-36e7ff37
--> amazon-ebs: 'aws' provider box: ./Packer/box/ubuntu-trusty-14.04-amd64-server-for-local.box
$ ls -la Packer/box/
total 8
drwxr-xr-x 3 *** staff 102 2 1 18:53 .
drwxr-xr-x 4 *** staff 136 2 1 18:51 ..
-rw-r--r-- 1 *** staff 355 2 1 18:53 ubuntu-trusty-14.04-amd64-server-for-local.box
$ 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 'local-mtburn-20150201' could not be found.
http://stackoverflow.com/questions/27953910/cannot-use-the-box-created-with-packer-post-processor-vagrant に同じ指摘が書いてあります
- http://smashingboxes.com/ideas/how-to-convert-ec2-ami-to-vmdk-for-vagrant に記載されているような変換処理を内部でしてくれるわけではありませんでした
- この結果を受けて、
ova
から作る最初からの手順によって構築することに決めました