Docker Engine

May 27, 2016

Business Insider, lyft, ebay, yelp, BBC news, ING, shopify... Docker is more and more adopted by companies even by the largest banks. What is docker and why is it so popular?

Docker is an operating-system-level virtualization which provides a secure way to run applications isolated inside containers.

From the official website:

Docker containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in.

While a Virtual Machine is emulating virtual hardware, docker uses shared operating systems. In other words, a VM needs its own CPU's, RAM, hard disk, kernel, etc... whereas containers have a host that uses some kernel extensions to isolate software. Hence, containers share the host computer's CPU's, RAM, kernel but they are secluded from each others.

docker containers vms

Docker engine can bear a hand for all your deployment workflow. Even if your local environment differs from your production, you can easily launch test in a similar way that it would run in production due to docker. Indeed, you can run containers with the same operating system and the same application version. For instance, you can specify ubuntu 14.04 and postgres 9.5.3. Furthemore, you can even deploy your containers for your production.

Otherwise, by the portability and lightweight nature of the containers, you can scale up or tear down your application and services with ease. Images can be updated and shared very simply.

Moreover, one can easily test any script with any version of operating system in a safety way. There is no risk of corruption for the host machine because it's completly isolated.

Docker's architecture

The architecture relies on three main components: a daemon, a client and registries. The client and the daemon are usually on the host machine and the official registry is reachable through the docker hub platform. However, you can have your own private registry.


Docker daemon

Running on the host machine, the docker daemon manages images, containers and registries. The users communicate with the docker daemon through the Docker client.

Docker client

Every docker commands is handled by the docker client. It's the user interface to Docker. It communicates back and forth with the Docker daemon.

Docker images

A docker image is a read-only build template. Containers are created from images. For instance, we could create a new container of Ubuntu from the ubuntu image. Images are based upon a Dockerfile: a file with instructions.

Docker registries

Server side application that stores and lets you distribute Docker images.

Docker containers

A container holds everything that is needed for an application to run. Docker containers can be run, started, stopped, moved and deleted.

Docker hub

Usually, all base images are pulled from Docker Hub. This platform stores all official images like nginx, ubuntu, redis, mongo.

First steps

Let's dig into it. Follow the installation guide if docker is not yet installed on your machine. Once you are done, you should be able to run docker --help from the command line. You can display docker's information with:

docker info

First of all, you need to have an image locally to create a new container. For example, you can pull ubuntu:

docker pull ubuntu

To see all images on your system, hit

$ docker images
REPOSITORY  TAG     IMAGE ID      CREATED       VIRTUAL SIZE
ubuntu      latest  7bd023c8937d  21 hours ago  122 MB

Later, you will remove the image with rmi command:

docker rmi ubuntu

Run bash in a new container:

docker run ubuntu /bin/bash

Note that if you try to run a container from an image you don't have locally, docker will first try to pull it from the official registry. Nothing was logged, however you can see something happened with the command ps:

$ docker ps -a
CONTAINER ID  IMAGE   COMMAND     CREATED        STATUS                      PORTS  NAMES
8254b15d7289  ubuntu  "/bin/bash" 2 minutes ago  Exited (0) 2 minutes ago           loving_kilby

docker ps list all running containers and docker ps -a list all your containers.

Bash was indeed run but it was not kept alive. Actually, a Docker container only stays alive as long as there is an active process being run in it.

Delete this container:

docker rm 8254b15d7289 # OR docker rm loving_kilby

Delete all exited container:

docker rm $(docker ps -q -f status=exited)

This time, we'll run bash in an interactive mode to execute commands from within and name our container bash_container.

docker run --name bash_container -it ubuntu /bin/bash
  • -i - Keep stdin open (so we can interact with it).
  • -t - Allocate a (pseudo) tty.
  • --rm - Remove the container once exited.
  • --name - If you do not assign a container name, then the daemon generates a random string for you. By specifying a name, you can use it when referencing the container whithin a Docker network.

Once you have play around, echo "Hello World!", hit ctrl+d to exist out of the shell. Docker keeps track of the changes in the container, you can see this with:

$ docker diff bash_container
C /root
A /root/.bash_history

Create a server

We will create a new image allowing us to run a server with Node.js. Inside a new directory, create two files:

server.js

// Load the http module to create an http server.
const http = require('http');
const PORT = 8080;

// Configure our HTTP server to respond with Hello World to all requests.
const server = http.createServer((request, response) => {
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello World\n');
});

// Listen on port 8080, IP defaults to 127.0.0.1
server.listen(PORT);

// Put a friendly message on the terminal
console.log(`Server running at http://127.0.0.1:${PORT}/`);

Then, create a new file Dockerfile:

# Specify the base image from which you are building.
FROM node:6.2.0

# Copy our server.js file into the container
COPY server.js /

# Informs Docker that the container listens on the specified network ports
# at runtime. It does not make the ports of the container accessible to the
# host.
EXPOSE 8080

# Launch the server
CMD node server.js

From your directory, build the new image:

docker build -t my_server .
  • -t - Name and optionally a tag in the 'name:tag' format

Then launch your server within a container:

docker run -d -p 8090:8080 --name my_running_app my_server
  • -p, --publish - Publish a container᾿s port or a range of ports to the host format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort.
  • -d, --detached - Start a container in detached mode

Note that publish will expose a port to the host but for a specific protocol. When not specified, the default protocol is tcp. If you want allow udp protocol, you have to add it: 8090:8080/udp

Test the connection to our server with netcat

$ nc -vz localhost 8090
Connection to localhost 8090 port [tcp/*] succeeded!
  • -v Have nc give more verbose output.
  • -z Specifies that nc should just scan for listening daemons, without sending any data to them

And check content with curl:

$ curl -i localhost:8090
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sun, 29 May 2016 09:22:50 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Hello World
  • -i, --include - Include the HTTP-header in the output.

Inspect the container in an interactive mode:

docker exec -it my_running_app /bin/bash # OR /bin/sh
  • -i, --interactive - Keep STDIN open even if not attached
  • -t, --tty - Allocate a pseudo-TTY

Sometimes, you need elevate privilage to run some command but sudo is not available. In these case, you can log in as root:

docker exec -it -u root my_running_app /bin/bash # OR /bin/sh
  • -u, --user - Username or UID (format: <name|uid>[:<group|gid>])

Our server.js file is indeed here:

root@5f604f615b52:/# ls
bin   dev  home  lib64  mnt  proc  run   server.js  sys  usr
boot  etc  lib   media  opt  root  sbin  srv      tmp  var

And node is running:

root@5f604f615b52:/# ps aux | grep node
root   1  0.0  0.0   4328   644 ?  Ss  09:17  0:00 /bin/sh -c node server.js
root   5  0.0  0.2 837644 16988 ?  Sl  09:17  0:00 node server.js
root  41  0.0  0.0  11120   712 ?  S+  09:41  0:00 grep node

Exiting the interactive shell will not interrupt the process hopefully.

Other usefull commands:

$ docker inspect my_running_app
[
{
    "Id": "5f604f615b52c15445726eaecbcb2daa1a8b47d19f0700bbc60069024e99326b",
    "Created": "2016-05-29T09:17:57.878196403Z",
    "Path": "/bin/sh",
    "Args": [
        "-c",
        "node server.js"
    ],
    "State": {
        "Running": true,
        "Paused": false,
        "Restarting": false,
        "OOMKilled": false,
        "Dead": false,
        "Pid": 13644,
        ...
    },
    "Name": "/my_running_app",
    ...
    },
    "Config": {
        "Hostname": "5f604f615b52",
        "Domainname": "",
        "User": "",
        ...
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "NPM_CONFIG_LOGLEVEL=info",
            "NODE_VERSION=6.2.0"
        ],
        "Cmd": [
            "/bin/sh",
            "-c",
            "node server.js"
        ],
        "Image": "my_server",
        "Volumes": null,
        "VolumeDriver": "",
        "WorkingDir": "",
        "Entrypoint": null,
        "NetworkDisabled": false,
        "MacAddress": "",
        "OnBuild": null,
        "Labels": {}
    }
}
]

Logs:

$ docker logs -ft my_running_app
2016-07-03T17:53:05.346227903Z Server running at http://127.0.0.1:8080/
  • -f, --follow - Follow log output
  • -t, --timestamps - Show timestamps

Finally, you can stop the container:

docker stop my_running_app

And remove it:

docker rm my_running_app

You can force the removal if you don't want to stop it before.

docker rm -vf my_running_app
  • -f, --force - Force the removal of a running container
  • -v, --volumes - Remove the volumes associated with the container

Remove all running containers

docker rm -f $(docker ps -qa)
  • -q, --quiet - Only display numeric IDs

In a future post, we'll see how to publish in a private registry.

References:

  1. Docker - wikipedia
  2. Operating-system-level virtualization- wikipedia
  3. Quickstart - docs.docker.com
  4. What is docker and why is it so darn popular? - zdnet
  5. Getting started with docker - serversforhackers.com
  6. Docker containers explained for the novice - westerndevs.com
  7. Dockerizing a Node.js web app - nodejs.org