Ein Basis-Setup für Magento Vagrantboxen
Im ersten Teil der Artikelserie zu Magento mit Vagrant habe ich Vagrant und die automatische Synchronisation mit Rsync vorgestellt. Nun kommen wir zu einem konkreten Setup der Vagrantbox für Magento.
Für das Provisionieren (also automatisches aufsetzen) der Box nutzen wir Shell-Skripte. Für Fortgeschrittene komme ich im letzten Teil der Serie zu einem anspruchsvolleres Setup mit Puppet und werde auch eine einsatzfertige Magento-Box veröffentlichen.
Zum Einstieg in Vagrant empfehle ich allerdings die hier vorgestellte Variante zu übernehmen und ein wenig mit der Konfiguration zu experimentieren. Sie steht vollständig auf Github zur Verfügung. Auch wenn das GUI Tool PuPHPet die Puppet-Konfiguration kinderleicht macht und man die Puppet DSL (Domain specific language) so nicht kennen muss, holt man sich zusätzliche Komplexität ins Haus, was im Fehlerfall einige Stunden und Frustration kosten kann.
Vagrantfile
Das Vagrantfile ist das Herzstück der Vagrantbox, hier werden die Parameter für den Provider (also Virtualbox in unserem Fall) gesetzt, unter anderem zu Netzwerk und Synchronisation.
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 |
# -*- mode: ruby -*- # vi: set ft=ruby : # 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 = "hashicorp/precise64" # CHANGE THIS: config.vm.hostname = "magento.local" # Create a private network, which allows host-only access to the machine # using a specific IP. # # /!\ The IP should be in the same range as the VirtualBox Host-Only Network adapter # => see ipconfig in Windows # config.vm.network :private_network, ip: "192.168.56.101" config.vm.provider "virtualbox" do |v| v.memory = 1024 v.cpus = 1 end config.vm.provision :shell, :path => "bin/vagrant-bootstrap.sh" config.vm.provision "shell", inline: "service apache2 restart", run: "always" config.vm.provision "shell", inline: "nc -z -w5 localhost 1025 || mailcatcher --ip=0.0.0.0", run: "always" config.vm.synced_folder "./src", "/home/vagrant/src", type: "rsync", rsync__exclude: [".git/", ".settings/"], rsync_args: ["--verbose", "--archive", "--delete", "-z", "--copy-links", "--omit-dir-times"] config.vm.synced_folder "./.modman", "/home/vagrant/.modman", type: "rsync", rsync__exclude: [".git/", "/src"], rsync_args: ["--verbose", "--archive", "--delete", "-z", "--copy-links", "--omit-dir-times"] config.vm.synced_folder "./vendor", "/home/vagrant/vendor", type: "rsync", rsync__exclude: [".git/"], rsync_args: ["--verbose", "--archive", "--delete", "-z", "--copy-links", "--omit-dir-times"] end |
Was hier im Einzelnen passiert:
- Das Basis-Image wird ausgewählt. Wir nutzen eine der Standard-Boxen, Ubuntu 12.04 (Precise Pangolin) 64 Bit. Unter http://vagrantbox.es/ finden sich noch unzählige mehr.
12config.vm.box ="hashicorp/precise64" - Der Hostname wird gesetzt. Auf Hosts mit Linux oder Apple Bonjour werden Hostnamen mit .local automatisch erkannt. Ansonsten muss er noch in die Datei /etc/hosts bzw. C:\Windows\System32\drivers\etc\hosts eingetragen werden:
1config.vm.hostname = "magento.local"
- Der Netzwerk-Adapter wird konfiguriert. Dies ist ein Host-Only Netzwerk, so dass die Vagrantbox nur mit unserem Rechner und anderen virtuellen Maschinen verbunden ist. Ein zusätzlicher NAT Adapter wird automatisch eingerichtet, so dass die Box Zugriff aufs Internet hat.
12config.vm.network:private_network, ip: "192.168.56.101"
- Die Hardware der VM wird spezifiziert:
12345config.vm.provider"virtualbox" do |v|v.memory = 1024v.cpus = 1end
- Provisionierungs-Skripte werden definiert. In bin/vagrant-bootstrap.sh wird das System eingerichtet. Das Skript läuft bei jeder Provisionierung, also beim ersten Start und bei explizitem Aufruf von vagrant provision. Zusätzlich sind zwei inline Commands definiert, die bei jedem Start ausgeführt werden: Neustart von Apache sowie von Mailcatcher.
123config.vm.provision :shell, :path => "bin/vagrant-bootstrap.sh"config.vm.provision "shell", inline: "service apache2 restart", run: "always"config.vm.provision "shell", inline: "nc -z -w5 localhost 1025 || mailcatcher --ip=0.0.0.0", run: "always"
- Die mit rsync synchronisierten Verzeichnisse (siehe Teil 1) werden eingerichtet. Beliebige rsync-Parameter können angegeben werden, sowie von der Synchronisation auszunehmende Dateien/Verzeichnisse. Ich synchronisiere /src, /vendor und /.modman. Auf die Verzeichnis-Struktur komme ich unten noch zurück.
12345678910config.vm.synced_folder "./src", "/home/vagrant/src", type: "rsync",rsync__exclude: [".git/", ".settings/"],rsync_args: ["--verbose", "--archive", "--delete", "-z", "--copy-links", "--omit-dir-times"]config.vm.synced_folder "./.modman", "/home/vagrant/.modman", type: "rsync",rsync__exclude: [".git/", "/src"],rsync_args: ["--verbose", "--archive", "--delete", "-z", "--copy-links", "--omit-dir-times"]config.vm.synced_folder "./vendor", "/home/vagrant/vendor", type: "rsync",rsync__exclude: [".git/"],rsync_args: ["--verbose", "--archive", "--delete", "-z", "--copy-links", "--omit-dir-times"]end
bin/vagrant-bootstrap.sh
Wie beschrieben, läuft dieses Skript bei der Provisionierung, das heißt beim ersten Start der Box oder durch expliziten Aufruf von vagrant provision
. Hier bringen wir alle Befehle unter, mit denen wir das System auf den gewünschten Stand bringen. Ein Beispiel:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
#!/usr/bin/env bash export DEBIAN_FRONTEND=noninteractive # copy ssh key cp -r /vagrant/.ssh/* /home/vagrant/.ssh/ chmod 0600 /home/vagrant/.ssh/* chown vagrant:vagrant /home/vagrant/.ssh/* # PHP 5.4 from PPA apt-get update apt-get install -y python-software-properties add-apt-repository -y ppa:ondrej/php5 apt-get update # Required packages apt-get -q -y install mysql-server apt-get install -y apache2 libnss-mdns curl git libssl0.9.8 sendmail language-pack-de-base apt-get install -y php5-dev libapache2-mod-php5 php5-cli php5-curl php5-mcrypt php5-gd php5-mysql php-pear php5-tidy # Zend Debug /vagrant/bin/vagrant-zenddebugger.sh cp /vagrant/conf/php/zend_debugger.ini /etc/php5/mods-available/ php5enmod zend_debugger/30 # Install Ruby 2.1 via RVM curl -s -L get.rvm.io | bash -s stable source /etc/profile.d/rvm.sh rvm requirements rvm install 2.1.0 rvm --default use 2.1.0 # Mailcatcher to test emails (needs latest Ruby) apt-get install -y libsqlite3-dev gem install mailcatcher # start mailcatcher if not already running on port 1025 # allow all ips, see https://github.com/sj26/mailcatcher/issues/89 nc -z -w5 localhost 1025 || mailcatcher --ip=0.0.0.0 cp /vagrant/conf/php/mailcatcher.ini /etc/php5/apache2/conf.d/ #Set up Git interface: use colors, add "git tree" command git config --global color.ui true git config --global alias.tree "log --oneline --decorate --all --graph" # Composer curl -sS https://getcomposer.org/installer | php mv composer.phar /usr/local/bin/composer # Modman curl -s -o /usr/local/bin/modman https://raw.githubusercontent.com/colinmollenhour/modman/master/modman chmod +x /usr/local/bin/modman # n98-magerun curl -s -o /usr/local/bin/n98-magerun https://raw.githubusercontent.com/netz98/n98-magerun/master/n98-magerun.phar chmod +x /usr/local/bin/n98-magerun # Magento installation script, installs project in /home/vagrant # /home/vagrant/src already exists due to rsync shared folder chown -R vagrant:vagrant /home/vagrant sudo -u vagrant -H sh -c "sh /vagrant/bin/vagrant-magento.sh" # make Magento directories writable as needed and add www-data user to vagrant group chmod -R 0777 /home/vagrant/www/var /home/vagrant/www/app/etc /home/vagrant/www/media usermod -a -G vagrant www-data usermod -a -G www-data vagrant # zend debug dummy file cp /vagrant/conf/php/dummy.php /home/vagrant/www/ # MySQL configuration, cannot be linked because MySQL refuses to load world-writable configuration cp -f /vagrant/conf/my.cnf /etc/mysql/my.cnf service mysql restart # Allow access from host mysql -uroot -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'; FLUSH PRIVILEGES;" # Set locale ln -fs /vagrant/conf/locale /etc/default/locale # Publish; Note that document root /home/vagrant/www is on the native virtual filesystem, the linked modules will be in an rsync'ed shared folder (one direction: host=>guest) ln -fs /vagrant/conf/vhost.conf /etc/apache2/sites-available/vhost.conf a2dissite 000-default a2ensite vhost.conf a2enmod proxy a2enmod rewrite service apache2 reload |
Was hier im Einzelnen passiert:
Installation
Mit apt-get werden nötige Pakete installiert, unter anderem PHP 5.4, MySQL, Apache, Git. Mailcatcher wird als Ruby Gem installiert. Da Vagrant auf Ruby basiert, ist Ruby bereits vorinstalliert, allerdings ohne RVM (Ruby Virtual Machine), dies holen wir nach. Composer, Modman und N98-Magerun werden nach /usr/local/bin heruntergeladen.
Konfiguration
Einige wichtige Konfigurationsdateien habe ich ins Git-Repository nach /conf ausgelagert. Diese werden im Bootstrap-Skript entweder gesymlinkt oder wenn das nicht möglich ist, kopiert:
- MySQL-Konfiguration: my.cnf
- Zusätzliche PHP INI Dateien:
- zend_debugger.ini aktiviert remote debugging
- mailcatcher.ini setzt den sendmail-Pfad nach catchmail, so dass alle ausgehenden Mails von Mailcatcher gefangen werden
- Alle Dateien in /.ssh, das kann der eigene SSH-Key und eine known_hosts Datei sein, z.B. für den Zugriff auf interne Repositories.
- Lokalisierungseinstellungen: locale
- Apache Virtualhost-Konfiguration:
vhost.conf
Magento
Ein zweites Skript vagrant-magento.sh wird als Vagrant-User ausgeführt. Dieses Skript kann von Installation zu Installation unterschiedlich aussehen, je nach Projektstruktur und Installations-Quellen. Wichtig ist, dass hier die Datenbank angelegt und Magento im Verzeichnis www deployed wird.
Sonstiges
- Die User vagrant und www-data werden gegenseitig ihren jeweiligen Gruppen zugewiesen, so dass bei Dateiberechtigungen von 660 bzw. 770 der User keine Rolle spielt.
- Einige Git-Konfigurationsparameter werden gesetzt
Mailcatcher
Mailcatcher ist ein Tool, das alle ausgehenden Mails abfängt und in einem Web-Interface bereitstellt. Dieses ist unter magento.local:1080 erreichbar (sofern magento.local der Hostname der Vagrantbox ist).
Zend_Debug Remote Debugging
Eine Anmerkung zum Remote Debugging: Für den Zugriff von Guest auf Host muss bei Windows-Hosts die Windows-Firewall konfiguriert werden. Siehe: http://serverfault.com/a/333584/165983
/bin/vagrant-magento.sh
Hier ein Beispiel für das Magento-Installations-Skript:
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 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#!/bin/sh # Set up default Git configuration for "The Installer" git config --global jbh-installer.license "proprietary" git config --global jbh-installer.company-name "integer_net GmbH" git config --global jbh-installer.company-name-short "IntegerNet" git config --global jbh-installer.company-url "http://www.integer-net.de/" # Directories cd ~ mkdir www #Install dependencies from composer. # Extensions from Composer will be deployed after Magento has been installed cd /vagrant composer install --dev --prefer-dist --no-interaction --no-scripts cd ~ # link project modman packages (src/modman imports others) modman link ./src modman deploy src --force # Use n98-magerun to set up Magento (database and local.xml) # CHANGE BASE URL AND MAGENTO VERSION HERE: # use --noDownload if Magento core is deployed with modman or composer. Remove the line if there already is a configured Magento installation n98-magerun install --dbHost="localhost" --dbUser="root" --dbPass="" --dbName="magento" --installSampleData=yes --useDefaultConfigParams=yes --magentoVersionByName="magento-ce-1.9.0.1" --installationFolder="www" --baseUrl="http://magento.local/" # Set up PHPUnit cd www/shell mysqladmin -uroot create magento_unit_tests php ecomdev-phpunit.php -a install php ecomdev-phpunit.php -a magento-config --db-name magento_unit_tests --base-url http://magento.local/ # Link local.xml from /etc, this overwrites the generated local.xml # from the install script. If it does not exist, the generated file gets copied to /etc first # This way you can put the devbox local.xml under version control if [ ! -f "/vagrant/etc/local.xml" ]; then cp $REPOSITORY/www/app/etc/local.xml /vagrant/etc/local.xml fi if [ ! -f "/vagrant/etc/local.xml.phpunit" ]; then cp $REPOSITORY/www/app/etc/local.xml.phpunit /vagrant/etc/local.xml.phpunit fi ln -fs /vagrant/etc/local.xml $REPOSITORY/www/app/etc/local.xml ln -fs /vagrant/etc/local.xml.phpunit $REPOSITORY/www/app/etc/local.xml.phpunit # Write permissions in media chmod -R 0770 /home/vagrant/www/media # Now after Magento has been installed, deploy all additional modules and run setup scripts modman deploy-all --force n98-magerun sys:setup:run # Some devbox specific Magento settings n98-magerun admin:user:create admin admin@example.com test123 Achmed Admin n98-magerun config:set dev/log/active 1 |
Hier führe ich unter anderem ein initales composer install und modman deploy aus, initialisiere EcomDev_PHPUnit und nutze n98-magerun für Magento Download und Installation (Aufsetzen von Datenbank und generierung der local.xml). Code und Kommentare sollten selbsterklärend sein.
composer.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 |
{ "config": { "bin-dir": "/usr/local/bin", "vendor-dir": "vendor", "cache-dir": "/dev/shm" }, "require": { "php": ">=5.3.0", "riconeitzel/german-localepack-de-de": "dev-preview", "firegento/magesetup": "dev-master", "aoepeople/composer-installers": "dev-master", "avstudnitz/scopehint": "dev-master" }, "require-dev": { "fbrnc/aoe_profiler": "dev-master", "fbrnc/aoe_templatehints": "dev-master", "jacquesbh/installer": "dev-master", "ecomdev/ecomdev_phpunit": "*" }, "minimum-stability": "dev", "repositories": [ { "type": "composer", "url": "http://packages.firegento.com" }, { "type": "git", "url": "https://github.com/jacquesbh/installer.git" } ], "extra":{ "magento-root-dir": "/home/vagrant/www/", "installer-paths": { "www/" : [ "type:magento-source" ], ".modman/{$name}/" : [ "type:magento-module" ] } }, "scripts":{ "post-install-cmd": "cd /home/vagrant && modman deploy-all --force", "post-update-cmd": "cd /home/vagrant && modman deploy-all --force" } } |
So kann die initiale Composer-Konfiguration aussehen. Folgendes ist wichtig für unser Setup:
- Composer Installer für die Installation von Magento-Extensions via Composer:
1"aoepeople/composer-installers": "dev-master"
- Die Konfiguration ebendieses Installers:
1234567"extra":{"magento-root-dir": "/home/vagrant/www/","installer-paths": {"www/" : [ "type:magento-source" ],".modman/{$name}/" : [ "type:magento-module" ]}},
- Composer-Skripte um Magento-Extensions nach Composer-Updates automatisch mit modman zu deployen:
1234"scripts":{"post-install-cmd": "cd /home/vagrant && modman deploy-all --force","post-update-cmd": "cd /home/vagrant && modman deploy-all --force"}
Die übrigen Abhängigkeiten und Konfigurationen sind als Empfehlung zu verstehen, können aber beliebig geändert werden.
Projektstruktur
Die hier vorgestellte Struktur empfehle ich für neu entwickelte Shops. Mögliche Alternativen werden in einem separaten Beitrag erläutert. Der Quellcode verteilt sich auf folgende Verzeichnisse:
Verzeichnis | Inhalt |
/.modman | Magento-Extensions |
/src | Projektspezifischer Code (inkl. gekaufter Extensions) |
/vendor | Nicht-Magento-spezifische Bibliotheken |
/www | Magento-Core |
Durch Modman werden auf der Vagrantbox zusätzlich Symlinks in .modman (nach src) und www (nach .modman) erstellt.
In /www/ gibt es also keinerlei custom code, alles kommt mit Composer und Modman entweder aus /vendor bzw. /.modman (für fremden Code und eigene, projektübergreifende Module) oder aus /src (für eigenen, shop-spezifischen Code).
Im Git Repository befindet sich nur der eigene Code sowie Abhängigkeitsdefinitionen und Installations-Anweisungen. Auszug aus der .gitignore:
1 2 3 4 5 6 |
# Externe Magento-Module: /.modman # Sonstiger externer Code: /vendor # Web Root: /www |
Diese Verzeichnisse werden mit composer install
und modman deploy
befüllt. Anmerkung: Es hat auch Vorteile, .modman und vendor mit ins Repository aufzunehmen, damit entfällt das oft langwierige composer install beim Umschalten zwischen Branches und die Verfolgung von Änderungen durch Updates wird einfacher.
Ich benutze https://github.com/AOEpeople/composer-installers als leichtgewichtige Alternative und Drop-in-Replacement zum Magento Composer Installer. Magento Extensions werden damit direkt nach .modman statt vendor heruntergeladen und können von dort ganz normal mit modman deployed werden, frei nach der Unix-Philosophie „one simple tool for each job“. Mehr Infos dazu gibt es in der Präsentation “Wiring Magento Projects“: http://de.slideshare.net/aoepeople/merged-32876900
In /src liegt eine zentrale modman-Datei, die alle eigenen Module mit @import importiert. So muss nur src selbst mit „modman link“ in .modman verlinkt werden. Daher habe ich im oben vorgestellten Vagrantfile bei den Synchronisations-Einstellungen für .modman auch /src exkludiert, dies ist der einzige in .modman angelegte Symlink, alle mit Composer installierten Module sind wie gesagt direkt hier installiert.
Vorteile:
- Strikte Trennung von Core, 3rd-Party Modulen und Eigenentwicklungen
- Komplett modular, der Modul-Code ist jeweils an einer Stelle gesammelt
Vollständiges Beispiel auf Github
Das Beispiel-Setup habe ich vollständig einsatzbereit auf Github gestellt. Wenn alle System-Anforderungen aus Teil 1 erfültl sind, kann die Box folgendermaßen installiert und gestartet werden:
1 2 |
$ git clone git@github.com:schmengler/simple-magento-boilerplate.git . $ ./up |
Anschließend ist Magento unter magento.local bzw. 192.168.56.101 erreichbar (Eintrag in /etc/hosts nicht vergessen!)
Als Grundlage für kleine bis mittlere Projekte ist dieses Setup schon brauchbar. Im dritten Teil werde ich alternative Projektstrukturen erläutern. Die Fortgeschrittenen-Variante mit Puppet als Provisioner stelle ich im vierten und letzten Teil der Serie vor.

Author: Fabian Schmengler
Fabian Schmengler ist Diplom-Informatiker und Magento Entwickler bei integer_net. Seine Schwerpunkte sind Backend-Entwicklung, Konzeptionierung und Test-Automatisierung. Seit 2011 ist er Magento Certified Developer, seit 2014 Magento Certified Solution Specialist.
Trackbacks/Pingbacks