Containers are small packaged applications that once built can run on pretty much any hardware/OS combo. They take the guesswork out of deploying applications as they provide a unified way of building, deploying, and operating applications. As of 2020 the de facto way to work with containers is Docker. The approach they took made creating and sharing dockerized applications so easy that nowadays when looking at how to run an open-source project, your first stop is the docker hub.
It’s quite common for developers to not even be aware that there is such a thing as non-docker containers. Here we will look a bit at how containers work under the hood. And win a follow-up article we will discuss why some believe you should use non-docker container providers.
Once you make a working container, you essentially lock all dependencies inside the container. And then you just start that container and the app works. This sort of takes the guesswork out of deploying random apps. It often happens that only a few people can and know how to deploy some software. And often the documentation to replicate their process is outright missing. If you build and run your apps through containers, you essentially ship this information with your code.
This essentially means any app to be built, tested, and deployed without much knowledge of the underlying system and technologies. This started a wide ecosystem of technologies that can be used to monitor, operate, control, CI/CD, and scale pretty much any dockerized applications.
The Open Container Initiative
When most people think of docker containers, they are actually thinking of OCI compliant containers. The Open Container Initiative was created to manage open-sourced parts of Docker. They basically donated image specification, the low lever container runtime, and a few other key system components. Docker nowadays simply provides a few extra features on top of the base OCI compliant container runtime. If the technology in question is OCI compliant it will work with all OCI compliant container technologies (and most container technologies are). This piece of info gets interesting when comparing docker alternatives and other container supporting software.
Let’s think a bit about what docker does. They provide a build system that produces an end image that can easily be shared over the internet. Through docker we can also manage images, running instances, networks, volumes as well as run commands on running instances. Also, docker tends to easily integrate into other tools such as kubernetes that can provide further orchestration for many instances of the app.
How do Docker containers work
As previously mentioned all docker instances share the same kernel. More specifically they are implementations of Linux namespaces and cgroups. Namespaces allow you to virtualize and assign some system resources and cgroups which provide CPU prioritization and resource limiting. Docker on low level invokes
runc which handles the filesystem and kernel specific actions by following
config.json which together with the root filesystem is part of the OCI image bundle.
This essentially means that from a host system standpoint, your container basically looks like any other process. If you talk to alternative projects they will point that Docker is not a truly sandboxed environment. This means that to some degree it will be vulnerable to container escape attacks. Creating containers with elevated permissions such as
CAP_SYS_ADMIN, or by explicitly mounting system binaries you allow containers to execute many actions on the host. And as docker tries to make it easy to run many apps on the same host, a single compromise can quickly spin out of control. Don’t let me even start on bad advice on the internet concerning mounting
/sys/fs/cgroup or similar host stuff inside containers. Or “fixing” a broken docker image by just running it with –privileged.
Anyhow, this is the gist behind the majority of low-level alternatives to docker/containerd/runc containerization. Most try to either add an additional kernel for the containerized workload, in order to isolate the host kernel. Or they attempt to somehow catch, control, or restrict system calls that the controller makes.
High level runtime
Above runc is containerd, which is responsible for higher considerations. This is what specifies and manages storage for the container, pushing and pulls images as well as manages network primitives. Kubernetes and similar orchestrators tend to interact with this particular layer, and you can basically use the ctr cli in the same way as you can use docker. At this point, it just doesn’t provide any end-user functionality. Things like building images, debugging, tagging, and reusing is left to other applications (eg. docker).
And other high-level runtimes simply provide solutions to different use cases. Some provide ways of bundling images together in a k8s-like pod, others allow daemonless execution of containers. This used to be a major reliability issue with docker, that deamon crash would take down the container as well. Now, this is something you can turn off as runtime shim can survive docker deamon crash.
While Docker was a monolithic giant. Nowadays it’s simply a combination of low-level and high-level opensource projects with only a few proprietary docker-specific features. This means that in a lot of cases you can substitute docker with alternative OSS or closed source projects. This is great news if you want to avoid a particular component in the stack for some reason. And containers are the backbone of many other popular IT buzzwords from kubernetes to serverless. Also due to the way they work, they enabled a whole new generation of IaC based software delivery solutions.
- A basic introduction to containers
- OCI Runtime
- OS level Virtualization
- A good introduction into how runtimes work and how to implement containers by hand
- docker components explained
This is part one of a series of articles on Docker and Containers. Stay tuned for part 2 on “When to avoid using docker containers”. In case you’d like to know or discuss more feel free to poke me on Twitter @buzzwrd_me,