Getting Started with Docker

Docker is a tool for developers that facilitates the creation, deployment, and running of applications using containers. In this post, I’ll go over what containers are, their benefits, and some of the fundamental Docker concepts and commands.

What are Containers?

Containers are groups of Linux processes – the collection of services and resources utilized by a program – that are run in isolation. Each Linux process is identified by:

  • PID – Process ID
  • UID – the ID of the user running the process
  • PPID – the parent process ID of the process that spawned the process (the init process (PID 1) is the root or mother process)
  • C – CPU utilization of the process
  • STIME – the start time of the process
  • TTY – the terminal type
  • TIME – the CPU time taken by the process
  • CMD – the command that started the process

Process Isolation

A container is a process itself, and the processes within the container are limited only to processes started inside the container. No processes being run outside of the container are visible within the container, and vise-versa.

The processes running inside of a Docker container share the operating system kernel of the host process. So, all contained processes must be able to run on the same kernel.

To achieve process isolation, containers use namespaces. When a Linux system starts, it has one namespace for each namespace type:

  • pid – for process IDs
  • user – for user and group IDs
  • UTS – hostname and domain name
  • mnt – mount points
  • net – network devices, stacks, ports
  • ipc – inter-process communication, message queues
  • cgroup – control group

Each container has its own set of namespaces. The namespaces inside of a container do not interact with other namespaces on the system. Containers also use cgroups – control groups – to allocate resources (e.g. CPU; memory; disk I/O; and networking) to containers. Having an independent set of namespaces and resources allocated means that what goes on in a container, stays in a container. This affords developers a high degree of control over the environment within a container.

But it Works on my Machine!

This ability to control the environment eliminates the problem of “it works on my machine”. When a developer produces an application that works in his test environment, but when it’s deployed to a production server it fails. Problems due to the discordance in the two environments is simply not an issue.

With containers, an application can be developed within the container, and the complete running environment of the application can be shipped to the production server.

Another benefit of using Docker is that it allows developers to work on containers regardless of their development machine’s operating system. The Docker tool has a Linux sub-system, so developers can build Linux container based applications on Mac or Windows.

As stated earlier, Docker is a tool that simplifies working with containers. The rest of this post will go into actually using Docker.

Note: Docker also supports native Windows kernel containers. Check out this blog post.

The Docker documentation can be found here.

First Run of a Docker Container

After installing Docker on your system, issue the container run command to start running a container.

# docker container run -t ubuntu ps aux

Note: The # symbolizes that I’m running commands as the root user. Docker requires superuser privileges, so either run commands with sudo or as root.

To avoid running docker as root, or under sudo, you can add your user to the docker group (usermod -aG docker [USER]).

The form of the command is: 

# docker container run [OPTIONS] IMAGE [COMMAND] [ARG…]

The -t option instructs Docker to set up a pseudo-TTY (pseudo terminal) where the ps program will run. Ubuntu is the selected image, and the aux is the argument (ARG) passed to the ps program. Looking at our output we see that the Ubuntu Docker image is not found.

Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
Digest: sha256:6d0e0c26489e33f5a6f0020edface2727db9489744ecc9b4f50c7fa671f23c49
Status: Downloaded newer image for ubuntu:latest

The latest Ubuntu image is pulled from the Docker Registry – a distribution repository for Docker images.

Note: Docker Official Images are security and stability tested and recommend for production use. It is not recommend to use unofficial images for production applications.

The ps command generates a snapshot of the current processes. Notice, there’s only one process listed.

# docker container run -t ubuntu ps aux
root 1 5.0 0.0 25944 1556 pts/0 Rs+ 21:36 0:00 ps aux

This is due to the isolation discussed in the What are Containers? section above.

Image Basics

Listing Local Images

We can list our locally available images with the docker image ls command:

# docker image ls
ubuntu latest 93fd78260bd1 3 weeks ago 86.2MB

Pulling Images from Docker Registry

To pull an image from the Docker Registry use the image pull command.

docker image pull [OPTIONS] NAME[:TAG|@DIGEST]

Removing Images

To remove images use the image rm command.

docker image rm [OPTIONS] IMAGE [IMAGE…]

The docker image prune command will remove all currently unused images.

Basic Container Operations

List all Running Containers

Issuing the docker container run command will create a new container. The syntax of the command follows:

docker container run [OPTIONS] IMAGE [COMMAND] [ARG…]

The COMMAND that is issued will run in a container based on the given image. To list all running containers issue the following command:

# docker container ls

Once the process started by the given command exits, the container will stop and you won’t be able to see the container listed (because it’s no longer running). If you’ve not used Docker before, except for following along with this post, you should not see any output.

List all Containers

To list all containers add the -a option.

# docker container ls -a
d60b0d6534d0 ubuntu "ps aux" 3 minutes ago Exited (0) 3 minutes ago awesome_ellis

Removing a Container

Let’s remove that container, and create a new Ubuntu container and run an interactive shell inside of it.

# docker container rm -f d60

I used only the first 3 characters of the container ID when telling the run command which image to use. When referencing images or containers, one only need provide as many characters to uniquely identify the resource (usually 3 is enough).

The docker container prune command will remove all stopped containers.

Running a Container Interactively

# docker run -it 93f bash

The -i option specifies we want to run interactively, and the 93f is the first 3 characters of the ID of the container we want run. If we issue an ls command, we’ll see the root directory structure for our Ubuntu image.

root@6fefa0432de9:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

To exit the container, simply issue the exit command. This will terminiate the shell and shut down the container.

root@6fefa0432de9:/# exit

Starting and Stopping Containers

Let’s run a shell in our Ubuntu container again, but this time we’ll not run interactively. We’ll use a container who’s ID begins with 6fe. To list all containers on the system, issue the docker container ls -a command).

# docker container start 6fe

The container has started and is running Bash.

# docker container ls
6fefa0432de9 93f "bash" 2 hours ago Up About a minute vigilant_poincare

We can stop the container with the docker container stop command. 

# docker container stop 6fe

Attaching to a Running Container

Because when the container was created, it was defined as interactive, we can start and then connect/attach to the running container.

# docker container start 6fe
# docker attach 6fe
root@6fefa0432de9:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

Leave a Reply

Your email address will not be published. Required fields are marked *