Basic management of an Ubuntu Core installation

Part 1: Setting up an “All-Snap” Ubuntu Core image in a QEMU/KVM virtual machine
Part 2: Basic management of an Ubuntu Core installation
Part 3: Mir and graphics on Ubuntu Core
Part 4: Unity8 on Mir on Ubuntu Core
Part 5: Confinement

After I’ve set up an “All-Snap” Ubuntu Core virtual machine in the last post, let’s see what I can do with it.

Logging in

After logging in, the VM greets me with the following message:

$ ssh -p 8022 sturmflut@localhost
Welcome to Ubuntu Core 16 (GNU/Linux 4.4.0-53-generic x86_64) 
 * Documentation: 
 * Management: 
 * Support: 
The programs included with the Ubuntu system are free software; 
the exact distribution terms for each program are described in the 
individual files in /usr/share/doc/*/copyright. 
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by 
applicable law. 
Welcome to Snappy Ubuntu Core, a transactionally updated Ubuntu. 
 * See 
It's a brave new world here in Snappy Ubuntu Core! This machine 
does not use apt-get or deb packages. Please see 'snap --help' 
for app installation and transactional updates. 

Hm, so there’s no apt-get and no deb packages? Really?

$ sudo apt-get update 
Ubuntu Core does not use apt-get, see 'snap --help'!

Ouch, that’s a clear message! Guess I’ll have to learn how to use Snappy.

Getting started

The first thing I want to do after logging in is to update the VM, the image was created in November 2016, so there’s probably a lot of outdated stuff. Ubuntu Core is transactionally updated, it cannot break, right? Let’s see what’s installed so I know what to update.

$ snap list 
Name       Version     Rev  Developer  Notes 
core       16.04.1     888  canonical  - 
pc         16.04-0.8   9    canonical  - 
pc-kernel  4.4.0-53-1  45   canonical  -

Just three packages! core is the base Ubuntu userspace with all the things common to all Ubuntu Core installs. pc-kernel is probably the generic AMD64 operating system kernel, but what’s pc?

$ snap info pc 
name:      pc 
summary:   "AMD64 generic package" 
publisher: canonical 
description: | 
  This package contains a simple OEM snappy package for system configuration 
type:        gadget 
tracking:    stable 
installed:   16.04-0.8 (9) 856kB - 
refreshed:   2016-09-01 09:27:22 +0000 UTC 
  stable:    16.04-0.8 (9)  856kB - 
  candidate: 16.04-0.8 (9)  856kB - 
  beta:      16.04-0.8 (9)  856kB - 
  edge:      16.04-0.8 (19) 856kB -

That was absolutely not helpful. Let’s look which files this package actually contains.

Snap packages and mounts

A Snap package is immutable, which means all the files it ships can not be changed. If a Snap wants to store data on the system, it can usually only do so in some additional, restricted paths pertaining to the Snap itself. Exceptions can be made for Snaps which have to access data in other places of the file system, but in general you try to limit things as much as possible. It’s called “Confinement” and it’s a basic feature of Ubuntu Core.

The solution to how to ship an immutable package is quite simple: a Snap package is a read-only, compressed SquashFS file system put into a single file. It’s not necessary to extract this file, you can simply mount it read-only to some place in your root file system tree, and that’s exactly what happens:

$ losetup 
/dev/loop0         0      0         0  0 /writable/system-data/var/lib/snapd/snaps/core_888.snap 
/dev/loop1         0      0         1  0 /writable/system-data/var/lib/snapd/snaps/pc-kernel_45.snap 
/dev/loop2         0      0         1  0 /var/lib/snapd/snaps/pc_9.snap 
/dev/loop3         0      0         1  0 /var/lib/snapd/snaps/core_888.snap 
/dev/loop5         0      0         1  0 /var/lib/snapd/snaps/pc-kernel_45.snap

A-ha! All the installed Snaps have been bound to loopback devices. Let’s see where our pc Snap has been mounted to:

$ df -h /dev/loop2 
Filesystem      Size  Used Avail Use% Mounted on 
/dev/loop2      896K  896K     0 100% /snap/pc/9

Well, we could have guessed that. Every Snap is mounted to /snap/${PACKAGE}/${VERSION} (because multiple versions of a Snap can exist side-by-side), and /snap/${PACKAGE}/current points to the currently active snap. Now what’s inside the pc snap?

$ find /snap/pc/9 

Looks like all the files necessary to enable system boot using the GRUB bootloader. It will be interesting to see how all of this fits together in the end. But on to updating and installing packages.

Updating the system

Now let’s finally try to update all our packages from the store.

sturmflut@localhost:~$ snap refresh 
[|] Setup snap "core" (1264) security profiles 
Broadcast message from root@localhost.localdomain (Tue 2017-02-21 18:38:42 UTC): 
reboot scheduled to update the system - temporarily cancel with 'sudo shutdown -c' 
The system is going down for reboot at Tue 2017-02-21 18:48:42 UTC! 
core 16-2 from 'canonical' refreshed

Okay, it updated the core Snap to version 1264 – and scheduled an automatic reboot ten minutes later. I’m not sure if I like that. While playing around I’ve actually run into a situation where snap refresh had to update a long list of packages, core was the first one and the reboot was scheduled immediately, but all the other packages still had to be updated and the Nextcloud Snap took a long time working on its database. Now this was in a (comparably) fast emulator, but imagine it happening on a slow ARM device. Ubuntu Core is transactional, but only in regard to the contents of the Snap packages, not in regards to the data those Snaps produce after installation. Would the Nextcloud database upgrade have been interrupted by the reboot, there would have been a probability of data corruption.

Often there won’t be anything to update though, even if you know there must be something new:

$ snap refresh
All snaps up to date.

Turns out the snapd daemon running in the background and doing all the work gets triggered  by a systemd timer every six hours to update all installed snaps.

$ cat /etc/systemd/system/                           
Description=Timer to automatically refresh installed snaps 
# spread the requests gently 

Notice the configuration introducing a randomized delay of six hours (see the referenced Launchpad bug 1537793), so not all Ubuntu Core instances decide to update at the same time and overload the store.

Installing Snaps

Now let’s install something I can actually use on a shell, since I haven’t gotten any proper graphics yet. snap has a find command to search for snaps.

$ snap find 
Name               Version   Developer   Notes  Summary 
docker             1.11.2-9  docker-inc  -      The docker app deployment mechanism 
lxd                2.8       canonical   -      LXD - the container lightervisor 
mongo32            3.2.7     niemeyer    -      MongoDB document-oriented database 
rocketchat-server  0.51.0    rocketchat  -      Group chat server for 100s,  installed in seconds.

Hmmm, it probably lists the most popular ones, but I don’t wany any of those. snap find game lists more interesting things, but a lot of those look like they need a graphics subsystem, and many need an X11 server, which doesn’t seem to exist on Ubuntu Core at all (it is built to use Mir). Many only work in a “classic” Ubuntu system (there’s a difference between using Snaps on “classic” Ubuntu and running it on Ubuntu Core). I found a game which runs on the command line:

$ snap install moon-buggy 
moon-buggy from 'dholbach' installed

$ moon-buggy

Yay, happy playing!

Installing snapweb

I am going to install the web-based snapweb interface to make package management easier.

$ snap install snapweb
snapweb 0.21.2 from 'canonical' installed

snapweb runs on ports 4200 and 4021, which I haven’t forwarded from QEMU yet. So I have to reboot the virtual machine and extend the QEMU command line:

-netdev user,id=mynet0,hostfwd=tcp::8022-:22,hostfwd=tcp::8090-:80,hostfwd=tcp::8040-:4200,hostfwd=tcp::8041-:4201

After the virtual machine is back up again, I can now access https://localhost:8041/ using a browser. It is necessary to add a security exception because snapweb uses a self-signed certificate, then the following appears:

snapweb doesn’t allow access to just anybody, you have to prove you’re authorized to manage packages on this device. The necessary token can be generated on the command line:

$ sudo snapweb.generate-token
Snapweb Access Token:

## $TOKEN displayed here ##

Use the above token in the Snapweb interface to be granted access.

Once the token is submitted, snapweb switches to the list of installed Snaps.

Since there is no “official” web interface for the store and snap find is a bit of a pain, snapweb jumps into the gap and lets you browse for and install packages from the store.

If you want an even more comfortable way to browse for new Snaps, use uAppExplorer.

Now how about installing something actually useful now, like Nextcloud?

Installing Nextcloud

Installation is as easy as clicking on the “Install” button in snapweb, or one line in the shell:

$ snap install nextcloud
nextcloud 11.0.1snap3 from 'nextcloud' installed

The Nextcould Snap ships a full Nextcloud installation including an Apache HTTP server, a mySQL instance, PHP7, Redis, mDNS and several nextcloud services. The process list is not the shortest:

$ ps ax | grep nextcloud
 3621 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/delay-on-failure mdns-publisher nextcloud
 3647 ?        Sl     0:00 mdns-publisher nextcloud
 3670 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/run-httpd -k start -DFOREGROUND
 3719 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/start_mysql
 3764 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/start-redis-server
 3813 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/start-php-fpm
 3870 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/renew-certs
 3917 ?        Ss     0:00 /bin/sh /snap/nextcloud/862/bin/nextcloud-cron
 4236 ?        S      0:00 /bin/sh /snap/nextcloud/862/bin/mysqld_safe --datadir=/var/snap/nextcloud/862/mysql --pid-file=/var/snap/nextcloud/862/mysql/ --lc-messages-dir=/snap/nextcloud/862/share --socket=/var/snap/nextcloud/862/mysql/mysql.sock
 4406 ?        Sl     0:00 /snap/nextcloud/862/bin/mysqld --basedir=/snap/nextcloud/862 --datadir=/var/snap/nextcloud/862/mysql --plugin-dir=/snap/nextcloud/862/lib/plugin --lc-messages-dir=/snap/nextcloud/862/share --log-error=/var/snap/nextcloud/862/mysql/localhost.localdomain.err --pid-file=/var/snap/nextcloud/862/mysql/ --socket=/var/snap/nextcloud/862/mysql/mysql.sock
 4441 ?        Ss     0:00 php-fpm: master process (/snap/nextcloud/862/config/php/php-fpm.conf)
 4455 ?        S      0:00 /bin/sh /snap/nextcloud/862/bin/httpd-wrapper -k start -DFOREGROUND
 4457 ?        S      0:00 httpd -d /snap/nextcloud/862 -k start -DFOREGROUND
 4458 ?        Sl     0:00 httpd -d /snap/nextcloud/862 -k start -DFOREGROUND
 4459 ?        Sl     0:00 httpd -d /snap/nextcloud/862 -k start -DFOREGROUND
 4460 ?        Sl     0:00 httpd -d /snap/nextcloud/862 -k start -DFOREGROUND

As you can see all services have been configured to use paths, files and sockets within the scope of the Snap itself, so this Nextcloud installation doesn’t need to touch any “outside” data and stays confined. This is nice, since the whole package has been made to work “as is” without requiring the user to e.g. manually connect Nextcloud to a database server. On the other hand it’s a waste of resources in case you already have a mySQL server or a web server, but that’s not what this package was built for.

The bigger problem I see is that the included Apache server just goes and grabs port 80, which might already be in use. snapd would have a solution for this, but the nextcloud snap doesn’t seem to support it (yet):

$ snap set nextcloud apache_port=81

If there are no problems with binding to already assigned ports or something like that, and in my case there aren’t, nextcloud just works as expected. The QEMU command line I’ve given forwards port 80 to host port 8090, so the URL is http://localhost:8090. The default login is user admin with password nextcloud.

The Snap does not only ship the preconfigured servers and the Nextcloud files, but also some utilities, e.g. to automatically enable HTTPS using a certificate issued by Let’s Encrypt.

$ sudo nextcloud.enable-https lets-encrypt

In order for Let's Encrypt to verify that you actually own the
domain(s) for which you're requesting a certificate, there are a
number of requirements of which you need to be aware:

Because of confinement, this Nextcloud package does not have access to removable media by default. If you want it to, you have to “connect the right interfaces” first.

$ snap connect nextcloud:removable-media :removable-media

I’m going to look at interfaces in the next post, I’m going to need them quite a lot when I set up Mir and applications with graphical user interfaces.