Docker Basics

Advertisement

Advertisement

Introduction

Docker is useful for containerizing and managing processes. It is a great tool for developers and devops teams. Docker is good for creating reproducible environments. It is easy to customize images and document how an environment should be configured. We will look at how to:

  • Install Docker
  • Manage images, containers, and volumes
  • Create custom images
  • Pushing images to DockerHub and running custom registry
  • and more!

Installation

Download and install Docker from the official Docker website. After installing, you may need to restart your computer. After installation, the docker command should be available in the shell.

Debian based distributions

Download the .deb package from https://download.docker.com/linux/ubuntu/dists/. Find the files inside the pool/stable/ subsection.

For example:

  • https://download.docker.com/linux/ubuntu/dists/bionic/pool/stable/amd64/containerd.io_1.2.5-1_amd64.deb
  • https://download.docker.com/linux/ubuntu/dists/bionic/pool/stable/amd64/docker-ce-cli_18.09.4~3-0~ubuntu-bionic_amd64.deb
  • https://download.docker.com/linux/ubuntu/dists/bionic/pool/stable/amd64/docker-ce_18.09.4~3-0~ubuntu-bionic_amd64.deb

Download and install in order:

  1. containerd.io
  2. docker-ce-cli
  3. docker-ce

For example:

sudo apt install runc  # A dependency
sudo dpkg -i containerd.io_1.2.5-1_amd64.deb
sudo dpkg -i docker-ce-cli_18.09.4~3-0~ubuntu-bionic_amd64.deb
sudo dpkg -i docker-ce_18.09.4~3-0~ubuntu-bionic_amd64.deb

Add the necessary users to the docker group and then reboot:

sudo gpasswd -a someuser docker
sudo reboot

Manage Images

Pull an image

Find an image on Docker Hub. Alpine is the first image I would recommend getting. It's a tiny \~5MB Linux base with BusyBox shell. It's good for basic docker testing and can be extended to create your own custom images. You can get the image by running:

docker pull alpine
# Some other examples
docker pull ubuntu
docker pull python:3.7.0
docker pull openjdk:11-jre
docker pull openjdk:11-jdk

Here is a list of some other popular images:

nginx, busybox, redis, httpd, mongo, ubuntu, postgres, node, mysql, memcached, registry, hello world, golang, docker, centos, mariadb, drupal, cassandra, vault, jetty, redmine, pypy, django, gcc, rails, swift, rust, irssi

List images

After you have an image installed you can list them with the following commands:

    docker images
    docker image ls

Remove an image

To permanently delete a base image. Add the -f flag if you need to kill running containers currently runnng that image.

# Delete an image, 
docker rmi -f alpine

Save and load images to files

To export and import images as files you can use save and load commands. This is useful for transferring and sharing images without a registry.

docker save ubuntu > ubuntu-all.tar
docker save ubuntu:latest > ubuntu-latest.tar

docker load < ubuntu-latest.tar

Push an image to a registry

You can use DockerHub or a custom registry server.

Push to DockerHub public registry

docker tag xxxidxxx myusername/my_image:1.0.0
docker login --username=myusername --email=myemail@example.com
docker push username/my_image

Push to a custom registry

You can push and pull to a custom registry using similar commands. For example, if you have a custom registry on localhost:9999:

docker tag image_name localhost.local:9999/myimage
docker push localhost.local:9999/myimage
docker pull localhost.local:9999/myimage
Run a custom registry

If you want to run your own custom or private registry, there is a special Docker image named registry that can be run to host your own registry. Read more about registry deployment.

# Boot custom registry
docker run -d -p 5000:5000 registry

Create a custom image

You can create a custom image a couple ways:

  • Build an image using instructions in a Dockerfile
  • Take a snapshot from an existing container

You can build custom images from scratch or build on top of base images like Alpine. You can find the Dockerfiles that are used to create the images on DockerHub. You can use those Dockerfiles as references. To create an image, you need to define a Dockerfile first which acts as the template which is then built using docker build to create the image which can then be pushed to DockerHub, or used to create containers.

Create image from a Dockerfile

Any image can serve as a base image. Some recommended base images are alpine, python3, and ubuntu. Docker also provide a base image named scratch that allows you to build totally custom images. The scratch image is good if you only want to drop in one single executable. You can read more about creating base images from Official Docker Base Images Documentation.

Simplest Dockerfile possible

This example won't do anything but create an useless image.

FROM scratch
MAINTAINER Your Name <youremail@example.com>

Here is a practical minimal example that will at least add an executable

FROM scratch

ADD my_executable /
CMD ["/my_executable"]  # Default command run with `docker run image_name`

Example Dockerfile with common options

Here is an example that includes some common operations:

FROM ubuntu:latest
MAINTAINER NanoDano <nanodano@devdungeon.com>

# Set environment variables
ENV PATH=$PATH:/new/bin

# Copy file from local system in to image
COPY ./src ./dest
# Similar to copy, also supports remote URLs & auto-unpacks zips
ADD ./src ./dest

# Expose ports. Must be published at runtime `docker run -P -d my_image`
EXPOSE 80/tcp 443/tcp

# Mount points
VOLUME ["/etc/confdir", "/var/lib/datadir"]

# Change working dir (create if does not exist)
WORKDIR /new/working/dir 

# Run command when building image (e.g. RUN apt install git)
RUN apt-get update
RUN apt-get install -y git

# Command to run when `docker run` invoked directly.
# Any args passed get passed to the entrypoint executable
ENTRYPOINT ["/bin/bash", "-c"]

# Default args for entrypoint if none are provided to `docker run`.
# The CMD is executed directly if no ENTRYPOINT present.
CMD ["env"]

Then build and run the image:

# Provide the directory containing the `Dockerfile`
docker build . --tag myimage

docker run myimage  # Will run `/bin/bash -c env` by default
docker run myimage git --version

Read more about Docker best practices.

Common base images

Here are a few popular base images for progammers. These are just a few arbitrary examples.

  • alpine:latest
  • ubuntu:latest
  • openjdk
  • openjdk:jre
  • openjdk:14-jdk
  • python:3.8
  • python:3.6.8-alpine

You can explore more images at https://hub.docker.com/search?q=&type=image.

Build image from Dockerfile

From the directory containing the Dockerfile. Build will look for a Dockerfile in the directory.

docker build --tag my_image:latest .

Docker can also take input from STDIN, a direct filepath, or a URL.

# Take input from STDIN (three options)
docker build -
docker build - < Dockerfile
cat Dockerfile | docker build -

# Direct file path
docker build /path/to/Dockerfile

# Remote URL
docker build https://raw.githubusercontent.com/nodejs/docker-node/master/10/alpine/Dockerfile

Create image from container snapshot

Instead of building a image from a Dockerfile, you can create new images from existing containers by using commit and tag commands.

docker run 
docker commit container_id # Generates unnamed hash (Image ID)
docker tag <commit_hash> new_image_name # Tag an unnamed hash

# Commit and tag in a single step
docker commit container_id new_image_name:latest

Manage containers

Containers are basically running images. Containers are run from image templates. Containers can be started and stopped and images cannot. Many containers can be started from the same image.

Run a container

There are several ways to invoke and run a container. You can have the docker image run a single command and output the results or have it detach and run in the background as a service.

You can run with a container with all the defaults by just running it by name with no arguments. Alpine doesn't do anything by default so it will just exit.

docker run alpine

Run interactive terminal

To run an interactive (-i) and psuedo-tty(-t) to get a shell, use -it like this:

docker run -it alpine
docker run -it alpine /bin/sh

Connect to running container and get a shell

To connect to a running container and get a shell (regardless of entrypoint), you can use docker exec.

# Connect to an existing _container_ and get a shell
docker exec -it <container_id> /bin/sh

Specify restart options

You can have a container automatically restart by specifying the --restart option.

docker run --restart=always image_name

Options for --restart:

  • no
  • on-failure[:max-retries]
  • always
  • unless-stopped

Run a single command

You can run single commands and view the output directly using this format: docker run <image_name> <command>. For example:

docker run alpine echo Hello, world
docker run openjdk:jre java -version
docker run python:3.7.0 python --version

# Will run forever, until stopped or sigint received
docker run alpine watch date

# Override the entrypoint with --entrypoint
docker run -it --entrypoint /bin/sh python:3.6.8-alpine

Running in detached mode

You can run a docker image in the background similar to adding & to the end of a command. Just add the -d flag to detach after running.

docker -d run alpine watch date

# Name the container
docker run -d --name watcher alpine watch date 

Automatically delete image after running

If you don't need an image to persist after a single run, you can flag it to delete itself after running.

docker run --rm alpine echo Hello

Mapping ports

To open ("publish") ports that are exposed inside the container, use the -p flag in the format of docker run -p <host_port>:<container_port>. Use -P to publish all exposed ports in container.

For example, to allow 80 and 443 from an nginx container:

# Make localhost:9999 on host viewable
docker run -d -p 9999:80 nginx

# Publish ALL exposed ports defined in container
docker run -d -P nginx

View ports that are listening with:

docker ps
netstat -lt  # sudo apt install net-tools

Mount volumes

If a docker container needs to read or write a directory outside of itself, you need to mount a volume. You can mount it directly to a directory or to a proper "Docker volume".

It is easier to simply mount a directory, but it is better to create a Docker volume to better share it between multiple containers, manage it using Docker CLI, can easily be encrypted, stored remotely, shared between Windows and Linux, and other benefits. Volumes will be covered more below.

There is a --mount and a --volume/-v option. The simpler and recommended one to use is --mount.

docker run -v /path/on/host:/path/inside/container my_image
docker run -v my_volume:/mnt/path my_image

docker run --mount source=/path/on/host,target=/path/inside/container my_image
docker run --mount source=my_volume,target=/path/inside/container my_image

There are many more options available like readonly. Refer to the Official Docker Volume.

List containers

To see what containers are active and how long they've been up, use the ps command. This will provide the ID and the name of the containers, both of which you can use to call stop.

# List running containers
docker ps

# List all containers, including stopped ones
docker ps -a    

Get usage statistics of running containers

The stats command will give you information about memory, CPU, disk I/O, network usage, and process count of each running container.

# Memory, CPU, disk, network usage, process count
# Like `top` for docker
docker stats

Pause, stop and restart a container

You can shutdown a container by calling stop and passing it the name or the ID of the container, which you can get with the ps command.

Instead of shutting down or restarting a container, there are times when pausing a container is useful. This could help if a container performs heavy computation and you want to pause it for a while without shutting down. It can also be useful in certain debugging situations. You can use the pause and unpause commands.

# Shutdown a container by name or id `docker ps`
docker stop wonky_wombat

# Restart a container, works on running and stopped containers
docker restart tall_tiffany

# Pause all processes, but keeps container running
docker pause captain_carl
docker unpause captain_carl

Delete a container

When a container is stopped, it is not completely gone. To delete a container use the docker rm command. You can list all containers with docker ps -a. You can't delete a running container without including the -f flag.

# Delete a stopped container
docker rm some_container

# Delete a running container
docker rm -f some_container

Copy files to/from a container

The docker cp command works similar to scp but the remote host name is the container id.

docker cp /host/path container_name:/path/in/container
docker cp container_name:/path/in/container /host/path

Manage volumes

Volumes are for persistent file storage. You could tie a running image to a directory on the host system, but it is better to create a volume that is managed by docker. Containers can even be backed up to and restored from volumes.

Create a volume

docker volume create my_volume

List volumes

docker volume ls

Get volume details

docker volume inspect my_volume

Delete a volume

docker volume rm my_volume

# Delete all unused volumes
docker volume prune

Learn more

As always, refer to the official documentation for the most thorough and up-to-date information. You can also get direct help from the docker application itself using the following commands:

# Help info
docker --help

# Examples of sub-help options
docker ps -h
docker container -h
docker image -h

Explore public images on Docker Hub.

Conclusion

You should now understand the very basics of Docker, how to find and pull existing images, build your own custom images, and run the containers. Check out the official Docker site for more information.

Advertisement

Advertisement