đł Docker Up and Running
Docker helps developers build, share, run, and verify applications anywhere â without tedious environment configuration or management.
Introduction
When you install Docker, you get two major components:
- The Docker client
- The Docker engine (sometimes called the "Docker daemon")
The Docker engine is the core software that runs and manages containers, it implements the runtime, API and everything else required to run containers. Once installed, you can use the docker version
command to test that the client and daemon (server) are running and talking to each other.
If you get a response back from the Client and Server, youâre good to go.
Images
Image, Docker image, container image, and OCI (Open Container Initiative ) image all mean the same thing. A container image is read-only package that contains everything you need to run an application. It includes application code, application dependencies, a minimal set of OS constructs, and metadata. A single image can be used to start one or more containers.
You get container images by pulling them from a registry. The most common registry is Docker Hub but others exist. The pull operation downloads an image to your local Docker host where Docker can use it to start one or more containers.
Pulling Images
Note: tag
is optional when you pull images, it default to latest
, if the repository doesn't have an image tagged as latest
, the command will fail. You should note the latest
tag does not guarantee it is the most recent image in the repository.
Filtering the Output of Docker Images
A dangling image is an image that is no longer tagged and appears in listings as <none>:<none>
. A common way they occur is when building a new image with a tag that already exists. When this happens, Docker will build the new image, notice that an existing image already has the same tag, remove the tag from the existing image and give it to the new image.
Docker currently supports the following filters:
dangling
: Acceptstrue
orfalse
, and returns only dangling images (true), or non-dangling images (false).before
: Requires an image name or ID as argument, and returns all images created before it.since
: Same as above, but returns images created after the specified image.label
: Filters images based on the presence of a label or label and value. Thedocker images
command does not display labels in its output.
For all other filtering you can use reference
.
You can also use the --format flag to format output using Go templates.
Deleting Images
When you no longer need an image on your Docker host, you can delete it with the docker rmi
command.
Deleting an image will remove the image and all of its layers from your Docker host. This means it will no longer show up in docker images commands and all directories on the Docker host containing the layer data will be deleted. However, if an image layer is shared by another image, it wonât be deleted until all images that reference it have been deleted.
Images and Layers
A Docker image is a collection of loosely-connected read-only layers where each layer comprises one or more files. Docker takes care of stacking the layers and representing them as a single unified object. All Docker images start with a base layer, and as changes are made and new content is added, new layers are added on top.
Consider the following oversimplified example of building a simple Python application. You might have a corporate policy that all applications are based on the official Ubuntu 22:04 image. This would be your imageâs base layer. Adding the Python package will add a second layer on top of the base layer. If you later add source code files, these will be added as additional layers. The final image will have three layers.
The docker inspect
command is a great way to see the details of an image. The docker history
command is another way of inspecting an image and seeing layer data. However, it shows the build history of an image and is not a strict list of layers in the final image.
Containers
A container is the runtime instance of an image. The simplest way to start a container is with the docker run
command. The command can take a lot of arguments, but in its most basic form you tell it an image to use and an app to run: docker run <image> <app>
. The following command will start a new container based on the Ubuntu Linux image and start a Bash shell.
The -it
flags connect your current terminal window to the containerâs shell.
The Commands
docker ps
: list all containers in the running state. If you add the-a
flag you will also see containers in the stopped (Exited
) state.docker exec
: run a new process inside of a running container. Itâs useful for attaching the shell of your Docker host to a terminal inside a running container. This command will start a new Bash shell inside a running container and connect to it:docker exec -it <container-name or container-id> bash
. For this to work, the image used to create the container must include the Bash shell.docker stop
: stop a running container and put it in theExited (0)
state. It issues aSIGTERM
to the process with PID 1 inside of the container. If the process hasnât cleaned up and stopped within 10 seconds it will send aSIGKILL
to forcibly stop the container.docker stop
accepts container IDs and container names as arguments.docker start
: restart a stopped container. You can give it the name or ID of a container.docker rm
: delete a stopped container. You can specify containers by name or ID. It is recommended that you stop a container with thedocker stop
command before deleting it withdocker rm
.docker inspect
: show you detailed configuration and runtime information about a container. It accepts container names and container IDs as its main argument.
Containerizing an App
Containers are all about making apps simple to build, ship, and run. The end-to-end process looks like this:
- Start with your application code and dependencies
- Create a Dockerfile that describes your app, dependencies, and how to run it
- Build it into an image by passing the Dockerfile to the docker build command
- Push the new image to a registry (optional)
- Run a container from the image
Inspecting the Dockerfile
A Dockerfile describes an application and tells Docker how to build it into an image.
The Dockerfile FROM
instruction specifies the base image for the new image youâre building. It is also used to distinguish a new build stage in multi-stage builds. The RUN
instruction lets you to run commands inside the image during a build. Each RUN
instruction adds a new layer to the overall image. The COPY
instruction adds files into the image as a new layer. Itâs common to use it to copy your application code into an image. The ENV
instruction sets the environment variables. The EXPOSE
instruction documents the network port an application uses. The ENTRYPOINT
instruction sets the default application to run when the image is started as a container.
Build the Image
Be sure to include the trailing period .
and be sure to run the command from the same directory where Dockerfile
exists.
docker build
is the command that reads a Dockerfile and containerizes an application. The -t
flag tags the image, and the -f
flag lets you specify the name and location of the Dockerfile. With the -f
flag, you can use a Dockerfile with an arbitrary name and in an arbitrary location.
Run the App
The -d
flag runs the container in the background, and the -p 80:8080
flag maps port 80 on the host to port 8080 inside the running container.
Moving to Production with Multi-stage Builds
The goal of the base
stage is to create a reusable build image with all the tools needed to build the application in the later stages. The image created by this stage will only be used to build the app and not used for production.
Perform the build. You should see the prod-deps
and build
stages execute in parallel. This makes large builds faster.
Volumes and Persistent Data
Containers are designed to be immutable, which means it's read-only, you should not change the configuration of a container after itâs deployed. If something breaks or you need to change something, you create a brand-new container with the fixes or updates and replace the old container with this new one. You should never log into a running container and make configuration changes. However, many applications require a read-write filesystem in order to run â they wonât even run on a read-only filesystem. To help with this, containers created by Docker have a thin read-write layer on top of the read-only images theyâre based on.
Volumes are the recommended way to persist data in containers. There are three major reasons for this:
- Volumes are independent objects that are not tied to the lifecycle of a container
- Volumes can be mapped to specialized external storage systems
- Volumes enable multiple containers on different Docker hosts to access and share the same data
At a high-level, you create a volume, then you create a container and mount the volume into it. The volume is mounted into a directory in the containerâs filesystem, and anything written to that directory is stored in the volume. If you delete the container, the volume and its data will still exist.
Creating and Mounting Docker Volumes
The command uses the --mount
flag to mount a volume called "bizvol" into the container at /vol
. The command completes successfully despite the fact there is no volume on the system called bizvol. This raises an interesting point:
- If you specify an existing volume, Docker will use the existing volume
- If you specify a volume that doesnât exist, Docker will create it for you
The Commands
docker volume ls
: list all volumes on the local Docker host.docker volume inspect
: show detailed volume information. Use this command to see many interesting volume properties, including where a volume exists in the Docker hostâs filesystem.docker volume prune
: delete all volumes that are not in use by a container or service replica. Use with caution!docker volume rm
: delete specific volumes that are not in use.
Deploying Apps with Compose
Compose uses YAML files to define microservices applications. The default name for a Compose YAML file is compose.yaml
. However, it also accepts compose.yml
and you can use the -f
flag to specify custom filenames.
Within the definition of the web
service, we give Docker the following instructions:
build: .
: This tells Docker to build a new image using theDockerfile
in the current directory (.). The newly built image will be used in a later step to create the container for this service.command: python app.py
: This tells Docker to run a Python app calledapp.py
in every container for this service. Theapp.py
file must exist in the image, and the image must have Python installed. The Dockerfile takes care of both of these requirements.ports:
: The example in our Compose file tells Docker to map port8080
inside the container (target) to port5000
on the host (published). This means traffic hitting the Docker host on port 5000 will be directed to port 8080 on the container.networks:
: This tells Docker which network to attach the serviceâs containers to. The network should already exist or be defined in thenetworks
top-level key.volumes:
: This tells Docker to mount thecounter-vol
volume (source:
) to/app
(target:
) inside the container. The counter-vol volume needs to already exist or be defined in thevolumes
top-level key in the file.
Letâs use Compose to bring the app up.
Normally youâll use the --detach
flag to bring the app up in the background. However, we brought it up in the foreground and used the &
to give us the terminal window back. This forces Compose to output all messages to the terminal window.
The commands
docker compose stop
: stop all containers in a Compose app without deleting them from the system.docker compose rm
: delete a stopped Compose app. It will delete containers and networks, but it wonât delete volumes and images by default.docker compose restart
: restart a Compose app that has been stopped with docker compose stop. If you make changes to your Compose app while itâs stopped, these changes will not appear in the restarted app. You need to re-deploy the app to get the changes.docker compose ps
: list each container in the Compose app. It shows current state, the command running inside each container, and network ports.docker compose down
: stop and delete a running Compose app. It deletes containers and networks, but not volumes and images.
Summary
We used to live in a world where every time the business needed a new application we had to buy a brand-new server. VMware came along and allowed us to drive more value out of new and existing IT assets. As good as VMware and the VM model is, itâs not perfect. Following the success of VMware and hypervisors came a newer more efficient and portable virtualization technology called containers. But containers were initially hard to implement and were only found in the data centers of web giants that had Linux kernel engineers on staff. Docker came along and made containers easy and accessible to the masses. Containers have taken over the world!