In this six-part tutorial, you will:
- Get set up and oriented, on this page.
- Build and run your first app
- Turn your app into a scaling service
- Span your service across multiple machines
- Add a visitor counter that persists data
- Deploy your swarm to production
The application itself is very simple so that you are not too distracted by what the code is doing. After all, the value of Docker is in how it can build, ship, and run applications; it’s totally agnostic as to what your application actually does.
We also need to assume you are familiar with a few concepts before we continue:
- IP Addresses and Ports
- Virtual Machines
- Editing configuration files
- Basic familiarity with the ideas of code dependencies and building
- Machine resource usage terms, like CPU percentages, RAM use in bytes, etc.
A brief explanation of containers
An image is a lightweight, stand-alone, executable package that includes everything needed to run a piece of software, including the code, a runtime, libraries, environment variables, and config files.
A container is a runtime instance of an image – what the image becomes in memory when actually executed. It runs completely isolated from the host environment by default, only accessing host files and ports if configured to do so.
Containers run apps natively on the host machine’s kernel. They have better performance characteristics than virtual machines that only get virtual access to host resources through a hypervisor. Containers can get native access, each one running in a discrete process, taking no more memory than any other executable.
Containers vs. virtual machines
Consider this diagram comparing virtual machines to containers:
Virtual Machine diagram
Virtual machines run guest operating systems – note the OS layer in each box. This is resource intensive, and the resulting disk image and application state is an entanglement of OS settings, system-installed dependencies, OS security patches, and other easy-to-lose, hard-to-replicate ephemera.
Containers can share a single kernel, and the only information that needs to be in a container image is the executable and its package dependencies, which never need to be installed on the host system. These processes run like native processes, and you can manage them individually by running commands like
docker ps – just like you would run
ps on Linux to see active processes. Finally, because they contain all their dependencies, there is no configuration entanglement; a containerized app “runs anywhere.”
Before we get started, make sure your system has the latest version of Docker installed.
Note version 1.13 or higher is required
You should be able to run
docker run hello-world and see a response like this:
$ docker run hello-world Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: ...(snipped)...
Now would also be a good time to make sure you are using version 1.13 or higher
$ docker version Docker version 17.03.1-ce, build c6d412e
If you see a messages like the ones above, you’re ready to begin the journey.
The unit of scale being an individual, portable executable has vast implications. It means that CI/CD can push updates to one part of a distributed application, system dependencies aren’t a thing you worry about, resource density is increased, and orchestrating scaling behavior is a matter of spinning up new executables, not new VM hosts. We’ll be learning about all of those things, but first let’s learn to walk.