- Not Every Container Has an Operating System Inside
- You Don't Need an Image To Run a Container
- You Need Containers To Build Images
- Containers Aren't Linux Processes
- From Docker Container to Bootable Linux Disk Image
Don't miss new posts in the series! Subscribe to the blog updates and get deep technical write-ups on Cloud Native topics direct into your inbox.
Not every container has an operating system inside, but every one of them needs your Linux kernel.
Before going any further it's important to understand the difference between a kernel, an operating system, and a distribution.
- Linux kernel is the core part of the Linux operating system. It's what originally Linus wrote.
- Linux OS is a combination of the kernel and a user-land (libraries, GNU utilities, config files, etc).
- Linux distribution is a particular version of the Linux operating system like Debian or CentOS.
To be technically accurate, the title of this article should have sounded something like Does container image have a whole Linux distribution inside? But I find this wording a bit boring for a title 🤪
Does a Container have an Operating System inside?
The majority of Docker examples out there explicitly or implicitly rely on some flavor of the Linux operating system sitting inside a container. I tried to quickly compile a list of the most prominent samples.
Running an interactive shell in the debian jessie
distribution:
$ docker run -it debian:jessie
Running an nginx
web-sever in a container and examine its config using cat
utility:
$ docker run -d -P --name nginx nginx:latest
$ docker exec -it nginx cat /etc/nginx/nginx.conf
Building an image based on Alpine Linux:
$ cat <<EOF > Dockerfile
FROM alpine:3.7
RUN apk add --no-cache mysql-client
ENTRYPOINT ["mysql"]
EOF
$ docker build -t mysql-alpine .
$ docker run mysql-alpine
And so on and so forth...
For the newcomers learning the containerization through hands-on experimentation, this may lead to a false impression that containers are somewhat indistinguishable from full-fledged operating systems and that they are always based on well-known and wide-spread Linux distributions like debian
, centos
, or alpine
.
At the same time, approaching the containerization topic from the theoretical side (1, 2, 3) may lead to a rather opposite impression that containers (unlike the traditional virtual machines) are supposed to pack only the application (i.e. your code) and its dependencies (i.e. some libraries) instead of a trying to ship a full operating system.
As it usually happens, the truth lies somewhere in between both statements. From the implementation standpoint, a container is indeed just a process (or a bunch of processes) running on the Linux host. The container process is isolated (namespaces) from the rest of the system and restricted from both the resource consumption (cgroups) and security (capabilities, AppArmor, Seccomp) standpoints. But in the end, this is still a regular process, same as any other process on the host system.
OCI/Docker containers thread:
— Ivan Velichko (@iximiuz) May 10, 2020
Containers are simply isolated and restricted Linux processes.#Docker #containers #linux
Just run docker run -d nginx
and conduct your own investigation:
ps axf
output (excerpt)
systemctl status
output (excerpt)
sudo lsns
output
Well, if a container is just a regular Linux process, we could try to run a single executable file inside of a container. I.e. instead of putting our application into a fully-featured Linux distribution, we will try to build a container image consisting of a folder with a single file inside. Upon the launch, this folder will become a root folder for the containerized environment.
Create a Container from scratch (with a single executable binary inside)
If you have Go installed on your system, you can utilize its handy cross-compilation abilities:
// main.go
package main
import "fmt"
func main() {
fmt.Println("Hello from OS-less container (Go edition)")
}
Build the program from above using:
$ GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o hello
$ file hello
> hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked
Click here to see how to compile a similar C program.
// main.c
#include <stdio.h>
int main() {
printf("Hello from OS-less container (C edition)\n");
}
Compile it using the following builder container:
# Dockerfile.builder
FROM gcc:4.9
COPY main.c /main.c
CMD ["gcc", "-std=c99", "-static", "-o", "/out/hello", "/main.c"]
$ docker build -t builder -f Dockerfile.builder .
$ docker run -v `pwd`:/out builder
$ file hello
> hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32
Finally, let's build the target container using the following trivial Dockerfile:
FROM scratch
COPY hello /
CMD ["/hello"]
$ docker build -t hello .
$ docker run hello
> Hello from OS-less container (Go edition)
If we now inspect the hello
image with the wonderful dive
tool, we will notice that it consists of a directory with the single executable file in it:
dive hello
This exercise is roughly what the Docker's hello-world example does. There are two key moments here. First, we based our image on a so-called scratch
image. This is just an empty image, i.e. the building starts from the empty folder and then just copies the executable file hello
into it. Second, we used a statically linked binary file. I.e. there is no dependency on some shared libraries from the system. So, a bare Linux kernel is enough to execute it.
Now, what if we inspect the nginx
image which we used at the beginning of this article?
dive nginx
Well, the directory tree looks like a root filesystem of some Linux distribution. If we take a look at the corresponding Dockerfile we can notice that nginx
image is based on debian
:
FROM debian:buster-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
ENV NGINX_VERSION 1.17.10
ENV NJS_VERSION 0.3.9
ENV PKG_RELEASE 1~buster
...
And if we dive deeper and examine debian:buster-slim
Dockerfile we will see that it just copies a root filesystem to an empty folder:
FROM scratch
ADD rootfs.tar.xz /
CMD ["bash"]
Combining Debian's user-land with the host's kernel containers start resembling fully-featured operating systems. With nginx
image we can use the shell to interact with the container:
Interactive shell with running nginx container.
Can we do the same for our slim hello
container? Obviously not, there is no bash
executable inside:
hello
container doesn't have bash
inside.
Wrapping up
So, what should be the conclusion here? The virtualization capabilities of the modern operating systems, namely Linux, turned out to be so powerful that people started packing fully-featured user-lands like debian
(or more lightweight alternatives like alpine
) into isolated and restricted VM-like execution environments called containers. By virtue of this ability:
- We can play with various Linux distribution using a simple
docker run -it fedora bash
. - We can use OS commands including package managers like
yum
orapt
while building our images. - We can interact with running containers using various OS utilities.
But with great power comes great responsibility. Huge containers carrying lots of unnecessary tools slow down deployments and increase the surface of potential cyberattacks. So, it's good to remember that having a full-blown Linux distro in your container is not technically mandatory. Keep your container images slim!
Related articles
- Containers Aren't Linux Processes
- You don't need an image to run a container
- You need containers to build images
- A journey from containerization to orchestration and beyond
- From Docker Container to Bootable Linux Disk Image
- The Need For Slimmer Containers
- In Pursuit of Better Container Images: Alpine, Distroless, Apko, Chisel, DockerSlim, oh my!
- Not Every Container Has an Operating System Inside
- You Don't Need an Image To Run a Container
- You Need Containers To Build Images
- Containers Aren't Linux Processes
- From Docker Container to Bootable Linux Disk Image
Don't miss new posts in the series! Subscribe to the blog updates and get deep technical write-ups on Cloud Native topics direct into your inbox.