Docker is pretty useful. It has revolutionized software development. I've used it for containerizing some [[Command line interface]]s. I have also used it for [[AWS CodeBuild]] and in a popular [[Security Orchestration, Automation and Response|SOAR]] system.
## Why Docker
From [[Using Docker]]:
> Operations engineers can concentrate on networking, resources, and uptime—and spend less time configuring environments and battling system dependencies.
> The portability of containers has the potential to eliminate a whole class of bugs caused by subtle changes in the running environment—it could even put an end to the age old developer refrain of “but it works on my machine!”
## The history
From [[Using Docker]]:
> Early versions of Docker were little more than a wrapper around [[Linux Containers|LXC]] paired with a union filesystem ([[UnionFS]]), but the uptake and speed of development was shockingly fast. Within six months, it had more than 6,700 stars on GitHub and 175 nonemployee contributors. This led dotCloud to change its name to Docker, Inc., and to refocus its business model. Docker 1.0 was announced in June 2014, just 15 months after the 0.1 release. Docker 1.0 represented a major jump in stability and reliability—it was now declared “production ready,” although it had already seen production use in several companies, including Spotify and Baidu. At the same time, Docker started moving toward being a complete platform rather than just a container engine, with the launch of the Docker Hub, a public repository for containers.
## Docker multi stage builds
> With multi-stage builds, you use multiple `FROM` statements in your Dockerfile. Each `FROM` instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image. To show how this works, let’s adapt the Dockerfile from the previous section to use multi-stage builds.
In https://github.com/mikalsande/ubuntu-distroless/blob/master/Dockerfile.runner we can see that the builder image is used first, then the image is "reset" with a new `FROM` statement.
## Resources
[The official Dockerfile best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) is a nice starting point.
[GitHub - hexops/dockerfile: Dockerfile best-practices for writing production-worthy Docker images.](https://github.com/hexops/dockerfile) is also pretty nice.
Docker stuff for [[Python]]: [Broken by default: why you should avoid most Dockerfile examples](https://pythonspeed.com/articles/dockerizing-python-is-hard/) and [Using Alpine can make Python Docker builds 50× slower](https://pythonspeed.com/articles/alpine-docker-python/).
[How containers work: overlayfs](https://jvns.ca/blog/2019/11/18/how-containers-work--overlayfs/) and [Reverse Engineering a Docker Image — The Art of Machinery](https://theartofmachinery.com/2021/03/18/reverse_engineering_a_docker_image.html) are great.
TL;DR: Pull the image, save the image to `tar` and look at the files.