Introduction to Docker, Part 1: Getting Started
Docker radically changed the IT world as it was the product that popularized container technology.
Containers, unlike virtual machines (VMs), do not virtualize an entire operating system (OS); instead, they isolate each instance from one another and have their own software, libraries and configuration files. Because containers share the services of a single OS kernel, they use fewer resources than VMs. Moreover, being that they are self-contained, containers can be stored and pulled from a central repository and run on other computers.
Docker is a technology that I have been wanting to work with for a while, but I have not made time to do so until now. In this series article, I will show you how to install Docker containers on a Linux system. I will also discuss Docker images and containers, the performance hit of Docker, and how to use network and storage with containers.
By way of terminology, Docker is a set of products that allow you to set up a Platform-as-a-Service (PaaS) implementation that runs instances (containers) of an application or OS. These instances are created from images that can be created or pulled down from a central repository. I will use the terms "instance" and "container" interchangeably throughout this series of articles to describe a running image.
Docker is both hardware and OS agnostic. It can run on any number of OSes, including Linux, Windows and macOS. Likewise, it runs on Intel, ARM and other hardware. For these articles, I will be installing Docker on top of Ubuntu running on a Maxtang NX6412-B11, a low-cost ($300), small form-factor computer containing a quad-core Intel Celeron J6412 processor with 8GB of RAM and a 128GB SATA SSD. You can read my hands-on review of the NX6412-B11 here.
I chose Ubuntu 22.04.1 as my OS as it has long-term support (LTS) and, as such, is a likely candidate for deployment for both companies and individual users alike. The NX6412-B11 met the recommended system requirements for Ubuntu 22.04.1 LTS; it has a 2 GHz dual-core processor or better, 4 GB of system memory, and 25 GB of free hard drive space.
I booted off a USB with the Ubuntu install ISO and installed Ubuntu using its default settings without any difficulties.
To install Docker, I logged in to Ubuntu, became root user, updated my apt repository, installed the other packages that I would need, added the Docker repository to my apt repository, and then added Docker to my system by entering:
apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"
apt-cache policy docker-ce
apt install docker-ce
After Docker was installed, I verified that it was running by entering:
systemctl status docker
To verify that it could download a test image and run it, I entered:
docker run hello-world
As shown in its output, Docker ran a "hello-world" command, pulled an amd64 image down from the Docker hub (a repository for Docker images), created a container for it and ran it. This displayed a message from the container.
Following the suggestions in the hello-world output, I downloaded and ran an Ubuntu container in an interactive mode (the -it switches) by entering:
docker run -it ubuntu bash
The command prompt (root@cbe131435785:/#) told me I was inside the Ubuntu instance and not on the host OS.
Due to the fact that the container is a bare-bones Ubuntu installation, I found that when I tried to run some common commands, they were not available, but it did display the same kernel (5.15.0-53-generic) as the machine that the instance was running on.
Basic Docker Commands
So far, I have only run the docker run <image> command, which downloaded a Docker image and instantiated (started) it. Docker has a rich set of commands to help manage the containers. Toward the end of this article are a few examples of commands I used to gain more insight into the Docker system.
I opened another SSH connection to the host on which Docker was running and listed the images that had been download by entering:
docker image ls
This showed the two images that I had downloaded (note the extremely small size of the Ubuntu image).
To see which containers were running, I entered:
By adding the "-a" switch to the command, I could see the status of all the containers.
The "Up" status of the Ubuntu image indicated that it was still running in the background, while the "Exited" status of hello-world showed that it was no longer actively running.
When I reentered docker run hello-world, and entered ps -a, it showed two hello-world containers, each with a unique container ID.
To stop my Ubuntu image, I entered:
docker stop cbe131435785
To restart the Ubuntu image, I entered docker start cbe131435785.
At this point, the container was running, but I had lost the access I had when I originally started it with the "-it" command. To get an interactive prompt to the container, I entered:
docker exec -it cbe131435785 /bin/sh
This allowed me to interact with the bash shell of the container. From this prompt, I could see my hostname, which was different than that of my host OS.
To stop the container, I entered:
docker stop cbe131435785
So far, I have used the container ID here to manage the container, but you can use the "-name" switch to give it a friendly name when you instantiate it. To demonstrate this, I started two different containers, each running a different Linux distribution (Ubuntu and Alpine) in the background (-d switch) with one using a name and the other without by entering:
docker run -itd --name=DocUbuntu ubuntu
docker run -itd alpine
The ps command showed that one container had an assigned name (DocUbuntu) and the other had an auto-assigned name (angry_ganguly).
I entered the following commands to execute a command in the containers that showed the distribution of the two container Linux distributions and then the host OS:
docker exec DocUbuntu cat /etc/*-release | grep PRETTY
docker exec angry_ganguly cat /etc/*-release | grep PRETTY
cat /etc/*-release | grep PRETTY
To stop all my running containers and delete them from my system, I entered:
docker ps -a
docker stopl $(docker ps -aq )
docker ps -a
docker container rm -f $(docker ps -aq)
docker ps -a
Here is a summary list of Docker commands used in this article:
Docker image (hello-world) from the public repository if needed and
-it ubuntu bash
Open a shell
inside the container (-it)
container in the background (-d) with a given name (DocUbuntu)
images on the local system
List the running
docker ps -a
running and stopped containers (-a)
exec -it cbe131435785 /bin/sh
Run a command
(/bin/sh) in the container specified
In this article, I walked you through how I installed Docker on top of Ubuntu. I also showed you how I downloaded and ran two different Linux distributions in containers and did basic commands to start, stop and run containers in interactive mode, as well as run a single command in a container.
It was interesting to run Linux in a container, but containers prove even more powerful in their ability to run applications. In my next articles in this series on containers, I will look at the ephemeral nature of containers and the resources that they use, explain how to use a container with networking and persistent storage, and how to run a containerized application.
Update: The second article is now live.