Using Packer to Build Alpine Linux Box for Vagrant

Posted on June 2, 2017

Auto-installing Alpine thru VirtualBox may fail if the build takes too long to complete. Conservative delays are used, but they make the build long. To make the build less strenuous, install from ISO and provisioning (installing packages, configuring services, adding users, etc) are two separate steps (eg, separate packer builds).

Here is the first build, the bare minimum install of Alpine from an upstream ISO:

ᐅ packer build alpine-iso-install.json                                                                              
virtualbox-iso output will be in this color.

==> virtualbox-iso: Downloading or copying ISO
    virtualbox-iso: Downloading or copying: https://nl.alpinelinux.org/alpine/v3.6/releases/x86_64/alpine-virt-3.6.1-x86_64.iso
==> virtualbox-iso: Starting HTTP server on port 8835
==> virtualbox-iso: Creating virtual machine...
==> virtualbox-iso: Creating hard drive...
==> virtualbox-iso: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 3883)
==> virtualbox-iso: Executing custom VBoxManage commands...
    virtualbox-iso: Executing: modifyvm alpine-amd64-3.6.1 --memory 512
    virtualbox-iso: Executing: modifyvm alpine-amd64-3.6.1 --cpus 1
==> virtualbox-iso: Starting the virtual machine...
==> virtualbox-iso: Waiting 30s 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 (5.0.10)
==> virtualbox-iso: Gracefully halting virtual machine...
==> virtualbox-iso: Preparing to export machine...
    virtualbox-iso: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 3883)
==> virtualbox-iso: Exporting virtual machine...
    virtualbox-iso: Executing: export alpine-amd64-3.6.1 --output output-virtualbox-iso/alpine-amd64-3.6.1.ovf
==> 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/alpine-amd64-3.6.1-disk1.vmdk
    virtualbox-iso (vagrant): Copying from artifact: output-virtualbox-iso/alpine-amd64-3.6.1.ovf
    virtualbox-iso (vagrant): Renaming the OVF to box.ovf...
    virtualbox-iso (vagrant): Compressing: Vagrantfile
    virtualbox-iso (vagrant): Compressing: alpine-amd64-3.6.1-disk1.vmdk
    virtualbox-iso (vagrant): Compressing: box.ovf
    virtualbox-iso (vagrant): Compressing: metadata.json
Build 'virtualbox-iso' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox-iso: VM files in directory: output-virtualbox-iso
--> virtualbox-iso: 'virtualbox' provider box: out/alpine-clean-3.6.1.box

That build produces a box that can be loaded up into Vagrant with: vagrant box add alpine-clean-3.6.1 out/alpine-clean-3.6.1.box.

We can now use this OVF to run another build to complete the provisioning process:

ᐅ packer build alpine-base.json
virtualbox-ovf output will be in this color.

==> virtualbox-ovf: Downloading or copying OVF/OVA
    virtualbox-ovf: Downloading or copying: file:///src/packer-alpine/00-iso-install/output-virtualbox-iso/alpine-amd64-3.6.1.ovf
==> virtualbox-ovf: Importing VM: /src/packer-alpine/00-iso-install/output-virtualbox-iso/alpine-amd64-3.6.1.ovf
==> virtualbox-ovf: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 2595)
==> virtualbox-ovf: Executing custom VBoxManage commands...
    virtualbox-ovf: Executing: modifyvm alpine-amd64-3.6.1 --memory 512
    virtualbox-ovf: Executing: modifyvm alpine-amd64-3.6.1 --cpus 1
==> virtualbox-ovf: Starting the virtual machine...
==> virtualbox-ovf: Waiting 30s for boot...
==> virtualbox-ovf: Typing the boot command...
==> virtualbox-ovf: Waiting for SSH to become available...
==> virtualbox-ovf: Connected to SSH!
==> virtualbox-ovf: Uploading VirtualBox version info (5.0.10)
==> virtualbox-ovf: Provisioning with shell script: scripts/00-apk.sh
    virtualbox-ovf: + set -exu
    virtualbox-ovf: + apk upgrade -U --available
    virtualbox-ovf: fetch http://dl-5.alpinelinux.org/alpine/v3.6/main/x86_64/APKINDEX.tar.gz
    virtualbox-ovf: OK: 98 MiB in 37 packages
    virtualbox-ovf: + source /etc/os-release
    virtualbox-ovf: + NAME=Alpine Linux
    virtualbox-ovf: + ID=alpine
    virtualbox-ovf: + VERSION_ID=3.6.1
    virtualbox-ovf: + PRETTY_NAME=Alpine Linux v3.6
    virtualbox-ovf: + HOME_URL=http://alpinelinux.org
    virtualbox-ovf: + BUG_REPORT_URL=http://bugs.alpinelinux.org
    virtualbox-ovf: + apk add bash ca-certificates wget curl
    virtualbox-ovf: (1/10) Installing ncurses-terminfo-base (6.0-r7)
    virtualbox-ovf: (2/10) Installing ncurses-terminfo (6.0-r7)
    virtualbox-ovf: (3/10) Installing ncurses-libs (6.0-r7)
    virtualbox-ovf: (4/10) Installing readline (6.3.008-r5)
    virtualbox-ovf: (5/10) Installing bash (4.3.48-r1)
    virtualbox-ovf: Executing bash-4.3.48-r1.post-install

vf: (6/10) Installing ca-certificates (20161130-r2)
    virtualbox-ovf: (7/10) Installing libssh2 (1.8.0-r1)
    virtualbox-ovf: (8/10) Installing libcurl (7.54.0-r0)
    virtualbox-ovf: (9/10) Installing curl (7.54.0-r0)
    virtualbox-ovf: (10/10) Installing wget (1.19.1-r2)
    virtualbox-ovf: Executing busybox-1.26.2-r4.trigger
    virtualbox-ovf: Executing ca-certificates-20161130-r2.trigger
    virtualbox-ovf: OK: 108 MiB in 47 packages
==> virtualbox-ovf: Provisioning with shell script: scripts/01-sshd.sh
    virtualbox-ovf: + set -eux
    virtualbox-ovf: + sed -i /^PermitRootLogin yes/d /etc/ssh/sshd_config
    virtualbox-ovf: + echo UseDNS no
==> virtualbox-ovf: Provisioning with shell script: scripts/02-vagrant.sh
    virtualbox-ovf: + set -exu
    virtualbox-ovf: + date
    virtualbox-ovf: + adduser -D vagrant
    virtualbox-ovf: + chpasswd
    virtualbox-ovf: + echo vagrant:vagrant
    virtualbox-ovf: chpasswd: password for 'vagrant' changed
    virtualbox-ovf: + mkdir -pm 700 /home/vagrant/.ssh
    virtualbox-ovf: + chown -R vagrant:vagrant /home/vagrant/.ssh
    virtualbox-ovf: + chmod -R go-rwsx /home/vagrant/.ssh
==> virtualbox-ovf: Provisioning with shell script: scripts/03-sudo.sh
    virtualbox-ovf: + set -eux
    virtualbox-ovf: + apk add sudo
    virtualbox-ovf: (1/1) Installing sudo (1.8.19_p2-r0)
    virtualbox-ovf: Executing busybox-1.26.2-r4.trigger
    virtualbox-ovf: OK: 109 MiB in 48 packages
    virtualbox-ovf: + adduser vagrant wheel
    virtualbox-ovf: + echo Defaults exempt_group=wheel
    virtualbox-ovf: + echo %wheel ALL=NOPASSWD:ALL
==> virtualbox-ovf: Provisioning with shell script: scripts/98-cleanup.sh
    virtualbox-ovf: + rm -rf /var/cache/apk/APKINDEX.3029cfab.tar.gz
    virtualbox-ovf: + rm -rf /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ed25519_k
ey /etc/ssh/ssh_host_ed25519_key.pub /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.pub
==> virtualbox-ovf: Provisioning with shell script: scripts/99-minimize.sh
    virtualbox-ovf: + set -ux
    virtualbox-ovf: + dd if=/dev/zero of=/EMPTY bs=1M
    virtualbox-ovf: dd: writing '/EMPTY': No space left on device
    virtualbox-ovf: 525+0 records in
    virtualbox-ovf: 523+1 records out
    virtualbox-ovf: + rm -f /EMPTY
    virtualbox-ovf: + sync
    virtualbox-ovf: + sync
    virtualbox-ovf: + sync
    virtualbox-ovf: + exit 0
==> virtualbox-ovf: Gracefully halting virtual machine...
==> virtualbox-ovf: Preparing to export machine...
    virtualbox-ovf: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 2595)
==> virtualbox-ovf: Exporting virtual machine...
    virtualbox-ovf: Executing: export alpine-amd64-3.6.1 --output output-virtualbox-ovf/alpine-amd64-3.6.1.ovf
==> virtualbox-ovf: Unregistering and deleting imported VM...
==> virtualbox-ovf: Running post-processor: vagrant
==> virtualbox-ovf (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox-ovf (vagrant): Copying from artifact: output-virtualbox-ovf/alpine-amd64-3.6.1-disk1.vmdk
    virtualbox-ovf (vagrant): Copying from artifact: output-virtualbox-ovf/alpine-amd64-3.6.1.ovf
    virtualbox-ovf (vagrant): Renaming the OVF to box.ovf...
    virtualbox-ovf (vagrant): Compressing: Vagrantfile
    virtualbox-ovf (vagrant): Compressing: alpine-amd64-3.6.1-disk1.vmdk
    virtualbox-ovf (vagrant): Compressing: box.ovf
    virtualbox-ovf (vagrant): Compressing: metadata.json
Build 'virtualbox-ovf' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox-ovf: VM files in directory: output-virtualbox-ovf
--> virtualbox-ovf: 'virtualbox' provider box: out/alpine-base-3.6.1.box

How small is the image?

ᐅ ls -lh out/alpine-base-3.6.1.box
-rw-r--r-- 1 user user 37M Jun  2 22:22 out/alpine-base-3.6.1.box

WOW, 37M without a lot of trouble.

We can now add that box to vagrant with:

ᐅ vagrant box add alpine-base-3.6.1 out/alpine-base-3.6.1.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'alpine-base-3.6.1' (v0) for provider: 
    box: Unpacking necessary files from: file:///src/packer-alpine/01-alpine-base/out/alpine-base-3.6.1.box
    ==> box: Successfully added box 'alpine-base-3.6.1' (v0) for 'virtualbox'!

See the full code here.