I. Création de base boxes Vagrant▲
Dans la plupart des cas, Vagrant est utilisé à partir d'une base box telle que celles que l'on peut trouver sur Vagrant Box. Un des mécanismes de provisioning de Vagrant (Shell, Chef Solo, Puppet, etc.) est alors utilisé pour configurer la base box.
Il peut être intéressant de constituer sa propre base box, par exemple :
- si vous voulez maîtriser le contenu de votre VM (c'est parfois une question de sécurité…) ;
- s'il n'existe pas déjà de base box répondant à vos besoins ;
- si le provisioning de votre box est long (compilation de paquets, téléchargements volumineux…) ;
- si vous voulez freezer une version exacte de votre environnement (pour ne pas dépendre d'un apt-get update, par exemple).
Les opérations permettant la création d'une base box sont documentées : il faut suivre le guide de base et appliquer les règles spécifiques au provider (ici pour VirtualBox). La procédure n'est pas très complexe, mais elle est fastidieuse et surtout, une erreur est vite arrivée. C'est là qu'intervient Packer…
II. Création de base boxes Vagrant avec Packer▲
Packer permet d'automatiser entièrement la création de base box Vagrant. Mais l'outil n'est pas réservé à Vagrant : il peut servir à préparer des containers Docker, des AMI pour Amazon AWS, etc.
Notez que, pour Vagrant, les base boxes sont généralement créées avec Veewee (le précurseur de Packer) et il existe un large repository de définitions. Un outil de conversion des définitions Veewee vers des définitions Packer existe : Veewee-to-packer.
Une configuration Packer est définie avec un fichier JSON comportant des « builders » et des « provisioners » :
- les builders servent à piloter les machines virtuelles. Il en existe pour VirtualBox, VMware, Amazon AWS, Google Cloud Platform, etc. ;
- les provisioners servent à préparer la configuration logicielle à partir de scripts Shell ou avec Chef, Puppet, etc.
III. En pratique▲
Dans cet exemple, nous cherchons à préparer une même configuration (Ubuntu 13.04 avec Node.JS 10.x) pour deux environnements d'exécution : VirtualBox et AWS. Nous aurons donc deux boxes Vagrant.
Notons tout de suite que la méthode d'identification sera différente suivant l'environnement :
- en local (VirtualBox), nous utiliserons la méthode traditionnelle de Vagrant, à savoir un user Vagrant configuré avec une clé SSH non secure ;
- sur AWS, le mécanisme des « keypairs » sera utilisé, notre keypair étant déjà configurée dans l'interface de management AWS.
IV. Préparation de la box VirtualBox▲
La box VirtualBox sera constituée à partir d'une ISO Ubuntu que nous spécifierons par son URL sur le site de l'éditeur. Nous donnerons un fichier de « preseed » qui permettra de configurer l'installation (plus d'infos sur le preseeding : guide d'installation).
Le plus rapide est de partir d'une configuration Veewee existante. Téléchargeons un template Veewee pour Ubuntu. Ce template est constitué d'une définition (« definition.rb »), d'un fichier de preseed (« preseed.cfg ») et de scripts shell :
$
cd ubuntu-13
.04
-server-amd64
$
find .
.
./apt.sh
./build_time.sh
./chef.sh
./cleanup.sh
./definition.rb
./preseed.cfg
./puppet.sh
./ruby.sh
./sudo.sh
./vagrant.sh
./vbox.sh
Lançons la conversion Veewee vers Packer :
$
veewee-to-packer definition.rb
Success!
Your Veewee definition was converted to a Packer template!
The template can be found in
the `template.json`
file in
the output
directory: output
Please be sure to run `packer validate`
against the new template
to verify settings are correct.
Nous obtenons un fichier de définition Packer (“template.json”). Les autres fichiers sont conservés :
$
cd output
$
find .
.
./http
./http/preseed.cfg
./scripts
./scripts/apt.sh
./scripts/build_time.sh
./scripts/chef.sh
./scripts/cleanup.sh
./scripts/puppet.sh
./scripts/ruby.sh
./scripts/sudo.sh
./scripts/vagrant.sh
./scripts/vbox.sh
./template.json
La configuration doit être légèrement modifiée (bogues mineurs du convertisseur) : virtualbox doit être remplacé par virtualbox-iso, et la plupart des commandes <wait> doivent être supprimées. La configuration du builder obtenue est alors :
{
"type"
: "virtualbox-iso"
,
"boot_command"
: [
"<esc><esc><enter><wait>"
,
"/install/vmlinuz noapic preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg "
,
"debian-installer=en_US auto locale=en_US kbd-chooser/method=us "
,
"hostname={{ .Name }} "
,
"fb=false debconf/frontend=noninteractive "
,
"keyboard-configuration/modelcode=SKIP keyboard-configuration/layout=USA keyboard-configuration/variant=USA console-setup/ask_detect=false "
,
"initrd=/install/initrd.gz -- <enter>"
],
"boot_wait"
: "4s"
,
"disk_size"
: 65536
,
"guest_os_type"
: "Ubuntu_64"
,
"http_directory"
: "http"
,
"iso_checksum"
: "7d335ca541fc4945b674459cde7bffb9"
,
"iso_checksum_type"
: "md5"
,
"iso_url"
: "http://releases.ubuntu.com/13.04/ubuntu-13.04-server-amd64.iso"
,
"ssh_username"
: "vagrant"
,
"ssh_password"
: "vagrant"
,
"ssh_port"
: 22
,
"ssh_wait_timeout"
: "10000s"
,
"shutdown_command"
: "echo 'shutdown -P now' > shutdown.sh; echo 'vagrant'|sudo -S sh 'shutdown.sh'"
,
"guest_additions_path"
: "VBoxGuestAdditions_{{.Version}}.iso"
,
"virtualbox_version_file"
: ".vbox_version"
}
V. Préparation de la box Amazon AWS▲
Préparons maintenant le builder pour Amazon AWS. Dans ce cas, nous ne pourrons pas utiliser l'ISO d'installation. Nous partirons d'une AMI (Amazon Machine Image) préparée par l'éditeur et que nous sélectionnerons en suivant ce lien.
Notre builder sera configuré comme suit :
{
"type"
: "amazon-ebs"
,
"access_key"
: "{{user `aws_access_key`}}"
,
"secret_key"
: "{{user `aws_secret_key`}}"
,
"region"
: "eu-west-1"
,
"source_ami"
: "ami-dea653a9"
,
"instance_type"
: "t1.micro"
,
"ssh_username"
: "ubuntu"
,
"ami_name"
: "ubuntu-13.04__Node.JS"
}
VI. Fin de la préparation du template Packer▲
Il nous reste à modifier les provisioners :
- nous pouvons retirer l'installation de Ruby, Chef et Puppet qui ne nous intéressent pas ;
- les « Guest Additions » ne doivent être installés que pour VirtualBox ;
- le user Vagrant ne doit pas être créé pour AWS ;
- nous ajoutons un script pour installer Node.JS.
La configuration des provisioners devient :
"provisioners"
: [
{
"type"
: "shell"
,
"execute_command"
: "echo 'vagrant'|sudo -S sh '{{.Path}}'"
,
"scripts"
: [
"scripts/build_time.sh"
,
"scripts/apt.sh"
]
},
{
"type"
: "shell"
,
"only"
: ["virtualbox-iso"
],
"execute_command"
: "echo 'vagrant'|sudo -S sh '{{.Path}}'"
,
"scripts"
: [
"scripts/vbox.sh"
,
"scripts/vagrant.sh"
]
},
{
"type"
: "shell"
,
"execute_command"
: "echo 'vagrant'|sudo -S sh '{{.Path}}'"
,
"scripts"
: [
"scripts/sudo.sh"
,
"scripts/nodejs.sh"
,
"scripts/cleanup.sh"
]
}
]
Et notre script nodejs.sh est défini comme suit :
apt-get -y install software-properties-common
add-apt-repository -y ppa:chris-lea/node.js
apt-get -y update
apt-get -y install nodejs
Enfin, nous rajoutons un post-processeur qui créera les boxes Vagrant à partir de l'AMI AWS et de la VM VirtualBox :
"post-processors"
: [
{
"type"
: "vagrant"
,
"keep_input_artifact"
: true
}
]
Attention, sans le paramètre keep_input_artifact à true, le post-processeur supprimera l'AMI ce qui rendra la box inutilisable…
VII. Création de la box VirtualBox▲
Nous pouvons maintenant lancer la préparation des boxes. Packer lance par défaut la création de toutes les boxes en parallèle. Toutefois, pour commencer, nous allons uniquement « builder » l'image VirtualBox grâce au flag « -only=virtualbox-iso ».
Packer va télécharger l'ISO Ubuntu et les « Guest Additions ». Une VM sera créée dans VirtualBox. Celle-ci va « booter » sur l'ISO et, quand le SSH sera prêt, le provisioning sera effectué (par scripts shell, dans notre cas). Enfin, une box Vagrant sera créée.
$
packer build -only
=
virtualbox-iso template.json
virtualbox-iso output will be in
this color.
==>
virtualbox-iso: Downloading or copying Guest additions checksums
virtualbox-iso: Downloading or copying: http://download.virtualbox.org/virtualbox/4
.3
.6
/SHA256SUMS
==>
virtualbox-iso: Downloading or copying Guest additions
virtualbox-iso: Downloading or copying: http://download.virtualbox.org/virtualbox/4
.3
.6
/VBoxGuestAdditions_4.3
.6
.iso
==>
virtualbox-iso: Downloading or copying ISO
virtualbox-iso: Downloading or copying: http://releases.ubuntu.com/13
.04
/ubuntu-13
.04
-server-amd64.iso
==>
virtualbox-iso: Starting HTTP server on port 8081
==>
virtualbox-iso: Creating virtual machine...
==>
virtualbox-iso: Creating hard drive...
==>
virtualbox-iso: Creating forwarded port mapping for
SSH (
host port 3213
)
==>
virtualbox-iso: Executing custom VBoxManage commands...
virtualbox-iso: Executing: modifyvm packer-virtualbox-iso --memory 512
virtualbox-iso: Executing: modifyvm packer-virtualbox-iso --cpus 1
==>
virtualbox-iso: Starting the virtual machine...
==>
virtualbox-iso: Waiting 4s for
boot...
==>
virtualbox-iso: Typing the boot command...
==>
virtualbox-iso: Waiting for
SSH to become available...
==>
virtualbox-iso: Connected to SSH!
==>
virtualbox-iso: Uploading VirtualBox version info (
4
.3
.6
)
==>
virtualbox-iso: Uploading VirtualBox guest additions ISO...
==>
virtualbox-iso: Provisioning with shell script: scripts/build_time.sh
virtualbox-iso: [sudo] password for
vagrant:
==>
virtualbox-iso: Provisioning with shell script: scripts/apt.sh
...
==>
virtualbox-iso: Provisioning with shell script: scripts/sudo.sh
...
==>
virtualbox-iso: Provisioning with shell script: scripts/nodejs.sh
...
==>
virtualbox-iso: Provisioning with shell script: scripts/vagrant.sh
...
==>
virtualbox-iso: Provisioning with shell script: scripts/cleanup.sh
...
==>
virtualbox-iso: Gracefully halting virtual machine...
virtualbox-iso: [sudo] password for
vagrant:
virtualbox-iso: Broadcast message from root@packer-virtualbox-iso
virtualbox-iso: (
unknown) at 14
:23
...
virtualbox-iso:
virtualbox-iso: The system is going down for
power off NOW!
==>
virtualbox-iso: Preparing to export machine...
virtualbox-iso: Deleting forwarded port mapping for
SSH (
host port 3213
)
==>
virtualbox-iso: Exporting virtual machine...
==>
virtualbox-iso: Unregistering and deleting virtual machine...
==>
virtualbox-iso: Running post-processor: vagrant
==>
virtualbox-iso (
vagrant): Creating Vagrant box for
'virtualbox'
provider
virtualbox-iso (
vagrant): Copying from artifact: output-virtualbox-iso/packer-virtualbox-iso-disk1.vmdk
virtualbox-iso (
vagrant): Copying from artifact: output-virtualbox-iso/packer-virtualbox-iso.ovf
virtualbox-iso (
vagrant): Renaming the OVF to box.ovf...
virtualbox-iso (
vagrant): Compressing: Vagrantfile
virtualbox-iso (
vagrant): Compressing: box.ovf
virtualbox-iso (
vagrant): Compressing: metadata.json
virtualbox-iso (
vagrant): Compressing: packer-virtualbox-iso-disk1.vmdk
Build 'virtualbox-iso'
finished.
==>
Builds finished. The artifacts of successful builds are:
-->
virtualbox-iso: 'virtualbox'
provider box: packer_virtualbox-iso_virtualbox.box
Le fichier « packer_virtualbox-iso_virtualbox.box » a été créé. Il s'agit d'un fichier TAR. Inspectons-le :
$
tar xvf ../packer_virtualbox-iso_virtualbox.box
x Vagrantfile
x box.ovf
x metadata.json
x packer-virtualbox-iso-disk1.vmdk
$
cat Vagrantfile
# The contents below were provided by the Packer Vagrant post-processor
Vagrant.configure
(
"2"
) do
|
config|
config.vm.base_mac =
"08002751E780"
end
# The contents below (if any) are custom contents provided by the
# Packer template during image build.
$
cat metadata.json
{"provider"
:"virtualbox"
}
La box contient une image disque pour VirtualBox ainsi que des fichiers de configuration pour Vagrant.
VIII. Création de la box AWS▲
Lançons ensuite le build de l'image AWS avec l'option « -only=amazon-ebs ». Les clés d'accès AWS seront spécifiées sur la ligne de commande.
Packer va démarrer une instance EC2, puis, quand le SSH sera prêt, lancera le provisioning. L'instance sera ensuite arrêtée pour permettre la création d'une AMI. Enfin, une box Vagrant sera créée.
$
packer build -only
=
amazon-ebs -var "aws_access_key=<...>"
-var "aws_secret_key=<...>"
template.json
amazon-ebs output will be in
this color.
==>
amazon-ebs: Creating temporary keypair: packer 52e8e83f-d8bf-1a2e-45d0-62141afa9d85
==>
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-8d587ac3
==>
amazon-ebs: Waiting for
instance (
i-8d587ac3) to become ready...
==>
amazon-ebs: Waiting for
SSH to become available...
==>
amazon-ebs: Connected to SSH!
==>
amazon-ebs: Provisioning with shell script: scripts/build_time.sh
==>
amazon-ebs: Provisioning with shell script: scripts/apt.sh
...
==>
amazon-ebs: Provisioning with shell script: scripts/sudo.sh
...
==>
amazon-ebs: Provisioning with shell script: scripts/nodejs.sh
...
==>
amazon-ebs: Provisioning with shell script: scripts/vagrant.sh
...
==>
amazon-ebs: Provisioning with shell script: scripts/cleanup.sh
...
==>
amazon-ebs: Stopping the source instance...
==>
amazon-ebs: Waiting for
the instance to stop...
==>
amazon-ebs: Creating the AMI: ubuntu-13
.04__Node.JS
amazon-ebs: AMI: ami-9a07f0ed
==>
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:
eu-west-1
: ami-9a07f0ed
-->
amazon-ebs: 'aws'
provider box: packer_amazon-ebs_aws.box
Inspectons le fichier “packer_amazon-ebs_aws.box” :
$
tar xvf packer_amazon-ebs_aws.box
x Vagrantfile
x metadata.json
$
cat Vagrantfile
# The contents below were provided by the Packer Vagrant post-processor
Vagrant.configure
(
"2"
) do
|
config|
config.vm.provider "aws"
do
|
aws|
aws.region_config "eu-west-1"
, ami: "ami-9a07f0ed"
end
end
# The contents below (if any) are custom contents provided by the
# Packer template during image build.
$
cat metadata.json
{"provider"
:"aws"
}
Contrairement à la box VirtualBox, cette box ne contient pas d'image disque. En revanche, nous avons bien une référence à l'AMI qui a été créée.
IX. Ajout des base boxes Vagrant▲
Les opérations de build ont créé deux fichiers de boxes : packer_virtualbox-iso_virtualbox.box et packer_amazon-ebs_aws.box. Nous pouvons les ajouter à Vagrant :
$
vagrant box add ubuntu-13
.04
-amd64-nodejs packer_virtualbox-iso_virtualbox.box
Downloading box from URL: file:/Users/aseigneurin/dev/vms/vagrant/ubuntu-13
.04
-server-amd64/packer_virtualbox-iso_virtualbox.box
Extracting box...te: 16
.0M/s, Estimated time remaining: 0
:00
:01
)
Successfully added box 'ubuntu-13.04-amd64-nodejs'
with provider 'virtualbox'
!
$
vagrant box add ubuntu-13
.04
-amd64-nodejs packer_amazon-ebs_aws.box
Downloading box from URL: file:/Users/aseigneurin/dev/vms/vagrant/ubuntu-13
.04
-server-amd64/packer_amazon-ebs_aws.box
Extracting box...e: 0
/s, Estimated time remaining: --:--:--)
Successfully added box 'ubuntu-13.04-amd64-nodejs'
with provider 'aws'
!
$
vagrant box list
ubuntu-13
.04
-amd64-nodejs (
aws)
ubuntu-13
.04
-amd64-nodejs (
virtualbox)
Notez que plusieurs base boxes peuvent porter le même nom si leur provider est différent. C'est le cas ici.
Nous pouvons alors créer une box Vagrant reposant sur notre base box :
$
vagrant init ubuntu-13
.04
-amd64-nodejs
A `Vagrantfile`
has been placed in
this directory. You are now
ready to `vagrant up`
your first virtual environment!
Please read
the comments in
the Vagrantfile as well as documentation on
`vagrantup.com`
for
more information on using Vagrant.
Nous n'avons pas spécifié le provider (VirtualBox ou AWS). Ceci est défini avec le paramètre --provider de la commande up.
Pour lancer la VM en local, dans VirtualBox, nous lançons donc :
$
vagrant up --provider
=
virtualbox
Bringing machine 'default'
up with 'virtualbox'
provider...
[default] Importing base box 'ubuntu-13.04-amd64-nodejs'
...
...
Et pour la lancer sur Amazon :
$
vagrant up --provider
=
aws
Bringing machine 'default'
up with 'aws'
provider...
...
There are errors in
the configuration of this machine. Please fix
the following errors and try again:
AWS Provider:
* A secret access key is required via "secret_access_key"
* An AMI must be configured via "ami"
Plusieurs problèmes apparaissent :
- la clé d'accès AWS n'est pas configurée, ce qui est plutôt rassurant. Pour y remédier, le plugin Vagrant-AWS lit automatiquement les variables d'environnement AWS_ACCESS_KEY et AWS_SECRET_KEY. On peut donc lancer la commande suivante :
$
AWS_ACCESS_KEY
=<
...>
AWS_SECRET_KEY
=<
...>
vagrant up --provider
=
aws
- l'identifiant de l'AMI est manquant. Il s'agit probablement d'un bogue du plugin puisque le fichier Vagrantfile contenu dans notre base box contenait bien cette information ainsi que le nom de la région. Il est possible de contourner ce problème en rajoutant uniquement la région dans la configuration locale :
config.vm.provider :aws do
|
aws, override|
aws.region =
"eu-west-1"
end
- il nous faut également indiquer le type d'instance, le ou les security groups, la keypair, ainsi que les informations d'accès SSH :
aws.instance_type =
"t1.micro"
aws.security_groups =
"default"
aws.keypair_name =
"..."
override.ssh.username =
"ubuntu"
override.ssh.private_key_path =
"..."
Une fois ces modifications effectuées, on peut alors relancer notre vagrant up :
$
AWS_ACCESS_KEY
=<
...>
AWS_SECRET_KEY
=<
...>
vagrant up --provider
=
aws
Bringing machine 'default'
up with 'aws'
provider...
[default] -- Type: m1.small
[default] -- AMI: ami-040ff873
[default] -- Region: eu-west-1
...
[default] Waiting for
instance to become "ready"
...
[default] Waiting for
SSH to become available…
[default] Machine is booted and ready for
use!
...
Et, sur AWS comme avec VirtualBox, il est alors possible de se connecter à la VM et d'utiliser Node.JS.
Sur VirtualBox, nous obtenons :
$
vagrant ssh
Welcome to Ubuntu 13
.04
(
GNU/Linux 3
.8
.0
-19
-generic x86_64)
...
vagrant@packer-virtualbox-iso:~$
node -v
v0.10
.25
Et sur AWS, seul le prompt change :
$
vagrant ssh
Welcome to Ubuntu 13
.04
(
GNU/Linux 3
.8
.0
-35
-generic x86_64)
...
ubuntu@ip-172
-31
-30
-2
:~$
node -v
v0.10
.25
X. Conclusion▲
Vagrant est un outil très pratique pour unifier les environnements de développement et de production. Packer rend réellement la chose possible en permettant d'assembler des base boxes identiques sur des environnements différents.
Alors, certes, la chose n'est pas parfaite :
- nos boxes ne sont pas parfaitement identiques, nous sommes partis de deux sources similaires (une ISO pour VirtualBox, une AMI pour AWS) que nous avons provisionnées de la même manière. L'idéal serait de pouvoir convertir une box dans l'autre format, par exemple de convertir une image VirtualBox en AMI ;
- créer une box depuis une ISO est loin d'être trivial : la séquence de boot et le preseed sont peu ou pas documentés. Il vaut mieux dans ce cas commencer avec une image OVF telles celles qu'on peut trouver à cette adresse (les downloads sont au bas de la page) ;
- le plugin AWS pour Vagrant est encore relativement jeune et gagnerait à être stabilisé.
Toutefois, le but est atteint et c'est l'essentiel : les développeurs peuvent utiliser Vagrant avec VirtualBox tandis que les Ops utilisent un provider cloud.
XI. Ressources▲
- Le code utilisé dans ce post est disponible sur GitHub
- Vagrant
- Packer
- AMIs Ubuntu
XII. Remerciements▲
Cet article a été publié avec l'aimable autorisation de Alexis SEIGNEURIN. L'article original peut être vu sur le blog de Ippon.
Nous remercions également Francis Walter pour la mise au gabarit, kimz et Claude LELOUP pour la relecture orthographique de cet article.