Build Bootable FreeBSD Image for Raspberry Pi 2 with Vagrant, Crochet, and Salt
An issue in FreeBSD 11-CURRENT - where make buildworld fails when run on RPi2 - prompted an investigation on automating the creation of bootable FreeBSD images from 11-CURRENT on a Mac OS X machine.
In an ideal situation, a build process can be kicked off at any time to produce an image that can be written to a Micro SD card for the RPi2 to boot from.
Things you need to get this project up and running:
- git
- VirtualBox
- Vagrant
- FreeBSD 11.0-CURRENT Vagrant box
- Crochet
- Salt
- OS X host
UPDATE (2016-04-19): I have created a companion git repo that provides a quick start alternative to the manual steps described here.
First Steps
user@host:~ $ mkdir -p ~/devbox/projects/freebsd-rpi user@host:~ $ cd ~/devbox/projects/freebsd-rpi user@host:~/devbox/projects/freebsd-rpi $ git init
Salt
Create a Python (2.7) virtualenv and install Salt.
user@host:~/devbox/projects/freebsd-rpi $ virtualenv-2.7 pyvenv user@host:~/devbox/projects/freebsd-rpi $ echo "pyvenv/" >> .gitignore user@host:~/devbox/projects/freebsd-rpi $ git add .gitignore user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Add pyvenv/ to .gitignore" user@host:~/devbox/projects/freebsd-rpi $ source pyvenv/bin/activate (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ pip install --upgrade pip
Create the master configuration file. There's a security no-no of accepting all minions in this config file but that's the easiest thing to do. Your outlook may be different.
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ pip install salt (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ mkdir salt (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ vim salt/master auto_accept: True interface: 0.0.0.0 root_dir: /Users/user/devbox/projects/freebsd-rpi/salt pki_dir: /Users/user/devbox/projects/freebsd-rpi/salt/pki pidfile: /Users/user/devbox/projects/freebsd-rpi/salt/var/run/salt-master.pid user: user file_roots: base: - /Users/user/devbox/projects/freebsd-rpi/salt/states (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git add salt/ (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Add master config file" (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ echo "salt/pki/" >> .gitignore (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ echo "salt/var/" >> .gitignore (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git add .gitignore (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Add salt/{pki,var} to .gitignore"
Delete accepted key for minion1 since it will be re-added as part of Vagrant provision
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ salt-key -c salt -D minion1 -y
Now it is possible to start Salt master. Before you can provision using Vagrant the Salt master must be running.
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ echo "*.log" >> .gitignore (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git add .gitignore (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Add *.log to .gitignore" (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ salt-master -c salt --log-file "./salt.log" --log-file-level "debug"
VirtualBox
Install it.
Vagrant
Download and install Vagrant.
Create a brand new Vagrant file.
user@host:~/devbox/projects/freebsd-rpi $ vagrant init user@host:~/devbox/projects/freebsd-rpi $ git add Vagrantfile user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Add Vagrantfile"
Download FreeBSD 11.0-CURRENT "box" from HashiCorp ATLAS.
user@host:~/devbox/projects/freebsd-rpi $ vagrant box add --provider virtualbox --clean freebsd/FreeBSD-11.0-CURRENT
Use the downloaded box in Vagrantfile.
user@host:~/devbox/projects/freebsd-rpi $ sed -i .bak -e 's/ config.vm.box = "base"/ config.vm.box = "freebsd\/FreeBSD-11.0-CURRENT"/g' Vagrantfile user@host:~/devbox/projects/freebsd-rpi $ echo "*.bak" >> .gitignore user@host:~/devbox/projects/freebsd-rpi $ echo ".vagrant/" >> .gitignore user@host:~/devbox/projects/freebsd-rpi $ git add .gitignore user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Create .gitignore" user@host:~/devbox/projects/freebsd-rpi $ git add Vagrantfile user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Use FreeBSD-11.0-CURRENT box"
Configure required settings by adding these lines before end in Vagrantfile. Modify this information as needed, especially the IP address of Salt master.
config.vm.base_mac = "000AAAAAAAAA" config.vm.guest = :freebsd config.vm.hostname = "freebsd" config.vm.synced_folder ".", "/vagrant_data", disabled: true config.ssh.shell = "sh" config.vm.provision "shell", inline: <<-SHELL pkg update pkg install -y py27-salt cp /usr/local/etc/salt/minion.sample /usr/local/etc/salt/minion sed -i .1.bak -e 's/^#master: salt$/master: 10.201.107.246/g' /usr/local/etc/salt/minion sed -i .2.bak -e 's/^#file_client: remote$/file_client: remote/g' /usr/local/etc/salt/minion sed -i .3.bak -e 's/^#id:$/id: minion1/g' /usr/local/etc/salt/minion sysrc salt_minion_enable="YES" service salt_minion start SHELL
Commit keep these changes in git.
user@host:~/devbox/projects/freebsd-rpi $ git add Vagrantfile user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Configure VM"
Provision the VM. Since Salt is installed in a virtualenv we need to activate it first and then start the daemon if it isn't already.
user@host:~/devbox/projects/freebsd-rpi $ source pyvenv/bin/activate (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ salt-master -d -c salt --log-file "./salt.log" --log-file-level "debug"
Provision the VM
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ vagrant up
Crochet
Create salt state files to install git.
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ mkdir -p salt/states/{git,vim-lite} (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ vim salt/states/top.sls base: 'minion*': - git - vim-lite (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ vim salt/states/git/init.sls git: pkg: - installed (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ vim salt/states/vim-lite/init.sls vim-lite: pkg: - installed (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git add salt/states (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ git commit -m "Add salt states"
Restart salt master
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ pkill -F /Users/user/devbox/projects/freebsd-rpi/salt/var/run/salt-master.pid (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ salt-master -d -c salt --log-file "./salt.log" --log-file-level "debug"
Apply the configuration.
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ mkdir -p salt/var/log/salt (pyvenv) user@host:~/devbox/projects/freebsd-rpi $ salt -c salt '*' state.highstate
On the FreeBSD VM build a Raspberry Pi 2 bootable image with Crochet.
(pyvenv) user@host:~/devbox/projects/freebsd-rpi $ vagrant ssh vagrant@freebsd:~ % sudo pkg install vim-lite vagrant@freebsd:~ % git clone https://github.com/freebsd/crochet.git vagrant@freebsd:~ % cd crochet
Copy the sample config file ...
vagrant@freebsd:~/crochet % cp config.sh.sample config.rpi2.sh vagrant@freebsd:~/crochet % vim config.rpi2.sh
... and apply this diff
vagrant@freebsd:~/crochet % diff config.sh.sample config.rpi2.sh 34c34 < #board_setup RaspberryPi2 --- > board_setup RaspberryPi2 55c55 < #option ImageSize 3900mb # for 4 Gigabyte card --- > option ImageSize 3900mb # for 4 Gigabyte card 97c97 < #option Growfs --- > option Growfs 115c115 < #option User <username> --- > option User raspberry 126a127 > option SwapFile 768mb deferred file=/swapfile0 160c161 < #option Ntpd --- > option Ntpd 164c165 < #KERNCONF=MYCONF --- > KERNCONF=RPI2 171c172 < #FREEBSD_SRC=/usr/src --- > FREEBSD_SRC=/usr/src 194c195 < #IMG=${WORKDIR}/FreeBSD-${KERNCONF}.img --- > IMG=${WORKDIR}/FreeBSD-${KERNCONF}.img 202c203 < #FREEBSD_INSTALL_WORLD=y --- > FREEBSD_INSTALL_WORLD=y
Download sources, install u-boot-rpi2, and build an image
vagrant@freebsd:~/crochet % sudo svnlite checkout svn://svn.freebsd.org/base/head /usr/src vagrant@freebsd:~/crochet % sudo pkg install -y sysutils/u-boot-rpi2 vagrant@freebsd:~/crochet % sudo /bin/sh crochet.sh -c config.rpi2.sh
Take the resulting image, dd it to a micro SD card, insert into RPi2 and boot it.
TODO
Make this an automated process that requires minimal manual intervention.