Skip to main content

Excursion into LXC containers

lxc1.png

As I like to live on the bleeding edge by using Debian testing (currently Bookworm), from time to time I run into situations that are not ease to solve from within the distribution. For example currently its impossible to install the yarn package manager:

dzu@krikkit:~$ sudo apt install yarnpkg
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
 node-esprima : Depends: nodejs:any
E: Unable to correct problems, you have held broken packages.
dzu@krikkit:~$ 

As I do not see an easy way to fix this and as I am pretty sure that this will work in Debian stable, let's try spinning up an LXC Debian Bullseye container and work from there instead. The ultimate goal for today is to locally build and view the GNOME Javascript guide from a git clone to locally test modifications.

Creating a Bullseye container

Trying to get started with LXC, you are confronted with the choice of using the "old-style" lxc command line tools or the more modern LXD wrapper (built mainly by Canonical) on top of liblxc and Go bindings of the tooling. As Debian still does not package LXD and as I am also not a fan of the (Canonical driven) snap ecosystem, we will use the old-style command line tools. As we will see, they are sometimes verbose but get the job done.

Also for this short excursion, we will simply use a privileged container to make our life easier. The intention of the container is not to sandbox things but to use a different GNU/Linux distribution without resorting to a full blown virtual machine.

After several cycles between failed attempts and reading the error manages, I finally managed to download the base system like this:

dzu@krikkit:~$ sudo lxc-create --name bullseye --template download -- --dist debian --release bullseye --arch amd64
Setting up the GPG keyring
Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs

---
You just created a Debian bullseye amd64 (20211017_05:24) container.

To enable SSH, run: apt install openssh-server
No default root or user password are set by LXC.
dzu@krikkit:~$

That's all there is to it - it takes less than a minute and we are left with only a few finishing touches to make things work:

  • Start the container
  • Attach to it
  • Assign proper root password
  • Create user account matching the host system
  • Add sudo rights to that user

Be sure to verify the id of the user in the container coincides with your real id as privileged containers do not do any id mapping on file systems and we want to share work directories between the host and the container later on:

dzu@krikkit:~$ sudo lxc-start bullseye
dzu@krikkit:~$ sudo lxc-attach bullseye
# passwd
New password: 
Retype new password: 
passwd: password updated successfully
# apt install openssh-server
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  libwrap0 ncurses-term openssh-sftp-server runit-helper sensible-utils ucf
Suggested packages:
  molly-guard monkeysphere ssh-askpass ufw
The following NEW packages will be installed:
  libwrap0 ncurses-term openssh-server openssh-sftp-server runit-helper sensible-utils ucf
0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded.
Need to get 1098 kB of archives.
After this operation, 6446 kB of additional disk space will be used.
Do you want to continue? [Y/n] 
Get:1 http://deb.debian.org/debian bullseye/main amd64 sensible-utils all 0.0.14 [14.8 kB]
Get:2 http://deb.debian.org/debian bullseye/main amd64 ncurses-term all 6.2+20201114-2 [504 kB]
Get:3 http://deb.debian.org/debian bullseye/main amd64 ucf all 3.0043 [74.0 kB]
Get:4 http://deb.debian.org/debian bullseye/main amd64 libwrap0 amd64 7.6.q-31 [59.0 kB]
Get:5 http://deb.debian.org/debian bullseye/main amd64 openssh-sftp-server amd64 1:8.4p1-5 [52.3 kB]
Get:6 http://deb.debian.org/debian bullseye/main amd64 runit-helper all 2.10.3 [7808 B]
Get:7 http://deb.debian.org/debian bullseye/main amd64 openssh-server amd64 1:8.4p1-5 [385 kB]
Fetched 1098 kB in 0s (4423 kB/s)
debconf: delaying package configuration, since apt-utils is not installed
Selecting previously unselected package sensible-utils.
(Reading database ... 11577 files and directories currently installed.)
Preparing to unpack .../0-sensible-utils_0.0.14_all.deb ...
Unpacking sensible-utils (0.0.14) ...
Selecting previously unselected package ncurses-term.
Preparing to unpack .../1-ncurses-term_6.2+20201114-2_all.deb ...
Unpacking ncurses-term (6.2+20201114-2) ...
Selecting previously unselected package ucf.
Preparing to unpack .../2-ucf_3.0043_all.deb ...
Moving old data out of the way
Unpacking ucf (3.0043) ...
Selecting previously unselected package libwrap0:amd64.
Preparing to unpack .../3-libwrap0_7.6.q-31_amd64.deb ...
Unpacking libwrap0:amd64 (7.6.q-31) ...
Selecting previously unselected package openssh-sftp-server.
Preparing to unpack .../4-openssh-sftp-server_1%3a8.4p1-5_amd64.deb ...
Unpacking openssh-sftp-server (1:8.4p1-5) ...
Selecting previously unselected package runit-helper.
Preparing to unpack .../5-runit-helper_2.10.3_all.deb ...
Unpacking runit-helper (2.10.3) ...
Selecting previously unselected package openssh-server.
Preparing to unpack .../6-openssh-server_1%3a8.4p1-5_amd64.deb ...
Unpacking openssh-server (1:8.4p1-5) ...
Setting up runit-helper (2.10.3) ...
Setting up openssh-sftp-server (1:8.4p1-5) ...
Setting up libwrap0:amd64 (7.6.q-31) ...
Setting up sensible-utils (0.0.14) ...
Setting up ncurses-term (6.2+20201114-2) ...
Setting up ucf (3.0043) ...
Setting up openssh-server (1:8.4p1-5) ...

Creating config file /etc/ssh/sshd_config with new version
Creating SSH2 RSA key; this may take some time ...
3072 SHA256:y2rtvIyjYNTdfSAmHfU171aXULzSRl/TTT6sP+z2eHQ root@bullseye (RSA)
Creating SSH2 ECDSA key; this may take some time ...
256 SHA256:wSl8DnbMWlqXFEufzDavobyJ5z/TcfAjljsXNUGFTuI root@bullseye (ECDSA)
Creating SSH2 ED25519 key; this may take some time ...
256 SHA256:GZDge+Bat/LdnzMnH5XlTA8Wgh3GSus0C5zrnWwe5rs root@bullseye (ED25519)
Created symlink /etc/systemd/system/sshd.service → /lib/systemd/system/ssh.service.
Created symlink /etc/systemd/system/multi-user.target.wants/ssh.service → /lib/systemd/system/ssh.service.
rescue-ssh.target is a disabled or a static unit, not starting it.
Processing triggers for libc-bin (2.31-13+deb11u2) ...
# adduser dzu
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LANGUAGE = (unset),
	LC_ALL = (unset),
	LANG = "de_DE.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Adding user `dzu' ...
Adding new group `dzu' (1000) ...
Adding new user `dzu' (1000) with group `dzu' ...
Creating home directory `/home/dzu' ...
Copying files from `/etc/skel' ...
New password: 
Retype new password: 
passwd: password updated successfully
Changing the user information for dzu
Enter the new value, or press ENTER for the default
	Full Name []: Detlev Zundel
	Room Number []: 
	Work Phone []: 
	Home Phone []: 
	Other []: 
Is the information correct? [Y/n] 
# adduser dzu sudo
Adding user `dzu' to group `sudo' ...
Adding user dzu to group sudo
Done.
#

Setting up directory sharing

Now that we have created the container we will add a statement to the configuration so that we can access our git repositories also from within the container. On my machine those repositories live below /opt/src/git so we will share this path. The diff shows what needs to be changed in the (superuser owned) configuration file:

root@krikkit:/var/lib/lxc/bullseye# diff --color -u config{.ORIG,}
--- config.ORIG	2021-10-22 15:46:06.000000000 +0200
+++ config	2021-10-22 15:51:40.372003465 +0200
@@ -21,3 +21,6 @@
 lxc.net.0.type = veth
 lxc.net.0.link = lxcbr0
 lxc.net.0.flags = up
+
+# Dzu
+lxc.mount.entry = /opt/src/git opt/src/git none bind,optional,create=dir
root@krikkit:/var/lib/lxc/bullseye# 

Attaching to the prepared container

Once the container is prepared, I use a small script to start it and connect via ssh to it:

#!/bin/sh

usage() {
    echo "usage: `basename $0` <container> ..." >&2
    echo "        Starts <container>, waits for it to come up and ssh's in" >&2
    echo "        When ssh exits, the container will be stopped again" >&2
    exit 1
}

if [ $# -lt 1 ]; then
    usage
fi

sudo lxc-start $1
echo "Waiting for container to come up ..." ; sleep 5
ip=`sudo lxc-info -i $1 | awk '{print $2}'`
echo Container has IP address $ip
ssh -Y $ip
sudo lxc-stop $1

With this in place, we can proceed with installing yarnpkg:

dzu@krikkit:~$ ssh-lxc bullseye
Waiting for container to come up ...
Container has IP address 10.0.3.228
dzu@10.0.3.228's password: 
Linux bullseye 5.14.0-2-amd64 #1 SMP Debian 5.14.9-2 (2021-10-03) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Fri Oct 22 20:32:47 2021 from 10.0.3.1
dzu@bullseye:~$ sudo apt update && sudo apt install yarnpkg
[sudo] password for dzu: 
Hit:1 http://deb.debian.org/debian bullseye InRelease
Get:2 http://security.debian.org bullseye-security InRelease [44.1 kB]
Fetched 44.1 kB in 0s (130 kB/s)    
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  javascript-common libbrotli1 libc-ares2 libicu67 libjs-highlight.js libjs-inherits libjs-is-typedarray
  libjs-psl libjs-source-map libjs-sprintf-js libnghttp2-14 libnode72 libuv1 node-ajv node-ansi-escapes
  node-ansi-regex node-ansi-styles node-anymatch node-argparse node-array-find-index node-arrify node-asap
  node-asn1 node-assert-plus node-asynckit node-aws-sign2 node-aws4 node-babel7-runtime node-balanced-match
  node-bcrypt-pbkdf node-big.js node-bl node-brace-expansion node-braces node-builtin-modules node-bytes
  node-camelcase node-caseless node-chalk node-chownr node-ci-info node-cli-cursor node-cli-table
  node-cli-width node-clone node-color-convert node-color-name node-colors node-combined-stream
  node-commander node-core-util-is node-currently-unhandled node-dashdash node-death node-debug
  node-deep-equal node-defaults node-delayed-stream node-detect-indent node-duplexify node-ecc-jsbn
  node-emoji node-emojis-list node-end-of-stream node-escape-string-regexp node-esprima node-exit-hook
  node-extend node-external-editor node-extsprintf node-fast-deep-equal node-fast-levenshtein
  node-fill-range node-forever-agent node-form-data node-fs.realpath node-function-bind node-getpass
  node-glob node-graceful-fs node-har-schema node-har-validator node-has-flag node-http-signature
  node-iconv-lite node-imports-loader node-inflight node-inherits node-ini node-inquirer node-invariant
  node-is-buffer node-is-builtin-module node-is-number node-is-plain-obj node-is-promise node-is-typedarray
  node-isarray node-isstream node-js-tokens node-js-yaml node-jsbn node-jschardet node-json-schema
  node-json-schema-traverse node-json-stable-stringify node-json-stringify-safe node-json5 node-jsonify
  node-jsprim node-kind-of node-loader-utils node-lodash node-lodash-packages node-loose-envify
  node-loud-rejection node-lru-cache node-micromatch node-mime node-mime-types node-minimatch node-minimist
  node-mkdirp node-ms node-mute-stream node-normalize-path node-oauth-sign node-object-assign
  node-object-path node-once node-path-is-absolute node-path-root node-path-root-regex node-performance-now
  node-prepend-http node-process-nextick-args node-proper-lockfile node-psl node-puka node-pump node-pumpify
  node-punycode node-qs node-read node-readable-stream node-regenerator-runtime node-repeat-string
  node-request node-request-capture-har node-resolve node-restore-cursor node-retry node-rimraf
  node-run-async node-rx node-safe-buffer node-semver node-signal-exit node-sort-keys node-source-map
  node-spdx-correct node-spdx-exceptions node-spdx-expression-parse node-spdx-license-ids node-sprintf-js
  node-sshpk node-ssri node-stream-shift node-strict-uri-encode node-string-decoder node-string-width
  node-strip-ansi node-strip-bom node-supports-color node-tar-stream node-through2 node-tmp
  node-to-regex-range node-tough-cookie node-tunnel-agent node-tweetnacl node-universalify node-uri-js
  node-util-deprecate node-uuid node-validate-npm-package-license node-verror node-wcwidth.js node-wrappy
  node-yallist node-yn nodejs nodejs-doc
Suggested packages:
  apache2 | lighttpd | httpd libjs-angularjs npm
The following NEW packages will be installed:
  javascript-common libbrotli1 libc-ares2 libicu67 libjs-highlight.js libjs-inherits libjs-is-typedarray
  libjs-psl libjs-source-map libjs-sprintf-js libnghttp2-14 libnode72 libuv1 node-ajv node-ansi-escapes
  node-ansi-regex node-ansi-styles node-anymatch node-argparse node-array-find-index node-arrify node-asap
  node-asn1 node-assert-plus node-asynckit node-aws-sign2 node-aws4 node-babel7-runtime node-balanced-match
  node-bcrypt-pbkdf node-big.js node-bl node-brace-expansion node-braces node-builtin-modules node-bytes
  node-camelcase node-caseless node-chalk node-chownr node-ci-info node-cli-cursor node-cli-table
  node-cli-width node-clone node-color-convert node-color-name node-colors node-combined-stream
  node-commander node-core-util-is node-currently-unhandled node-dashdash node-death node-debug
  node-deep-equal node-defaults node-delayed-stream node-detect-indent node-duplexify node-ecc-jsbn
  node-emoji node-emojis-list node-end-of-stream node-escape-string-regexp node-esprima node-exit-hook
  node-extend node-external-editor node-extsprintf node-fast-deep-equal node-fast-levenshtein
  node-fill-range node-forever-agent node-form-data node-fs.realpath node-function-bind node-getpass
  node-glob node-graceful-fs node-har-schema node-har-validator node-has-flag node-http-signature
  node-iconv-lite node-imports-loader node-inflight node-inherits node-ini node-inquirer node-invariant
  node-is-buffer node-is-builtin-module node-is-number node-is-plain-obj node-is-promise node-is-typedarray
  node-isarray node-isstream node-js-tokens node-js-yaml node-jsbn node-jschardet node-json-schema
  node-json-schema-traverse node-json-stable-stringify node-json-stringify-safe node-json5 node-jsonify
  node-jsprim node-kind-of node-loader-utils node-lodash node-lodash-packages node-loose-envify
  node-loud-rejection node-lru-cache node-micromatch node-mime node-mime-types node-minimatch node-minimist
  node-mkdirp node-ms node-mute-stream node-normalize-path node-oauth-sign node-object-assign
  node-object-path node-once node-path-is-absolute node-path-root node-path-root-regex node-performance-now
  node-prepend-http node-process-nextick-args node-proper-lockfile node-psl node-puka node-pump node-pumpify
  node-punycode node-qs node-read node-readable-stream node-regenerator-runtime node-repeat-string
  node-request node-request-capture-har node-resolve node-restore-cursor node-retry node-rimraf
  node-run-async node-rx node-safe-buffer node-semver node-signal-exit node-sort-keys node-source-map
  node-spdx-correct node-spdx-exceptions node-spdx-expression-parse node-spdx-license-ids node-sprintf-js
  node-sshpk node-ssri node-stream-shift node-strict-uri-encode node-string-decoder node-string-width
  node-strip-ansi node-strip-bom node-supports-color node-tar-stream node-through2 node-tmp
  node-to-regex-range node-tough-cookie node-tunnel-agent node-tweetnacl node-universalify node-uri-js
  node-util-deprecate node-uuid node-validate-npm-package-license node-verror node-wcwidth.js node-wrappy
  node-yallist node-yn nodejs nodejs-doc yarnpkg
0 upgraded, 194 newly installed, 0 to remove and 0 not upgraded.
Need to get 24.8 MB of archives.
After this operation, 123 MB of additional disk space will be used.
Do you want to continue? [Y/n] 
Get:1 http://deb.debian.org/debian bullseye/main amd64 javascript-common all 11+nmu1 [6,260 B]
Get:2 http://deb.debian.org/debian bullseye/main amd64 libbrotli1 amd64 1.0.9-2+b2 [279 kB]
Get:3 http://deb.debian.org/debian bullseye/main amd64 libc-ares2 amd64 1.17.1-1+deb11u1 [102 kB]
Get:4 http://deb.debian.org/debian bullseye/main amd64 libicu67 amd64 67.1-7 [8,622 kB]
[...]
Setting up node-inquirer (3.3.0-3) ...
Setting up node-braces (3.0.2+~3.0.0-1) ...
Setting up node-micromatch (4.0.2+repack+~4.0.1-1) ...
Setting up yarnpkg (1.22.10+~cs22.25.14-3) ...
Processing triggers for libc-bin (2.31-13+deb11u2) ...
dzu@bullseye:~$ 

And finally - the whole idea of this excursion - we can build the GNOME Javascript guide:

dzu@bullseye:~$ cd /opt/src/git/gjs-guide/
dzu@bullseye:/opt/src/git/gjs-guide$ yarnpkg dev
yarn run v1.22.10
$ yarn build:search && vuepress dev docs
$ tsc -p docs/.vuepress/plugins/flexsearch
info Initializing VuePress and preparing data...
 Compiling with webpack...Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
 Compilation finished in 7082ms
webpack compiled successfully
success VuePress webpack dev server is listening at http://localhost:8080/

Directing firefox to http://10.0.3.228:8080 shows the result of the rendering.

Summary

As we saw, it is only a matter of a few commands to download, configure and run a different GNU/Linux distribution inside an LXC container. Compared to docker, the container remembers all state and is simply a "detached" system ready to be run when the need arises. Sharing directories is achieved by a single line configuration change in the LXC config file and by ensuring that the container uses the same user id we do not have any problems with file permissions. Once the container is no longer needed, it is easily disposed by the lxc-destroy command line tool.

Comments

Comments powered by Disqus