В предыдущей статье я рассказал, как создать базовый образ Ubuntu 14.04 для Vagrant с провайдером Parallels самым простым способом — с помощью Veewee. Простой, потому что Parallels штатно поддерживается этой утилитой и базовые образы отлично собираются лишь с минимальными ухищрениями.

Сейчас мне тяжело рассказать, что лучше, Packer или Veewee (про отношения Packer’а и Vagrant’а лучше прочитать в сообщении Хашимото). Я просто опишу, как сделать базовый образ для Vagrant с привлечением многообещающего инструмента — Packer.

Скажу сразу, что мне удалось решить задачу не сразу, пришлось попотеть. Начиная с установки самого билдера, заканчивая настройками шаблона и созданием скриптов. Также предупрежу, что я использую OS X, но это и так подразумевается, так как описываю использование Parallels.

Установка Packer

Итак, Packer. Его можно установить или из пакета, или, что лучше, с помощью Homebrew:

$ brew tap homebrew/binary
$ brew install packer

Проверяю, всё в порядке:

$ packer
    usage: packer [--version] [--help] <command> [<args>]

    Available commands are:
        build       build image(s) from template
        fix         fixes templates from old versions of packer
        inspect     see components of a template
        validate    check that a template is valid

    Globally recognized options:
        -machine-readable    Machine-readable output format.

$ packer --version
    Packer v0.5.2

Установка билдера Parallels для Packer

Использую плагин Rickard von Essen’а.

Предварительно нужно установить Parallels Virtualization SDK 9.

Устанавливаю prl-utils

$ brew tap rickard-von-essen/formulae
$ brew install --HEAD prl-utils

Сообщение “Warning: Could not fix libprl_sdk.5.dylib in …/prltype” проигнорировал.

Проверяю, работает:

$ prlctl --version
    prlctl version 9.0.24229.991745

Устанавливаю Go и Mercurial (git уже давно установлен в составе Command line tools из Xcode):

$ brew install go
$ brew install hg

Для Go создаю отдельный каталог и прописываю его в .zshrc или .bashrc:

$ mkdir -p $HOME/Documents/DevOps/go
$ vim ~/.zshrc
    export GOPATH="$HOME/Documents/DevOps/go"
    export PATH="$GOPATH/bin:$PATH"
$ source ~/.zshrc

Здесь “тонкий” момент. Я некоторое время бился с проблемой создания образа vagrant’а из-за того, что packer-post-processor-vagrant ставится вместе с плагином и он же есть в штатной установке Packer. Если поставить путь к Go в конце $PATH, то будут ошибки. После наступания на эти грабли я перешёл на явное указание путей в ~/.packerconfig.

Устанавливаю билдер:

$ mkdir -p $GOPATH/src/github.com/rickard-von-essen/
$ cd $GOPATH/src/github.com/rickard-von-essen/
$ git clone https://github.com/rickard-von-essen/packer-parallels.git
$ cd packer-parallels

Устанавливаю кросс-компилятор Gox:

$ go get github.com/mitchellh/gox

Если сейчас запустить компиляцию, то будет ошибка:

builder/parallels/common/ssh.go:31: undefined: "code.google.com/p/go.crypto/ssh".ClientAuth

Нужно использовать старую версию библиотеки ssh:

$ vim builder/parallels/common/ssh.go
    import (
        // gossh "code.google.com/p/go.crypto/ssh"
        gossh "code.google.com/p/gosshold/ssh"

Можно компилировать:

$ make

Вот что поставилось:

$ ls -l $GOPATH/bin
    -rwxr-xr-x  1 ctrld  staff   4112768 Apr 24 17:14 gox
    -rwxr-xr-x  1 ctrld  staff  13444028 Apr 25 11:19 packer-builder-parallels-iso
    -rwxr-xr-x  1 ctrld  staff  13063596 Apr 25 11:19 packer-builder-parallels-pvm
    -rwxr-xr-x  1 ctrld  staff  11707548 Apr 25 11:19 packer-post-processor-vagrant

Следующий очень важный момент — указать, где искать новые билдеры и постпроцессор vagrant, переопределяющий штатный (!):

$ vim ~/.packerconfig
    {
      "builders": {
        "parallels-iso": "/Users/ctrld/Documents/DevOps/go/bin/builder-parallels-iso",
        "parallels-pvm": "/Users/ctrld/Documents/DevOps/go/bin/builder-parallels-pvm"
       },
      "post-processors": {
        "vagrant": "/Users/ctrld/Documents/DevOps/go/bin/packer-post-processor-vagrant"
       }
    }

Создание базового образа для Vagrant

Все инструменты подготовлены. В процессе работы я смотрел на примеры в репозитории rickard-von-essen/packer-examples, а также на сконвертированный из моего предыдущего шаблона с помощью утилиты veewee-to-packer.

Полный шаблон вы можете взять в моём репозитории на github ctrld/packer-parallels-ubuntu-14.04.

Приступаю к созданию шаблона.

$ mkdir -p /Users/ctrld/Documents/DevOps/p4
$ cd /Users/ctrld/Documents/DevOps/p4

$ vim trusty64.json
{
    "variables": {
        "ssh_name": "vagrant",
        "ssh_pass": "vagrant",
        "hostname": "trusty64"
    },

    "builders": [{
        "type": "parallels-iso",

        "guest_os_type": "linux",
        "guest_os_distribution": "linux-2.6",

        "iso_url": "http://releases.ubuntu.com/trusty/ubuntu-14.04-server-amd64.iso",
        "iso_checksum": "01545fa976c8367b4f0d59169ac4866c",
        "iso_checksum_type": "md5",

        "http_directory" : "http",
        "http_port_min" : 9001,
        "http_port_max" : 9001,

        "ssh_username": "{{user `ssh_name`}}",
        "ssh_password": "{{user `ssh_pass`}}",
        "ssh_wait_timeout": "20m",

        "parallels_tools_mode": "upload",
        "parallels_tools_path": "prl-tools-lin.iso",
        "parallels_tools_url": "/Applications/Parallels Desktop.app/Contents/Resources/Tools/prl-tools-lin.iso",

        "boot_wait": "4s",

        "delete_vm": "false",
        "prlctl": [
            ["set", "{{.Name}}", "--memsize", "512"]
        ],

        "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={{user `hostname`}} ",
            "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><wait>"
        ],

        "shutdown_command": "echo {{user `ssh_pass`}} | sudo -S shutdown -P now"
    }],
    "provisioners": [{
      "type": "shell",
      "scripts": [
        "scripts/apt.sh",
        "scripts/sudo.sh",
        "scripts/vagrant.sh",
        "scripts/parallels.sh",
        "scripts/cleanup.sh"
      ],
      "execute_command": "echo 'vagrant'|sudo -S sh '{{.Path}}'"
    }],
    "post-processors": [{
        "type": "vagrant",
        "output": "{{user `hostname`}}.box"
    }]
}

Очередные очень важные моменты:

  • обратите внимание на parallels_tools_path. Если его явно не указать, то будет списан prl-tools-other.iso, который далеко не для Linux
  • если не выполнить скрипт scripts/cleanup.sh, то в созданной Vagrant’ом виртуальной машине не подымется сеть
  • в скрипте scripts/vagrant.sh списывается ключ для соединения по ssh из Vagrant
  • перечень возможных guest_os_distribution можно посмотреть командой

    $ prlctl create x –distribution list

Создаю файл preseed.cfg для автоматизированной установки (копия из veewee-packer с единственным изменением в get_hostname и get_domain):

$ mkdir http
$ vim http/preseed.cfg
## Options to set on the command line
d-i debian-installer/locale string en_US.utf8
d-i console-setup/ask_detect boolean false
d-i console-setup/layout string us

d-i netcfg/get_hostname string this-host
d-i netcfg/get_domain string this-host

d-i time/zone string UTC
d-i clock-setup/utc-auto boolean true
d-i clock-setup/utc boolean true

d-i kbd-chooser/method select American English

d-i netcfg/wireless_wep string

d-i base-installer/kernel/override-image string linux-server

d-i debconf debconf/frontend select Noninteractive

d-i pkgsel/install-language-support boolean false
tasksel tasksel/first multiselect standard, ubuntu-server

d-i partman-auto/method string lvm

d-i partman-lvm/confirm boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-auto/choose_recipe select atomic

d-i partman/confirm_write_new_label boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true

# Write the changes to disks and configure LVM?
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto-lvm/guided_size string max

# Default user
d-i passwd/user-fullname string vagrant
d-i passwd/username string vagrant
d-i passwd/user-password password vagrant
d-i passwd/user-password-again password vagrant
d-i user-setup/encrypt-home boolean false
d-i user-setup/allow-password-weak boolean true

# Minimum packages (see postinstall.sh)
d-i pkgsel/include string openssh-server ntp

# Upgrade packages after debootstrap? (none, safe-upgrade, full-upgrade)
# (note: set to none for speed)
d-i pkgsel/upgrade select none

d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true
d-i finish-install/reboot_in_progress note

d-i pkgsel/update-policy select none

choose-mirror-bin mirror/http/proxy string

Создаю скрипты (veewee-packer с небольшими изменениями):

$ mkdir scripts

scripts/apt.sh:

$ vim scripts/apt.sh
apt-get -y update
apt-get -y upgrade
apt-get -y install linux-headers-$(uname -r)
apt-get -y install dkms
apt-get -y install ntp
apt-get -y install build-essential
apt-get -y install vim

scripts/sudo.sh:

$ vim scripts/sudo.sh
groupadd -r admin
usermod -a -G admin vagrant
cp /etc/sudoers /etc/sudoers.orig
sed -i -e '/Defaults\s\+env_reset/a Defaults\texempt_group=admin' /etc/sudoers
sed -i -e 's/%admin ALL=(ALL) ALL/%admin ALL=NOPASSWD:ALL/g' /etc/sudoers

scripts/vagrant.sh:

$ vim scripts/vagrant.sh
mkdir /home/vagrant/.ssh
chmod 700 /home/vagrant/.ssh
cd /home/vagrant/.ssh
wget --no-check-certificate 'https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub' -O authorized_keys
chmod 600 /home/vagrant/.ssh/authorized_keys
chown -R vagrant /home/vagrant/.ssh

scripts/parallels.sh:

$ vim scripts/parallels.sh
# Install the Parallels Tools
PARALLELS_TOOLS_ISO=/home/vagrant/prl-tools-lin.iso
mount -o ro -o loop $PARALLELS_TOOLS_ISO /media/cdrom
/media/cdrom/install --install-unattended-with-deps --progress
umount /media/cdrom

# Cleanup
# rm $PARALLELS_TOOLS_ISO

scripts/cleanup.sh:

$ vim scripts/cleanup.sh
apt-get -y autoremove

dd if=/dev/zero of=/EMPTY bs=1M
rm -f /EMPTY

echo "cleaning up dhcp leases"
rm /var/lib/dhcp/*

echo "cleaning up udev rules"
rm /etc/udev/rules.d/70-persistent-net.rules
mkdir /etc/udev/rules.d/70-persistent-net.rules
rm -rf /dev/.udev/
rm /lib/udev/rules.d/75-persistent-net-generator.rules

echo "pre-up sleep 2" >> /etc/network/interfaces
exit

Проверяю шаблон:

$ packer validate trusty64.json
    Template validated successfully.

При создании шаблона полезно включить отладку:

$ export PACKER_LOG=true
$ export PACKER_LOG_PATH=/tmp/packer-p4.log

Создаю базовый образ, на моём Mac’е это заняло 11 минут:

$ packer build trusty64.json

==> Builds finished. The artifacts of successful builds are:
--> parallels-iso: 'parallels' provider box: trusty64.box

Полный вывод команды можно посмотреть в gist’е ctrld/11286520.

Размер образа:

$ ls -l *box
    -rw-r--r--  1 ctrld  staff  581315973 Apr 25 17:37 trusty64.box

Добавляю образ в Vagrant (–force был полезен при перестройке образа):

$ vagrant box add --force --provider parallels trusty64 trusty64.box
    ==> box: Adding box 'trusty64' (v0) for provider: parallels
        box: Downloading: file:///Users/ctrld/Documents/DevOps/p4/trusty64.box
    ==> box: Successfully added box 'trusty64' (v0) for 'parallels'!

Готовлю описание виртуальной машины:

$ vagrant init trusty64

Создаю её:

$ vagrant up --provider parallels
    Bringing machine 'default' up with 'parallels' provider...
    ==> default: Importing base box 'trusty64'...
    ==> default: Setting the name of the VM: p4_default_1398437110491_78850
    ==> default: Preparing network interfaces based on configuration...
        default: Adapter 0: shared
    ==> default: Clearing any previously set network interfaces...
    ==> default: Booting VM...
    ==> default: Waiting for machine to boot. This may take a few minutes...
        default: SSH address: 10.211.55.47:22
        default: SSH username: vagrant
        default: SSH auth method: private key
        default: Warning: Connection refused. Retrying...
    ==> default: Machine booted and ready!
    ==> default: Checking for Parallels Tools installed on the VM...
    ==> default: Mounting shared folders...
        default: /vagrant => /Users/ctrld/Documents/DevOps/p4

Игнорирую “Connection refused”, так как машина нормально запустилась.

Захожу по ssh:

$ vagrant ssh

    Welcome to Ubuntu 14.04 LTS (GNU/Linux 3.13.0-24-generic x86_64)

     * Documentation:  https://help.ubuntu.com/
    Last login: Fri Apr 25 14:35:00 2014 from 10.211.55.2
    vagrant@trusty64:~$ uname -a
    Linux trusty64 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

Всё работает нормально. После пары десятков запусков за время работы над шаблоном я был почти счастлив.