Docker creates packaged applications called containers. Each container provides an isolated environment similar to a virtual machine (VM). Unlike VMs, Docker containers don’t run a full operating system. They share your host’s kernel and virtualize at a software level.
Docker has become a standard tool for software developers and system administrators. It’s a neat way to quickly launch applications without impacting the rest of your system. You can spin up a new service with a single
docker run command.
Containers encapsulate everything needed to run an application, from OS package dependencies to your own source code. You define a container’s creation steps as instructions in a
Dockerfile. Docker uses the Dockerfile to construct an image.
Images define the software available in containers. This is loosely equivalent to starting a VM with an operating system ISO. If you create an image, any Docker user will be able to launch your app with
How Does Docker Work?
Containers utilize operating system kernel features to provide partially virtualized environments. It’s possible to create containers from scratch with commands like
chroot. This starts a process with a specified root directory instead of the system root. But using kernel features directly is fiddly, insecure, and error-prone.
Docker is a complete solution for the production, distribution, and use of containers. Modern Docker releases are comprised of several independent components. First, there’s the Docker CLI, which is what you interact with in your terminal. The CLI sends commands to a Docker daemon. This can run locally or on a remote host. The daemon is responsible for managing containers and the images they’re created from.
The final component is called the container runtime. The runtime invokes kernel features to actually launch containers. Docker is compatible with runtimes that adhere to the OCI specification. This open standard allows for interoperability between different containerization tools.
You don’t need to worry too much about Docker’s inner workings when you’re first getting started. Installing
docker on your system will give you everything you need to build and run containers.
Why Do So Many People Use Docker?
Containers have become so popular because they solve many common challenges in software development. The ability to containerize once and run everywhere reduces the gap between your development environment and your production servers.
Using containers gives you confidence that every environment is identical. If you have a new team member, they only need to
docker run to set up their own development instance. When you launch your service, you can use your Docker image to deploy to production. The live environment will exactly match your local instance, avoiding “it works on my machine” scenarios.
Docker is more convenient than a full-blown virtual machine. VMs are general-purpose tools designed to support every possible workload. By contrast, containers are lightweight, self-sufficient, and better suited to throwaway use cases. As Docker shares the host’s kernel, containers have a negligible impact on system performance. Container launch time is almost instantaneous, as you’re only starting processes, not an entire operating system.
You can check that your installation is working by starting a simple container:
docker run hello-world
This will start a new container with the basic
hello-world image. The image emits some output explaining how to use Docker. The container then exits, dropping you back to your terminal.
Once you’ve run
hello-world, you’re ready to create your own Docker images. A Dockerfile describes how to run your service by installing required software and copying in files. Here’s a simple example using the Apache web server:
FROM httpd:latest RUN echo "LoadModule headers_module modules/mod_headers.so" >> /usr/local/apache2/conf/httpd.conf COPY .htaccess /var/www/html/.htaccess COPY index.html /var/www/html/index.html COPY css/ /var/www/html/css
FROM line defines the base image. In this case, we’re starting from the official Apache image. Docker applies the remaining instructions in your Dockerfile on top of the base image.
RUN stage runs a command within the container. This can be any command available in the container’s environment. We’re enabling the
headers Apache module, which could be used by the
.htaccess file to set up routing rules.
The final lines copy the HTML and CSS files in your working directory into the container image. Your image now contains everything you need to run your website.
Now, you can build the image:
docker build -t my-website:v1 .
Docker will use your Dockerfile to construct the image. You’ll see output in your terminal as Docker runs each of your instructions.
-t in the command tags your image with a given name (
my-website:v1). This makes it easier to refer to in the future. Tags have two components, separated by a colon. The first part sets the image name, while the second usually denotes its version. If you omit the colon, Docker will default to using
latest as the tag version.
. at the end of the command tells Docker to use the Dockerfile in your local working directory. This also sets the build context, allowing you to use files and folders in your working directory with
COPY instructions in your Dockerfile.
Once you’ve created your image, you can start a container using
docker run -d -p 8080:80 my-website:v1
We’re using a few extra flags with
docker run here. The
-d flag makes the Docker CLI detach from the container, allowing it to run in the background. A port mapping is defined with
-p, so port 8080 on your host maps to port 80 in the container. You should see your web page if you visit
localhost:8080 in your browser.
Docker images are formed from layers. Each instruction in your Dockerfile creates a new layer. You can use advanced building features to reference multiple base images, discarding intermediary layers from earlier images.
When you run a command that references an image, Docker first checks whether it’s available locally. If it isn’t, it will try to pull it from Docker Hub. You can manually pull images with the
docker pull command:
docker pull httpd:latest
If you want to publish an image, create a Docker Hub account. Run
docker login and enter your username and password.
Next, tag your image using your Docker Hub username:
docker tag my-image:latest docker-hub-username/my-image:latest
Now, you can push your image:
docker push docker-hub-username/my-image:latest
Other users will be able to pull your image and start containers with it.
Managing Your Containers
The Docker CLI has several commands to let you manage your running containers. Here are some of the most useful ones to know:
docker ps shows you all your running containers. Adding the
-a flag will show stopped containers, too.
Stopping and Starting Containers
To stop a container, run
docker stop my-container. Replace
my-container with the container’s name or ID. You can get this information from the
ps command. A stopped container is restarted with
docker start my-container.
Containers usually run for as long as their main process stays alive. Restart policies control what happens when a container stops or your host restarts. Pass
--restart always to
docker run to make a container restart immediately after it stops.
Getting a Shell
You can run a command in a container using
docker exec my-container my-command. This is useful when you want to manually invoke an executable that’s separate to the container’s main process.
-it flag if you need interactive access. This lets you drop into a shell by running
docker exec -it my-container sh.
Docker automatically collects output emitted to a container’s standard input and output streams. The
docker logs my-container command will show a container’s logs inside your terminal. The
--follow flag sets up a continuous stream so that you can view logs in real time.
Cleaning Up Resources
Old containers and images can quickly pile up on your system. Use
docker rm my-container to delete a container by its ID or name.
The command for images is
docker rmi my-image:latest. Pass the image’s ID or full tag name. If you specify a tag, the image won’t be deleted until it has no more tags assigned. Otherwise, the given tag will be removed but the image’s other tags will remain usable.
Bulk clean-ups are possible using the
docker prune command. This gives you an easy way to remove all stopped containers and redundant images.
If the terminal’s not your thing, you can use third-party tools to set up a graphical interface for Docker. Web dashboards let you quickly monitor and manage your installation. They also help you take remote control of your containers.
Persistent Data Storage
Docker containers are ephemeral by default. Changes made to a container’s filesystem won’t persist after the container stops. It’s not safe to run any form of file storage system in a container started with a basic
docker run command.
There are a few different approaches to managing persistent data. The most common is to use a Docker Volume. Volumes are storage units that are mounted into container filesystems. Any data in a volume will remain intact after its linked container stops, letting you connect another container in the future.
Dockerized workloads can be more secure than their bare metal counterparts, as Docker provides some separation between the operating system and your services. Nonetheless, Docker is a potential security issue, as it normally runs as
root and could be exploited to run malicious software.
If you’re only running Docker as a development tool, the default installation is generally safe to use. Production servers and machines with a network-exposed daemon socket should be hardened before you go live.
Audit your Docker installation to identify potential security issues. There are automated tools available that can help you find weaknesses and suggest resolutions. You can also scan individual container images for issues that could be exploited from within.
Working with Multiple Containers
docker command only works with one container at a time. You’ll often want to use containers in aggregate. Docker Compose is a tool that lets you define your containers declaratively in a YAML file. You can start them all up with a single command.
This is helpful when your project depends on other services, such as a web backend that relies on a database server. You can define both containers in your
docker-compose.yml and benefit from streamlined management with automatic networking.
Here’s a simple
version: "3" services: app: image: app-server:latest ports: - 8000:80 database: image: database-server:latest volumes: - database-data:/data volumes: database-data:
This defines two containers (
database). A volume is created for the database. This gets mounted to
/data in the container. The app server’s port 80 is exposed as 8000 on the host. Run
docker-compose up -d to spin up both services, including the network and volume.
The use of Docker Compose lets you write reusable container definitions that you can share with others. You could commit a
docker-compose.yml into your version control instead of having developers memorize
docker run commands.
There are other approaches to running multiple containers, too. Docker App is an emerging solution that provides another level of abstraction. Elsewhere in the ecosystem, Podman is a Docker alternative that lets you create “pods” of containers within your terminal.
Docker isn’t normally run as-is in production. It’s now more common to use an orchestration platform such as Kubernetes or Docker Swarm mode. These tools are designed to handle multiple container replicas, which improves scalability and reliability.
Docker is only one component in the broader containerization movement. Orchestrators utilize the same container runtime technologies to provide an environment that’s a better fit for production. Using multiple container instances allows for rolling updates as well as distribution across machines, making your deployment more resilient to change and outage. The regular
docker CLI targets one host and works with individual containers.
A Powerful Platform for Containers
Docker gives you everything you need to work with containers. It has become a key tool for software development and system administration. The principal benefits are increased isolation and portability for individual services.
Getting acquainted with Docker requires an understanding of the basic container and image concepts. You can apply these to create your specialized images and environments that containerize your workloads.