Mounting Devices in LXD

Posted on Sat 25 January 2020 in Containers

Some use cases for containers depend on passing devices through from the host system into the container. This can allow you to do anything from mount you home directory to passing through a USB device.

Adding Devices in LXD

NIC

Mounting a NIC device on the container allows a host system network interface (physical or logical) to the container. See here for more detail. NOTE! Mounting the NIC to the container will cause the interface to disappear from the host system as it now "belongs" to the container. Make sure before you do this that nothing on your host system is utilizing the device intended to be mounted!

Command

lxc config device add $ctr_name $dev_name nic parent=$parent_iface_name

Example

lxc config device add app1 eth99 nic parent=enp3s0

The above will take the network interface enp3s0 from the host system and attach it to the container app1 as network interface eth99.

Apply the configuration to the container

Disk

The disk device type is probably the most straightforward of the device types and the one I most often use. This allows a directory on the host system to also be mounted in the container itself to either share between containers or to maintain state in the example of an ephemeral container.

Command

lxc config device add $ctr_name $dev_name disk source=/path/to/host/dir path=/path/to/ctr/mount/point

Apply the configuration to the container

Example

To mount a home drive from the host system to the container named app1:

lxc config device add app1 home_directory disk source=/home/eric path=/home/ubuntu

This creates a disk device named home_directory and attaches it to the container app1. The host system directory /home/eric is now available within app1 as /home/ubuntu.

Apply the configuration to the container

UNIX Char

The unix-char device type allows a "character" device to be attached to the container. Character devices are devices that convey data character-by-character rather than block-by-block. Examples of these types of devices are printers, keyboards, terminals, sound cards, etc. I can't think of a use case for any of these, but that doesn't mean there isn't one.

Command

lxc config device add $ctr_name $dev_name unix-char source=/path/to/host/device path=/path/to/container/device

Example

lxc config device add app1 tty42 source=/dev/tty42 path=/dev/tty1

The above will take tty42 from the host system and pass it through to the container app1 as /dev/tty1. Does it make sense to do that? Probably not, but you do you. It's your life. LIVE IT TO THE MAX!

Apply the configuration to the container

UNIX Block

The unix-block device type is similar to disk, but rather than sharing a directory, this is passing a block device to the container. The important distinction here is that the device is a volume rather than a directory within the volume. The device is added to the /dev directory on the container to be mounted within the container as appropriate.

Command

lxc config device add $ctr_name $dev_name unix-block source=/path/to/host/device path=/path/to/container/device

Example

lxc config device add app1 sdc5 unix-block source=/dev/sdc5 path=/dev/sda1

The above will take the unmounted volume /dev/sdc5 on the host system and add to the app1 container as /dev/sda1.

Apply the configuration to the container

USB

The usb device type is pretty self-explanitory: it takes a physical USB device and passes it through to the container rather than using it locally on the host system. The neat thing about usb devices is that they support hotplugging by default; if the device is removed and reattached at a later point, the device will automatically be attached to the container. Neat! NOTE! If the USB device uses a kernel driver, it will be more efficient to locate the device added in /dev and attach it as a unix-block or unix-char device as appropriate.

Commmand

lxc config device add $ctr_name $dev_name usb vendor_id=$vendorid product_id=$productid

Example

Before looking at an example command, we're going to have to do some homework to look up values to pass to the command. To find the appropriate values, let's take a look at what USB devices I have plugged in at the moment:

host# lsusb
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 003: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 005 Device 002: ID 045e:028e Microsoft Corp. Xbox360 Controller
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

Not a lot that would make sense to pass through to a container, but let's say for some reason I want to pass my XBOX controller through to the container. The command would look something like this:

lxc config device add app1 xbox usb vendor_id=045e product_id=028e

This creates a device called xbox and attaches to the container app1 if the product has the vendor_id 045e and the product_id 028e. These vaules are pulled from the ID in the above lsusb output where the number before the colon is the vendor and the number following the colon is the product.

Apply the configuration to the container

GPU

The gpu device type passes a graphic card through to the container in the event that some heavy computation is required (think cryptocurrency mining or participating in SETI or folding@home).

Command

lxc config device add $ctr_name $dev_name gpu vendorid=$vendorid productid=$productid

Example

Let's start by finding the vendor and product IDs of the GPU we want to pass through:

root@host# lspci -nn | grep -i vga
0f:00.0 VGA compatible controller [0300]: NVIDIA Corporation GF100GL [Quadro 4000] [10de:06dd] (rev a3)

Now let's configure attach the physical device to the container:

root@host# lxc config device add app1 quadro gpu vendorid=10de productid=06dd

This creates a device called quadro and attaches to the container app1. I identifies the physical device via the vendor ID 10de (NVIDIA) and the productid 06dd.

Apply the configuration to the container

Infiniband

If you have a use-case for connecting infiniband devices to containers, you're probably smarter than me so I won't bother trying to mansplain.

Proxy

Proxy devices are a neat option to allow the container to bind to a port on the host systems. For example, if a container is running NGINX but the LXD server is using the built-in bridge, the container can bind the host system's port 80/443 directly to 80/443 on the container itself. Prior to the addition of the proxy device type, this type of setup required manually configuring iptables to perform a destination NAT to re-write the IP header; this feature will actually bind to the IP and port rather than mangle the incoming packet. This is a feature that has been available in Docker for a long time and is a welcome addition.

Command

lxc config device add $ctr_name $devname proxy connect=$host_ip:$host_port listen=$ctr_ip:$ctr_port

Example

lxc config device add nxing1 tcp_443 proxy connect=0.0.0.0:443 listen=0.0.0.0:443

The above example adds a proxy device named tcp_443 to the container nginx1. The container is listening on all ports to TCP port 443, and binds to all IP addresses on the host system.

This can be verified with the following:

host# sudo ss state listening sport = :443
Netid              Recv-Q               Send-Q                               Local Address:Port                                Peer Address:Port
tcp                0                    128                                               *:443                                           *:*

Apply the configuration to the container

Removing Devices from a Container

lxc config device remove $ctr_name $devname

Apply the configuration to the container

Verifying Device Configuration for a Container

lxc config device show $ctr_name

Apply Configuration Changes to Container

lxc restart $ctr_name