Container
Contents
Motivation
Container provide a light-weighted virtualization where the kernel of your system is used but the processes that have a different view onto the system. Instead of a Virtual Machine (VM) we only have a Virtual Environment (VE) Here you will learn the basic terminology of the container world and some examples.
Why Container?
In the beginning was the chroot command that allows a process to see only a part of the file-system but it was never meant as a secure confinement. FreeBSD developed something out of this with the name "jail" where you could really limit a process. Jails where limited to be only able to interact with processes inside its jail and also restricted with certain operations.
When Linux become popular hosting-providers wanted to offer cheap "root-servers" to customers without the overhead of full virtualization. This lead to the creation of "linux-VServers" but this was not part of the mainline kernel.
Today, Linux has a generalized framework for limiting what processes can see with the "cgroups" which is used for container-type virtualization.
Container and Dependency Hell
What was driving this was not so much a need for security or the desire to offer cheap root-servers but the ever more increasing dependency hell in languages as ruby, python, etc.. With the fast pace of development in this newer languages developers need to keep track about which version of the programming language and which modules they use. What worked in python2.4 did not work in python2.6, and then there are dependencies on many modules which are also available in newer and older versions and which are not always compatible. While languages like python and ruby already have some ways to deal with that in a way that they install a different set of modules for each project in a sub-directory, it can also be that case that some of the modules might have dependencies with special versions of system libraries. So the solutions, to put all of that into a container where one can make sure that there are no more outside dependencies makes sense.
This certainly has some advantages but also comes with some problems of its own: Now for security updates of modules you do not only have to take care of your system libraries but also a ton of code that is hidden away in containers and then there is a tendency to more or less ignore backwards compatibility of code as people can revert to containers. To manage the increased complexity then additional tools become necessary.
Overview of Container Technologies
On of the first projects that made use of the cgroups containers was the LXC (LinuX Containers) Project. LXCs are often kind of virtual machines which a more or less complete OS install but only sharing the kernel. They are usually meant as permanent machines that are started at boot and maintained with lxc- tools. LXC Container can also be maintained via the libvirt virsh tools. (Yet one should decide for one of the 2 as they are not fully interchangeable). The cgroups allows Virtual Environments (VE) to share some of the "namespace" (e.g. network).
Docker is the most known Container technology today. It started as extension of LXC but now is independent of it. It still makes use of the cgroups of course. The focus of docker is not to run a small OS in the VE (virtual environment) but just one application. What docker offers above that are tools to download container images from a git like repository and to build on existing containers.
Podman after the success of docker RedHat announced the podman project which aims to be a drop-in replacement for docker. It will be part of RedHat8. As of now (2020) it is not well integrated into debian or other distributions yet. Podman offers a few security enhancements over docker, e.g. the option to run containers without root and better integration into the systemd environment.
First Steps with LXC Containers using the libvirt virsh environment
If you have used virsh before then you know how to manage KVM VMs with it. If we want to use virsh for LXC we need to "connect" it to a different endpoint:
# list virtual machines: virsh list # list LXC VEs: virsh -c lxc:/// list
If we set: export LIBVIRT_DEFAULT_URI=lxc:/// then virsh will assume the -c above, but in the examples here I will explicitly specify the -c in order to make clear that we are dealing with LXC and not with KVM.
virsh -c lxc:/// define myhello.xml cat myhello.xml
<domain type='lxc'> <name>mylxc</name> <memory>102400</memory> <os> <type>exe</type> <init>/bin/bash</init> </os> <devices> <console type='pty'/> </devices> </domain>
virsh -c lxc:/// list --all virsh -c lxc:/// start mylxc virsh -c lxc:/// console mylxc
If we look around in the console we find that our hostname is now "mylxc" and that "top" only shows us 2 processes: the bash and the top.
Also useful is the virt-sandbox tool: This would create a sandbox on the fly and execute /bin/hostname in it:
virt-sandbox -c lxc:/// /bin/hostname
There is also a tool to create a container from an OCI (Open Container Image) which is also used by docker, podman, etc..
Different View on the Filesystem
If we include the following in the XML definition of our LXC VE then we will get a different filesystem:
<filesystem type='mount'> <source dir='/space/mylxc/root'/> <target dir='/'/> </filesystem> <filesystem type='mount'> <source dir='/space/mylxc/var'/> <target dir='/var'/> </filesystem>
In order to be able to run anything we either need static binaries there that do not need any library or we need to setup an envinronment that has all the necessary stuff in there.
Docker, Podman
The following examples are for docker, yet podman should be mostly compatible:
The docker-CE (comunity edition) is free. The EE needs a commercial license.
To test your docker isntallation you could e.g. use:
docker run --rm -it --name mydocker alpine:latest /bin/sh
rm would remove an older image and -it attaches stdin and terminal.
It downloads and installs "alpine" which is a small 6MB, very limited linux enviroment and runs ist
docker ps # would list the currently running docker versions. e.g: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e8b274b83b99 alpine:latest "/bin/sh" 10 seconds ago Up 7 seconds mydocker
If we want to list what images we have downloaded we could use:
docker images # download another image: e.g docker pull busybox docker images
And then run it with:
docker run -it --name mybbox busybox
Busybox is a small binary that contains a simple shell and a few simply tools all in one binary and it is often used for tiny installations (e.g. in an applicance).
We can also pass arguments to the container which will then given to the shell or command that runs in the container. If we do not specify a name we will get a random name. (the name of the container is not the hostname) e.g:
docker run busybox hostname docker run busybox echo "hello world" <pre> Once a container exits it still exists: E.g. with <pre> # list all containers (even the one that exited) docker ps -a
You can start them again, attach to them or remove them with rm.
To restart and attach use:
docker start bf47e934ff16 -a
To remove all remains of containers that are no longer running you could use:
docker container prune
You can create your own containers with a "dockerfile" that contains info on where it builds on and extra files that need to be copied into it. You can then share this if you have an account on the docker hub or you can use alternative hubs e.g. gitlab.
Building A Container with Podman
FROM debian:12 WORKDIR /app RUN apt-get -y update ; apt-get -y upgrade ; apt-get -y install python3 python3-flask COPY . . ENV FLASK_APP lvapp.py EXPOSE 8000 CMD [ "flask", "run", "--host=0.0.0.0","--port=8000" ]
Building an image from this: In the UPPER directory
podman build test1 -t lvapp:latest podman run -d -p 8000:8000 lvapp
Exercises
- Setup LXC (e.g. inside a VM) and build a simple environment.
- setup docker or podman and run some command inside a virtual environment.