Isolating development environments using LXD containers

I will discuss the approach to organizing local isolated development environments on my workstation. This 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 involves conducting development within LXD containers running locally on a laptop or workstation with graphical output redirected to the host.

Configuration example Ubuntu 20.04.

Reflections on alternatives and reasons are provided at the end of the article.

1. Installing LXD

In Ubuntu 20.04 LXD is no longer available for installation as a deb package, only via snap:

$ snap install lxd

After installation, initialization must be performed:

$ lxd init

The only parameter I change is storage backend — I use dir as the simplest. As I do not use snapshots and copies, the warnings in the documentation do not 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. Configuring the LXD profile

Profiles in LXD — are sets of parameters applied to multiple containers. For my needs, a single default profile created is sufficient default with the following modifications:

  • $ 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 — so that the environment variable DISPLAY in containers is set correctly;
  • $ lxc profile set default raw.idmap "both 1000 1000" — for proper ID mapping..

3. Creating and configuring the container

Creating a container based on the image images:ubuntu/20.04:

$ lxc launch images:ubuntu/20.04 dev1

I prefer images from the repository https://images.linuxcontainers.org, as they have less pre-installed software. For this reason, I added a prefix images: to the image name. 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

I will install Firefox and VS Code (from the 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 start the container for visibility

poweroff

Bonus! It’s quite simple to pass through the GPU to the container so that applications running in it can take advantage of the graphics card. To do this, you need to:

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

4. Using the container

If the container is not yet running, you need to start it:

lxc start dev1

Starting VS Code as a non-privileged 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 run inside the container — similar to graphics forwarding using ssh.

I don’t manually shut down running containers, as I don’t see much point in it — I just close the windows of the running applications.

5. Conclusion

I prefer not to use the host OS for development, as it would require installing development tools, debug versions of libraries, configuring system components in specific ways, and other manipulations. All this can lead to unexpected behavior from other software that is not related to development, or even the entire OS. For example, changes to the OpenSSL configuration can cause the OS to fail to boot correctly.

I have tried different tools for isolating development environments:

  • virtual machines (KVM, VirtualBox, etc.) — the most obvious option, but consumes significantly more resources, though for development on Windows (if the host is Linux) there are no other options;
  • cloud development tools running on a local machine (Cloud9 in a container or virtual machine, Eclipse Che, etc.) — they are not designed for this mode of operation, requiring additional configuration and maintenance; it's best to use them for their intended purpose — in the cloud;
  • Docker containers are intended for different purposes; in my opinion, they aren't very convenient for quickly prototyping with software that hasn't been packaged into separate containers yet.

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

It's important to remember that in terms of isolation, containerization has a larger attack surface compared to virtualization — the host and containers share a single kernel, and a vulnerability in it could allow malicious software to escape the container. For experiments with dubious software, it's better to use more suitable isolation mechanisms.

Useful links

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster