It largely depends on what your view of an O.S. is.

Containers fundamentally are a method to isolate both a running process from all other processes on a machine, and the binary and all its dependencies from the host machine filesystem. This then makes them trivial to move between and run on any number of machines. They are in no way virtualisation, therefore as you mention in your question, any process running in a container is running on the host machines kernel. From some points of view an operating system, fundamentally is the kernel.

Balanced against that, is that most software is not built to be monolithic, but is instead linked at compile time, against any number of shared libraries. So when you install an operating system distribution, as well as a kernel, you get a whole series of libraries and utilities, and any programs you install from the O.S. repos will be built against the libraries that are shipped with that OS distribution.

Now when building a container, all that is required is the binary that you want to run, plus anything that that binary depends on. There is no requirement to package a complete O.S. distro filesystem. If your binary is not linked against any other library, you can almost get away with the single binary and nothing else. Check out this project https://github.com/davidcarboni-archive/ddd written by a chap who works with the same client I currently do, which demonstrates how little is required to build a functional container.

So in an ideal world, given best practice largely is to build a container that runs a single process, each container would consist of nothing more than the relevant binary, plus any libraries, config files and working directories it needs. It is however, quite an involved and time consuming process to get that right in a lot of cases. Therefore it has now become a very common pattern, to simply package up the entire filesystem from a donor OS inside a container, as that way you know that any dependencies the process to be run has, will be present. It also adds the convenience of meaning that package management tools will be present, which makes building the container easier. It also means that utilities such as shells are also present, which makes life easier to debug a container (tho in some peoples minds, other than perhaps when you are developing a new container image, if you need to access a shell inside the container, you are doing it wrong).

Whilst I understand why this pattern has become so prevalent, I do think it has disadvantages. First demonstrated by your question - it has made containers look a lot like virtualistation and thus sown a lot of confusion. More over, as someone who has just finished applying CIS server hardening to an estate of machines, packaging everything including the kitchen sink in every container, doesn't feel like great security practice, and I suspect at some point, that may come back to bite us.

To expand on your two specific questions:

1) I have addressed the subjects of shells already. As for things like systemd, they essentially have no place in a container. The Dockefile defines the process to run when the container is started. If you are trying to run a service manager and multiple services inside a container, you are likely doing it wrong.

2) This is back to the question of what is an O.S.? The only sense that you install a given distro in a container, is that you get the filesystem and therefore the distros copies of binaries and shared libraries. It is not the same as virtualisation, where you have a copy of say, Centos running in a virtual machine on a host running say, Debian.


Yes, they do. Every container is based on an OS image, e.g. Alpine, CentOS or Ubuntu.

They just share the host kernel, but run every user-space process in a separate name space specific for that container.

Look into a dockerfile example (adapted by me) to understand this:

FROM ubuntu

MAINTAINER Kimbro Staken version: 0.1 

RUN apt-get update && apt-get install -y apache2 && apt-get clean && rm -rf /var/lib/apt/lists/*

ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2

EXPOSE 80

CMD ["/usr/sbin/apache2", "-D", "FOREGROUND"]

This tells Docker to create a container from an Ubuntu base image (latest version if no version is specified), install and run Apache in it and expose port 80 to the host OS.