Docker - up and running

Docker - up and running

Originally published on bojana.dev

Installing Docker (Linux, Windows, Mac)

I will not go into much details about how to install docker environment on your desired OS. There are a lot of quality articles out there that will help you with that. Official docker documentation seems to be a good option, but it's up to you. Here are official guides for installing docker engine (desktop) on Ubuntu, Windows and MacOS.

Docker - open source container engine

In the last article we talked about how containerization has been a big trend in the IT world for the past few years, and it continues to be as we speak. Although it's kind of a synonym for containers these days, it's worth noting that Docker isn't the only container engine or solution for managing containers out there. Container engines such as LXC, RKT, Railcar, CRI-O and others also exist. We also mentioned OCI - Open Container Initiative, a consortium created for purpose of creating open industry standards around container formats and runtimes.

Basic architecture

Docker uses a client-server architecture.
docker is an executable, a client, that talks to dockerd, a daemon process, that implements container and images operations. These two can run on the same system or the client can connect to a remote docker daemon.The client communicates with docker daemon over UNIX sockets, via REST API or over a network interface. Another example of docker client is docker compose.

As said above, docker client is the one who interfaces with docker daemon, by sending commands over the command line.

bojana@linux:~$ docker info
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Build with BuildKit (Docker Inc., v0.6.1-docker)
  scan: Docker Scan (Docker Inc., v0.8.0)

Server: Containers: 10 Running: 2 Paused: 0 Stopped: 8 Images: 5 Server Version: 20.10.8 Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true userxattr: false Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: 1 ...........

An image is a template for the container. Typically, images are based on some Linux distribution with some additional customizations (these are called parent images, in you Dockerfile it refers to the content of FROM directive). This is because Linux distributions define a complete operating environment. Image doesn't have to be based on some Linux distribution. For example, there is "scratch" image, an explicitly empty image, intended to be a basis for other images.

You may build an image which is based on the debian image, but installs .NET Core SDK and your application, as well as the configuration details needed to make your application run.

Basic Docker commands

To create your own image, you create a file name Dockerfile, with simple directives on how to create the image and run it.

Typically images are pulled from some private or public docker registry (such as Docker Hub).
To pull the image from the registry we use command docker pull:

bojana@linux:~$ docker pull debian
Using default tag: latest
latest: Pulling from library/debian
955615a668ce: Pull complete 
Digest: sha256:08db48d59c0a91afb802ebafc921be3154e200c452e4d0b19634b426b03e0e25
Status: Downloaded newer image for debian:latest
docker.io/library/debian:latest

The hex strings are the layers of the union file system.
Each instruction in a Dockerfile creates a layer in the image. When you change the Dockerfile and rebuild the image, only those layers which have changed are rebuilt. This is part of what makes Docker so lightweight and fast.

In the example above we used docker pull command to retrieve a docker image for Debian Linux distribution. Because we didn't requested specific version or a tag it downloaded "latest" tag by default. On the docker hub web page you can find supported tags and respective Dockerfile links for each image of interest. https://hub.docker.com/_/debian

To inspect all locally available images, we use command docker images

bojana@linux:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
postgres latest 293e4ed402ba 3 months ago 315MB
jenkins/jenkins lts de181f8c70e8 4 months ago 569MB
portainer/portainer latest 580c0e4e98b0 5 months ago 79.1MB
hello-world latest d1165f221234 6 months ago 13.3kB

When we have an image, it very easy to create a running container out of it.

bojana@linux:~$ docker run -p 80:80 --detach --name web nginx:latest
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
a330b6cecb98: Pull complete 
5ef80e6f29b5: Pull complete 
f699b0db74e3: Pull complete 
0f701a34c55e: Pull complete 
3229dce7b89c: Pull complete 
ddb78cb2d047: Pull complete 
Digest: sha256:a05b0cdd4fc1be3b224ba9662ebdf98fe44c09c0c9215b45f84344c12867002e
Status: Downloaded newer image for nginx:latest
aab3ce850b9a2a6a8f2469b8773362133f95839406ce276a6d04d2bfc2b7f67f


We didn’t have the “nginx” image locally, so Docker had to pull it from the registry.
When we run this command, docker will install nginx:latest from the nginx repository hosted on dockerhub and run the software. After that one line of seemingly random characters will be displayed.
This is the identifier of the container we just created.
It seems nothing else happened, because we used --detach option and started the program in the background. Detached - means detached from the terminal. Typically, server software runs in detached mode because we don't want to depend on terminal session.

We use docker ps to list all running containers:

bojana@linux:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b101295f67e nginx:latest "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 80/tcp

By default, when you create or run a container using docker create or docker run, it does not publish any of its ports to the outside world. Using the flag -p or --publish in the docker run command, will create a firewall rule which maps a container port to a port on the Docker host to the outside world.

With NGINX running in the container and port 80 mapped from the host, we can make HTTP requests to the container with curl. NGINX serves a generic HTML landing page by default.

bojana@linux$ curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

If we want to see STDOUT from the container we use docker logs command, in this case nginx access log.

bojana@linux:~$ docker logs web
172.17.0.1 - - [06/Sep/2021:13:34:47 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.68.0" "-"

If we add -f flag we can examine a real-time stream of container's output. To debug or troubleshoot the running container we could also start an interactive shell

bojana@linux:~$ docker exec -it nginx bash
root@aab3ce850b9a:/# apt-get update && apt-get -y install procps
root@aab3ce850b9a:/# ps ax
    PID TTY      STAT   TIME COMMAND
      1 ?        Ss     0:00 nginx: master process nginx -g daemon off;
     32 ?        S      0:00 nginx: worker process
     33 ?        S      0:00 nginx: worker process
     34 ?        S      0:00 nginx: worker process
     35 ?        S      0:00 nginx: worker process
     43 pts/0    Ss     0:00 bash
    372 pts/0    R+     0:00 ps ax

The process list reveals the nginx master daemon, an nginx worker, and our bash shell. When we exit the shell created with docker exec, the container continues to run.

We can start and stop the container:

bojana@linux:~$ docker stop nginx
nginx
bojana@linux:~$ docker ps
CONTAINER ID   IMAGE       COMMAND        CREATED      STATUS       PORTS
bojana@linux:~$ docker start nginx nginx bojana@linux:~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES aab3ce850b9a nginx:latest "/docker-entrypoint.…" 52 minutes ago Up 5 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp

When containers are stopped/exited they remain in the system in the dormant state. We can see all the containers on the local system (running/exited) by supplying -a flag to the docker ps command:

bojana@linux:~$ docker ps -a
CONTAINER ID   IMAGE                    COMMAND                  CREATED             STATUS                         PORTS                                                                                  NAMES
aab3ce850b9a   nginx:latest             "/docker-entrypoint.…"   54 minutes ago      Up 2 minutes                   0.0.0.0:80->80/tcp, :::80->80/tcp                                                      web
65787d8734ab   debian                   "/bin/echo 'Hello Wo…"   About an hour ago   Exited (0) About an hour ago                                                                                          strange_napier
33d2c89cdc09   nginxdemos/hello         "/docker-entrypoint.…"   7 days ago          Exited (0) 7 days ago    

It's not particularly bad to keep them lying around, but it's considered bad practice (due to possible name collisions with new containers). We stop and remove the container with:

bojana@linux:~$ docker stop nginx && docker rm nginx
nginx
nginx

And if we want to remove the image:

bojana@linux:~$ docker rmi nginx
Untagged: nginx:latest
Untagged: nginx@sha256:a05b0cdd4fc1be3b224ba9662ebdf98fe44c09c0c9215b45f84344c12867002e
Deleted: sha256:822b7ec2aaf2122b8f80f9c7f45ca62ea3379bf33af4e042b67aafbf6eac1941
Deleted: sha256:47dec9bde9e483e6265a6f52ab1e726724927e2e9d2ac358fdf58fbfcd6cf418
Deleted: sha256:7920a27f48f198550d59f64681b99441bbc3d2ce4778a855ce1ef9bafc96ae69
Deleted: sha256:a3c5a94eb1ea071c73dcea1969e0b71beea445d3b9d0735eaf6715d8e351434c
Deleted: sha256:e73eb58ed241e67a7a2c8589dde85eb72811eac1eb4cf3b586e40d2b9cc9d0c1
Deleted: sha256:b5d976dc9b0fa380affe1f6a17df18f02ab7debec2d35a0407fb863338591ed7
Deleted: sha256:d000633a56813933cb0ac5ee3246cf7a4c0205db6290018a169d7cb096581046

These are some basic docker commands to get you up and running when it comes to dealing with images and containers. There are a lot of other docker commands related to networking, volumes, building images etc. that we will be tackling in some of the upcoming blog posts.