On the road to lean infrastructure

Michael Iatrou

Michael Iatrou

on 13 April 2018

On April 24 2008, Ubuntu 8.04 LTS Hardy Heron was released. That was a decade ago, when the modern cloud computing era was dawning: Amazon’s EC2 was still in beta, Google had just released the Google App Engine and the word “container” was dominating the plastics industry rather than IT. A lot has changed since then, but it’s not uncommon to come across organizations with machines still running Hardy or other equally dated distributions.

The Gordian Knot of traditional, pre-DevOps IT infrastructure encompasses meticulously crafted, opportunistically documented and precariously automated “snowflake” environments. Managing such systems induces a slow pace of change, and yet in many cases rip and replace is not a justifiable investment. Invariably though, unabated progress dictates the reconciliation of today’s best practices with the legacy artifacts of the past. Lift and shift can be an efficient, reliable and automated approach to this conundrum.

LXD enables a straightforward path to consolidate services running on old VMs and physical servers, increase infrastructure density, improve resource utilization and performance, and add a layer of security, while keeping familiar operations primitives. Let’s see how easy it is to migrate an Ubuntu 8.04 VM into a LXD container running on a modern, Spectre/Meltdown patched kernel.

Depending on the virtualization platform in use, one may start with a VDI(VirtualBox), VMDK (VMware) or QCOW2 (QEMU/KVM) image. Our VM is currently stopped and it resides in an image named hardy.vmdk that has a single partition. We can extract the contents of the VM filesystem and create the rootfs for the LXD container:

$ sudo apt-get install qemu-utils libguestfs-tools
$ mkdir -p hardy/rootfs
$ sudo virt-copy-out -a hardy.vmdk / hardy/rootfs

There are two formats for LXD container images: (1) a unified tarball, which contains both the container rootfs and the needed metadata. (2) a split format, with two tarballs, one containing the rootfs, the other containing the metadata. We will be producing a unified tarball image here, and all its contents will reside under the hardy directory. The rootfs subdirectory contains the entire file system tree of what will become the container’s /.

We can modify the contents of rootfs to reflect that this is a container:

$ sudo sed -ri 's/^([^#].*)/#\1/g' hardy/rootfs/etc/fstab
$ sudo truncate -s 0 hardy/rootfs/etc/{mtab,blkid.tab}
$ sudo find hardy/rootfs/etc -name 'S??klogd' -type l -exec rm -f {} \;

The container metadata are captured in metadata.yaml and provide information relevant to running the image under LXD. Let’s create it:

$ cd hardy/
$ cat > metadata.yaml << EOF
architecture: "x86_64"
creation_date: 1523424242
properties:
    architecture: "x86_64"
    description: "Ubuntu 8.04 LTS server"
    os: "ubuntu"
    release: "hardy"
templates:
    /etc/hostname:
        when:
            - create
            - copy
        template: hostname.tpl
    /var/lib/cloud/seed/nocloud-net/meta-data:
        when:
            - create
            - copy
        template: cloud-init-meta.tpl
    /var/lib/cloud/seed/nocloud-net/user-data:
        when:
            - create
            - copy
        template: cloud-init-user.tpl
        properties:
            default: |
                #cloud-config
                {}
    /var/lib/cloud/seed/nocloud-net/vendor-data:
        when:
            - create
            - copy
        template: cloud-init-vendor.tpl
        properties:
            default: |
                #cloud-config
                {}
    /etc/network/interfaces.d/eth0.cfg:
        when:
            - create
        template: interfaces.tpl
    /etc/init/console.override:
        when:
            - create
        template: upstart-override.tpl
    /etc/init/tty1.override:
        when:
            - create
        template: upstart-override.tpl
    /etc/init/tty2.override:
        when:
            - create
        template: upstart-override.tpl
    /etc/init/tty3.override:
        when:
            - create
        template: upstart-override.tpl
    /etc/init/tty4.override:
        when:
            - create
        template: upstart-override.tpl
EOF

We also need to create a templates subdirectory, which will contain the pongo2-formatted templates for the container customization during the instantiation time.

$ mkdir templates && cd templates
$ cat << EOF > cloud-init-meta.tpl  
#cloud-config
instance-id: {{ container.name }}
local-hostname: {{ container.name }}
{{ config_get("user.meta-data", "") }}
EOF
$ cat << EOF > cloud-init-user.tpl  
{{ config_get("user.user-data", properties.default) }}
EOF
$ cat << EOF > cloud-init-vendor.tpl  
{{ config_get("user.vendor-data", properties.default) }}
EOF
$ cat << EOF > hostname.tpl  
{{ container.name }}
EOF
$ cat << EOF > interfaces.tpl  
iface eth0 inet {% if config_get("user.network_mode", "") == "link-local" %}manual{% else %}dhcp{% endif %}
EOF
$ cat << EOF > upstart-override.tpl
manual
EOF

Now our hardy directory should look as follows:

$ tree -L 1 hardy
hardy
├── metadata.yaml
├── rootfs
└── templates

2 directories, 1 file

Let’s assemble the container image tarball and import it to our local LXD image
store:

$ cd hardy
$ sudo tar --numeric-owner -zcvSf ../hardy.tar.gz *
$ lxc image import ../hardy.tar.gz --alias  hardy

Finally, we will configure a custom profile, and use it to spin up our new
Ubuntu 8.04 LXD container:

$ lxc profile create hardy
$ lxc profile set hardy environment.TERM linux
$ lxc launch local:hardy h1 -p default -p hardy

The first boot of the container will take a bit longer than usual, as LXD detects and automatically shifts the UID/GID of the rootfs to align with the user namespace.
That was it! Our VM with all its services and data is now running on LXD. The entire v2c conversion process is simple and can be easily automated. Actually, the LXD team has just released their latest LTS version, LXD 3.0, which includes a new tool called lxd-p2c that enables importing a (running) system’s filesystem, physical or virtual, into a LXD container using the LXD API. You can quickly build the tool as follows:

$ sudo apt-get install golang-go
$ mkdir ~/gocode && cd ~/gocode 
$ export GOPATH=~/gocode
$ go get -v -x github.com/lxc/lxd/lxd-p2c

The resulting binary can be found in ~/gocode/bin/lxd-p2c and can be transferred to any system that you want to turn into a container. Point it to a remote LXD server and it will create a new container, using the entire filesystem as rootfs.

LXD containers enable you to intelligently adapt to change. Converting virtual machines (or physical) to LXD containers infuse mobility to your infrastructure, improve resource utilization, add an additional layer of security and enhance performance. Give it a try and join the community.

Ubuntu cloud

Ubuntu offers all the training, software infrastructure, tools, services and support you need for your public and private clouds.

Newsletter signup

Select topics you’re interested in

In submitting this form, I confirm that I have read and agree to Canonical’s Privacy Notice and Privacy Policy.

Related posts

Ubuntu 18.10:Multi-cloud,new desktop theme & enhanced snap integration

London, UK, 18th October 2018; Canonical today announced the release of Ubuntu 18.10, focused on multi-cloud deployments, AI software development, a new community desktop theme and richer snap desktop integration. “Ubuntu is now the…

Firefox ESR 60 availability on Ubuntu

Mozilla Release Engineering created a special customised packaging of the Ubuntu version of Firefox intended for our enterprise partners of Canonical. This is particularly useful if partners decide they need to apply policies to Firefox…

The Road to Kubernetes & vSphere Integration

This article on Kubernetes & vSphere integration originally appeared at Kevin Monroe’s blog Background Recently, Juju began supporting cloud-native features via “integrator” charms (e.g.: aws-integrator, gcp-integrator,…