
Prerequisites: If you are looking to try this, you should already be familiar (not expert) with:
- Using SSH
- Using the vi text editor (Ubuntu Core lacks nano)
- Basic networking concepts like dhcp
- Basic VM and Container concepts
Download Ubuntu Core:
- Create Ubuntu SSO Account (if you don't have one already)
- Create a SSH Key (if you don't have one already)
- Import your SSH Public Key to Ubuntu SSO.
- Download an Ubuntu core .img file from https://ubuntu.com/download/iot#core
- Convert the Ubuntu Core .img to a Virtualbox .vdi:
me@desktop:~$ VBoxManage convertdd ubuntu-core-18-amd64.img ubuntu-core.vdi
Set up a new machine in VirtualBox:
- Install VirtualBox (if you haven't already):
me@desktop:~$ sudo apt install virtualbox
- In the Virtualbox Settings, File -> Virtual Media Manager. Add the ubuntu-core.vdi
- Create a New Machine. Use an existing Hard Disk File --> ubuntu-core.vdi
- Check the network settings. You want a network that you will be able to access. I chose bridged networking so I could play with the new system from different locations, and set up a static IP address on the router. ENABLE promiscuous mode, so containers can get IP addresses from the router. Otherwise, VirtualBox will filter out the dhcp requests.
- OPTIONAL: Additional tweaks to enhance performance.
Take a snapshot of your current network neighborhood:
- Use this to figure out Ubuntu Core's IP address later on:
me@Desktop:~$ ip neigh 192.168.1.227 dev enp3s0 lladdr 00:1c:b3:75:23:a3 STALE 192.168.1.234 dev enp3s0 lladdr d8:31:34:2c:b8:3a STALE 192.168.1.246 dev enp3s0 lladdr f4:f5:d8:29:e5:90 REACHABLE 192.168.1.213 dev enp3s0 lladdr 98:e0:d9:77:5d:6b STALE 192.168.1.1 dev enp3s0 lladdr 2c:fd:a1:67:2a:d0 STALE fe80::2efd:a1ff:fe67:2ad0 dev enp3s0 lladdr 2c:fd:a1:67:2a:d0 router DELAY
Boot the image in VirtualBox:
- The first boot of Ubuntu Core requires a screen and keyboard (one reason we're trying this in VirtualBox). Subsequent logins will be done by ssh.
- Answer the couple setup questions.
- Use your Ubuntu One login e-mail address.
- The VM will reboot itself (perhaps more than once) when complete.
- Note you cannot login to the VM's TTY. Ubuntu Core's default login is via ssh. Instead, the VM's TTY tells you the IP address to use for ssh.
- Since we are using a VM, this is a convenient place to take an initial snapshot. If you make a mess of networking in the next step, you can revert the snapshot.
Let's do some initial configuration:
- After the VM reboots, the Virtualbox screen only shows the IP address.
// SSH into the Ubuntu Core Guest me@desktop:~$ ssh my-Ubuntu-One-login-name@IP-address [...Welcome message and MOTD...] me@localhost:~$ // The default name is "localhost" // Let's change that. Takes effect after reboot. me@localhost:~$ sudo hostnamectl set-hostname 'ubuntu-core-vm' // Set the timezone. Takes effect immediately. me@localhost:~$ sudo timedatectl set-timezone 'America/Chicago' // OPTIONAL: Create a TTY login // This can be handy if you have networking problems. me@localhost:~$ sudo passwd my-Ubuntu-One-login-name
Let's set up the network bridge so containers can draw their IP address from the router:
- We use vi to edit the netplan configuration. When we apply the changes, the ssh connection will be severed so we must discover the new IP address to login again.
me@localhost:~$ sudo vi /writable/system-data/etc/netplan/00-snapd-config.yaml #// The following seven lines are the original file. Commented instead of deleted. # This is the network config written by 'console_conf' #network: # ethernets: # eth0: # addresses: [] # dhcp4: true # version: 2 #// The following lines are the new config network: version: 2 renderer: networkd ethernets: eth0: dhcp4: no dhcp6: no bridges: # br0 is the name that containers use as the parent br0: interfaces: # eth0 is the device name in 'ip addr' - eth0 dhcp4: yes dhcp6: yes #// End // After the file is ready, implement it: me@localhost:~$ sudo netplan generate me@localhost:~$ sudo netplan apply // If all goes well...your ssh session just terminated without warning.
Test our new network settings:
- The Ubuntu Core VM window will NOT change the displayed IP address after the netplan change...but that IP won't work anymore.
- If you happen to reboot (not necessary) you will see that the TTY window displays no IP address when bridged...unless you have created an optional TTY login.
- Instead of rebooting, let's take another network snapshot and compare to earlier:
me@Desktop:~$ ip neigh 192.168.1.226 dev enp3s0 lladdr c6:12:89:22:56:e4 STALE 192.168.1.227 dev enp3s0 lladdr 00:1c:b3:75:23:a3 STALE 192.168.1.234 dev enp3s0 lladdr d8:31:34:2c:b8:3a STALE <---- NEW 192.168.1.235 dev enp3s0 lladdr DELAY <-----NEW 192.168.1.246 dev enp3s0 lladdr f4:f5:d8:29:e5:90 REACHABLE 192.168.1.213 dev enp3s0 lladdr 98:e0:d9:77:5d:6b STALE 192.168.1.1 dev enp3s0 lladdr 2c:fd:a1:67:2a:d0 STALE fe80::2efd:a1ff:fe67:2ad0 dev enp3s0 lladdr 2c:fd:a1:67:2a:d0 router DELAY
- We have two new lines: .226 and .235 One of those was the old IP address and one is the new. SSH into the new IP address, and you're back in.
me@desktop:~$ ssh my-Ubuntu-One-user-name@192.168.1.226 Welcome to Ubuntu Core 18 (GNU/Linux 4.15.0-99-generic x86_64) [...Welcome message and MOTD...] Last login: Thu May 7 16:11:38 2020 from 192.168.1.6 me@localhost:~$
- Let's take a closer look at our new, successful network settings.
me@localhost:~$ ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether c6:12:89:22:56:e4 brd ff:ff:ff:ff:ff:ff inet 192.168.1.226/24 brd 192.168.1.255 scope global dynamic br0 valid_lft 9545sec preferred_lft 9545sec inet6 2683:4000:a450:1678:c412:89ff:fe22:56e4/64 scope global dynamic mngtmpaddr noprefixroute valid_lft 600sec preferred_lft 600sec inet6 fe80::c412:89ff:fe22:56e4/64 scope link valid_lft forever preferred_lft forever 3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000 link/ether 08:00:27:fd:20:92 brd ff:ff:ff:ff:ff:ff // Note that ubuntu-core-vm now uses the br0 address, and lacks an eth0 address. // That's what we want.
Set up static IP addresses on the Router and then reboot to use the new IP address.
- Remember, the whole point of bridged networking is for the router to issue all the IP addresses and avoid doing a lot of NATing and Port Forwarding.
- So now is the time to login to the Router and have it issue a constant IP address to the Bridge MAC address (in this case c6:12:89:22:56:e4). After this, ubuntu-core-vm (the Ubuntu Core Guest VM) will always have a predictable IP address.
- Use VirtualBox to ACPI shutdown the VM, then restart it headless. We're looking for two changes: The hostname and the login IP address.
- Starting headless can be done two ways:
- GUI: Virtualbox Start button submenu
me@Desktop:~$ VBoxHeadless --startvm name-of-vm
- Success at rebooting headless and logging into the permanent IP address is a good point for another VM Snapshot. And maybe a sandwich. Well done!
Install LXD onto ubuntu-core-vm:
- Install:
me@ubuntu-core-vm:~$ snap install lxd lxd 4.0.1 from Canonical✓ installed me@ubuntu-core-vm:~$
- Add myself to the `lxd` group so 'sudo' isn't necessary anymore. This SHOULD work, but doesn't due to a bug (discussion)
host:~$ sudo adduser --extrausers me lxd // Works on most Ubuntu; does NOT work on Ubuntu Core even with --extrausers host:~$ newgrp lxd // New group takes effect without logout/login
- Instead, edit the groups file directly using vi:
// Use vi to edit the file: me@ubuntu-core-vm:~$ sudo vi /var/lib/extrausers/group // Change the lxd line: lxd:x:999: // Old Line lxd:x:999:my-login-name // New Line // Apply the new group settings without logout me@ubuntu-core-vm:~$ newgrp lxd
- LXD is easy to configure. We need to make three changes from the default settings since we already have a bridge (br0) set up that we want to use.
me@ubuntu-core-vm:~$ lxd init Would you like to use LXD clustering? (yes/no) [default=no]: Do you want to configure a new storage pool? (yes/no) [default=yes]: Name of the new storage pool [default=default]: Name of the storage backend to use (dir, lvm, ceph, btrfs) [default=btrfs]: Create a new BTRFS pool? (yes/no) [default=yes]: Would you like to use an existing block device? (yes/no) [default=no]: Size in GB of the new loop device (1GB minimum) [default=15GB]: Would you like to connect to a MAAS server? (yes/no) [default=no]: Would you like to create a new local network bridge? (yes/no) [default=yes]: no <------------------------- CHANGE Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: yes <-- CHANGE Name of the existing bridge or host interface: br0 <----------------------------------------------------- CHANGE Would you like LXD to be available over the network? (yes/no) [default=no]: Would you like stale cached images to be updated automatically? (yes/no) [default=yes] Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: me@ubuntu-core-vm:~$
- Next, we change the networking profile so containers use the bridge:
// Open the default container profile in vi me@ubuntu-core-vm:~$ lxc profile edit default config: {} description: Default LXD profile devices: # Container eth0, not ubuntu-core-vm eth0 eth0: name: eth0 nictype: bridged # This is the ubuntu-core-vm br0, the real network connection parent: br0 type: nic root: path: / pool: default type: disk name: default used_by: []
- Add the Ubuntu-Minimal stream for cloud-images, so our test container is small:
me@ubuntu-core-vm:~$ lxc remote add --protocol simplestreams ubuntu-minimal https://cloud-images.ubuntu.com/minimal/releases/
me@ubuntu-core-vm:~$ lxc launch ubuntu-minimal:20.04 test1 Creating test1 Starting test1 me@ubuntu-core-vm:~$ lxc list +-------+---------+----------------------+-----------------------------------------------+-----------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-------+---------+----------------------+-----------------------------------------------+-----------+-----------+ | test1 | RUNNING | 192.168.1.248 (eth0) | 2603:6000:a540:1678:216:3eff:fef0:3a6f (eth0) | CONTAINER | 0 | +-------+---------+----------------------+-----------------------------------------------+-----------+-----------+ // Let's test outbound connectivity from the container me@ubuntu-core-vm:~$ lxc shell test1 root@test1:~# apt update Get:1 http://archive.ubuntu.com/ubuntu focal InRelease [265 kB] [...lots of succesful server connections...] Get:26 http://archive.ubuntu.com/ubuntu focal-backports/universe Translation-en [1280 B] Fetched 16.3 MB in 5s (3009 kB/s) Reading package lists... Done Building dependency tree... Reading state information... Done 5 packages can be upgraded. Run 'apt list --upgradable' to see them. root@test1:~#