Isolate Development Environments with LXD Containers

I will talk about the approach to organizing local isolated development environments on my workstation. The approach was developed under the influence of the following factors:

  • different languages ​​require different IDEs and toolchains;
  • Different projects may use different versions of toolchains and libraries.

The approach is to develop inside LXD containers running locally on a laptop or workstation with graphics output redirected to the host.

Example configuration Ubuntu 20.04.

Reflections on options and reasons are given at the end of the article.

1. Install LXD

Π’ Ubuntu 20.04 LXD is no longer available as a deb package, only via snap:

$ snap install lxd

After installation, you need to initialize:

$ lxd init

The only setting I change is storage bakend - I use dir as the simplest. Since I do not use snapshots and copies, the warnings in documentation don't scare me:

Similarly, the directory backend is to be considered as a last resort option.
It does support all main LXD features, but is terribly slow and inefficient as it can't perform
instant copies or snapshots and so needs to copy the entirety of the instance's storage every time.

2. LXD profile setup

Profiles in LXD are sets of parameters applied to multiple containers. For my needs, a single default profile is enough for me default with the following changes:

  • $ lxc profile device add default X0 disk source=/tmp/.X11-unix/X0 path=/tmp/.X11-unix/X0 - so that applications in containers can interact with the host X11 server;
  • $ lxc profile set default environment.DISPLAY :0 - to environment variable DISPLAY in containers was installed correctly;
  • $ lxc profile set default raw.idmap "both 1000 1000" - for correct ID mapping.

3. Creating and configuring a container

Create a container from an image images:ubuntu/20.04:

$ lxc launch images:ubuntu/20.04 dev1

I prefer images from the repository https://images.linuxcontainers.org, since they have less pre-installed software. For this reason, I added the prefix images: to the name of the image. Creating a container based on an image from the Ubuntu repository can be done as follows: $ lxc launch ubuntu/20.04 dev1.

Access to the root shell of the container:

$ lxc exec dev1 -- bash

Install Firefox and VS Code (from repository according to the instructions):

$ apt update
$ apt install curl gpg firefox

$ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
$ install -o root -g root -m 644 packages.microsoft.gpg /usr/share/keyrings/
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list

$ apt update
$ apt install code

I will include a container for clarity

poweroff

Bonus! It is quite simple to forward the GPU to the container so that the applications running in it can use the graphics card. For this you need:

  • add device $ lxc config device add dev1 mygpu gpu;
  • install video card drivers in the container - the same ones that are installed on the host.

4. Using the container

In the event that the container is not yet running, you need to start it:

lxc start dev1

Running VS Code as a non-root user ubuntu:

lxc exec dev1 -- sudo --login --user ubuntu code

Starting Firefox:

lxc exec dev1 -- sudo --login --user ubuntu firefox

Application windows will be displayed on the host, but they will be executed inside the container - similar to forwarding graphics using ssh.

I do not turn off running containers manually, because I don’t see much point in this - I limit myself to closing the windows of running applications.

5. Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

I prefer not to use the host OS for development, as this would require installing development tools, debug versions of libraries, configuring system components in a specific way, and other manipulations. All this can lead to unexpected behavior of the rest, not related to development, software, or even the entire OS. For example, changes in the OpenSSL configuration can cause the OS to stop starting correctly.

I have tried different tools for isolating development environments:

  • virtual machines (KVM, VirtualBox, etc.) - the most obvious option, but consumes significantly more resources, although there are no other options for developing under Windows (if the host is Linux);
  • cloud development tools running on a local machine (Cloud9 in a container or virtual machine, Eclipse Che, etc.) - they are not developed for this mode of operation, they require additional configuration and maintenance, it is best to use them for their intended purpose - in the cloud;
  • Docker containers - again, they are designed for something else, in my opinion it is not very convenient to quickly prototype them using software that has not yet been packaged in separate containers.

The chosen approach impresses me with its simplicity and low entry threshold. In the containers themselves, you can use project-specific approaches: install and configure everything manually, or use automation (Puppet, Ansible, etc.), even deploy infrastructure based on Docker. I also use LXD containers to run specific software that either requires a large number of dependencies to be installed, or a different OS version - in this case, you can create a container with the desired OS version, for example $ lxc launch images:ubuntu/16.04 dev16.

It is important to remember that in terms of isolation, containerization has a larger attack surface compared to virtualization - the host and container share the same core, a vulnerability in which could allow malware to escape from the container. For experiments with questionable software, it is better to use more appropriate isolation mechanisms.

Useful links

Source: habr.com

Add a comment