[Web] Try Docker

Dive into the basics of Docker and learn how to create a self-contained, shareable development environment with containers, images, Dockerfiles, and volumes.


Image credit: What is Docker

Here’s the notes of Try Docker.
I recommend you click the link and enjoy the vivid course (with video lessons, coding challenges, and screencasts). Read this notes if you want to go through the course quickly or refer back to the keypoints.
Images credit: slide of Try Docker.

Containers & Images

Learn how to use images to create Docker containers.

Concepts

Linux containers

Linux containers are a way to create isolated environments that can run code while sharing a single operating system.

Containers will live on top of that base operating system and they can have their own code dependencies and application code inside of them, but each one of those containers will be completely isolated from any of the other ones that are running.
These containers have their own file system and useful parts of the operating system that you can customize and control while still sharing the largest parts of the operating system with your host. That means the conatiners are really small and therefore really fast.

Why Docker?

Docker is a tool that makes it much easier to manage Linux containers.
That said, creating Linux containers isn’t easy and requires a lot of specialized knowledge of Unix operating systems, and that’s where Docker comes in.

How Can Docker Help Me?

  • Developers
    • Create contained, controlled dev environment (Here we focus on this point)
    • Share identical dev environment across team (creating a reproducible environment)
    • Bug reporting (isolating the state of an application)
  • IT Ops
    • Testing
    • Deployment

Containers & Images

An image is a blueprint for creating a container.

Containers are this light weight isolated section of a Linux operating system that can be filled up with code, like libraries and application code. Well images are prepackaged sets of instructions, kind of like blueprints, that can be used to build and run containers.
The reason images are so useful is that you can create a custom container and save the state of it as an image that can then be used to recreate the container exactly as described. You can even share images with other developers so that you can easily recreate an exact development across multiple people and computers on your team.
Docker publishes a bunch of pre-built images in the Docker Store and by default that’s where Docker will look when you try to start a new container based on one of those images.

Commands

Running a Container

To start running a container, type docker container run followed by the name of the image used to make the container, then a colon : followed by the version number of the image used to make the container.
This container is the bare minimum OS plus the Apache webserver.

1
$ docker container run httpd:2.4

Mapping Ports

To access the default index.html page on that server by visiting http://localhost:80/index.html: Adding a -p flag with some port numbers after it to the run command.

  • The -p means “publish ports”.
  • The first number is the port on the host we want to open up.
  • The second number is the port in the container we want to map it to.
  • Port 80 is a standard port for handling web requests
1
$ docker container run -p 80:80 httpd:2.4

Accessing a Container

Each container gets a unique ID, and Docker also assigns them a random name. You can use either the container ID or the container name whenever you’re running a command that needs you to specify a container.

1
2
3
4
5
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
STATUS PORTS NAMES
a8c267869b0b httpd:2.4 "httpd-foreground" 53 seconds ago
Up 52 seconds 0.0.0.0:9999->80/tcp elegant_noether

The docker container exec command lets us run commands against a running container.

1
$ docker container exec elegant_noether du -mh

Attaching a Shell to a Container

Attach a shell to those containers and browse them just like they were a full operating system — which they kind of are.

  • The -i and -t flags can be passed to the exec command to keep an interactive shell open.
  • Specify the shell you want to attach after the container ID. Here we’ll attach a standard Bash shell.
1
2
$ docker container exec -it elegant_noether /bin/bash
root@a8c267869b0b:/usr/local/apache2# du -mh

Now we’re just navigating a normal Unix system, so any commands we type will run inside of that system. So we can use that same command du -mh we just ran with exec from inside the container.

Installing Things in Containers

Install new programs, or packages, that aren’t part of the base image.
Let’s install the fortunes package.

1
2
3
4
$ docker container exec -it elegant_noether /bin/bash
root@a8c267869b0b:/usr/local/apache2# apt-get install -y fortunes
root@a8c267869b0b:/usr/local/apache2# /usr/games/fortune
"I found out why my car was humming. It had forgotten the words."

If we see a random fortune displayed then we know the installation worked!

Update the ENV in a Container

Update the PATH environment variable in the container so that we don’t have to type /usr/games/ before the fortune command anymore.

1
2
3
4
5
$ docker container exec -it elegant_noether /bin/bash
root@a8c267869b0b:/usr/local/apache2# PATH=$PATH:/usr/games/
root@a8c267869b0b:/usr/local/apache2# export PATH
root@a8c267869b0b:/usr/local/apache2# fortune
Offer limited to residents of the contiguous United States.

Dockerfiles

Use Dockerfiles to configure images in a centralized and repeatable way.

Concepts

A Dockerfile is a specially formatted text file where you can add a list of instructions that will run and result in a new image that can be used to make a container.
Creating containers from the command line works, but it quickly gets a little clunky the more customization that you need to do.

Commands

Customize Dockerfile

Use a Dockerfile to customize a base image before creating a container.

1
2
3
4
FROM httpd:2.4 # base the custom image off of version 2.4 of the httpd image
EXPOSE 80 # expose port 80
RUN apt-get update && apt-get install -y fortunes # install fortunes
LABEL maintainer="moby-dock@example.com" # label the `maintainer` of this image.
  • Line 1: The first line of a Dockerfile is usually the FROM keyword followed a base image name. Every other instruction in the Dockerfile following the FROM instruction will create a new image that blends together everything in the base plus the modifications we’re making in the rest of the Dockerfile.
  • Line 2: Next, we can use the Dockerfile to expose a port inside of its associated container. Note that we’ll still need to map ports between the host and container when we run docker container run, but this line will let the image know which ports inside of the container should become available.
  • Line 3: Remember how in the last level we had to docker container exec to install fortunes? Well, with a Dockerfile we can run any commands as the image is being built with the RUN command. Line 3 is a combination of
1
2
RUN apt-get update
RUN apt-get install -y fortunes
  • Line 4: Since we can distribute Dockerfiles to other developers, it’s a good idea to put our contact information in them with the LABEL instruction. LABEL accepts key-value pairs in the form of key="value".

Building an Image From a Dockerfile

Use that Dockerfile to build a new image with the docker image build command. The --tag command is a useful option to pass to docker build that lets us specify the name of our new image followed by a version number.
Note: End the command with a single . so it knows to look for the Dockerfile in the same folder that the command is run in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ docker image build --tag web-server:1.0 .
Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM httpd:2.4
---> 278cd55ca6c5
Step 2/4 : EXPOSE 80
---> Running in 3df6726122c6
---> 3928f2805894
Removing intermediate container 3df6726122c6
Step 3/4 : RUN apt-get update && apt-get install -y fortunes
---> Running in 619f90d50e91
Ign http://deb.debian.org jessie InRelease
# ...... (omit some output here)
Setting up fortunes (1:1.99.1-7) ...
Processing triggers for libc-bin (2.19-18+deb8u7) ...
---> 2735abf14761
Removing intermediate container 619f90d50e91
Step 4/4 : LABEL maintainer moby-dock@example.com
---> Running in 726074fa125a
---> 5eceabe3da13
Removing intermediate container 726074fa125a
Successfully built 5eceabe3da1

Get a list of all images on the computer. In this list, we’ll see our newly created web-server image.

1
2
3
4
5
6
7
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED
SIZE
web-server 1.0 5eceabe3da13 About a minute ago
203MB
httpd 2.4 278cd55ca6c5 4 weeks ago
177MB

Running a Container From a Custom Image

Now we can create a container with the new web-server image.

  • Uses the web-server version 1.0 image
  • Maps port 80 on the host to 80 on the container.
1
$ docker container run --publish 80:80 web-server:1.0

Volumes

Take control of the data in your containers with volumes.

Concepts

If the image you’re building a container with doesn’t already contain application files, you’ll need an extra step to get them into your container.

  • Copy a file into a container from the command line
  • Copy a file into an image with instructions in a Dockerfile

Now if you’re already got the files then including them in the container from the start works fine, but what about files that aren’t created yet or files that are going to change after the container’s already been started? The problem with just copying files in is the containers won’t persist data.
As soon as you stop that container from running, all of the data inside of it is gone too and won’t be there when you start it up again.

One way to fix this problem is to use a data volume to create a connection between files on our local computer (host) and the filesystem in the container.

Data volumes expose files on your host machine to the container.

That way whenever you stop the container, you can still access that data from inside the container when you start it up again. This is a great option for local development when you want a container to have access to files that you’re working in and that are actively changing.

Commands

Copying Files into a Running Container

Here, we’ve got a container that’s already running with the name elegant_noether.

1
$ docker container cp page.html elegant_noether:/usr/local/apache2/htdocs/

Notice the order that the commands must be written: First, the path to the file on your host machine, then the container’s ID followed by a colon, then the path to the file in the container.

Copying Files Through Dockerfile Instructions

1
COPY page.html /usr/local/apache2/htdocs/

Creating a Volume

1
2
3
4
5
6
7
8
9
$ docker run -d -p 80:80 -v /my-files:/usr/local/apache2/htdocs web-server:1.1
429c875f14361d356f40e42d645219c6fb3f8c6a53e6dcbc83b5084ff40b26b2
$ docker container exec -it elegant_noether /bin/bash
root@429c875f1436:/usr/local/apache2# cd /usr/local/apache2/htdocs
root@429c875f1436:/usr/local/apache2/htdocs# ls -la
total 12
drwxr-xr-x 2 root root 4096 Apr 19 04:48 .
drwxr-sr-x 1 www-data www-data 4096 Apr 19 04:50 ..
-rw-r--r-- 1 root root 158 Apr 19 04:48 local-file.html
  • Line 1: Create a link between the folder /my-files on your host machine and the htdocs folder in the container. This also runs the container in the background.
  • Line 3: Get a shell in the container
  • Line 5: This will show us that the container thinks those files on our local machine are inside of it. we’ve created a volume that’s linking those local files to the container!