Packer build config for Ubuntu server: subiquity vs debian-installer

Ubuntu is discontinuing support for the Debian-installer based classic server installer from 20.04 LTS (Focal Fossa) making the way for subiquity server installer. This post shows how the Packer build config vary for both installers.

Ubuntu 20.04 live server has only subiquity support. For debian-installer you can use legacy server version.

subiquity

subiquity is the Ubuntu server’s new automated installer, which was introduced in 18.04. It is the server counterpart of ubiquity installer used by desktop live CD installation.

Autoinstallation lets you answer all those configuration questions ahead of time with autoinstall config and lets the installation process run without any external interaction. The autoinstall config is provided via cloud-init configuration. Values are taken from the config file if set, else default values are used.

There are multiple ways to provide configuration data for cloud-init. Typically user config is stored in user-data and cloud specific config in meta-data file. The list of supported cloud datasources can be found in cloudinit docs. Since packer builds it locally, data source is NoCloud in our case and the config files will served to the installer over http.

Packer config to build a VMWare virtual machine from Ubuntu 20.04 live server ISO

1) ubuntu-20.04-live-server-packer.json:

ubuntu-20.04-live-server-packer.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
{
  "builders": [
    {
      "type": "vmware-iso",

      "guest_os_type": "ubuntu-64",
      "memory": 1024,
      "name": "ubuntu-20.04-live-server",
      "iso_urls": [
        "iso/ubuntu-20.04-live-server-amd64.iso",
        "http://cdimage.ubuntu.com/ubuntu/releases/20.04/release/ubuntu-20.04-live-server-arm64.iso"
      ],
      "iso_checksum_type": "sha256",
      "iso_checksum": "caf3fd69c77c439f162e2ba6040e9c320c4ff0d69aad1340a514319a9264df9f",

      "http_directory": "subiquity/http",
      "output_directory": "output/live-server",

      "boot_wait": "5s",
      "boot_command": [
        "<enter><enter><f6><esc><wait> ",
        "autoinstall ds=nocloud-net;seedfrom=http://{{ .HTTPIP }}:{{ .HTTPPort }}/",
        "<enter><wait>"
      ],
      "shutdown_command": "shutdown -P now",

      "ssh_username": "ubuntu",
      "ssh_password": "ubuntu",
      "ssh_pty": true,
      "ssh_timeout": "20m",
      "ssh_handshake_attempts": "20"
    }
  ],

  "provisioners": [
    {
      "type": "shell",
      "inline": ["ls /"]
    }
  ]
}

2) http/meta-data: empty file

3) http/user-data:

http/user-data
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
#cloud-config
autoinstall:
  version: 1
  locale: en_US
  keyboard:
    layout: en
    variant: us
  network:
    network:
      version: 2
      ethernets:
        ens33:
          dhcp4: true
  storage:
    layout:
      name: lvm
  identity:
    hostname: ubuntu
    username: ubuntu
    password: $6$rounds=4096$8dkK1P/oE$2DGKKt0wLlTVJ7USY.0jN9du8FetmEr51yjPyeiR.zKE3DGFcitNL/nF1l62BLJNR87lQZixObuXYny.Mf17K1
  ssh:
    install-server: yes
  user-data:
    disable_root: false
  late-commands:
    - 'sed -i "s/dhcp4: true/&\n      dhcp-identifier: mac/" /target/etc/netplan/00-installer-config.yaml'
    - echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/ubuntu


Run the packer build:

$ packer build -force ubuntu-20.04-live-server-packer.json

==> Retrieving ISO
==> Trying iso/ubuntu-20.04-live-server-amd64.iso
==> Trying iso/ubuntu-20.04-live-server-amd64.iso?checksum=sha256%3Acaf3fd69c77c439f162e2ba6040e9c320c4ff0d69aad1340a514319a9264df9f
==> iso/ubuntu-20.04-live-server-amd64.iso?checksum=sha256%3Acaf3fd69c77c439f162e2ba6040e9c320c4ff0d69aad1340a514319a9264df9f => /path/to/packer-ubuntu/iso/ubuntu-20.04-live-server-amd64.iso
==> Deleting previous output directory...
==> Creating required virtual machine disks
==> Building and writing VMX file
==> Starting HTTP server on port 8100
==> Starting virtual machine...
==> Waiting 5s for boot...
==> Connecting to VM via VNC (127.0.0.1:5984)
==> Typing the boot command over VNC...
==> Using ssh communicator to connect: 172.16.255.203
==> Waiting for SSH to become available...

==> Connected to SSH!
==> Provisioning with shell script: /var/folders/lw/n4rl9vm16t38zzv2x_kl74xc0000gn/T/packer-shell298726450
    bin   cdrom  etc   lib	  lib64   lost+found  mnt  proc  run   snap  sys  usr
    boot  dev    home  lib32  libx32  media       opt  root  sbin  srv   tmp  var

==> Gracefully halting virtual machine...
    Waiting for VMware to clean up after itself...
==> Deleting unnecessary VMware files...
    Deleting: output/live-server/packer-ubuntu-20.04-live-server.plist
    Deleting: output/live-server/startMenu.plist
    Deleting: output/live-server/vmware.log
==> Compacting all attached virtual disks...
    Compacting virtual disk 1
==> Cleaning VMX prior to finishing up...
    Detaching ISO from CD-ROM device ide0:0...
    Disabling VNC server...
==> Skipping export of virtual machine (export is allowed only for ESXi)...
Build 'ubuntu-20.04-live-server' finished.

==> Builds finished. The artifacts of successful builds are:
--> VM files in directory: output/live-server

Notes

⏳ Boot interaction sequence for live server
  1. Initial empty screen
  2. Press <any key> to goto advanced welcome page
  3. Press F6 to open Other Options popup + activate boot commandline
  4. Press ESC to close popup & focus on edit existing boot command initrd=/casper/initrd quiet --- (with cursor at the end)
  5. The autoinstall boot command in the following format is entered by Packer via VNC connection and then awaits the installation completion:
     initrd=/casper/initrd quiet --- autoinstall ds=nocloud-net;seedfrom=http://<ip>:<port>
    
Initial empty screen of live server
Initial empty screen
Live server boot command
Live server boot command
🔐 Generating hashed password
mkpasswd --methhod=SHA-512 --rounds=4096
⚠️ Packer SSH timeout issue due to IP change on instance restart

While building in VMWare, restart after installation causes change in IP address of the instance. This leads packer build to timeout awaiting SSH connection. To fix this issue, we can configure MAC address to be send as identifier in DHCP request.

    dhcp-identifier: mac

Since there is no option to set dhcp-identifier via cloud config, this is appended to ens33 interface in /etc/netplan/00-installer-config.yaml via late-commands.

🔧 Default user sudo with no-password

Cloud config identity doesn’t provide a way to set sudo NOPASSWD option. That is also being directly written to to sudoers.d/ubuntu file.



debian-installer

debian-installer or just d-i is a text-based automated installer with little user interaction. It consists of a number of components to perform each installation task. Component asks questions to the user based on the priority set.

Preseeding is a way to set answers to questions asked during the installation process, without having to manually enter the answers while the installation is running. We can create a preseed.cfg file and pass it to the debian-installer. In the default mode, when the answer to a question is not present in a preseed, d-i stops and asks the user for input.

Installation process is quite slow compared to subiquity.

Packer config to build a VMWare virtual machine from Ubuntu 20.04 legacy server ISO

1) ubuntu-20.04-legacy-server-packer.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
43
44
45
46
47
48
{
  "builders": [
    {
      "type": "vmware-iso",

      "guest_os_type": "ubuntu-64",
      "memory": 1024,
      "name": "ubuntu-20.04-legacy-server",
      "iso_urls": [
        "iso/ubuntu-20.04-legacy-server-amd64.iso",
        "http://cdimage.ubuntu.com/ubuntu-legacy-server/releases/20.04/release/ubuntu-20.04-legacy-server-amd64.iso"
      ],
      "iso_checksum_type": "sha256",
      "iso_checksum": "36f15879bd9dfd061cd588620a164a82972663fdd148cce1f70d57d314c21b73",

      "http_directory": "debian-installer/http",
      "output_directory": "output/live-server",

      "boot_wait": "5s",
      "boot_command": [
        "<esc><wait>",
        "<esc><wait>",
        "<enter><wait>",
        "/install/vmlinuz<wait>",
        " initrd=/install/initrd.gz",
        " auto-install/enable=true",
        " debconf/priority=critical",
        " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg<wait>",
        " -- <wait>",
        "<enter><wait>"
      ],
      "shutdown_command": "shutdown -P now",

      "ssh_username": "ubuntu",
      "ssh_password": "ubuntu",
      "ssh_pty": true,
      "ssh_timeout": "20m",
      "ssh_handshake_attempts": "20"
    }
  ],

  "provisioners": [
    {
      "type": "shell",
      "inline": ["ls /"]
    }
  ]
}

2) http/preseed.cfg:

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
# Localization
d-i debian-installer/locale string en_US.UTF-8

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

# Keyboard selection.
d-i keyboard-configuration/layoutcode string us
d-i keyboard-configuration/modelcode string pc105
d-i console-setup/ask_detect boolean false

# Base system installation
d-i base-installer/kernel/override-image string linux-server

# Finishing up the installation
d-i finish-install/reboot_in_progress note

# Boot loader installation
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean true

# Partitioning
d-i partman-auto/disk string /dev/sda
d-i partman-auto-lvm/guided_size string max
d-i partman-auto/choose_recipe select atomic
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true
d-i partman/confirm_write_new_label boolean true

# Mirror settings
choose-mirror-bin mirror/http/proxy string
d-i mirror/country string manual
d-i mirror/http/directory string /ubuntu/
d-i mirror/http/hostname string archive.ubuntu.com
d-i mirror/http/proxy string

# Package selection
tasksel tasksel/first standard
d-i pkgsel/include string openssh-server build-essential
d-i pkgsel/install-language-support boolean false
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select full-upgrade

# Account setup
d-i passwd/user-fullname string ubuntu
d-i passwd/username string ubuntu
d-i passwd/user-password password ubuntu
d-i passwd/user-password-again password ubuntu
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false
d-i passwd/user-default-groups ubuntu sudo

# Running custom commands during the installation
d-i preseed/late_command string \
    echo 'ubuntu ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/ubuntu ; \
    in-target /bin/chmod 440 /etc/sudoers.d/ubuntu

Notes

⏳ Boot interaction sequence for legacy server
  1. Press ESC to goto advanced welcome page
  2. Press ESC to get popup alert “You are leaving the graphical boot menu and startinng the text mode interface”
  3. Press Enter to select OK button
  4. In the boot text mode interface, the boot command in the following format is entered by Packer via VNC connection and then awaits the installation completion. The installation progress will be visible via graphical interface, incase if any necessary value is not present in preseed, installer stops and waits for the user input to proceed.
    /install/vmlinuz initrd=/install/initrd.gz auto-install/enable=true debconf/priority=critical preseed/url=http://<ip>:<port>/preseed.cfg --
    
Exiting from graphical menu
Exiting from graphical menu
Legacy server boot command
Legacy server boot command
Legacy server boot progress
Legacy server boot progress