Webdevelopment workflow met Vagrant en Chef

Door armageddon_2k1 op vrijdag 11 januari 2013 13:27 - Reacties (9)
Categorie: Dev, Views: 3.377

Welkom op de allereerste post van mijn Tweakblog. Een paar maanden heb ik deze maar eens geactiveerd maar ik ben er eigenlijk nog niet aan toegekomen om wat te schrijven. Naar aanleiding van wat post en een ander Tweakblog over workflow met een VM besloot ik mijn ervaringen ook maar eens op te schrijven omdat daar specifiek naar gevraagd werd :).

Ontwikkelen binnen een VM


Ik ga hier niet uitgebreid in op de voordelen van het ontwikkelen binnen een Virtual Machine, want daar is genoeg over te vinden op het web. Er zitten nadelen aan vast, maar voor mij wegen die niet op tegen de enorme voordelen. Het biedt mij een enorme flexibiliteit en 'schoonheid' van mijn systeem, alhoewel ik me voor kan stellen dat als je niet, zoals ik, louter met PHP en Python werkt voor web-applicaties en je meer aan de .NET kant doet het verhaal wellicht anders is.

Mijn insteek is, net als in veel programmeertalen het geval is, seperation of concerns. Mijn computer is geen webserver en geen database server. Het is wél een ontwikkelbak waar op getypt kan worden. Mijn server is geen ontwikkelbak waar tools opstaan zoals PHPStorm en SublimeText, maar wél Apache2 en PHP5. Verder niks.

Idealiter wil ik dus een VM hebben waar alles op draait en ik zonder al te veel gedoe mijn code die ik aan het tikken ben live kan zetten op de server en het daar kan zien.

Vagrant


Laat dit nu net zijn waar Vagrant goed in is! En dan knippen ze de laatste stap eruit, want het live zetten hoeft niet eens. Ik ontwikkel direct op de VM, mét mijn eigen tools op mijn eigen bak.

Ik ga hier geen tutorial neerplempen van Vagrant, want dat kunnen die mannen en vrouwen prima zelf. Ik zou maar eens kijken op: www.vagrantup.com: Getting Started With Vagrant. Die tutorial kost je 10 minuten en dan snap je precies wat ik bedoel.

Web-app development met Vagrant


Op naar de hoofdmoot van mijn verhaal. Ik ga een basis Apache2 server met PHP hier maken met de volgende eisen.
  • Debian-like server 32 bit, hetzelfde als op mijn VPS draait
  • Apache2
  • PHP5.3.8 of hoger
  • php_apc (voor het cachen van php-code en data)
  • XDebug (voor het debuggen)
Al met al geen grote lijst en snel op te zetten met Vagrant. Let wel, Vagrant i.c.m. Chef is tot heel veel in staat en kan je hele specifieke configuraties opzetten.
Ik ontwikkel alles op OSX, dus alle commandos zijn terminal commands binnen de terminal van OSX. Alle commandos zijn vanuit een directory ergens in mijn home-dir (~/Vagrant/php-dev), maar dat mag je natuurlijk helemaal zelf kiezen.

Vagrant base-box installeren

Het project is gestart en nu ga ik een vagrant instantie toevoegen aan dit project. Het gaat een VM zijn gebaseerd op Ubuntu Precise 32 welke al in base-box vorm bestaat gemaakt door de lui van Vagrant zelf. Andere base-boxes kan je hier en hier vinden. Informatie over het maken van een eigen base box staat hier.

Tijd om de base-box te downloaden, een VM te maken in de map vagrant en hem te starten.
$ mkdir vagrant
$ cd vagrant
$ vagrant box add precise32 http://files.vagrantup.com/precise32.box
[vagrant] Downloading with Vagrant::Downloaders::HTTP...
[vagrant] Downloading box: http://files.vagrantup.com/precise32.box
[vagrant] Extracting box...
[vagrant] Verifying box...
[vagrant] Cleaning up downloaded box...

$ vagrant init precise32
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.

Zoals je ziet heb ik de base box gedownload van een webserver en hem lokaal precise32 genoemd. Daarna heb ik met het init command een nieuwe Vagrant box aangemaakt gebaseerd op de precise32 base box.

De VM staat klaar! Tijd om hem te runnen.
$ vagrant up
[default] Matching MAC address for NAT networking...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Booting VM...
[default] Waiting for VM to boot. This can take a few minutes.
[default] VM booted and ready for use!
[default] Mounting shared folders...
[default] -- v-root: /vagrant

Zoals je merkt is ie eerst bezig met het kopieren van de precise32 base box en daarna start ie! De SSH port 22 op het VM systeem wordt doorgelust naar onze eigen 2222 port number, dus we kunnen gewoon SSH'en naar 127.0.0.1:2222 en dan zitten we in het Vagrant systeem.

Gelukkig heeft Vagrant ook gewoon het ssh commando zodat we even kunnen rondneuzen op het systeem.
$ vagrant ssh
Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic-pae i686)
vagrant@precise32:~$ ls /
bin   dev  home        lib         media  opt   root  sbin     srv  tmp  vagrant  vmlinuz
boot  etc  initrd.img  lost+found  mnt    proc  run   selinux  sys  usr  var
vagrant@precise32:~$ ls /vagrant
Vagrantfile
vagrant@precise32:~$ logout

Zoals je ziet is er een root folder /vagrant bijgekomen welke letterlijk naar onze Vagrant folder op onze host verwijst. Zoals je ziet staat daar de Vagrantfile waar het hele systeem in gedefinieerd wordt. Door zo folders te mounten naar ons host systeem kunnen we makkelijk een web-applicatie op ons host systeem houden en hem gewoon draaien binnen het VM als web-app.

Provisioning Vagrant met Chef Solo

Chef Solo is een onderdeel van Opscode Chef waar je hele installaties mee kan automatiseren, wat ook wel provisioning genoemd wordt. Meer over provisioning van Vagrant kan je hier lezen.

Laten we de Vagrantfile aanpassen naar een basis waarmee we kunnen gaan provisionen met Chef:

ruby:
1
2
3
4
5
6
7
8
9
10
11
12
Vagrant::Config.run do |config|
  config.vm.box = "precise32"

  # ik zorg voor 2 virtuele netwerkkaarten, zodat ik onderling VM's ook makkelijk met elkaar kan laten communiceren (denk aan PHP -> MySQL)
  config.vm.network :hostonly, "33.33.33.10" 
  config.vm.network :bridged
  config.vm.forward_port 80, 8080 # ik forward port 80 op de guest naar 8080 op de host
 
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "cookbooks"
  end
end


Op het moment is er nog geen enkel Chef recipe ingeladen maar denkt ie z'n cookbooks, met daarin recipes, in de cookbook folder relatief t.o.v. de Vagrantfile te vinden, dus laten we die maken.
$ mkdir cookbooks

Nu is het tijd om de cookbooks op te halen die Opscode al gemaakt heeft of elders online beschikbaar zijn. In ons geval gaat het om apt, apache2, php cookbooks. De apt-cookbook gebruik ik omdat dan de aptitude repositories elke keer geupdate worden en voor het toevoegen van eventuele andere repositories. Voor meer cookbooks raadt ik je aan te kijken op de Opscode Github. Daar kan je van alle cookbooks alle broodnodige informatie vinden.

Tijd om de cookbooks binnen te halen met Git:
$ git clone git://github.com/opscode-cookbooks/apt.git cookbooks/apt
$ git clone git://github.com/opscode-cookbooks/apache2.git cookbooks/apache2
$ git clone git://github.com/opscode-cookbooks/php.git cookbooks/php


en om ze te activeren in de Vagrantfile:

ruby:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Vagrant::Config.run do |config|
  config.vm.box = "precise32"
  config.vm.network :hostonly, "33.33.33.10"
  config.vm.network :bridged
  config.vm.forward_port 80, 8080
 
  config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "cookbooks"
    chef.add_recipe("apt")
    chef.add_recipe("apache2")
    chef.add_recipe("apache2::mod_php5")
    chef.add_recipe("php")    
  end
end


Nu moeten we Vagrant box opnieuw opstarten de configuratie te harladen en om Chef z'n gang te laten gaan:
$ vagrant reload
[default] Attempting graceful shutdown of VM...
[default] Clearing any previously set forwarded ports...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
--- knip ---
$ vagrant ssh
vagrant@precise32:~$ apachectl -v
Server version: Apache/2.2.22 (Ubuntu)
Server built:   Nov  8 2012 21:37:45
vagrant@precise32:~$ php -v
PHP 5.3.10-1ubuntu3.4 with Suhosin-Patch (cli) (built: Sep 12 2012 19:00:43) 
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
vagrant@precise32:~$ logout


Voila, Apache2 en PHP5.3.10 staan erop! Momenteel draait er nog geen enkele website. Tijd om een website toe te voegen.

Web applicatie toevoegen

Als je bekend bent met Apache2 dan weet je dat je een conf bestand kan maken met de Virtualhost gegevens en die vervolgens in de sites-available directory kan activeren. Gelukkig kan dit ook allemaal met Chef. We maken een eigen recipe welke onze Virtualhost configureert en bijkans ook APC en XDebug installeert.

Begin met het maken een eigen cookbook met bijbehorende mappenstructuur en Ruby files (voor meer informatie over eigen recipes moet je de Chef documentatie lezen):
$ mkdir cookbooks/custom_app
$ mkdir cookbooks/custom_app/attributes
$ mkdir cookbooks/custom_app/recipes
$ mkdir cookbooks/custom_app/templates
$ mkdir cookbooks/custom_app/templates/default
$ touch cookbooks/custom_app/recipes/default.rb
$ touch cookbooks/custom_app/attributes/default.rb
$ touch cookbooks/custom_app/templates/default/custom_app.conf.erb

De laatste is een Ruby template waarmee we de VirtualHost conf maken.

En de inhoudt van de bestanden:
cookbooks/custom_app/attributes/default.rb

ruby:
1
2
default['custom_app']['server_name'] = 'web-app.dev'
default['custom_app']['docroot']      = '/web-app'


cookbooks/custom_app/templates/default/custom_app.conf.erb

ruby:
1
2
3
4
5
6
7
8
9
10
11
<VirtualHost *:80>
  ServerName <%= @params[:server_name] %>
  DocumentRoot <%= @params[:docroot] %>

  <Directory <%= @params[:docroot] %>>
    Options FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>


Als laatste hebben we de daadwerkelijke recipe nodig:
cookbooks/custom_app/recipes/default.rb

ruby:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
app_name = 'custom_app'
app_config = node[app_name]

# install APC
package "php-apc" do
    action :install
end

# install XDebug
package "php5-xdebug" do
    action :install
end

# set up the Apache virtual host 
web_app app_name do 
  server_name app_config['server_name']
  docroot app_config['docroot']
  template "custom_app.conf.erb" 
end

Zoals je ziet halen we in het begin een app_config op (welke naar de attributes verwijst of in onze Vagrantfile te definieren is), dan worden APC en XDebug middels aptitude geinstalleerd en daarna renderen we vhost.conf met die attributes.

Zoals je ziet in attributes/default.rb is de doc-root van onze website op /web-app in ons guest VM. Laten we die even toevoegen en mappen naar een map op onze host. Ik maak op onze host een map aan genaamd web-app welke 1 niveau hoger ligt dan de vagrant map. Dan houd ik alles netjes gescheiden:
$ mkdir ../web-app
$ ls ../
vagrant	web-app


En om een mapping toe te voegen passen we onze Vagrantfile als volgt aan:

ruby:
1
2
3
4
5
--- knip ---
config.vm.network :bridged
config.vm.forward_port 80, 8080
config.vm.share_folder "web-app", "/web-app", "../web-app"  # voeg deze regel toe.
--- knip ---

Hier voegen we een nieuwe mapping, genaamd web-app toe welke /web-app op onze guest mount op ../web-app op onze host.

Nu moeten we alleen onze web-app nog via Chef toevoegen, door hem in onze Chef lijst in de Vagrantfile te zetten:

ruby:
1
2
3
4
5
6
7
8
9
--- knip ---
config.vm.provision :chef_solo do |chef|
    chef.cookbooks_path = "cookbooks"
    chef.add_recipe("apt")
    chef.add_recipe("apache2")
    chef.add_recipe("apache2::mod_php5")
    chef.add_recipe("php")
    chef.add_recipe("custom_app") # voeg hier onze eigen recipe toe
  end


En tijd om een documentje in de web-app folder te zetten en Vagrant te starten:
$ echo "<?php phpinfo(); ?>" > ../web-app/index.php
$ vagrant reload


Als het goed is draait onze web-applicatie nu op een Apache2 server binnen een Ubuntu distributie op onze VM maar is ie te benaderen vanuit localhost:8080. Even checken of het zo is:
vagrant-vm-working
Jawel! Het werkt allemaal, en zoals je kan zien zijn XDebug en APC ook aanwezig.

Gebruik van de box voor verder gebruik

Het mooie is dat deze VM nu helemaal gedefinieerd wordt door de Vragrantfile en de bijbehorende cookbooks. Het enige wat ik hoef te doen om deze VM te hergebruiken is de map vagrant kopieren en dan kan ik hem voor andere web-applicaties gebruiken. Voordat je zo'n kopieeractie doet is het wel handig alle gegenereerde data van vagrant up, weg te halen zodat je schoon kan beginnen.
$ vagrant destroy


Zelf heb ik een vergelijkbare box als Git repository ergens staan en die clone ik dan in mijn projecten. In mijn .gitignore van het project staat dan simpelweg /vagrant zodat de VM netjes gescheiden staat van mijn source.

Het aanpassen van attributes is ook geen enkel probleem en kan vanuit onze Vagrantfile. Hiervoor verwijs ik je naar de Chef documentatie. In ons geval kan een je de custom_app attributen als volgt aanpassen in de Vagrantfile:

ruby:
1
2
3
4
5
6
  chef.json = {
      "custom_app" => {
        "server_name" => 'symfony2-app.dev',
        "docroot"     => '/web-app/web'
      }
    }

Zoals je ziet kan dat voor krachtige flexibiliteit zorgen.

Afsluiting


Dit is een hele korte introductie van Chef en Vagrant, maar hopelijk laat het z'n kracht zien. Ik raadt je zeker aan de volgende sites te bezoeken voor meer informatie:
http://www.vagrantup.com
http://wiki.opscode.com/display/chef/Home
http://www.jasongrimes.or...f-vagrant-and-ec2-3-of-3/
https://github.com/opscode-cookbooks
https://github.com/simshaun/symfony-vagrant

Ik vind Vagrant best een innovatieve manier om mijn PHP-applicaties te ontwikkelen, zonder dat het mijn host bevuilt en hopelijk heb ik dat duidelijk kunnen maken :)

Mochten er op- of aanmerkingen zijn, schroom dan niet, want aangezien dit mijn eerste post is zullen er wel wat dingen onduidelijk danwel fout ingezet zijn.