TL;DR: Docker container images are supported in OpenShift 4 and RHEL 8, but the Docker daemon and client are not. Instead, containers will be run with CRI-O in OpenShift 4, and Podman in RHEL 8 – the same images can be used anywhere because of container standards.
Cool, if that’s enough information for you and you’re more interested in why, check out part II: Why Is There No Docker in OpenShift 4 and RHEL 8? If that doesn’t make sense and you want to understand that statement better, read on.
Docker is a lot of different things to a lot of different people, so I’m going to try and break it down as five simple things:
- Image Format
- Registry Format
- Runtime Format
- Command Line Interface (CLI)
- Application Programming Interface (API)
TL;DR: Red Hat can support 1-4, but will not support number 5. The Docker API is not governed by an open standard, so it cannot easily be implemented by another open source project. Only the Docker daemon can service Docker API requests. If you want even MORE information, then read on some more 🙂
We often refer to container images as “Docker Containers” but really the format is governed by the Open Containers Initiative (OCI) which is based on the original Docker image format. Specifically, container images, including the ones built by the Docker daemon, Podman, Buildah and others comply with the OCI image specification. This means that the images built by Docker are consumable by Podman and vice versa. All Red Hat products build and run OCI compliant images which are interoperable with the Docker daemon, but our products will no longer run the Docker daemon itself.
The format of communication between the Docker Engine and a Docker Registry is governed by the OCI distribution specification. This is what lets users build images with Docker or Podman, and push them to, or pull them from the same registry server. This is also what allows third parties to build their own registry servers. AWS, Azure, Quay, and Artifactory can all interoperate with the Docker Engine, Podman, CRI-O and any other container engine which pushes/pulls images based on this specification. All Red Hat products comply with the distribution standard, making them able to push to and pull from any compliant registry including DockerHub, Docker Registry, the OpenShift registry, quay.io and Quay Enterprise, as well as third party registries like Artifactory and big cloud provider registries at AWS, Azure, Google and others.
This defines the set of technologies that get “turned on” in the Linux kernel when the container is created. This is governed by the OCI runtime specification, and even has an open source reference implementation in code called runc, which is also the mostly widely used implementation. If you start a Linux container with Podman, and you start another container with Docker, you actually can’t tell them apart once they are running. This lab shows Linux containers started by Podman and Docker, side by side in action. They are impossible to tell apart because both tools start containers governed by the OCI Runtime specification. in fact, both tools use the exact same tool: runc. The code provided by runc is the reference implementation for the OCI Runtime standard making it a very authoritative interpretation of the standard and guaranteeing containers started by Docker, Podman, and CRI-O (CRI-O: How Standards Power a Container Runtime) are identical when run on, for example, a RHEL kernel.
Command Line Interface (CLI)
This is the human interface that most people are familiar with when building and running containers (including the Dockerfile format). While this interface is not governed by community, or open standards, the fact that it is a human interface limits how quickly it can change in a natural way (people can’t stand when stuff changes too fast). This makes the engineering goal of chasing it not insurmountable. All of the common Docker commands like “docker run” – “docker kill” – “docker rm” are supported in podman. This makes it extremely easy to convert to podman from Docker. There may be very small changes between scripts but for the most part, with common use cases, podman is a drop in replacement.
All of the basic commands, including the Dockerfile (including multi-stage builds) are easy to run in podman, but as you get into advanced commands like “docker network” (in Podman, you need to use a CNI config like Kubernetes) which actually configures the daemon, not containers per se, your mileage may vary a bit.
Application Programming Interface (API)
The Docker Daemon provides the docker socket (API) at /var/run/docker.sock or on TCP port 2375 or 2376. The Docker CLI communicates with the Docker daemon over this interface, but it’s not the only tool that does (some homegrown scripts and third party products). Docker has never donated this API to any governing standards body (like the CNCF). One can assume it’s because they hoped to monetize this API by generating demand for their pay for product. If you have third party products that rely on the Docker socket, or have built homegrown tools that talk to the Docker socket, then you will have to demand changes from your vendor, or rewrite your code to talk to CRI-O or Podman (through varlink and systemd socket activation).
Podman is provided in RHEL 8, and CRI-O is provided in OpenShift 4. These new tools provide some really cool new features like daemonless and rootless containers, but neither of them supports the Docker socket/API. Since Podman doesn’t run a daemon, it relies on systemd socket activation to fire it up when called remotely and provides an API based on the Podman CLI (very similar to the docker CLI). If you have code written against the Docker socket, it could likely be converted over pretty easily. If on the other hand, you are running an OpenShift/Kubernetes environment, CRI-O is a daemon and provides an API compliant with the Container Runtime Interface (CRI) to service what the Kubelet needs to fire up containers. This interface can also be used for many third party tools and homegrown scripts. The CRI interface, while somewhat similar to the Docker API (run, kill, exec, etc) is still a completely different API (What is CRICTL and Why Should You Care?). Any third party products or homegrown scripts that want to support CRI would need to be rewritten from the Docker API.
Podman is provided in RHEL 8 and CRI-O is provided in OpenShift 4 which has support implications. CRI-O is not provided in RHEL 8, only in OpenShift because it’s only used for Kubernetes. There is actually no container daemon at all in RHEL 8. OpenShift 4 includes subscriptions to RHEL 8 (and the CoreOS derivative) so on an OpenShift box, you will have access to both CRI-O and Podman. This has implications for people trying to build homegrown Kubernetes environments on RHEL. RHEL 8 does not provide CRI-O nor Docker, and the Docker daemon provided in RHEL 7 will eventually be retired after the OpenShift 3.11 lifecycle ends.
This means that if you want to build a homegrown Kubernetes environment on RHEL, you have to either bring your own CRI-O (or contianerd), or Docker with shim, etc) or you have to build a CRI shim for Podman with systemd socket activation. If you bring your own daemon or CRI shim, you will have to support it yourself (patch, troubleshoot, smoke test, etc). There is no malice here, and Red Hat will not stop you/RHEL from doing it. We want you to get value out of your RHEL subscription and will continue to support RHEL as a container host (kernel, etc). But, it should be obvious that we can’t build a support business on home grown Kubernetes, it’s way too expensive to support every permutation of components that people build with Kubernetes and it would take engineering, quality engineering (QE), and support resources from our upstream Kubernetes work as well as our OpenShift product work where we make money. Homegrown means just that, you have to spend the engineering, QE and support resources to build a Kubernetes environment (Kube master, Kubelet, CRI daemon, and all of the projects you decide to run on top). I think that’s fair.
Red Hat believes that the path forward is not with the Docker daemon, nor it’s API. The much more important API is the Kubernetes API. Long term, community projects, vendor products, and home grown automation will all migrate to the Kubernetes API (and many already have), creating a much bigger ecosystem for everyone. That’s why OpenShift 4 and RHEL 8 will no longer support the Docker socket/API. If you have existing infrastructure that depends on it, feel free to reach out and file a support ticket, or ping me on Twitter (@fatherlinux), and we will try and find a work around for you.
But, for most users, the conversion is as simple as this: https://twitter.com/ialanmoran/status/1001671953571303425