Introduction to LXD projects

Key Value
Summary An introduction to the projects feature in LXD
Categories containers
Difficulty 3
Author Thomas Parrott thomas.parrott@canonical.com

Overview

Duration: 2:00

LXD is a container hypervisor providing a REST API to manage LXC containers. It provides a virtual machine like experience without incurring the overhead of a traditional hypervisor.

However when you are managing lots of containers providing different services, it can become confusing to see which containers are dependent on each other.

The projects feature is designed to help in this situation by providing the ability to group one or more containers together into related “projects” that can be used with the lxc tool.

This tutorial will show how to create LXD projects, add containers into a project and explore the features that can be specified at a project level.

Requirements

  • LXD snap installed and running (although we will cover this briefly if not)
  • You should know how to create and launch a LXD container

Originally authored by Thomas Parrott

Installing LXD Snap

Duration: 3:00

We will need LXD installed and running before we can use it to create a project.

The easiest way to stay up to date with LXD is to use the Snap package.

We can install LXD using Snap as follows:

snap install lxd

If you are running Ubuntu 18.04 LTS or earlier then you may already have LXD installed as an apt package.

You can migrate your containers to the newer Snap package and remove the old packages by running:

lxd.migrate -yes

If this is the first time you have used LXD, then you also need to initialise it.

ⓘ You can run lxd init without the --auto flag if you want to answer a series of questions about how it should be setup. Otherwise if you want to accept the defaults then lxd init --auto will do.

lxd init --auto

Lets check that LXD is working by running:

lxc ls

If you don’t get any errors, then we can move onto the next step!

ⓘ Note: If you already had LXD installed you may need to logout and log back in again for the lxc command to recognise the new Snap package.

Creating our first project

Duration: 3:00

Imagine we have a scenario where we are setting up a web site for a particular client. We want to create two containers; one for the web service and one for the database service. Because we have lots of different web sites for different clients we want to ensure that we can quickly see and manage all of the containers for this particular web site.

Projects help us in this situation because we can create a project called client-website and add the containers to it that are related to providing the client’s web site.

lxc project create client-website -c features.images=false -c features.profiles=false

In the above command we have passed the -c features.images=false and -c features.profiles=false flags so that our new project will still use the images and profiles of the default project and will not namespace them (we’ll come onto that some more later in the tutorial).

Now we switch the lxc tool into that project.

lxc project switch client-website

Let’s check that we have switched to the new empty project:

lxc ls

We would expect to see an empty list at this point.

Creating containers in our project

Duration: 2:00

Now that we have an empty project, we can go ahead and create our two containers (the web server and the database server).

lxc launch ubuntu:18.04 webserver
lxc launch ubuntu:18.04 dbserver

Lets check we now have those containers created:

lxc ls

We should expect to see output similar to this:

+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
|   NAME    |  STATE  |         IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
| dbserver  | RUNNING | 10.140.78.128 (eth0) | fd42:bc8c:c2b1:32fa:216:3eff:feb8:2bec (eth0) | PERSISTENT |           |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
| webserver | RUNNING | 10.140.78.202 (eth0) | fd42:bc8c:c2b1:32fa:216:3eff:fea5:e8a9 (eth0) | PERSISTENT |           |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+

Listing projects

Duration: 02:00

Sometimes you don’t know which project you want to work in, so LXD allows you to list the projects we have.

Lets list our projects so that we can see our client-website project in relation to the default project:

lxc project ls

You should see output that looks like this:

+--------------------------+--------+----------+---------+
|           NAME           | IMAGES | PROFILES | USED BY |
+--------------------------+--------+----------+---------+
| client-website (current) | NO     | NO       | 2       |
+--------------------------+--------+----------+---------+
| default                  | YES    | YES      | 4       |
+--------------------------+--------+----------+---------+

ⓘ You can see the current project you are switched to as it has (current) after its name.
The default project is the project that LXD uses normally for resources (containers, profiles, images) that don’t belong to any other project.

Listing containers in a project

Duration: 5:00

At this point lets reflect on how our project behaves in relation to the other projects by experimenting with ways we can list the containers within them.

Let’s switch back to the default project (this is the project that LXD uses normally).

lxc project switch default

To prove that we can create a container of the same name as one in another project, lets create another webserver container:

lxc launch ubuntu:18.04 webserver

Now lets list our containers in the current default project:

lxc ls

You should expect to see a list of all of your existing containers, including the webserver we just created. Importantly, however, the list should not contain the two client-website containers we created earlier.

+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
|   NAME    |  STATE  |         IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
| tutorials | RUNNING | 172.17.0.1 (docker0) | fd42:bc8c:c2b1:32fa:216:3eff:fec4:1b34 (eth0) | PERSISTENT |           |
|           |         | 10.140.78.67 (eth0)  |                                               |            |           |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
| webserver | RUNNING | 10.140.78.34 (eth0)  | fd42:bc8c:c2b1:32fa:216:3eff:fea9:5ea (eth0)  | PERSISTENT |           |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+

Now lets list the containers in our client-website project, but without switching into it.

lxc ls --project client-website

This should give you the output:

+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
|   NAME    |  STATE  |         IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
| dbserver  | RUNNING | 10.140.78.128 (eth0) | fd42:bc8c:c2b1:32fa:216:3eff:feb8:2bec (eth0) | PERSISTENT |           |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+
| webserver | RUNNING | 10.140.78.202 (eth0) | fd42:bc8c:c2b1:32fa:216:3eff:fea5:e8a9 (eth0) | PERSISTENT |           |
+-----------+---------+----------------------+-----------------------------------------------+------------+-----------+

You can see that we now have 2 containers called webserver, but with different IP addresses and inside different projects.

Creating further isolated projects

Duration: 5:00

So far we have created a project that allowed containers of the same name to be created in different projects.

However both the default and client-website projects shared the same profiles and image libraries.

This means that the the default profile in client-website project is the same as the default profile in the default project.

But there are scenarios where we may want to further isolate a project such that profile and image names are not shared beyond it.

A new client project

Lets imagine we have now taken on a new client and we need to create a new project for them. However this client requires a different network configuration than the previous client required.

We could create a new global profile called client2-default with the required networking configuration and then assign that profile to the containers in this new project.

However this would mean that the client2-default profile would be accessible to all containers that shared the default project (including our previously created client-website project). This would be confusing.

Instead, lets create a new project called client2-website, but this time specify that it should not share the default project’s profiles and image library.

lxc project create client2-website -c features.images=true -c features.profiles=true

This is equivalent to running just:

lxc project create client2-website

Now lets create a container in the new project:

lxc launch ubuntu:18.04 webserver --project client2-website

ⓘ Note: We are using the --project flag on the lxc launch command to save switching into the new project.

Oops! Something went wrong though, you will get this output:

Creating webserver
Error: Failed container creation: Create container: Create LXC container: Invalid devices: Detect root disk device: No root device could be found

Continue to the next step to find out what is going wrong!

Setting up a new project profile

Duration: 5:00

In the last step we created a new isolated project and then tried to create a new container inside it. Unfortunately we were unable to because we got a No root device could be found error. This is because the default profile within the new project has not been configured yet.

Lets do so now…

Comparing profiles

First, we can compare the default profile from our default project:

lxc profile show default --project default
config: {}
description: Default LXD profile
devices:
  eth0:
    name: eth0
    nictype: bridged
    parent: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: default
used_by:
- /1.0/containers/tutorials
- /1.0/containers/webserver?project=client-website
- /1.0/containers/dbserver?project=client-website
- /1.0/containers/webserver

With the default profile in the client2-website project:

lxc profile show default --project client2-website
config: {}
description: Default LXD profile for project client2-website
devices: {}
name: default
used_by: []

We can see that the missing configuration is for the devices section, specifically the nic and disk devices.

Creating the disk device

Lets add a root disk device sharing the global default storage pool:

lxc profile device add default root disk path=/ pool=default --project client2-website

Creating the nic device

Next we add an eth0 nic device to the default profile:

lxc profile device add default eth0 nic name=eth0 nictype=p2p --project client2-website

ⓘ Note: We have changed the networking config for this nic device to be p2p rather than bridge to demonstrate the ability to have different default profiles in separate projects.

Create the containers

Now lets try again to create the containers for this project:

lxc launch ubuntu:18.04 webserver --project client2-website
lxc launch ubuntu:18.04 dbserver --project client2-website

And check they are created:

lxc ls --project client2-website
+-----------+---------+------+------+------------+-----------+
|   NAME    |  STATE  | IPV4 | IPV6 |    TYPE    | SNAPSHOTS |
+-----------+---------+------+------+------------+-----------+
| dbserver  | RUNNING |      |      | PERSISTENT |           |
+-----------+---------+------+------+------------+-----------+
| webserver | RUNNING |      |      | PERSISTENT |           |
+-----------+---------+------+------+------------+-----------+

Moving containers between projects

Duration: 03:00

So far we have seen how containers can be grouped together and isolated to varying levels.

The last feature we can explore is how to move a container from one project to another.

Lets move the dbserver from client-website project into the client2-website project.

lxc move dbserver dbserver --project client-website --target-project client2-website

But we have got an error again:

Error: Add container info to the database: This container already exists

This is because you cannot have 2 containers with the same name existing in the same project, and we already have a container called dbserver in project client2-website.

There are two courses of action here; either delete the existing dbserver container, or move the container into the project using a different name.

We’ll choose the latter option in this tutorial.

lxc move dbserver dbserver2 --project client-website --target-project client2-website

Now lets confirm that the container has been moved:

lxc ls --project client2-website
+-----------+---------+------+------+------------+-----------+
|   NAME    |  STATE  | IPV4 | IPV6 |    TYPE    | SNAPSHOTS |
+-----------+---------+------+------+------------+-----------+
| dbserver  | RUNNING |      |      | PERSISTENT |           |
+-----------+---------+------+------+------------+-----------+
| dbserver2 | STOPPED |      |      | PERSISTENT |           |
+-----------+---------+------+------+------------+-----------+
| webserver | RUNNING |      |      | PERSISTENT |           |
+-----------+---------+------+------+------------+-----------+

Clearing up

Duration: 03:00

As a final task, lets clean up the containers and projects we created.

You cannot remove a project that is in use, so we need to remove the containers and images first.

lxc delete dbserver -f --project client2-website
lxc delete dbserver2 -f --project client2-website
lxc delete webserver -f --project client2-website

We also need to delete any images created in the project:

lxc image list --project client2-website

For each image do:

lxc image delete <fingerprint> --project client2-website

Then delete the project

lxc project delete client2-website

Repeat these steps for the client-website project.

That’s all!

Now we have seen how to create LXD projects and use them to group containers.

If you’d like to know more about LXD, take a look at the following resources:

Also, if you have questions or need help, you can find direct help here:

Would it be useful to add the set up work and questions from lxd init?

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 empty disk or partition? (yes/no) [default=no]:
Size in GB of the new loop device (1GB minimum) [default=30GB]:
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]:
What should the new bridge be called? [default=lxdbr0]:
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
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]:

Hi, I am following this on a fresh installed Ubuntu 22.04 server ( on a VM) with snap lxd 5.0.1.

Not sure if I should cross post on the main lxd forum - but I will try it here first.

When running the command:

lxc move dbserver dbserver2 --project client-website --target-project client2-website

I get this error:
Error: Migration operation failure: Unable to perform container live migration. CRIU isn´t installed.

I have then:
sudo snap set lxd criu.enable=true
sudo snap restart lxd

But no luck. I get this error:

lxc move dbserver dbserver2 --project client-website --target-project client2-website
Error: Migration operation failure: snapshot dump failed
(00.004488) Error (criu/namespaces.c:463): Can’t dump nested uts namespace for 1443
(00.004490) Error (criu/namespaces.c:724): Can’t make utsns id
(00.005484) Error (criu/util.c:641): exited, status=1
(00.006541) Error (criu/util.c:641): exited, status=1
(00.006741) Error (criu/cr-dump.c:2053): Dumping FAILED.

Not sure if it is a bug on lxd or a missing configuration that needs to be addressed on the tutorial.

Thanks in advance.