GDI Logo

Intro to Docker

Class 1

Welcome!

Girl Develop It is here to provide affordable and accessible programs to learn software through mentorship and hands-on instruction.

Some "rules"

  • We are here for you!
  • Every question is important
  • Help each other
  • Have fun

Welcome!

Tell us about yourself.

  • Who are you?
  • What do you hope to get out of the class?
  • Who was your favorite character as a child?

What we will cover today

  • What is containerization, and why should you care?
  • Container Basics
  • Background Containers
  • Docker Images

What's all the buzz about containers'?

The software industry has changed!

Before we had:

  • monolithic applications
  • long development cycles
  • single environment
  • slowly scaling up

Now we have:

  • decoupled services
  • fast, iterative improvements
  • multiple environments
  • quickly scaling out

Deployment becomes very complex.

Many different stacks:

  • languages
  • frameworks
  • databases

Many different targets:

  • individual development environments
  • pre-production, QA, staging
  • production: on prem, cloud, hybrid

What Docker gives you

  • Manage dependencies with ease
  • Onboard developers and contributors rapidly
  • Implement reliable CI easily
  • Use container images as build artifacts
  • Decouple "plumbing" from application logic

Using Docker, The Big Picture

GDI Logo

Docker is a client-server application.

  • The Docker Engine (or "daemon")
    • Receives and processes incoming Docker API requests.
  • The Docker client
    • Talks to the Docker daemon via the Docker API.
    • We'll use mostly the CLI embedded within the docker binary.
  • Docker Hub Registry
    • Collection of public images.
    • The Docker daemon talks to it via the registry API.

Docker Terminology

Images

The file system and configuration of our application which are used to create containers.

Containers

Running instances of Docker images — containers run the actual applications. A container includes an application and all of its dependencies. It shares the kernel with other containers, and runs as an isolated process in user space on the host OS.

Docker daemon

The background service running on the host that manages building, running and distributing Docker containers.

Docker client

The command line tool that allows the user to interact with the Docker daemon.

Docker Hub

A registry of Docker images. You can think of the registry as a directory of all available Docker images. You'll be using this later in this tutorial.

Our First Containers

In your Docker environment, just run the following command:

$ docker run busybox echo hello world
hello world  

That's it!

That was our first container.

A more useful container

Let's run a more exciting container:

$ docker run -it ubuntu
  root@04c0bb0a6c07:/#
  • This is a brand new container.It runs a bare-bones, no-frills ubuntu system.
  • -it is shorthand for -i -t.
  • -i tells Docker to connect us to the container's stdin.
  • -t tells Docker that we want a pseudo-terminal.

Do something in our container

root@04c0bb0a6c07:/# figlet hello
bash: figlet: command not found

Alright, we need to install it.

An observation

Let's check how many packages are installed here.

root@04c0bb0a6c07:/# dpkg -l | wc -l
189
  • dpkg -l lists the packages installed in our container
  • wc -l counts them
  • If you have a Debian or Ubuntu machine, you can run the same command and compare the results.

Install a package in our container

We want figlet, so let's install it:

root@04c0bb0a6c07:/# apt-get update
...
Fetched 1514 kB in 14s (103 kB/s)
Reading package lists... Done
root@04c0bb0a6c07:/# apt-get install figlet
Reading package lists... Done
...

One minute later, figlet is installed!

# figlet hello _ __
| |__   ___| | | ___
| '_ \ / _ \ | |/ _ \
| | | |  __/ | | (_) |
|_| |_|\___|_|_|\___/

Exiting our container

Just exit the shell, like you would usually do.

(E.g. with ^D or exit)

root@04c0bb0a6c07:/# exit
  • Our container is now in a stopped state.
  • It still exists on disk, but all compute resources have been freed up.

Starting another container

What if we start a new container, and try to run figlet again?

$ docker run -it ubuntu
root@b13c164401fb:/# figlet
bash: figlet: command not found
  • We started a brand new container.
  • The basic Ubuntu image was used, and figlet is not here.
  • We will learn later how to bake a custom image with figlet.

Background Containers

A non-interactive container

We will run a small custom container.

This container just displays the time every second.

$ docker run jpetazzo/clock
Fri Feb 20 00:28:53 UTC 2015
Fri Feb 20 00:28:54 UTC 2015
Fri Feb 20 00:28:55 UTC 2015
...
  • This container will run forever.
  • To stop it, press ^C.
  • Docker has automatically downloaded the image jpetazzo/clock.
  • This image is a user image, created by jpetazzo.
  • We will hear more about user images (and other types of images) later.

Run a container in the background

Containers can be started in the background, with the -d flag (daemon mode):

$ docker run -d jpetazzo/clock
47d677dcfba4277c6cc68fcaa51f932b544cab1a187c853b7d0caf4e8debe5ad
  • We don't see the output of the container.
  • But don't worry: Docker collects that output and logs it!
  • Docker gives us the ID of the container.

List running containers

How can we check that our container is still running?

With docker ps, just like the UNIX ps command, lists running processes.

$ docker ps
CONTAINER ID  IMAGE           ...  CREATED        STATUS        ...
47d677dcfba4  jpetazzo/clock  ...  2 minutes ago  Up 2 minutes  ...

Docker tells us:

  • The (truncated) ID of our container.
  • The image used to start the container.
  • That our container has been running (Up) for a couple of minutes.
  • Other information (COMMAND, PORTS, NAMES) that we will explain later.

Starting more containers

Let's start two more containers:

$ docker run -d jpetazzo/clock
57ad9bdfc06bb4407c47220cf59ce21585dce9a1298d7a67488359aeaea8ae2a
$ docker run -d jpetazzo/clock
068cc994ffd0190bbe025ba74e4c0771a5d8f14734af772ddee8dc1aaf20567d

Check that docker ps correctly reports all 3 containers.

Two useful flags for docker ps

To see only the last container that was started:

$ docker ps -l
CONTAINER ID  IMAGE           ...  CREATED        STATUS        ...
068cc994ffd0  jpetazzo/clock  ...  2 minutes ago  Up 2 minutes  ...

To see only the ID of containers:

$ docker ps -q
068cc994ffd0
57ad9bdfc06b
47d677dcfba4

Combine those flags to see only the ID of the last container started!

$ docker ps -lq
068cc994ffd0

View the logs of a container

We said that Docker was logging the container output.

Let's see that now.

$ docker logs 068
Fri Feb 20 00:39:52 UTC 2015
Fri Feb 20 00:39:53 UTC 2015
...
  • We specified a prefix of the full container ID.
  • You can, of course, specify the full ID.
  • The logs command will output the entire logs of the container. (Sometimes, that will be too much. Let's see how to address that.)

View only the tail of the logs

To avoid being spammed with eleventy pages of output, we can use the --tail option:

$ docker logs --tail 3 068
Fri Feb 20 00:55:35 UTC 2015
Fri Feb 20 00:55:36 UTC 2015
Fri Feb 20 00:55:37 UTC 2015

The parameter is the number of lines that we want to see.

Follow the logs in real time

Just like with the standard UNIX command tail -f, we can follow the logs of our container:

$ docker logs --tail 1 --follow 068
Fri Feb 20 00:57:12 UTC 2015
Fri Feb 20 00:57:13 UTC 2015
^C
  • This will display the last line in the log file.
  • Then, it will continue to display the logs in real time.
  • Use ^C to exit.

Stop our container

There are two ways we can terminate our detached container.


  • Killing it using the docker kill command.
  • Stopping it using the docker stop command.


The first one stops the container immediately, by using the KILL signal.

The second one is more graceful. It sends a TERM signal, and after 10 seconds, if the container has not stopped, it sends KILL.

Reminder: the KILL signal cannot be intercepted, and will forcibly terminate the container.

Stopping our containers

Let's stop one of those containers:

$ docker stop 47d6
47d6

This will take 10 seconds:

  • Docker sends the TERM signal;
  • the container doesn't react to this signal (it's a simple Shell script with no special signal handling);
  • 10 seconds later, since the container is still running, Docker sends the KILL signal;
  • this terminates the container.

Killing the remainder containers

Let's be less patient with the two other containers:

$ docker kill 068 57ad
068
57ad

The stop and kill commands can take multiple container IDs.

Those containers will be terminated immediately (without the 10 seconds delay).

Let's check that our containers don't show up anymore:

$ docker ps

List stopped containers

We can also see stopped containers, with the -a (--all) option.

$ docker ps -a
CONTAINER ID  IMAGE           ...  CREATED      STATUS
068cc994ffd0  jpetazzo/clock  ...  21 min. ago  Exited (137) 3 min. ago
57ad9bdfc06b  jpetazzo/clock  ...  21 min. ago  Exited (137) 3 min. ago
47d677dcfba4  jpetazzo/clock  ...  23 min. ago  Exited (137) 3 min. ago
5c1dfd4d81f1  jpetazzo/clock  ...  40 min. ago  Exited (0) 40 min. ago
b13c164401fb  ubuntu          ...  55 min. ago  Exited (130) 53 min. ago

Restarting and Attaching to Containers

Background and foreground

The distinction between foreground and background containers is arbitrary.

From Docker's point of view, all containers are the same.

All containers run the same way, whether there is a client attached to them or not. It is always possible to detach from a container, and to reattach to a container.

Analogy: attaching to a container is like plugging a keyboard and screen to a physical server.

Detaching from a container

  • If you have started an interactive container (with option -it), you can detach from it.
  • The "detach" sequence is ^P^Q.
  • Otherwise you can detach by killing the Docker client. (But not by hitting ^C, as this would deliver SIGINT to the container.)

What does -it stand for?

  • -t means "allocate a terminal."
  • -i means "connect stdin to the terminal."

Specifying a custom detach sequence

  • You don't like ^P^Q? No problem!
  • You can change the sequence with docker run --detach-keys.
  • This can also be passed as a global option to the engine.

Start a container with a custom detach command:

$ docker run -ti --detach-keys ctrl-x,x jpetazzo/clock

Detach by hitting ^X x. (This is ctrl-x then x, not ctrl-x twice!)

Check that our container is still running:

$ docker ps -l

Attaching to a container

You can attach to a container:

$ docker attach <containerID>
  • The container must be running.
  • There can be multiple clients attached to the same container.
  • If you don't specify --detach-keys when attaching, it defaults back to ^P^Q.

Try it on our previous container:

$ docker attach $(docker ps -lq)

Check that ^X x doesn't work, but ^P ^Q does.

Detaching from non-interactive containers

Warning: if the container was started without -it...


  • You won't be able to detach with ^P^Q.
  • If you hit ^C, the signal will be proxied to the container.


Remember: you can always detach by killing the Docker client

Checking container output

Use docker attach if you intend to send input to the container.

If you just want to see the output of a container, use docker logs.

$ docker logs --tail 1 --follow <containerID>

Restarting a container

When a container has exited, it is in stopped state.

It can then be restarted with the start command.

$ docker start <yourContainerID>

The container will be restarted using the same options you launched it with.

You can re-attach to it if you want to interact with it:

$ docker attach <yourContainerID>

Use docker ps -a to identify the container ID of a previous jpetazzo/clock container, and try those commands.

Attaching to a REPL

  • REPL = Read Eval Print Loop
  • Shells, interpreters, TUI ...
  • Symptom: you docker attach, and see nothing
  • The REPL doesn't know that you just attached, and doesn't print anything
  • Try hitting ^L or Enter

SIGWINCH

  • When you docker attach, the Docker Engine sends a couple of SIGWINCH signals to the container.
  • SIGWINCH = WINdow CHange; indicates a change in window size.
  • This will cause some CLI and TUI programs to redraw the screen.
  • But not all of them.

Important PSA about Security

  • The docker user is root equivalent.
  • It provides root-level access to the host.
  • You should restrict access to it like you would protect root.
  • If you give somebody the ability to access the Docker API, you are giving them full access on the machine.
  • Therefore, the Docker control socket is (by default) owned by the docker group, to avoid unauthorized access on multi-user machines.
  • If your user is not in the docker group, you will need to prefix every command with sudo; e.g. sudo docker version.

Develop It!

Please complete the lab at the docker classroom.

Understanding Docker Images

What is an image?

An image is a collection of files + some meta data. (Technically: those files form the root filesystem of a container.)

Images are made of layers, conceptually stacked on top of each other.

Each layer can add, change, and remove files.

Images can share layers to optimize disk usage, transfer times, and memory use.

Example:

  • CentOS
  • JRE
  • Tomcat
  • Dependencies
  • Application JAR
  • Configuration

Differences between containers and images

  • An image is a read-only filesystem.
  • A container is an encapsulated set of processes running in a read-write copy of that filesystem.
  • To optimize container boot time, copy-on-write is used instead of regular copy.
  • docker run starts a container from a given image.
    • Let's give a couple of metaphors to illustrate those concepts.

Image as stencils

Images are like templates or stencils that you can create containers from.

Object-Oriented Programming

  • Images are conceptually similar to classes.
  • Layers are conceptually similar to inheritance.
  • Containers are conceptually similar to instances.

Wait a minute...

If an image is read-only, how do we change it?

  • We don't.
  • We create a new container from that image.
  • Then we make changes to that container.
  • When we are satisfied with those changes, we transform them into a new layer.
  • A new image is created by stacking the new layer on top of the old image.

A chicken-and-egg problem

  • The only way to create an image is by "freezing" a container.
  • The only way to create a container is by instantiating an image.
  • Help!

Creating the first images

There is a special empty image called scratch.

It allows to build from scratch.

The docker import command loads a tarball into Docker.

  • The imported tarball becomes a standalone image.
  • That new image has a single layer.


Note: you will probably never have to do this yourself.

Creating other images

  • docker commit
    • Saves all the changes made to a container into a new layer.
    • Creates a new image (effectively a copy of the container).
  • docker build
    • Performs a repeatable build sequence.
    • This is the preferred method!


We will explain both methods in a moment.

Images namespaces

There are three namespaces:

  • Official images, e.g. ubuntu, busybox ...
  • User (and organizations) images, e.g. jpetazzo/clock
  • Self-hosted images, e.g. registry.example.com:5000/my-private/image


Let's explain each of them.

Root namespace

The root namespace is for official images. They are put there by Docker Inc., but they are generally authored and maintained by third parties.

Those images include:

  • Small, "swiss-army-knife" images like busybox.
  • Distro images to be used as bases for your builds, like ubuntu, fedora...
  • Ready-to-use components and services, like redis, postgresql...

User namespace

The user namespace holds images for Docker Hub users and organizations.

For example:

jpetazzo/clock

The Docker Hub user is:

jpetazzo

The image name is:

clock

Self-hosted namespace

This namespace holds images which are not hosted on Docker Hub, but on third party registries.

They contain the hostname (or IP address), and optionally the port, of the registry server.

For example:

localhost:5000/wordpres
  • localhost:5000 is the host and port of the registry
  • wordpress is the name of the image

How do you store and manage images?

Images can be stored:

  • On your Docker host.
  • In a Docker registry.


You can use the Docker client to download (pull) or upload (push) images.

To be more accurate: you can use the Docker client to tell a Docker server to push and pull images to and from a registry.

Showing current images

Let's look at what images are on our host now.

 $ docker images
REPOSITORY      TAG       IMAGE ID      CREATED       SIZE
fedora          latest    ddd5c9c1d0f2  3 days ago    204.7 MB
centos          latest    d0e7f81ca65c  3 days ago    196.6 MB
ubuntu          latest    07c86167cdc4  4 days ago    188 MB
redis           latest    4f5f397d4b7c  5 days ago    177.6 MB
postgres        latest    afe2b5e1859b  5 days ago    264.5 MB
alpine          latest    70c557e50ed6  5 days ago    4.798 MB
debian          latest    f50f9524513f  6 days ago    125.1 MB
busybox         latest    3240943c9ea3  3 days ago    1.114 MB
training/namer  latest    902673acc741  2 weeks ago   289.3 MB
jpetazzo/clock  latest    12068b93616f  9 months ago  2.433 MB

Searching for images

We cannot list all images on a remote registry, but we can search for a specific keyword:

$ docker search zookeeper
NAME                  DESCRIPTION                 STARS  OFFICIAL AUTOMATED
jplock/zookeeper      Builds a docker image ...   103              [OK]
mesoscloud/zookeeper  ZooKeeper                   42               [OK]
springxd/zookeeper    A Docker image that ca...   5                [OK]
elevy/zookeeper       ZooKeeper configured t...   3                [OK]
  • "Stars" indicate the popularity of the image.
  • "Official" images are those in the root namespace.
  • "Automated" images are built automatically by the Docker Hub. (This means that their build recipe is always available.)

Downloading images


There are two ways to download images.


  • Explicitly, with docker pull.
  • Implicitly, when executing docker run and the image is not found locally.

Pulling an image

$ docker pull debian:jessie
Pulling repository debian
b164861940b8: Download complete
b164861940b8: Pulling image (jessie) from debian
d1881793a057: Download complete
  • As seen previously, images are made up of layers.
  • Docker has downloaded all the necessary layers.
  • In this example, :jessie indicates which exact version of Debian we would like. It is a version tag.

Image and tags

  • Images can have tags.
  • Tags define image versions or variants.
  • docker pull ubuntu will refer to ubuntu:latest.
  • The :latest tag is generally updated often.

When to (not) use tags

Don't specify tags:


  • When doing rapid testing and prototyping.
  • When experimenting.
  • When you want the latest version.


Do specify tags:

  • When recording a procedure into a script.
  • When going to production.
  • To ensure that the same version will be used everywhere.
  • To ensure repeatability later.

Develop It!

Time permitting, you can complete these exercises in class or as homework.

Questions?