Initial commit

This commit is contained in:
Ria Bhatia
2017-12-04 13:32:57 -06:00
committed by Erik St. Martin
commit 0075e5b0f3
9056 changed files with 2523100 additions and 0 deletions

View File

@@ -0,0 +1,554 @@
<!--[metadata]>
+++
aliases = ["/engine/userguide/dockerimages/"]
title = "Build your own images"
description = "How to work with Docker images."
keywords = ["documentation, docs, the docker guide, docker guide, docker, docker platform, docker.io, Docker images, Docker image, image management, Docker repos, Docker repositories, docker, docker tag, docker tags, Docker Hub, collaboration"]
[menu.main]
parent = "engine_learn"
weight = -4
+++
<![end-metadata]-->
# Build your own images
Docker images are the basis of containers. Each time you've used `docker run`
you told it which image you wanted. In the previous sections of the guide you
used Docker images that already exist, for example the `ubuntu` image and the
`training/webapp` image.
You also discovered that Docker stores downloaded images on the Docker host. If
an image isn't already present on the host then it'll be downloaded from a
registry: by default the [Docker Hub Registry](https://registry.hub.docker.com).
In this section you're going to explore Docker images a bit more
including:
* Managing and working with images locally on your Docker host.
* Creating basic images.
* Uploading images to [Docker Hub Registry](https://registry.hub.docker.com).
## Listing images on the host
Let's start with listing the images you have locally on our host. You can
do this using the `docker images` command like so:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 14.04 1d073211c498 3 days ago 187.9 MB
busybox latest 2c5ac3f849df 5 days ago 1.113 MB
training/webapp latest 54bb4e8718e8 5 months ago 348.7 MB
You can see the images you've previously used in the user guide.
Each has been downloaded from [Docker Hub](https://hub.docker.com) when you
launched a container using that image. When you list images, you get three crucial pieces of information in the listing.
* What repository they came from, for example `ubuntu`.
* The tags for each image, for example `14.04`.
* The image ID of each image.
> **Tip:**
> You can use [a third-party dockviz tool](https://github.com/justone/dockviz)
> or the [Image layers site](https://imagelayers.io/) to display
> visualizations of image data.
A repository potentially holds multiple variants of an image. In the case of
our `ubuntu` image you can see multiple variants covering Ubuntu 10.04, 12.04,
12.10, 13.04, 13.10 and 14.04. Each variant is identified by a tag and you can
refer to a tagged image like so:
ubuntu:14.04
So when you run a container you refer to a tagged image like so:
$ docker run -t -i ubuntu:14.04 /bin/bash
If instead you wanted to run an Ubuntu 12.04 image you'd use:
$ docker run -t -i ubuntu:12.04 /bin/bash
If you don't specify a variant, for example you just use `ubuntu`, then Docker
will default to using the `ubuntu:latest` image.
> **Tip:**
> You should always specify an image tag, for example `ubuntu:14.04`.
> That way, you always know exactly what variant of an image you are using.
> This is useful for troubleshooting and debugging.
## Getting a new image
So how do you get new images? Well Docker will automatically download any image
you use that isn't already present on the Docker host. But this can potentially
add some time to the launch of a container. If you want to pre-load an image you
can download it using the `docker pull` command. Suppose you'd like to
download the `centos` image.
$ docker pull centos
Pulling repository centos
b7de3133ff98: Pulling dependent layers
5cc9e91966f7: Pulling fs layer
511136ea3c5a: Download complete
ef52fb1fe610: Download complete
. . .
Status: Downloaded newer image for centos
You can see that each layer of the image has been pulled down and now you
can run a container from this image and you won't have to wait to
download the image.
$ docker run -t -i centos /bin/bash
bash-4.1#
## Finding images
One of the features of Docker is that a lot of people have created Docker
images for a variety of purposes. Many of these have been uploaded to
[Docker Hub](https://hub.docker.com). You can search these images on the
[Docker Hub](https://hub.docker.com) website.
![indexsearch](search.png)
You can also search for images on the command line using the `docker search`
command. Suppose your team wants an image with Ruby and Sinatra installed on
which to do our web application development. You can search for a suitable image
by using the `docker search` command to find all the images that contain the
term `sinatra`.
$ docker search sinatra
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
training/sinatra Sinatra training image 0 [OK]
marceldegraaf/sinatra Sinatra test app 0
mattwarren/docker-sinatra-demo 0 [OK]
luisbebop/docker-sinatra-hello-world 0 [OK]
bmorearty/handson-sinatra handson-ruby + Sinatra for Hands on with D... 0
subwiz/sinatra 0
bmorearty/sinatra 0
. . .
You can see the command returns a lot of images that use the term `sinatra`.
You've received a list of image names, descriptions, Stars (which measure the
social popularity of images - if a user likes an image then they can "star" it),
and the Official and Automated build statuses. [Official
Repositories](https://docs.docker.com/docker-hub/official_repos) are a carefully
curated set of Docker repositories supported by Docker, Inc. Automated
repositories are [Automated Builds](dockerrepos.md#automated-builds) that allow
you to validate the source and content of an image.
You've reviewed the images available to use and you decided to use the
`training/sinatra` image. So far you've seen two types of images repositories,
images like `ubuntu`, which are called base or root images. These base images
are provided by Docker Inc and are built, validated and supported. These can be
identified by their single word names.
You've also seen user images, for example the `training/sinatra` image you've
chosen. A user image belongs to a member of the Docker community and is built
and maintained by them. You can identify user images as they are always
prefixed with the user name, here `training`, of the user that created them.
## Pulling our image
You've identified a suitable image, `training/sinatra`, and now you can download it using the `docker pull` command.
$ docker pull training/sinatra
The team can now use this image by running their own containers.
$ docker run -t -i training/sinatra /bin/bash
root@a8cb6ce02d85:/#
## Creating our own images
The team has found the `training/sinatra` image pretty useful but it's not quite
what they need and you need to make some changes to it. There are two ways you
can update and create images.
1. You can update a container created from an image and commit the results to an image.
2. You can use a `Dockerfile` to specify instructions to create an image.
### Updating and committing an image
To update an image you first need to create a container from the image
you'd like to update.
$ docker run -t -i training/sinatra /bin/bash
root@0b2616b0e5a8:/#
> **Note:**
> Take note of the container ID that has been created, `0b2616b0e5a8`, as you'll
> need it in a moment.
Inside our running container let's add the `json` gem.
root@0b2616b0e5a8:/# gem install json
Once this has completed let's exit our container using the `exit`
command.
Now you have a container with the change you want to make. You can then
commit a copy of this container to an image using the `docker commit`
command.
$ docker commit -m "Added json gem" -a "Kate Smith" \
0b2616b0e5a8 ouruser/sinatra:v2
4f177bd27a9ff0f6dc2a830403925b5360bfe0b93d476f7fc3231110e7f71b1c
Here you've used the `docker commit` command. You've specified two flags: `-m`
and `-a`. The `-m` flag allows us to specify a commit message, much like you
would with a commit on a version control system. The `-a` flag allows us to
specify an author for our update.
You've also specified the container you want to create this new image from,
`0b2616b0e5a8` (the ID you recorded earlier) and you've specified a target for
the image:
ouruser/sinatra:v2
Break this target down. It consists of a new user, `ouruser`, that you're
writing this image to. You've also specified the name of the image, here you're
keeping the original image name `sinatra`. Finally you're specifying a tag for
the image: `v2`.
You can then look at our new `ouruser/sinatra` image using the `docker images`
command.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
training/sinatra latest 5bc342fa0b91 10 hours ago 446.7 MB
ouruser/sinatra v2 3c59e02ddd1a 10 hours ago 446.7 MB
ouruser/sinatra latest 5db5f8471261 10 hours ago 446.7 MB
To use our new image to create a container you can then:
$ docker run -t -i ouruser/sinatra:v2 /bin/bash
root@78e82f680994:/#
### Building an image from a `Dockerfile`
Using the `docker commit` command is a pretty simple way of extending an image
but it's a bit cumbersome and it's not easy to share a development process for
images amongst a team. Instead you can use a new command, `docker build`, to
build new images from scratch.
To do this you create a `Dockerfile` that contains a set of instructions that
tell Docker how to build our image.
First, create a directory and a `Dockerfile`.
$ mkdir sinatra
$ cd sinatra
$ touch Dockerfile
If you are using Docker Machine on Windows, you may access your host
directory by `cd` to `/c/Users/your_user_name`.
Each instruction creates a new layer of the image. Try a simple example now for
building your own Sinatra image for your fictitious development team.
# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith <ksmith@example.com>
RUN apt-get update && apt-get install -y ruby ruby-dev
RUN gem install sinatra
Examine what your `Dockerfile` does. Each instruction prefixes a statement and
is capitalized.
INSTRUCTION statement
> **Note:** You use `#` to indicate a comment
The first instruction `FROM` tells Docker what the source of our image is, in
this case you're basing our new image on an Ubuntu 14.04 image. The instruction uses the `MAINTAINER` instruction to specify who maintains the new image.
Lastly, you've specified two `RUN` instructions. A `RUN` instruction executes
a command inside the image, for example installing a package. Here you're
updating our APT cache, installing Ruby and RubyGems and then installing the
Sinatra gem.
Now let's take our `Dockerfile` and use the `docker build` command to build an image.
$ docker build -t ouruser/sinatra:v2 .
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 1 : FROM ubuntu:14.04
---> e54ca5efa2e9
Step 2 : MAINTAINER Kate Smith <ksmith@example.com>
---> Using cache
---> 851baf55332b
Step 3 : RUN apt-get update && apt-get install -y ruby ruby-dev
---> Running in 3a2558904e9b
Selecting previously unselected package libasan0:amd64.
(Reading database ... 11518 files and directories currently installed.)
Preparing to unpack .../libasan0_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libasan0:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libatomic1:amd64.
Preparing to unpack .../libatomic1_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libatomic1:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libgmp10:amd64.
Preparing to unpack .../libgmp10_2%3a5.1.3+dfsg-1ubuntu1_amd64.deb ...
Unpacking libgmp10:amd64 (2:5.1.3+dfsg-1ubuntu1) ...
Selecting previously unselected package libisl10:amd64.
Preparing to unpack .../libisl10_0.12.2-1_amd64.deb ...
Unpacking libisl10:amd64 (0.12.2-1) ...
Selecting previously unselected package libcloog-isl4:amd64.
Preparing to unpack .../libcloog-isl4_0.18.2-1_amd64.deb ...
Unpacking libcloog-isl4:amd64 (0.18.2-1) ...
Selecting previously unselected package libgomp1:amd64.
Preparing to unpack .../libgomp1_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libgomp1:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libitm1:amd64.
Preparing to unpack .../libitm1_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libitm1:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libmpfr4:amd64.
Preparing to unpack .../libmpfr4_3.1.2-1_amd64.deb ...
Unpacking libmpfr4:amd64 (3.1.2-1) ...
Selecting previously unselected package libquadmath0:amd64.
Preparing to unpack .../libquadmath0_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libquadmath0:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libtsan0:amd64.
Preparing to unpack .../libtsan0_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libtsan0:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libyaml-0-2:amd64.
Preparing to unpack .../libyaml-0-2_0.1.4-3ubuntu3_amd64.deb ...
Unpacking libyaml-0-2:amd64 (0.1.4-3ubuntu3) ...
Selecting previously unselected package libmpc3:amd64.
Preparing to unpack .../libmpc3_1.0.1-1ubuntu1_amd64.deb ...
Unpacking libmpc3:amd64 (1.0.1-1ubuntu1) ...
Selecting previously unselected package openssl.
Preparing to unpack .../openssl_1.0.1f-1ubuntu2.4_amd64.deb ...
Unpacking openssl (1.0.1f-1ubuntu2.4) ...
Selecting previously unselected package ca-certificates.
Preparing to unpack .../ca-certificates_20130906ubuntu2_all.deb ...
Unpacking ca-certificates (20130906ubuntu2) ...
Selecting previously unselected package manpages.
Preparing to unpack .../manpages_3.54-1ubuntu1_all.deb ...
Unpacking manpages (3.54-1ubuntu1) ...
Selecting previously unselected package binutils.
Preparing to unpack .../binutils_2.24-5ubuntu3_amd64.deb ...
Unpacking binutils (2.24-5ubuntu3) ...
Selecting previously unselected package cpp-4.8.
Preparing to unpack .../cpp-4.8_4.8.2-19ubuntu1_amd64.deb ...
Unpacking cpp-4.8 (4.8.2-19ubuntu1) ...
Selecting previously unselected package cpp.
Preparing to unpack .../cpp_4%3a4.8.2-1ubuntu6_amd64.deb ...
Unpacking cpp (4:4.8.2-1ubuntu6) ...
Selecting previously unselected package libgcc-4.8-dev:amd64.
Preparing to unpack .../libgcc-4.8-dev_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libgcc-4.8-dev:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package gcc-4.8.
Preparing to unpack .../gcc-4.8_4.8.2-19ubuntu1_amd64.deb ...
Unpacking gcc-4.8 (4.8.2-19ubuntu1) ...
Selecting previously unselected package gcc.
Preparing to unpack .../gcc_4%3a4.8.2-1ubuntu6_amd64.deb ...
Unpacking gcc (4:4.8.2-1ubuntu6) ...
Selecting previously unselected package libc-dev-bin.
Preparing to unpack .../libc-dev-bin_2.19-0ubuntu6_amd64.deb ...
Unpacking libc-dev-bin (2.19-0ubuntu6) ...
Selecting previously unselected package linux-libc-dev:amd64.
Preparing to unpack .../linux-libc-dev_3.13.0-30.55_amd64.deb ...
Unpacking linux-libc-dev:amd64 (3.13.0-30.55) ...
Selecting previously unselected package libc6-dev:amd64.
Preparing to unpack .../libc6-dev_2.19-0ubuntu6_amd64.deb ...
Unpacking libc6-dev:amd64 (2.19-0ubuntu6) ...
Selecting previously unselected package ruby.
Preparing to unpack .../ruby_1%3a1.9.3.4_all.deb ...
Unpacking ruby (1:1.9.3.4) ...
Selecting previously unselected package ruby1.9.1.
Preparing to unpack .../ruby1.9.1_1.9.3.484-2ubuntu1_amd64.deb ...
Unpacking ruby1.9.1 (1.9.3.484-2ubuntu1) ...
Selecting previously unselected package libruby1.9.1.
Preparing to unpack .../libruby1.9.1_1.9.3.484-2ubuntu1_amd64.deb ...
Unpacking libruby1.9.1 (1.9.3.484-2ubuntu1) ...
Selecting previously unselected package manpages-dev.
Preparing to unpack .../manpages-dev_3.54-1ubuntu1_all.deb ...
Unpacking manpages-dev (3.54-1ubuntu1) ...
Selecting previously unselected package ruby1.9.1-dev.
Preparing to unpack .../ruby1.9.1-dev_1.9.3.484-2ubuntu1_amd64.deb ...
Unpacking ruby1.9.1-dev (1.9.3.484-2ubuntu1) ...
Selecting previously unselected package ruby-dev.
Preparing to unpack .../ruby-dev_1%3a1.9.3.4_all.deb ...
Unpacking ruby-dev (1:1.9.3.4) ...
Setting up libasan0:amd64 (4.8.2-19ubuntu1) ...
Setting up libatomic1:amd64 (4.8.2-19ubuntu1) ...
Setting up libgmp10:amd64 (2:5.1.3+dfsg-1ubuntu1) ...
Setting up libisl10:amd64 (0.12.2-1) ...
Setting up libcloog-isl4:amd64 (0.18.2-1) ...
Setting up libgomp1:amd64 (4.8.2-19ubuntu1) ...
Setting up libitm1:amd64 (4.8.2-19ubuntu1) ...
Setting up libmpfr4:amd64 (3.1.2-1) ...
Setting up libquadmath0:amd64 (4.8.2-19ubuntu1) ...
Setting up libtsan0:amd64 (4.8.2-19ubuntu1) ...
Setting up libyaml-0-2:amd64 (0.1.4-3ubuntu3) ...
Setting up libmpc3:amd64 (1.0.1-1ubuntu1) ...
Setting up openssl (1.0.1f-1ubuntu2.4) ...
Setting up ca-certificates (20130906ubuntu2) ...
debconf: unable to initialize frontend: Dialog
debconf: (TERM is not set, so the dialog frontend is not usable.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (This frontend requires a controlling tty.)
debconf: falling back to frontend: Teletype
Setting up manpages (3.54-1ubuntu1) ...
Setting up binutils (2.24-5ubuntu3) ...
Setting up cpp-4.8 (4.8.2-19ubuntu1) ...
Setting up cpp (4:4.8.2-1ubuntu6) ...
Setting up libgcc-4.8-dev:amd64 (4.8.2-19ubuntu1) ...
Setting up gcc-4.8 (4.8.2-19ubuntu1) ...
Setting up gcc (4:4.8.2-1ubuntu6) ...
Setting up libc-dev-bin (2.19-0ubuntu6) ...
Setting up linux-libc-dev:amd64 (3.13.0-30.55) ...
Setting up libc6-dev:amd64 (2.19-0ubuntu6) ...
Setting up manpages-dev (3.54-1ubuntu1) ...
Setting up libruby1.9.1 (1.9.3.484-2ubuntu1) ...
Setting up ruby1.9.1-dev (1.9.3.484-2ubuntu1) ...
Setting up ruby-dev (1:1.9.3.4) ...
Setting up ruby (1:1.9.3.4) ...
Setting up ruby1.9.1 (1.9.3.484-2ubuntu1) ...
Processing triggers for libc-bin (2.19-0ubuntu6) ...
Processing triggers for ca-certificates (20130906ubuntu2) ...
Updating certificates in /etc/ssl/certs... 164 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d....done.
---> c55c31703134
Removing intermediate container 3a2558904e9b
Step 4 : RUN gem install sinatra
---> Running in 6b81cb6313e5
unable to convert "\xC3" to UTF-8 in conversion from ASCII-8BIT to UTF-8 to US-ASCII for README.rdoc, skipping
unable to convert "\xC3" to UTF-8 in conversion from ASCII-8BIT to UTF-8 to US-ASCII for README.rdoc, skipping
Successfully installed rack-1.5.2
Successfully installed tilt-1.4.1
Successfully installed rack-protection-1.5.3
Successfully installed sinatra-1.4.5
4 gems installed
Installing ri documentation for rack-1.5.2...
Installing ri documentation for tilt-1.4.1...
Installing ri documentation for rack-protection-1.5.3...
Installing ri documentation for sinatra-1.4.5...
Installing RDoc documentation for rack-1.5.2...
Installing RDoc documentation for tilt-1.4.1...
Installing RDoc documentation for rack-protection-1.5.3...
Installing RDoc documentation for sinatra-1.4.5...
---> 97feabe5d2ed
Removing intermediate container 6b81cb6313e5
Successfully built 97feabe5d2ed
You've specified our `docker build` command and used the `-t` flag to identify
our new image as belonging to the user `ouruser`, the repository name `sinatra`
and given it the tag `v2`.
You've also specified the location of our `Dockerfile` using the `.` to
indicate a `Dockerfile` in the current directory.
> **Note:**
> You can also specify a path to a `Dockerfile`.
Now you can see the build process at work. The first thing Docker does is
upload the build context: basically the contents of the directory you're
building in. This is done because the Docker daemon does the actual
build of the image and it needs the local context to do it.
Next you can see each instruction in the `Dockerfile` being executed
step-by-step. You can see that each step creates a new container, runs
the instruction inside that container and then commits that change -
just like the `docker commit` work flow you saw earlier. When all the
instructions have executed you're left with the `97feabe5d2ed` image
(also helpfully tagged as `ouruser/sinatra:v2`) and all intermediate
containers will get removed to clean things up.
> **Note:**
> An image can't have more than 127 layers regardless of the storage driver.
> This limitation is set globally to encourage optimization of the overall
> size of images.
You can then create a container from our new image.
$ docker run -t -i ouruser/sinatra:v2 /bin/bash
root@8196968dac35:/#
> **Note:**
> This is just a brief introduction to creating images. We've
> skipped a whole bunch of other instructions that you can use. We'll see more of
> those instructions in later sections of the Guide or you can refer to the
> [`Dockerfile`](../../reference/builder.md) reference for a
> detailed description and examples of every instruction.
> To help you write a clear, readable, maintainable `Dockerfile`, we've also
> written a [`Dockerfile` Best Practices guide](../eng-image/dockerfile_best-practices.md).
## Setting tags on an image
You can also add a tag to an existing image after you commit or build it. We
can do this using the `docker tag` command. Now, add a new tag to your
`ouruser/sinatra` image.
$ docker tag 5db5f8471261 ouruser/sinatra:devel
The `docker tag` command takes the ID of the image, here `5db5f8471261`, and our
user name, the repository name and the new tag.
Now, see your new tag using the `docker images` command.
$ docker images ouruser/sinatra
REPOSITORY TAG IMAGE ID CREATED SIZE
ouruser/sinatra latest 5db5f8471261 11 hours ago 446.7 MB
ouruser/sinatra devel 5db5f8471261 11 hours ago 446.7 MB
ouruser/sinatra v2 5db5f8471261 11 hours ago 446.7 MB
## Image Digests
Images that use the v2 or later format have a content-addressable identifier
called a `digest`. As long as the input used to generate the image is
unchanged, the digest value is predictable. To list image digest values, use
the `--digests` flag:
$ docker images --digests | head
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
ouruser/sinatra latest sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf 5db5f8471261 11 hours ago 446.7 MB
When pushing or pulling to a 2.0 registry, the `push` or `pull` command
output includes the image digest. You can `pull` using a digest value.
$ docker pull ouruser/sinatra@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
You can also reference by digest in `create`, `run`, and `rmi` commands, as well as the
`FROM` image reference in a Dockerfile.
## Push an image to Docker Hub
Once you've built or created a new image you can push it to [Docker
Hub](https://hub.docker.com) using the `docker push` command. This
allows you to share it with others, either publicly, or push it into [a
private repository](https://registry.hub.docker.com/plans/).
$ docker push ouruser/sinatra
The push refers to a repository [ouruser/sinatra] (len: 1)
Sending image list
Pushing repository ouruser/sinatra (3 tags)
. . .
## Remove an image from the host
You can also remove images on your Docker host in a way [similar to
containers](usingdocker.md) using the `docker rmi` command.
Delete the `training/sinatra` image as you don't need it anymore.
$ docker rmi training/sinatra
Untagged: training/sinatra:latest
Deleted: 5bc342fa0b91cabf65246837015197eecfa24b2213ed6a51a8974ae250fedd8d
Deleted: ed0fffdcdae5eb2c3a55549857a8be7fc8bc4241fb19ad714364cbfd7a56b22f
Deleted: 5c58979d73ae448df5af1d8142436d81116187a7633082650549c52c3a2418f0
> **Note:** To remove an image from the host, please make sure
> that there are no containers actively based on it.
# Next steps
Until now you've seen how to build individual applications inside Docker
containers. Now learn how to build whole application stacks with Docker
by networking together multiple Docker containers.
Go to [Network containers](networkingcontainers.md).

View File

@@ -0,0 +1,212 @@
<!--[metadata]>
+++
aliases = ["/engine/userguide/dockerizing/"]
title = "Hello world in a container"
description = "A simple 'Hello world' exercise that introduced you to Docker."
keywords = ["docker guide, docker, docker platform, how to, dockerize, dockerizing apps, dockerizing applications, container, containers"]
[menu.main]
parent="engine_learn"
weight=-6
+++
<![end-metadata]-->
# Hello world in a container
*So what's this Docker thing all about?*
Docker allows you to run applications, worlds you create, inside containers.
Running an application inside a container takes a single command: `docker run`.
>**Note**: Depending on your Docker system configuration, you may be required to
>preface each `docker` command on this page with `sudo`. To avoid this behavior,
>your system administrator can create a Unix group called `docker` and add users
>to it.
## Run a Hello world
Let's try it now.
$ docker run ubuntu /bin/echo 'Hello world'
Hello world
And you just launched your first container!
So what just happened? Let's step through what the `docker run` command
did.
First we specified the `docker` binary and the command we wanted to
execute, `run`. The `docker run` combination *runs* containers.
Next we specified an image: `ubuntu`. This is the source of the container
we ran. Docker calls this an image. In this case we used the Ubuntu
operating system image.
When you specify an image, Docker looks first for the image on your
Docker host. If it can't find it then it downloads the image from the public
image registry: [Docker Hub](https://hub.docker.com).
Next we told Docker what command to run inside our new container:
/bin/echo 'Hello world'
When our container was launched Docker created a new Ubuntu
environment and then executed the `/bin/echo` command inside it. We saw
the result on the command line:
Hello world
So what happened to our container after that? Well Docker containers
only run as long as the command you specify is active. Here, as soon as
`Hello world` was echoed, the container stopped.
## An interactive container
Let's try the `docker run` command again, this time specifying a new
command to run in our container.
$ docker run -t -i ubuntu /bin/bash
root@af8bae53bdd3:/#
Here we've again specified the `docker run` command and launched an
`ubuntu` image. But we've also passed in two flags: `-t` and `-i`.
The `-t` flag assigns a pseudo-tty or terminal inside our new container
and the `-i` flag allows us to make an interactive connection by
grabbing the standard in (`STDIN`) of the container.
We've also specified a new command for our container to run:
`/bin/bash`. This will launch a Bash shell inside our container.
So now when our container is launched we can see that we've got a
command prompt inside it:
root@af8bae53bdd3:/#
Let's try running some commands inside our container:
root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
You can see we've run the `pwd` to show our current directory and can
see we're in the `/` root directory. We've also done a directory listing
of the root directory which shows us what looks like a typical Linux
file system.
You can play around inside this container and when you're done you can
use the `exit` command or enter Ctrl-D to finish.
root@af8bae53bdd3:/# exit
As with our previous container, once the Bash shell process has
finished, the container is stopped.
## A daemonized Hello world
Now a container that runs a command and then exits has some uses but
it's not overly helpful. Let's create a container that runs as a daemon,
like most of the applications we're probably going to run with Docker.
Again we can do this with the `docker run` command:
$ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147
Wait, what? Where's our "hello world" output? Let's look at what we've run here.
It should look pretty familiar. We ran `docker run` but this time we
specified a flag: `-d`. The `-d` flag tells Docker to run the container
and put it in the background, to daemonize it.
We also specified the same image: `ubuntu`.
Finally, we specified a command to run:
/bin/sh -c "while true; do echo hello world; sleep 1; done"
This is the (hello) world's silliest daemon: a shell script that echoes
`hello world` forever.
So why aren't we seeing any `hello world`'s? Instead Docker has returned
a really long string:
1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147
This really long string is called a *container ID*. It uniquely
identifies a container so we can work with it.
> **Note:**
> The container ID is a bit long and unwieldy. A bit later,
> we'll see a shorter ID and ways to name our containers to make
> working with them easier.
We can use this container ID to see what's happening with our `hello world` daemon.
Firstly let's make sure our container is running. We can
do that with the `docker ps` command. The `docker ps` command queries
the Docker daemon for information about all the containers it knows
about.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e5535038e28 ubuntu /bin/sh -c 'while tr 2 minutes ago Up 1 minute insane_babbage
Here we can see our daemonized container. The `docker ps` has returned some useful
information about it, starting with a shorter variant of its container ID:
`1e5535038e28`.
We can also see the image we used to build it, `ubuntu`, the command it
is running, its status and an automatically assigned name,
`insane_babbage`.
> **Note:**
> Docker automatically generates names for any containers started.
> We'll see how to specify your own names a bit later.
Okay, so we now know it's running. But is it doing what we asked it to do? To
see this we're going to look inside the container using the `docker logs`
command. Let's use the container name Docker assigned.
$ docker logs insane_babbage
hello world
hello world
hello world
. . .
The `docker logs` command looks inside the container and returns its standard
output: in this case the output of our command `hello world`.
Awesome! Our daemon is working and we've just created our first
Dockerized application!
Now we've established we can create our own containers let's tidy up
after ourselves and stop our detached container. To do this we use the
`docker stop` command.
$ docker stop insane_babbage
insane_babbage
The `docker stop` command tells Docker to politely stop the running
container. If it succeeds it will return the name of the container it
has just stopped.
Let's check it worked with the `docker ps` command.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Excellent. Our container has been stopped.
# Next steps
So far, you launched your first containers using the `docker run` command. You
ran an *interactive container* that ran in the foreground. You also ran a
*detached container* that ran in the background. In the process you learned
about several Docker commands:
* `docker ps` - Lists containers.
* `docker logs` - Shows us the standard output of a container.
* `docker stop` - Stops running containers.
Now, you have the basis learn more about Docker and how to do some more advanced
tasks. Go to ["*Run a simple application*"](usingdocker.md) to actually build a
web application with the Docker client.

View File

@@ -0,0 +1,187 @@
<!--[metadata]>
+++
aliases = ["/engine/userguide/dockerrepos/"]
title = "Store images on Docker Hub"
description = "Learn how to use the Docker Hub to manage Docker images and work flow"
keywords = ["repo, Docker Hub, Docker Hub, registry, index, repositories, usage, pull image, push image, image, documentation"]
[menu.main]
parent = "engine_learn"
+++
<![end-metadata]-->
# Store images on Docker Hub
So far you've learned how to use the command line to run Docker on your local
host. You've learned how to [pull down images](usingdocker.md) to build
containers from existing images and you've learned how to [create your own
images](dockerimages.md).
Next, you're going to learn how to use the [Docker Hub](https://hub.docker.com)
to simplify and enhance your Docker workflows.
The [Docker Hub](https://hub.docker.com) is a public registry maintained by
Docker, Inc. It contains images you can download and use to build
containers. It also provides authentication, work group structure, workflow
tools like webhooks and build triggers, and privacy tools like private
repositories for storing images you don't want to share publicly.
## Docker commands and Docker Hub
Docker itself provides access to Docker Hub services via the `docker search`,
`pull`, `login`, and `push` commands. This page will show you how these commands work.
### Account creation and login
Typically, you'll want to start by creating an account on Docker Hub (if you haven't
already) and logging in. You can create your account directly on
[Docker Hub](https://hub.docker.com/account/signup/), or by running:
$ docker login
This will prompt you for a user name, which will become the public namespace for your
public repositories.
If your user name is available, Docker will prompt you to enter a password and your
e-mail address. It will then automatically log you in. You can now commit and
push your own images up to your repos on Docker Hub.
> **Note:**
> Your authentication credentials will be stored in the `~/.docker/config.json`
> authentication file in your home directory.
## Searching for images
You can search the [Docker Hub](https://hub.docker.com) registry via its search
interface or by using the command line interface. Searching can find images by image
name, user name, or description:
$ docker search centos
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
centos The official build of CentOS 1223 [OK]
tianon/centos CentOS 5 and 6, created using rinse instea... 33
...
There you can see two example results: `centos` and `tianon/centos`. The second
result shows that it comes from the public repository of a user, named
`tianon/`, while the first result, `centos`, doesn't explicitly list a
repository which means that it comes from the trusted top-level namespace for
[Official Repositories](https://docs.docker.com/docker-hub/official_repos/). The `/` character separates
a user's repository from the image name.
Once you've found the image you want, you can download it with `docker pull <imagename>`:
$ docker pull centos
Using default tag: latest
latest: Pulling from library/centos
f1b10cd84249: Pull complete
c852f6d61e65: Pull complete
7322fbe74aa5: Pull complete
Digest: sha256:90305c9112250c7e3746425477f1c4ef112b03b4abe78c612e092037bfecc3b7
Status: Downloaded newer image for centos:latest
You now have an image from which you can run containers.
### Specific Versions or Latest
Using `docker pull centos` is equivalent to using `docker pull centos:latest`.
To pull an image that is not the default latest image you can be more precise
with the image that you want.
For example, to pull version 5 of `centos` use `docker pull centos:centos5`.
In this example, `centos5` is the tag labeling an image in the `centos`
repository for a version of `centos`.
To find a list of tags pointing to currently available versions of a repository
see the [Docker Hub](https://hub.docker.com) registry.
## Contributing to Docker Hub
Anyone can pull public images from the [Docker Hub](https://hub.docker.com)
registry, but if you would like to share your own images, then you must
[register first](https://docs.docker.com/docker-hub/accounts).
## Pushing a repository to Docker Hub
In order to push a repository to its registry, you need to have named an image
or committed your container to a named image as we saw
[here](dockerimages.md).
Now you can push this repository to the registry designated by its name or tag.
$ docker push yourname/newimage
The image will then be uploaded and available for use by your team-mates and/or the
community.
## Features of Docker Hub
Let's take a closer look at some of the features of Docker Hub. You can find more
information [here](https://docs.docker.com/docker-hub/).
* Private repositories
* Organizations and teams
* Automated Builds
* Webhooks
### Private repositories
Sometimes you have images you don't want to make public and share with
everyone. So Docker Hub allows you to have private repositories. You can
sign up for a plan [here](https://registry.hub.docker.com/plans/).
### Organizations and teams
One of the useful aspects of private repositories is that you can share
them only with members of your organization or team. Docker Hub lets you
create organizations where you can collaborate with your colleagues and
manage private repositories. You can learn how to create and manage an organization
[here](https://registry.hub.docker.com/account/organizations/).
### Automated Builds
Automated Builds automate the building and updating of images from
[GitHub](https://www.github.com) or [Bitbucket](http://bitbucket.com), directly on Docker
Hub. It works by adding a commit hook to your selected GitHub or Bitbucket repository,
triggering a build and update when you push a commit.
#### To setup an Automated Build
1. Create a [Docker Hub account](https://hub.docker.com/) and login.
2. Link your GitHub or Bitbucket account through the ["Link Accounts"](https://registry.hub.docker.com/account/accounts/) menu.
3. [Configure an Automated Build](https://registry.hub.docker.com/builds/add/).
4. Pick a GitHub or Bitbucket project that has a `Dockerfile` that you want to build.
5. Pick the branch you want to build (the default is the `master` branch).
6. Give the Automated Build a name.
7. Assign an optional Docker tag to the Build.
8. Specify where the `Dockerfile` is located. The default is `/`.
Once the Automated Build is configured it will automatically trigger a
build and, in a few minutes, you should see your new Automated Build on the [Docker Hub](https://hub.docker.com)
Registry. It will stay in sync with your GitHub and Bitbucket repository until you
deactivate the Automated Build.
To check the output and status of your Automated Build repositories, click on a repository name within the ["Your Repositories" page](https://registry.hub.docker.com/repos/). Automated Builds are indicated by a check-mark icon next to the repository name. Within the repository details page, you may click on the "Build Details" tab to view the status and output of all builds triggered by the Docker Hub.
Once you've created an Automated Build you can deactivate or delete it. You
cannot, however, push to an Automated Build with the `docker push` command.
You can only manage it by committing code to your GitHub or Bitbucket
repository.
You can create multiple Automated Builds per repository and configure them
to point to specific `Dockerfile`'s or Git branches.
#### Build triggers
Automated Builds can also be triggered via a URL on Docker Hub. This
allows you to rebuild an Automated build image on demand.
### Webhooks
Webhooks are attached to your repositories and allow you to trigger an
event when an image or updated image is pushed to the repository. With
a webhook you can specify a target URL and a JSON payload that will be
delivered when the image is pushed.
See the Docker Hub documentation for [more information on
webhooks](https://docs.docker.com/docker-hub/repos/#webhooks)
## Next steps
Go and use Docker!

View File

@@ -0,0 +1,286 @@
<!--[metadata]>
+++
aliases = ["/engine/userguide/dockervolumes/"]
title = "Manage data in containers"
description = "How to manage data inside your Docker containers."
keywords = ["Examples, Usage, volume, docker, documentation, user guide, data, volumes"]
[menu.main]
parent = "engine_learn"
+++
<![end-metadata]-->
# Manage data in containers
So far we've been introduced to some [basic Docker concepts](../containers/usingdocker.md),
seen how to work with [Docker images](../containers/dockerimages.md) as well as learned about
[networking and links between containers](../networking/default_network/dockerlinks.md). In this section we're
going to discuss how you can manage data inside and between your Docker
containers.
We're going to look at the two primary ways you can manage data in
Docker.
* Data volumes, and
* Data volume containers.
## Data volumes
A *data volume* is a specially-designated directory within one or more
containers that bypasses the [*Union File System*](../../reference/glossary.md#union-file-system). Data volumes provide several useful features for persistent or shared data:
- Volumes are initialized when a container is created. If the container's
base image contains data at the specified mount point, that existing data is
copied into the new volume upon volume initialization. (Note that this does
not apply when [mounting a host directory](#mount-a-host-directory-as-a-data-volume).)
- Data volumes can be shared and reused among containers.
- Changes to a data volume are made directly.
- Changes to a data volume will not be included when you update an image.
- Data volumes persist even if the container itself is deleted.
Data volumes are designed to persist data, independent of the container's life
cycle. Docker therefore *never* automatically deletes volumes when you remove
a container, nor will it "garbage collect" volumes that are no longer
referenced by a container.
### Adding a data volume
You can add a data volume to a container using the `-v` flag with the
`docker create` and `docker run` command. You can use the `-v` multiple times
to mount multiple data volumes. Let's mount a single volume now in our web
application container.
$ docker run -d -P --name web -v /webapp training/webapp python app.py
This will create a new volume inside a container at `/webapp`.
> **Note:**
> You can also use the `VOLUME` instruction in a `Dockerfile` to add one or
> more new volumes to any container created from that image.
### Locating a volume
You can locate the volume on the host by utilizing the `docker inspect` command.
$ docker inspect web
The output will provide details on the container configurations including the
volumes. The output should look something similar to the following:
...
Mounts": [
{
"Name": "fac362...80535",
"Source": "/var/lib/docker/volumes/fac362...80535/_data",
"Destination": "/webapp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
...
You will notice in the above `Source` is specifying the location on the host and
`Destination` is specifying the volume location inside the container. `RW` shows
if the volume is read/write.
### Mount a host directory as a data volume
In addition to creating a volume using the `-v` flag you can also mount a
directory from your Docker daemon's host into a container.
```
$ docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py
```
This command mounts the host directory, `/src/webapp`, into the container at
`/opt/webapp`. If the path `/opt/webapp` already exists inside the container's
image, the `/src/webapp` mount overlays but does not remove the pre-existing
content. Once the mount is removed, the content is accessible again. This is
consistent with the expected behavior of the `mount` command.
The `container-dir` must always be an absolute path such as `/src/docs`.
The `host-dir` can either be an absolute path or a `name` value. If you
supply an absolute path for the `host-dir`, Docker bind-mounts to the path
you specify. If you supply a `name`, Docker creates a named volume by that `name`.
A `name` value must start with an alphanumeric character,
followed by `a-z0-9`, `_` (underscore), `.` (period) or `-` (hyphen).
An absolute path starts with a `/` (forward slash).
For example, you can specify either `/foo` or `foo` for a `host-dir` value.
If you supply the `/foo` value, Docker creates a bind-mount. If you supply
the `foo` specification, Docker creates a named volume.
If you are using Docker Machine on Mac or Windows, your Docker daemon has only limited access to your OS X or Windows filesystem. Docker Machine tries
to auto-share your `/Users` (OS X) or `C:\Users` (Windows) directory. So,
you can mount files or directories on OS X using.
```
docker run -v /Users/<path>:/<container path> ...
```
On Windows, mount directories using:
```
docker run -v /c/Users/<path>:/<container path> ...`
```
All other paths come from your virtual machine's filesystem. For example, if
you are using VirtualBox some other folder available for sharing, you need to do
additional work. In the case of VirtualBox you need to make the host folder
available as a shared folder in VirtualBox. Then, you can mount it using the
Docker `-v` flag.
Mounting a host directory can be useful for testing. For example, you can mount
source code inside a container. Then, change the source code and see its effect
on the application in real time. The directory on the host must be specified as
an absolute path and if the directory doesn't exist Docker will automatically
create it for you. This auto-creation of the host path has been [*deprecated*](#auto-creating-missing-host-paths-for-bind-mounts).
Docker volumes default to mount in read-write mode, but you can also set it to
be mounted read-only.
```
$ docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py
```
Here we've mounted the same `/src/webapp` directory but we've added the `ro`
option to specify that the mount should be read-only.
Because of [limitations in the `mount`
function](http://lists.linuxfoundation.org/pipermail/containers/2015-April/035788.html),
moving subdirectories within the host's source directory can give
access from the container to the host's file system. This requires a malicious
user with access to host and its mounted directory.
>**Note**: The host directory is, by its nature, host-dependent. For this
>reason, you can't mount a host directory from `Dockerfile` because built images
>should be portable. A host directory wouldn't be available on all potential
>hosts.
### Volume labels
Labeling systems like SELinux require that proper labels are placed on volume
content mounted into a container. Without a label, the security system might
prevent the processes running inside the container from using the content. By
default, Docker does not change the labels set by the OS.
To change a label in the container context, you can add either of two suffixes
`:z` or `:Z` to the volume mount. These suffixes tell Docker to relabel file
objects on the shared volumes. The `z` option tells Docker that two containers
share the volume content. As a result, Docker labels the content with a shared
content label. Shared volume labels allow all containers to read/write content.
The `Z` option tells Docker to label the content with a private unshared label.
Only the current container can use a private volume.
### Mount a host file as a data volume
The `-v` flag can also be used to mount a single file - instead of *just*
directories - from the host machine.
$ docker run --rm -it -v ~/.bash_history:/root/.bash_history ubuntu /bin/bash
This will drop you into a bash shell in a new container, you will have your bash
history from the host and when you exit the container, the host will have the
history of the commands typed while in the container.
> **Note:**
> Many tools used to edit files including `vi` and `sed --in-place` may result
> in an inode change. Since Docker v1.1.0, this will produce an error such as
> "*sed: cannot rename ./sedKdJ9Dy: Device or resource busy*". In the case where
> you want to edit the mounted file, it is often easiest to instead mount the
> parent directory.
## Creating and mounting a data volume container
If you have some persistent data that you want to share between
containers, or want to use from non-persistent containers, it's best to
create a named Data Volume Container, and then to mount the data from
it.
Let's create a new named container with a volume to share.
While this container doesn't run an application, it reuses the `training/postgres`
image so that all containers are using layers in common, saving disk space.
$ docker create -v /dbdata --name dbstore training/postgres /bin/true
You can then use the `--volumes-from` flag to mount the `/dbdata` volume in another container.
$ docker run -d --volumes-from dbstore --name db1 training/postgres
And another:
$ docker run -d --volumes-from dbstore --name db2 training/postgres
In this case, if the `postgres` image contained a directory called `/dbdata`
then mounting the volumes from the `dbstore` container hides the
`/dbdata` files from the `postgres` image. The result is only the files
from the `dbstore` container are visible.
You can use multiple `--volumes-from` parameters to combine data volumes from
several containers. To find detailed information about `--volumes-from` see the
[Mount volumes from container](../../reference/commandline/run.md#mount-volumes-from-container-volumes-from)
in the `run` command reference.
You can also extend the chain by mounting the volume that came from the
`dbstore` container in yet another container via the `db1` or `db2` containers.
$ docker run -d --name db3 --volumes-from db1 training/postgres
If you remove containers that mount volumes, including the initial `dbstore`
container, or the subsequent containers `db1` and `db2`, the volumes will not
be deleted. To delete the volume from disk, you must explicitly call
`docker rm -v` against the last container with a reference to the volume. This
allows you to upgrade, or effectively migrate data volumes between containers.
> **Note:** Docker will not warn you when removing a container *without*
> providing the `-v` option to delete its volumes. If you remove containers
> without using the `-v` option, you may end up with "dangling" volumes;
> volumes that are no longer referenced by a container.
> You can use `docker volume ls -f dangling=true` to find dangling volumes,
> and use `docker volume rm <volume name>` to remove a volume that's
> no longer needed.
## Backup, restore, or migrate data volumes
Another useful function we can perform with volumes is use them for
backups, restores or migrations. We do this by using the
`--volumes-from` flag to create a new container that mounts that volume,
like so:
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
Here we've launched a new container and mounted the volume from the
`dbstore` container. We've then mounted a local host directory as
`/backup`. Finally, we've passed a command that uses `tar` to backup the
contents of the `dbdata` volume to a `backup.tar` file inside our
`/backup` directory. When the command completes and the container stops
we'll be left with a backup of our `dbdata` volume.
You could then restore it to the same container, or another that you've made
elsewhere. Create a new container.
$ docker run -v /dbdata --name dbstore2 ubuntu /bin/bash
Then un-tar the backup file in the new container's data volume.
$ docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"
You can use the techniques above to automate backup, migration and
restore testing using your preferred tools.
## Important tips on using shared volumes
Multiple containers can also share one or more data volumes. However, multiple containers writing to a single shared volume can cause data corruption. Make sure your applications are designed to write to shared data stores.
Data volumes are directly accessible from the Docker host. This means you can read and write to them with normal Linux tools. In most cases you should not do this as it can cause data corruption if your containers and applications are unaware of your direct access.
# Next steps
Now we've learned a bit more about how to use Docker we're going to see how to
combine Docker with the services available on
[Docker Hub](https://hub.docker.com) including Automated Builds and private
repositories.
Go to [Working with Docker Hub](../containers/dockerrepos.md).

View File

@@ -0,0 +1,19 @@
<!--[metadata]>
+++
title = "Learn by example"
description = "Explains how to work with containers"
keywords = ["docker, introduction, documentation, about, technology, docker.io, user, guide, user's, manual, platform, framework, home, intro"]
[menu.main]
identifier="engine_learn"
parent = "engine_guide"
+++
<![end-metadata]-->
# Learn by example
* [Hello world in a container](dockerizing.md)
* [Run a simple application](usingdocker.md)
* [Build your own images](dockerimages.md)
* [Network containers](networkingcontainers.md)
* [Manage data in containers](dockervolumes.md)
* [Store images on Docker Hub](dockerrepos.md)

View File

@@ -0,0 +1,247 @@
<!--[metadata]>
+++
aliases = ["/engine/userguide/networkigncontainers/"]
title = "Network containers"
description = "How to network Docker containers."
keywords = ["Examples, Usage, volume, docker, documentation, user guide, data, volumes"]
[menu.main]
parent = "engine_learn"
weight = -3
+++
<![end-metadata]-->
# Network containers
If you are working your way through the user guide, you just built and ran a
simple application. You've also built in your own images. This section teaches
you how to network your containers.
## Name a container
You've already seen that each container you create has an automatically
created name; indeed you've become familiar with our old friend
`nostalgic_morse` during this guide. You can also name containers
yourself. This naming provides two useful functions:
* You can name containers that do specific functions in a way
that makes it easier for you to remember them, for example naming a
container containing a web application `web`.
* Names provide Docker with a reference point that allows it to refer to other
containers. There are several commands that support this and you'll use one in an exercise later.
You name your container by using the `--name` flag, for example launch a new container called web:
$ docker run -d -P --name web training/webapp python app.py
Use the `docker ps` command to see check the name:
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web
You can also use `docker inspect` with the container's name.
$ docker inspect web
[
{
"Id": "3ce51710b34f5d6da95e0a340d32aa2e6cf64857fb8cdb2a6c38f7c56f448143",
"Created": "2015-10-25T22:44:17.854367116Z",
"Path": "python",
"Args": [
"app.py"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
...
Container names must be unique. That means you can only call one container
`web`. If you want to re-use a container name you must delete the old container
(with `docker rm`) before you can reuse the name with a new container. Go ahead and stop and remove your old `web` container.
$ docker stop web
web
$ docker rm web
web
## Launch a container on the default network
Docker includes support for networking containers through the use of **network
drivers**. By default, Docker provides two network drivers for you, the
`bridge` and the `overlay` drivers. You can also write a network driver plugin so
that you can create your own drivers but that is an advanced task.
Every installation of the Docker Engine automatically includes three default networks. You can list them:
$ docker network ls
NETWORK ID NAME DRIVER
18a2866682b8 none null
c288470c46f6 host host
7b369448dccb bridge bridge
The network named `bridge` is a special network. Unless you tell it otherwise, Docker always launches your containers in this network. Try this now:
$ docker run -itd --name=networktest ubuntu
74695c9cea6d9810718fddadc01a727a5dd3ce6a69d09752239736c030599741
Inspecting the network is an easy way to find out the container's IP address.
```bash
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.17.0.1/16",
"Gateway": "172.17.0.1"
}
]
},
"Containers": {
"3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c": {
"EndpointID": "647c12443e91faf0fd508b6edfe59c30b642abb60dfab890b4bdccee38750bc1",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c": {
"EndpointID": "b047d090f446ac49747d3c37d63e4307be745876db7f0ceef7b311cbba615f48",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "9001"
}
}
]
```
You can remove a container from a network by disconnecting the container. To do this, you supply both the network name and the container name. You can also use the container id. In this example, though, the name is faster.
$ docker network disconnect bridge networktest
While you can disconnect a container from a network, you cannot remove the builtin `bridge` network named `bridge`. Networks are natural ways to isolate containers from other containers or other networks. So, as you get more experienced with Docker, you'll want to create your own networks.
## Create your own bridge network
Docker Engine natively supports both bridge networks and overlay networks. A bridge network is limited to a single host running Docker Engine. An overlay network can include multiple hosts and is a more advanced topic. For this example, you'll create a bridge network:
$ docker network create -d bridge my-bridge-network
The `-d` flag tells Docker to use the `bridge` driver for the new network. You could have left this flag off as `bridge` is the default value for this flag. Go ahead and list the networks on your machine:
$ docker network ls
NETWORK ID NAME DRIVER
7b369448dccb bridge bridge
615d565d498c my-bridge-network bridge
18a2866682b8 none null
c288470c46f6 host host
If you inspect the network, you'll find that it has nothing in it.
$ docker network inspect my-bridge-network
[
{
"Name": "my-bridge-network",
"Id": "5a8afc6364bccb199540e133e63adb76a557906dd9ff82b94183fc48c40857ac",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1/16"
}
]
},
"Containers": {},
"Options": {}
}
]
## Add containers to a network
To build web applications that act in concert but do so securely, create a
network. Networks, by definition, provide complete isolation for containers. You
can add containers to a network when you first run a container.
Launch a container running a PostgreSQL database and pass it the `--net=my-bridge-network` flag to connect it to your new network:
$ docker run -d --net=my-bridge-network --name db training/postgres
If you inspect your `my-bridge-network` you'll see it has a container attached.
You can also inspect your container to see where it is connected:
$ docker inspect --format='{{json .NetworkSettings.Networks}}' db
{"my-bridge-network":{"NetworkID":"7d86d31b1478e7cca9ebed7e73aa0fdeec46c5ca29497431d3007d2d9e15ed99",
"EndpointID":"508b170d56b2ac9e4ef86694b0a76a22dd3df1983404f7321da5649645bf7043","Gateway":"172.18.0.1","IPAddress":"172.18.0.2","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02"}}
Now, go ahead and start your by now familiar web application. This time leave off the `-P` flag and also don't specify a network.
$ docker run -d --name web training/webapp python app.py
Which network is your `web` application running under? Inspect the application and you'll find it is running in the default `bridge` network.
$ docker inspect --format='{{json .NetworkSettings.Networks}}' web
{"bridge":{"NetworkID":"7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
"EndpointID":"508b170d56b2ac9e4ef86694b0a76a22dd3df1983404f7321da5649645bf7043","Gateway":"172.17.0.1","IPAddress":"172.17.0.2","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02"}}
Then, get the IP address of your `web`
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
172.17.0.2
Now, open a shell to your running `db` container:
$ docker exec -it db bash
root@a205f0dd33b2:/# ping 172.17.0.2
ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
^C
--- 172.17.0.2 ping statistics ---
44 packets transmitted, 0 received, 100% packet loss, time 43185ms
After a bit, use `CTRL-C` to end the `ping` and you'll find the ping failed. That is because the two containers are running on different networks. You can fix that. Then, use the `exit` command to close the container.
Docker networking allows you to attach a container to as many networks as you like. You can also attach an already running container. Go ahead and attach your running `web` app to the `my-bridge-network`.
$ docker network connect my-bridge-network web
Open a shell into the `db` application again and try the ping command. This time just use the container name `web` rather than the IP Address.
$ docker exec -it db bash
root@a205f0dd33b2:/# ping web
PING web (172.18.0.3) 56(84) bytes of data.
64 bytes from web (172.18.0.3): icmp_seq=1 ttl=64 time=0.095 ms
64 bytes from web (172.18.0.3): icmp_seq=2 ttl=64 time=0.060 ms
64 bytes from web (172.18.0.3): icmp_seq=3 ttl=64 time=0.066 ms
^C
--- web ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.060/0.073/0.095/0.018 ms
The `ping` shows it is contacting a different IP address, the address on the `my-bridge-network` which is different from its address on the `bridge` network.
## Next steps
Now that you know how to network containers, see [how to manage data in containers](dockervolumes.md).

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,306 @@
<!--[metadata]>
+++
title = "Run a simple application"
description = "Learn how to manage and operate Docker containers."
keywords = ["docker, the docker guide, documentation, docker.io, monitoring containers, docker top, docker inspect, docker port, ports, docker logs, log, Logs"]
[menu.main]
parent="engine_learn"
weight=-5
+++
<![end-metadata]-->
# Run a simple application
In the ["*Hello world in a container*"](dockerizing.md) you launched your
first containers using the `docker run` command. You ran an *interactive container* that ran in the foreground. You also ran a *detached container* that ran in the background. In the process you learned about several Docker commands:
* `docker ps` - Lists containers.
* `docker logs` - Shows us the standard output of a container.
* `docker stop` - Stops running containers.
## Learn about the Docker client
If you didn't realize it yet, you've been using the Docker client each time you
typed `docker` in your Bash terminal. The client is a simple command line client
also known as a command-line interface (CLI). Each action you can take with
the client is a command and each command can take a series of flags and arguments.
# Usage: [sudo] docker [subcommand] [flags] [arguments] ..
# Example:
$ docker run -i -t ubuntu /bin/bash
You can see this in action by using the `docker version` command to return
version information on the currently installed Docker client and daemon.
$ docker version
This command will not only provide you the version of Docker client and
daemon you are using, but also the version of Go (the programming
language powering Docker).
Client:
Version: 1.8.1
API version: 1.20
Go version: go1.4.2
Git commit: d12ea79
Built: Thu Aug 13 02:35:49 UTC 2015
OS/Arch: linux/amd64
Server:
Version: 1.8.1
API version: 1.20
Go version: go1.4.2
Git commit: d12ea79
Built: Thu Aug 13 02:35:49 UTC 2015
OS/Arch: linux/amd64
## Get Docker command help
You can display the help for specific Docker commands. The help details the
options and their usage. To see a list of all the possible commands, use the
following:
$ docker --help
To see usage for a specific command, specify the command with the `--help` flag:
$ docker attach --help
Usage: docker attach [OPTIONS] CONTAINER
Attach to a running container
--help Print usage
--no-stdin Do not attach stdin
--sig-proxy=true Proxy all received signals to the process
> **Note:**
> For further details and examples of each command, see the
> [command reference](../../reference/commandline/cli.md) in this guide.
## Running a web application in Docker
So now you've learned a bit more about the `docker` client you can move onto
the important stuff: running more containers. So far none of the
containers you've run did anything particularly useful, so you can
change that by running an example web application in Docker.
For our web application we're going to run a Python Flask application.
Start with a `docker run` command.
$ docker run -d -P training/webapp python app.py
Review what the command did. You've specified two flags: `-d` and
`-P`. You've already seen the `-d` flag which tells Docker to run the
container in the background. The `-P` flag is new and tells Docker to
map any required network ports inside our container to our host. This
lets us view our web application.
You've specified an image: `training/webapp`. This image is a
pre-built image you've created that contains a simple Python Flask web
application.
Lastly, you've specified a command for our container to run: `python app.py`. This launches our web application.
> **Note:**
> You can see more detail on the `docker run` command in the [command
> reference](../../reference/commandline/run.md) and the [Docker Run
> Reference](../../reference/run.md).
## Viewing our web application container
Now you can see your running container using the `docker ps` command.
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
You can see you've specified a new flag, `-l`, for the `docker ps`
command. This tells the `docker ps` command to return the details of the
*last* container started.
> **Note:**
> By default, the `docker ps` command only shows information about running
> containers. If you want to see stopped containers too use the `-a` flag.
We can see the same details we saw [when we first Dockerized a
container](dockerizing.md) with one important addition in the `PORTS`
column.
PORTS
0.0.0.0:49155->5000/tcp
When we passed the `-P` flag to the `docker run` command Docker mapped any
ports exposed in our image to our host.
> **Note:**
> We'll learn more about how to expose ports in Docker images when
> [we learn how to build images](dockerimages.md).
In this case Docker has exposed port 5000 (the default Python Flask
port) on port 49155.
Network port bindings are very configurable in Docker. In our last example the
`-P` flag is a shortcut for `-p 5000` that maps port 5000 inside the container
to a high port (from *ephemeral port range* which typically ranges from 32768
to 61000) on the local Docker host. We can also bind Docker containers to
specific ports using the `-p` flag, for example:
$ docker run -d -p 80:5000 training/webapp python app.py
This would map port 5000 inside our container to port 80 on our local
host. You might be asking about now: why wouldn't we just want to always
use 1:1 port mappings in Docker containers rather than mapping to high
ports? Well 1:1 mappings have the constraint of only being able to map
one of each port on your local host.
Suppose you want to test two Python applications: both bound to port 5000 inside
their own containers. Without Docker's port mapping you could only access one at
a time on the Docker host.
So you can now browse to port 49155 in a web browser to
see the application.
![Viewing the web application](webapp1.png).
Our Python application is live!
> **Note:**
> If you have been using a virtual machine on OS X, Windows or Linux,
> you'll need to get the IP of the virtual host instead of using localhost.
> You can do this by running the `docker-machine ip your_vm_name` from your command line or terminal application, for example:
>
> $ docker-machine ip my-docker-vm
> 192.168.99.100
>
> In this case you'd browse to `http://192.168.99.100:49155` for the above example.
## A network port shortcut
Using the `docker ps` command to return the mapped port is a bit clumsy so
Docker has a useful shortcut we can use: `docker port`. To use `docker port` we
specify the ID or name of our container and then the port for which we need the
corresponding public-facing port.
$ docker port nostalgic_morse 5000
0.0.0.0:49155
In this case you've looked up what port is mapped externally to port 5000 inside
the container.
## Viewing the web application's logs
You can also find out a bit more about what's happening with our application and
use another of the commands you've learned, `docker logs`.
$ docker logs -f nostalgic_morse
* Running on http://0.0.0.0:5000/
10.0.2.2 - - [23/May/2014 20:16:31] "GET / HTTP/1.1" 200 -
10.0.2.2 - - [23/May/2014 20:16:31] "GET /favicon.ico HTTP/1.1" 404 -
This time though you've added a new flag, `-f`. This causes the `docker
logs` command to act like the `tail -f` command and watch the
container's standard out. We can see here the logs from Flask showing
the application running on port 5000 and the access log entries for it.
## Looking at our web application container's processes
In addition to the container's logs we can also examine the processes
running inside it using the `docker top` command.
$ docker top nostalgic_morse
PID USER COMMAND
854 root python app.py
Here we can see our `python app.py` command is the only process running inside
the container.
## Inspecting our web application container
Lastly, we can take a low-level dive into our Docker container using the
`docker inspect` command. It returns a JSON document containing useful
configuration and status information for the specified container.
$ docker inspect nostalgic_morse
You can see a sample of that JSON output.
[{
"ID": "bc533791f3f500b280a9626688bc79e342e3ea0d528efe3a86a51ecb28ea20",
"Created": "2014-05-26T05:52:40.808952951Z",
"Path": "python",
"Args": [
"app.py"
],
"Config": {
"Hostname": "bc533791f3f5",
"Domainname": "",
"User": "",
. . .
We can also narrow down the information we want to return by requesting a
specific element, for example to return the container's IP address we would:
$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nostalgic_morse
172.17.0.5
## Stopping our web application container
Okay you've seen web application working. Now you can stop it using the
`docker stop` command and the name of our container: `nostalgic_morse`.
$ docker stop nostalgic_morse
nostalgic_morse
We can now use the `docker ps` command to check if the container has
been stopped.
$ docker ps -l
## Restarting our web application container
Oops! Just after you stopped the container you get a call to say another
developer needs the container back. From here you have two choices: you
can create a new container or restart the old one. Look at
starting your previous container back up.
$ docker start nostalgic_morse
nostalgic_morse
Now quickly run `docker ps -l` again to see the running container is
back up or browse to the container's URL to see if the application
responds.
> **Note:**
> Also available is the `docker restart` command that runs a stop and
> then start on the container.
## Removing our web application container
Your colleague has let you know that they've now finished with the container
and won't need it again. Now, you can remove it using the `docker rm` command.
$ docker rm nostalgic_morse
Error: Impossible to remove a running container, please stop it first or use -f
2014/05/24 08:12:56 Error: failed to remove one or more containers
What happened? We can't actually remove a running container. This protects
you from accidentally removing a running container you might need. You can try
this again by stopping the container first.
$ docker stop nostalgic_morse
nostalgic_morse
$ docker rm nostalgic_morse
nostalgic_morse
And now our container is stopped and deleted.
> **Note:**
> Always remember that removing a container is final!
# Next steps
Until now you've only used images that you've downloaded from Docker Hub. Next,
you can get introduced to building and sharing our own images.
Go to [Working with Docker Images](dockerimages.md).

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,71 @@
<!--[metadata]>
+++
aliases = ["/engine/articles/baseimages/"]
title = "Create a base image"
description = "How to create base images"
keywords = ["Examples, Usage, base image, docker, documentation, examples"]
[menu.main]
parent = "engine_images"
+++
<![end-metadata]-->
# Create a base image
So you want to create your own [*Base Image*](../../reference/glossary.md#base-image)? Great!
The specific process will depend heavily on the Linux distribution you
want to package. We have some examples below, and you are encouraged to
submit pull requests to contribute new ones.
## Create a full image using tar
In general, you'll want to start with a working machine that is running
the distribution you'd like to package as a base image, though that is
not required for some tools like Debian's
[Debootstrap](https://wiki.debian.org/Debootstrap), which you can also
use to build Ubuntu images.
It can be as simple as this to create an Ubuntu base image:
$ sudo debootstrap raring raring > /dev/null
$ sudo tar -C raring -c . | docker import - raring
a29c15f1bf7a
$ docker run raring cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=13.04
DISTRIB_CODENAME=raring
DISTRIB_DESCRIPTION="Ubuntu 13.04"
There are more example scripts for creating base images in the Docker
GitHub Repo:
- [BusyBox](https://github.com/docker/docker/blob/master/contrib/mkimage-busybox.sh)
- CentOS / Scientific Linux CERN (SLC) [on Debian/Ubuntu](
https://github.com/docker/docker/blob/master/contrib/mkimage-rinse.sh) or
[on CentOS/RHEL/SLC/etc.](
https://github.com/docker/docker/blob/master/contrib/mkimage-yum.sh)
- [Debian / Ubuntu](
https://github.com/docker/docker/blob/master/contrib/mkimage-debootstrap.sh)
## Creating a simple base image using scratch
You can use Docker's reserved, minimal image, `scratch`, as a starting point for building containers. Using the `scratch` "image" signals to the build process that you want the next command in the `Dockerfile` to be the first filesystem layer in your image.
While `scratch` appears in Docker's repository on the hub, you can't pull it, run it, or tag any image with the name `scratch`. Instead, you can refer to it in your `Dockerfile`. For example, to create a minimal container using `scratch`:
FROM scratch
ADD hello /
CMD ["/hello"]
This example creates the hello-world image used in the tutorials.
If you want to test it out, you can clone [the image repo](https://github.com/docker-library/hello-world)
## More resources
There are lots more resources available to help you write your 'Dockerfile`.
* There's a [complete guide to all the instructions](../../reference/builder.md) available for use in a `Dockerfile` in the reference section.
* To help you write a clear, readable, maintainable `Dockerfile`, we've also
written a [`Dockerfile` Best Practices guide](dockerfile_best-practices.md).
* If your goal is to create a new Official Repository, be sure to read up on Docker's [Official Repositories](https://docs.docker.com/docker-hub/official_repos/).

View File

@@ -0,0 +1,495 @@
<!--[metadata]>
+++
aliases = ["/engine/articles/dockerfile_best-practices/"]
title = "Best practices for writing Dockerfiles"
description = "Hints, tips and guidelines for writing clean, reliable Dockerfiles"
keywords = ["Examples, Usage, base image, docker, documentation, dockerfile, best practices, hub, official repo"]
[menu.main]
parent = "engine_images"
+++
<![end-metadata]-->
# Best practices for writing Dockerfiles
Docker can build images automatically by reading the instructions from a
`Dockerfile`, a text file that contains all the commands, in order, needed to
build a given image. `Dockerfile`s adhere to a specific format and use a
specific set of instructions. You can learn the basics on the
[Dockerfile Reference](../../reference/builder.md) page. If
youre new to writing `Dockerfile`s, you should start there.
This document covers the best practices and methods recommended by Docker,
Inc. and the Docker community for creating easy-to-use, effective
`Dockerfile`s. We strongly suggest you follow these recommendations (in fact,
if youre creating an Official Image, you *must* adhere to these practices).
You can see many of these practices and recommendations in action in the [buildpack-deps `Dockerfile`](https://github.com/docker-library/buildpack-deps/blob/master/jessie/Dockerfile).
> Note: for more detailed explanations of any of the Dockerfile commands
>mentioned here, visit the [Dockerfile Reference](../../reference/builder.md) page.
## General guidelines and recommendations
### Containers should be ephemeral
The container produced by the image your `Dockerfile` defines should be as
ephemeral as possible. By “ephemeral,” we mean that it can be stopped and
destroyed and a new one built and put in place with an absolute minimum of
set-up and configuration.
### Use a .dockerignore file
In most cases, it's best to put each Dockerfile in an empty directory. Then,
add to that directory only the files needed for building the Dockerfile. To
increase the build's performance, you can exclude files and directories by
adding a `.dockerignore` file to that directory as well. This file supports
exclusion patterns similar to `.gitignore` files. For information on creating one,
see the [.dockerignore file](../../reference/builder.md#dockerignore-file).
### Avoid installing unnecessary packages
In order to reduce complexity, dependencies, file sizes, and build times, you
should avoid installing extra or unnecessary packages just because they
might be “nice to have.” For example, you dont need to include a text editor
in a database image.
### Run only one process per container
In almost all cases, you should only run a single process in a single
container. Decoupling applications into multiple containers makes it much
easier to scale horizontally and reuse containers. If that service depends on
another service, make use of [container linking](../../userguide/networking/default_network/dockerlinks.md).
### Minimize the number of layers
You need to find the balance between readability (and thus long-term
maintainability) of the `Dockerfile` and minimizing the number of layers it
uses. Be strategic and cautious about the number of layers you use.
### Sort multi-line arguments
Whenever possible, ease later changes by sorting multi-line arguments
alphanumerically. This will help you avoid duplication of packages and make the
list much easier to update. This also makes PRs a lot easier to read and
review. Adding a space before a backslash (`\`) helps as well.
Heres an example from the [`buildpack-deps` image](https://github.com/docker-library/buildpack-deps):
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
### Build cache
During the process of building an image Docker will step through the
instructions in your `Dockerfile` executing each in the order specified.
As each instruction is examined Docker will look for an existing image in its
cache that it can reuse, rather than creating a new (duplicate) image.
If you do not want to use the cache at all you can use the ` --no-cache=true`
option on the `docker build` command.
However, if you do let Docker use its cache then it is very important to
understand when it will, and will not, find a matching image. The basic rules
that Docker will follow are outlined below:
* Starting with a base image that is already in the cache, the next
instruction is compared against all child images derived from that base
image to see if one of them was built using the exact same instruction. If
not, the cache is invalidated.
* In most cases simply comparing the instruction in the `Dockerfile` with one
of the child images is sufficient. However, certain instructions require
a little more examination and explanation.
* For the `ADD` and `COPY` instructions, the contents of the file(s)
in the image are examined and a checksum is calculated for each file.
The last-modified and last-accessed times of the file(s) are not considered in
these checksums. During the cache lookup, the checksum is compared against the
checksum in the existing images. If anything has changed in the file(s), such
as the contents and metadata, then the cache is invalidated.
* Aside from the `ADD` and `COPY` commands, cache checking will not look at the
files in the container to determine a cache match. For example, when processing
a `RUN apt-get -y update` command the files updated in the container
will not be examined to determine if a cache hit exists. In that case just
the command string itself will be used to find a match.
Once the cache is invalidated, all subsequent `Dockerfile` commands will
generate new images and the cache will not be used.
## The Dockerfile instructions
Below you'll find recommendations for the best way to write the
various instructions available for use in a `Dockerfile`.
### FROM
[Dockerfile reference for the FROM instruction](../../reference/builder.md#from)
Whenever possible, use current Official Repositories as the basis for your
image. We recommend the [Debian image](https://registry.hub.docker.com/_/debian/)
since its very tightly controlled and kept extremely minimal (currently under
100 mb), while still being a full distribution.
### RUN
[Dockerfile reference for the RUN instruction](../../reference/builder.md#run)
As always, to make your `Dockerfile` more readable, understandable, and
maintainable, split long or complex `RUN` statements on multiple lines separated
with backslashes.
### apt-get
Probably the most common use-case for `RUN` is an application of `apt-get`. The
`RUN apt-get` command, because it installs packages, has several gotchas to look
out for.
You should avoid `RUN apt-get upgrade` or `dist-upgrade`, as many of the
“essential” packages from the base images won't upgrade inside an unprivileged
container. If a package contained in the base image is out-of-date, you should
contact its maintainers.
If you know theres a particular package, `foo`, that needs to be updated, use
`apt-get install -y foo` to update automatically.
Always combine `RUN apt-get update` with `apt-get install` in the same `RUN`
statement, for example:
RUN apt-get update && apt-get install -y \
package-bar \
package-baz \
package-foo
Using `apt-get update` alone in a `RUN` statement causes caching issues and
subsequent `apt-get install` instructions fail.
For example, say you have a Dockerfile:
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl
After building the image, all layers are in the Docker cache. Suppose you later
modify `apt-get install` by adding extra package:
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx
Docker sees the initial and modified instructions as identical and reuses the
cache from previous steps. As a result the `apt-get update` is *NOT* executed
because the build uses the cached version. Because the `apt-get update` is not
run, your build can potentially get an outdated version of the `curl` and `nginx`
packages.
Using `RUN apt-get update && apt-get install -y` ensures your Dockerfile
installs the latest package versions with no further coding or manual
intervention. This technique is known as "cache busting". You can also achieve
cache-busting by specifying a package version. This is known as version pinning,
for example:
RUN apt-get update && apt-get install -y \
package-bar \
package-baz \
package-foo=1.3.*
Version pinning forces the build to retrieve a particular version regardless of
whats in the cache. This technique can also reduce failures due to unanticipated changes
in required packages.
Below is a well-formed `RUN` instruction that demonstrates all the `apt-get`
recommendations.
RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
curl \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
mercurial \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.* \
&& rm -rf /var/lib/apt/lists/*
The `s3cmd` instructions specifies a version `1.1.0*`. If the image previously
used an older version, specifying the new one causes a cache bust of `apt-get
update` and ensure the installation of the new version. Listing packages on
each line can also prevent mistakes in package duplication.
In addition, cleaning up the apt cache and removing `/var/lib/apt/lists` helps
keep the image size down. Since the `RUN` statement starts with
`apt-get update`, the package cache will always be refreshed prior to
`apt-get install`.
> **Note**: The official Debian and Ubuntu images [automatically run `apt-get clean`](https://github.com/docker/docker/blob/03e2923e42446dbb830c654d0eec323a0b4ef02a/contrib/mkimage/debootstrap#L82-L105),
> so explicit invocation is not required.
### CMD
[Dockerfile reference for the CMD instruction](../../reference/builder.md#cmd)
The `CMD` instruction should be used to run the software contained by your
image, along with any arguments. `CMD` should almost always be used in the
form of `CMD [“executable”, “param1”, “param2”…]`. Thus, if the image is for a
service (Apache, Rails, etc.), you would run something like
`CMD ["apache2","-DFOREGROUND"]`. Indeed, this form of the instruction is
recommended for any service-based image.
In most other cases, `CMD` should be given an interactive shell (bash, python,
perl, etc), for example, `CMD ["perl", "-de0"]`, `CMD ["python"]`, or
`CMD [“php”, “-a”]`. Using this form means that when you execute something like
`docker run -it python`, youll get dropped into a usable shell, ready to go.
`CMD` should rarely be used in the manner of `CMD [“param”, “param”]` in
conjunction with [`ENTRYPOINT`](../../reference/builder.md#entrypoint), unless
you and your expected users are already quite familiar with how `ENTRYPOINT`
works.
### EXPOSE
[Dockerfile reference for the EXPOSE instruction](../../reference/builder.md#expose)
The `EXPOSE` instruction indicates the ports on which a container will listen
for connections. Consequently, you should use the common, traditional port for
your application. For example, an image containing the Apache web server would
use `EXPOSE 80`, while an image containing MongoDB would use `EXPOSE 27017` and
so on.
For external access, your users can execute `docker run` with a flag indicating
how to map the specified port to the port of their choice.
For container linking, Docker provides environment variables for the path from
the recipient container back to the source (ie, `MYSQL_PORT_3306_TCP`).
### ENV
[Dockerfile reference for the ENV instruction](../../reference/builder.md#env)
In order to make new software easier to run, you can use `ENV` to update the
`PATH` environment variable for the software your container installs. For
example, `ENV PATH /usr/local/nginx/bin:$PATH` will ensure that `CMD [“nginx”]`
just works.
The `ENV` instruction is also useful for providing required environment
variables specific to services you wish to containerize, such as Postgress
`PGDATA`.
Lastly, `ENV` can also be used to set commonly used version numbers so that
version bumps are easier to maintain, as seen in the following example:
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress &&
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
Similar to having constant variables in a program (as opposed to hard-coding
values), this approach lets you change a single `ENV` instruction to
auto-magically bump the version of the software in your container.
### ADD or COPY
[Dockerfile reference for the ADD instruction](../../reference/builder.md#add)<br/>
[Dockerfile reference for the COPY instruction](../../reference/builder.md#copy)
Although `ADD` and `COPY` are functionally similar, generally speaking, `COPY`
is preferred. Thats because its more transparent than `ADD`. `COPY` only
supports the basic copying of local files into the container, while `ADD` has
some features (like local-only tar extraction and remote URL support) that are
not immediately obvious. Consequently, the best use for `ADD` is local tar file
auto-extraction into the image, as in `ADD rootfs.tar.xz /`.
If you have multiple `Dockerfile` steps that use different files from your
context, `COPY` them individually, rather than all at once. This will ensure that
each step's build cache is only invalidated (forcing the step to be re-run) if the
specifically required files change.
For example:
COPY requirements.txt /tmp/
RUN pip install --requirement /tmp/requirements.txt
COPY . /tmp/
Results in fewer cache invalidations for the `RUN` step, than if you put the
`COPY . /tmp/` before it.
Because image size matters, using `ADD` to fetch packages from remote URLs is
strongly discouraged; you should use `curl` or `wget` instead. That way you can
delete the files you no longer need after they've been extracted and you won't
have to add another layer in your image. For example, you should avoid doing
things like:
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
And instead, do something like:
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
For other items (files, directories) that do not require `ADD`s tar
auto-extraction capability, you should always use `COPY`.
### ENTRYPOINT
[Dockerfile reference for the ENTRYPOINT instruction](../../reference/builder.md#entrypoint)
The best use for `ENTRYPOINT` is to set the image's main command, allowing that
image to be run as though it was that command (and then use `CMD` as the
default flags).
Let's start with an example of an image for the command line tool `s3cmd`:
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
Now the image can be run like this to show the command's help:
$ docker run s3cmd
Or using the right parameters to execute a command:
$ docker run s3cmd ls s3://mybucket
This is useful because the image name can double as a reference to the binary as
shown in the command above.
The `ENTRYPOINT` instruction can also be used in combination with a helper
script, allowing it to function in a similar way to the command above, even
when starting the tool may require more than one step.
For example, the [Postgres Official Image](https://registry.hub.docker.com/_/postgres/)
uses the following script as its `ENTRYPOINT`:
```bash
#!/bin/bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
```
> **Note**:
> This script uses [the `exec` Bash command](http://wiki.bash-hackers.org/commands/builtin/exec)
> so that the final running application becomes the container's PID 1. This allows
> the application to receive any Unix signals sent to the container.
> See the [`ENTRYPOINT`](../../reference/builder.md#entrypoint)
> help for more details.
The helper script is copied into the container and run via `ENTRYPOINT` on
container start:
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
This script allows the user to interact with Postgres in several ways.
It can simply start Postgres:
$ docker run postgres
Or, it can be used to run Postgres and pass parameters to the server:
$ docker run postgres postgres --help
Lastly, it could also be used to start a totally different tool, such as Bash:
$ docker run --rm -it postgres bash
### VOLUME
[Dockerfile reference for the VOLUME instruction](../../reference/builder.md#volume)
The `VOLUME` instruction should be used to expose any database storage area,
configuration storage, or files/folders created by your docker container. You
are strongly encouraged to use `VOLUME` for any mutable and/or user-serviceable
parts of your image.
### USER
[Dockerfile reference for the USER instruction](../../reference/builder.md#user)
If a service can run without privileges, use `USER` to change to a non-root
user. Start by creating the user and group in the `Dockerfile` with something
like `RUN groupadd -r postgres && useradd -r -g postgres postgres`.
> **Note:** Users and groups in an image get a non-deterministic
> UID/GID in that the “next” UID/GID gets assigned regardless of image
> rebuilds. So, if its critical, you should assign an explicit UID/GID.
You should avoid installing or using `sudo` since it has unpredictable TTY and
signal-forwarding behavior that can cause more problems than it solves. If
you absolutely need functionality similar to `sudo` (e.g., initializing the
daemon as root but running it as non-root), you may be able to use
[“gosu”](https://github.com/tianon/gosu).
Lastly, to reduce layers and complexity, avoid switching `USER` back
and forth frequently.
### WORKDIR
[Dockerfile reference for the WORKDIR instruction](../../reference/builder.md#workdir)
For clarity and reliability, you should always use absolute paths for your
`WORKDIR`. Also, you should use `WORKDIR` instead of proliferating
instructions like `RUN cd … && do-something`, which are hard to read,
troubleshoot, and maintain.
### ONBUILD
[Dockerfile reference for the ONBUILD instruction](../../reference/builder.md#onbuild)
An `ONBUILD` command executes after the current `Dockerfile` build completes.
`ONBUILD` executes in any child image derived `FROM` the current image. Think
of the `ONBUILD` command as an instruction the parent `Dockerfile` gives
to the child `Dockerfile`.
A Docker build executes `ONBUILD` commands before any command in a child
`Dockerfile`.
`ONBUILD` is useful for images that are going to be built `FROM` a given
image. For example, you would use `ONBUILD` for a language stack image that
builds arbitrary user software written in that language within the
`Dockerfile`, as you can see in [Rubys `ONBUILD` variants](https://github.com/docker-library/ruby/blob/master/2.1/onbuild/Dockerfile).
Images built from `ONBUILD` should get a separate tag, for example:
`ruby:1.9-onbuild` or `ruby:2.0-onbuild`.
Be careful when putting `ADD` or `COPY` in `ONBUILD`. The “onbuild” image will
fail catastrophically if the new build's context is missing the resource being
added. Adding a separate tag, as recommended above, will help mitigate this by
allowing the `Dockerfile` author to make a choice.
## Examples for Official Repositories
These Official Repositories have exemplary `Dockerfile`s:
* [Go](https://registry.hub.docker.com/_/golang/)
* [Perl](https://registry.hub.docker.com/_/perl/)
* [Hy](https://registry.hub.docker.com/_/hylang/)
* [Rails](https://registry.hub.docker.com/_/rails)
## Additional resources:
* [Dockerfile Reference](../../reference/builder.md)
* [More about Base Images](baseimages.md)
* [More about Automated Builds](https://docs.docker.com/docker-hub/builds/)
* [Guidelines for Creating Official
Repositories](https://docs.docker.com/docker-hub/official_repos/)

View File

@@ -0,0 +1,53 @@
<!--[metadata]>
+++
alias = [ "/reference/api/hub_registry_spec/", "/userguide/image_management/"]
title = "Image management"
description = "Documentation for docker Registry and Registry API"
keywords = ["docker, registry, api, hub"]
[menu.main]
parent="engine_images"
weight=90
+++
<![end-metadata]-->
# Image management
The Docker Engine provides a client which you can use to create images on the command line or through a build process. You can run these images in a container or publish them for others to use. Storing the images you create, searching for images you might want, or publishing images others might use are all elements of image management.
This section provides an overview of the major features and products Docker provides for image management.
## Docker Hub
The [Docker Hub](https://docs.docker.com/docker-hub/) is responsible for centralizing information about user accounts, images, and public name spaces. It has different components:
- Web UI
- Meta-data store (comments, stars, list public repositories)
- Authentication service
- Tokenization
There is only one instance of the Docker Hub, run and managed by Docker Inc. This public Hub is useful for most individuals and smaller companies.
## Docker Registry and the Docker Trusted Registry
The Docker Registry is a component of Docker's ecosystem. A registry is a
storage and content delivery system, holding named Docker images, available in
different tagged versions. For example, the image `distribution/registry`, with
tags `2.0` and `latest`. Users interact with a registry by using docker push and
pull commands such as, `docker pull myregistry.com/stevvooe/batman:voice`.
The Docker Hub has its own registry which, like the Hub itself, is run and managed by Docker. However, there are other ways to obtain a registry. You can purchase the [Docker Trusted Registry](https://docs.docker.com/docker-trusted-registry) product to run on your company's network. Alternatively, you can use the Docker Registry component to build a private registry. For information about using a registry, see overview for the [Docker Registry](https://docs.docker.com/registry).
## Content Trust
When transferring data among networked systems, *trust* is a central concern. In
particular, when communicating over an untrusted medium such as the internet, it
is critical to ensure the integrity and publisher of all of the data a system
operates on. You use Docker to push and pull images (data) to a registry.
Content trust gives you the ability to both verify the integrity and the
publisher of all the data received from a registry over any channel.
[Content trust](../../security/trust/index.md) is currently only available for users of the
public Docker Hub. It is currently not available for the Docker Trusted Registry
or for private registries.

View File

@@ -0,0 +1,16 @@
<!--[metadata]>
+++
title = "Work with images"
description = "The Docker user guide home page"
keywords = ["docker, introduction, documentation, about, technology, docker.io, user, guide, user's, manual, platform, framework, home, intro"]
[menu.main]
identifier="engine_images"
parent = "engine_guide"
+++
<![end-metadata]-->
# Work with images
* [Create a base image](baseimages.md)
* [Best practices for writing Dockerfiles](dockerfile_best-practices.md)
* [Image management](image_management.md)

View File

@@ -0,0 +1,13 @@
<!--[metadata]>
+++
title = "User guide"
description = "Welcome to the user guide"
keywords = ["docker, introduction, documentation, about, technology, docker.io, user, guide, user's, manual, platform, framework, home, intro"]
[menu.main]
parent="engine_use"
identifier = "engine_guide"
weight="-80"
+++
<![end-metadata]-->
# User guide

View File

@@ -0,0 +1,119 @@
<!--[metadata]>
+++
title = "Introduction"
description = "Introduction to user guide"
keywords = ["docker, introduction, documentation, about, technology, docker.io, user, guide, user's, manual, platform, framework, home, intro"]
[menu.main]
parent="engine_guide"
+++
<![end-metadata]-->
# Introduction to Engine user guide
This guide takes you through the fundamentals of using Docker Engine and
integrating it into your environment. You'll learn how to use Engine to:
* Dockerize your applications.
* Run your own containers.
* Build Docker images.
* Share your Docker images with others.
* And a whole lot more!
This guide is broken into major sections that take you through learning the basics of Docker Engine and the other Docker products that support it.
## Dockerizing applications: A "Hello world"
*How do I run applications inside containers?*
Docker Engine offers a containerization platform to power your applications. To
learn how to Dockerize applications and run them:
Go to [Dockerizing Applications](containers/dockerizing.md).
## Working with containers
*How do I manage my containers?*
Once you get a grip on running your applications in Docker containers, you'll learn how to manage those containers. To find out
about how to inspect, monitor and manage containers:
Go to [Working With Containers](containers/usingdocker.md).
## Working with Docker images
*How can I access, share and build my own images?*
Once you've learnt how to use Docker it's time to take the next step and
learn how to build your own application images with Docker.
Go to [Working with Docker Images](containers/dockerimages.md).
## Networking containers
Until now we've seen how to build individual applications inside Docker
containers. Now learn how to build whole application stacks with Docker
networking.
Go to [Networking Containers](containers/networkingcontainers.md).
## Managing data in containers
Now we know how to link Docker containers together the next step is
learning how to manage data, volumes and mounts inside our containers.
Go to [Managing Data in Containers](containers/dockervolumes.md).
## Docker products that complement Engine
Often, one powerful technology spawns many other inventions that make that easier to get to, easier to use, and more powerful. These spawned things share one common characteristic: they augment the central technology. The following Docker products expand on the core Docker Engine functions.
### Docker Hub
Docker Hub is the central hub for Docker. It hosts public Docker images
and provides services to help you build and manage your Docker
environment. To learn more:
Go to [Using Docker Hub](https://docs.docker.com/docker-hub).
### Docker Machine
Docker Machine helps you get Docker Engines up and running quickly. Machine
can set up hosts for Docker Engines on your computer, on cloud providers,
and/or in your data center, and then configure your Docker client to securely
talk to them.
Go to [Docker Machine user guide](https://docs.docker.com/machine/).
### Docker Compose
Docker Compose allows you to define a application's components -- their containers,
configuration, links and volumes -- in a single file. Then a single command
will set everything up and start your application running.
Go to [Docker Compose user guide](https://docs.docker.com/compose/).
### Docker Swarm
Docker Swarm pools several Docker Engines together and exposes them as a single
virtual Docker Engine. It serves the standard Docker API, so any tool that already
works with Docker can now transparently scale up to multiple hosts.
Go to [Docker Swarm user guide](https://docs.docker.com/swarm/).
## Getting help
* [Docker homepage](https://www.docker.com/)
* [Docker Hub](https://hub.docker.com)
* [Docker blog](https://blog.docker.com/)
* [Docker documentation](https://docs.docker.com/)
* [Docker Getting Started Guide](https://docs.docker.com/mac/started/)
* [Docker code on GitHub](https://github.com/docker/docker)
* [Docker mailing
list](https://groups.google.com/forum/#!forum/docker-user)
* Docker on IRC: irc.freenode.net and channel #docker
* [Docker on Twitter](https://twitter.com/docker)
* Get [Docker help](https://stackoverflow.com/search?q=docker) on
StackOverflow
* [Docker.com](https://www.docker.com/)

View File

@@ -0,0 +1,229 @@
<!--[metadata]>
+++
title = "Apply custom metadata"
description = "Learn how to work with custom metadata in Docker, using labels."
keywords = ["Usage, user guide, labels, metadata, docker, documentation, examples, annotating"]
[menu.main]
parent = "engine_guide"
weight=90
+++
<![end-metadata]-->
# Apply custom metadata
You can apply metadata to your images, containers, or daemons via
labels. Labels serve a wide range of uses, such as adding notes or licensing
information to an image, or to identify a host.
A label is a `<key>` / `<value>` pair. Docker stores the label values as
*strings*. You can specify multiple labels but each `<key>` must be
unique or the value will be overwritten. If you specify the same `key` several
times but with different values, newer labels overwrite previous labels. Docker
uses the last `key=value` you supply.
>**Note:** Support for daemon-labels was added in Docker 1.4.1. Labels on
>containers and images are new in Docker 1.6.0
## Label keys (namespaces)
Docker puts no hard restrictions on the `key` used for a label. However, using
simple keys can easily lead to conflicts. For example, you have chosen to
categorize your images by CPU architecture using "architecture" labels in
your Dockerfiles:
LABEL architecture="amd64"
LABEL architecture="ARMv7"
Another user may apply the same label based on a building's "architecture":
LABEL architecture="Art Nouveau"
To prevent naming conflicts, Docker recommends using namespaces to label keys
using reverse domain notation. Use the following guidelines to name your keys:
- All (third-party) tools should prefix their keys with the
reverse DNS notation of a domain controlled by the author. For
example, `com.example.some-label`.
- The `com.docker.*`, `io.docker.*` and `org.dockerproject.*` namespaces are
reserved for Docker's internal use.
- Keys should only consist of lower-cased alphanumeric characters,
dots and dashes (for example, `[a-z0-9-.]`).
- Keys should start *and* end with an alpha numeric character.
- Keys may not contain consecutive dots or dashes.
- Keys *without* namespace (dots) are reserved for CLI use. This allows end-
users to add metadata to their containers and images without having to type
cumbersome namespaces on the command-line.
These are simply guidelines and Docker does not *enforce* them. However, for
the benefit of the community, you *should* use namespaces for your label keys.
## Store structured data in labels
Label values can contain any data type as long as it can be represented as a
string. For example, consider this JSON document:
{
"Description": "A containerized foobar",
"Usage": "docker run --rm example/foobar [args]",
"License": "GPL",
"Version": "0.0.1-beta",
"aBoolean": true,
"aNumber" : 0.01234,
"aNestedArray": ["a", "b", "c"]
}
You can store this struct in a label by serializing it to a string first:
LABEL com.example.image-specs="{\"Description\":\"A containerized foobar\",\"Usage\":\"docker run --rm example\\/foobar [args]\",\"License\":\"GPL\",\"Version\":\"0.0.1-beta\",\"aBoolean\":true,\"aNumber\":0.01234,\"aNestedArray\":[\"a\",\"b\",\"c\"]}"
While it is *possible* to store structured data in label values, Docker treats
this data as a 'regular' string. This means that Docker doesn't offer ways to
query (filter) based on nested properties. If your tool needs to filter on
nested properties, the tool itself needs to implement this functionality.
## Add labels to images
To add labels to an image, use the `LABEL` instruction in your Dockerfile:
LABEL [<namespace>.]<key>=<value> ...
The `LABEL` instruction adds a label to your image. A `LABEL` consists of a `<key>`
and a `<value>`.
Use an empty string for labels that don't have a `<value>`,
Use surrounding quotes or backslashes for labels that contain
white space characters in the `<value>`:
LABEL vendor=ACME\ Incorporated
LABEL com.example.version.is-beta=
LABEL com.example.version.is-production=""
LABEL com.example.version="0.0.1-beta"
LABEL com.example.release-date="2015-02-12"
The `LABEL` instruction also supports setting multiple `<key>` / `<value>` pairs
in a single instruction:
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"
Long lines can be split up by using a backslash (`\`) as continuation marker:
LABEL vendor=ACME\ Incorporated \
com.example.is-beta= \
com.example.is-production="" \
com.example.version="0.0.1-beta" \
com.example.release-date="2015-02-12"
Docker recommends you add multiple labels in a single `LABEL` instruction. Using
individual instructions for each label can result in an inefficient image. This
is because each `LABEL` instruction in a Dockerfile produces a new IMAGE layer.
You can view the labels via the `docker inspect` command:
$ docker inspect 4fa6e0f0c678
...
"Labels": {
"vendor": "ACME Incorporated",
"com.example.is-beta": "",
"com.example.is-production": "",
"com.example.version": "0.0.1-beta",
"com.example.release-date": "2015-02-12"
}
...
# Inspect labels on container
$ docker inspect -f "{{json .Config.Labels }}" 4fa6e0f0c678
{"Vendor":"ACME Incorporated","com.example.is-beta":"", "com.example.is-production":"", "com.example.version":"0.0.1-beta","com.example.release-date":"2015-02-12"}
# Inspect labels on images
$ docker inspect -f "{{json .ContainerConfig.Labels }}" myimage
## Query labels
Besides storing metadata, you can filter images and containers by label. To list all
running containers that have the `com.example.is-beta` label:
# List all running containers that have a `com.example.is-beta` label
$ docker ps --filter "label=com.example.is-beta"
List all running containers with the label `color` that have a value `blue`:
$ docker ps --filter "label=color=blue"
List all images with the label `vendor` that have the value `ACME`:
$ docker images --filter "label=vendor=ACME"
## Container labels
docker run \
-d \
--label com.example.group="webservers" \
--label com.example.environment="production" \
busybox \
top
Please refer to the [Query labels](#query-labels) section above for information
on how to query labels set on a container.
## Daemon labels
docker daemon \
--dns 8.8.8.8 \
--dns 8.8.4.4 \
-H unix:///var/run/docker.sock \
--label com.example.environment="production" \
--label com.example.storage="ssd"
These labels appear as part of the `docker info` output for the daemon:
$ docker -D info
Containers: 12
Running: 5
Paused: 2
Stopped: 5
Images: 672
Server Version: 1.9.0
Storage Driver: aufs
Root Dir: /var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 697
Dirperm1 Supported: true
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.19.0-22-generic
Operating System: Ubuntu 15.04
CPUs: 24
Total Memory: 62.86 GiB
Name: docker
ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S
Debug mode (server): true
File Descriptors: 59
Goroutines: 159
System Time: 2015-09-23T14:04:20.699842089+08:00
EventsListeners: 0
Init SHA1:
Init Path: /usr/bin/docker
Docker Root Dir: /var/lib/docker
Http Proxy: http://test:test@localhost:8080
Https Proxy: https://test:test@localhost:8080
WARNING: No swap limit support
Username: svendowideit
Registry: [https://index.docker.io/v1/]
Labels:
com.example.environment=production
com.example.storage=ssd

View File

@@ -0,0 +1,138 @@
<!--[metadata]>
+++
title = "Configure container DNS in user-defined networks"
description = "Learn how to configure DNS in user-defined networks"
keywords = ["docker, DNS, network"]
[menu.main]
parent = "smn_networking"
+++
<![end-metadata]-->
# Embedded DNS server in user-defined networks
The information in this section covers the embedded DNS server operation for
containers in user-defined networks. DNS lookup for containers connected to
user-defined networks works differently compared to the containers connected
to `default bridge` network.
> **Note**: In order to maintain backward compatibility, the DNS configuration
> in `default bridge` network is retained with no behaviorial change.
> Please refer to the [DNS in default bridge network](default_network/configure-dns.md)
> for more information on DNS configuration in the `default bridge` network.
As of Docker 1.10, the docker daemon implements an embedded DNS server which
provides built-in service discovery for any container created with a valid
`name` or `net-alias` or aliased by `link`. The exact details of how Docker
manages the DNS configurations inside the container can change from one Docker
version to the next. So you should not assume the way the files such as
`/etc/hosts`, `/etc/resolv.conf` are managed inside the containers and leave
the files alone and use the following Docker options instead.
Various container options that affect container domain name services.
<table>
<tr>
<td>
<p>
<code>--name=CONTAINER-NAME</code>
</p>
</td>
<td>
<p>
Container name configured using <code>--name</code> is used to discover a container within
an user-defined docker network. The embedded DNS server maintains the mapping between
the container name and its IP address (on the network the container is connected to).
</p>
</td>
</tr>
<tr>
<td>
<p>
<code>--net-alias=ALIAS</code>
</p>
</td>
<td>
<p>
In addition to <code>--name</code> as described above, a container is discovered by one or more
of its configured <code>--net-alias</code> (or <code>--alias</code> in <code>docker network connect</code> command)
within the user-defined network. The embedded DNS server maintains the mapping between
all of the container aliases and its IP address on a specific user-defined network.
A container can have different aliases in different networks by using the <code>--alias</code>
option in <code>docker network connect</code> command.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code>--link=CONTAINER_NAME:ALIAS</code>
</p>
</td>
<td>
<p>
Using this option as you <code>run</code> a container gives the embedded DNS
an extra entry named <code>ALIAS</code> that points to the IP address
of the container identified by <code>CONTAINER_NAME</code>. When using <code>--link</code>
the embedded DNS will guarantee that localized lookup result only on that
container where the <code>--link</code> is used. This lets processes inside the new container
connect to container without without having to know its name or IP.
</p>
</td>
</tr>
<tr>
<td><p>
<code>--dns=[IP_ADDRESS...]</code>
</p></td>
<td><p>
The IP addresses passed via the <code>--dns</code> option is used by the embedded DNS
server to forward the DNS query if embedded DNS server is unable to resolve a name
resolution request from the containers.
These <code>--dns</code> IP addresses are managed by the embedded DNS server and
will not be updated in the container's <code>/etc/resolv.conf</code> file.
</tr>
<tr>
<td><p>
<code>--dns-search=DOMAIN...</code>
</p></td>
<td><p>
Sets the domain names that are searched when a bare unqualified hostname is
used inside of the container. These <code>--dns-search</code> options are managed by the
embedded DNS server and will not be updated in the container's <code>/etc/resolv.conf</code> file.
When a container process attempts to access <code>host</code> and the search
domain <code>example.com</code> is set, for instance, the DNS logic will not only
look up <code>host</code> but also <code>host.example.com</code>.
</p>
</td>
</tr>
<tr>
<td><p>
<code>--dns-opt=OPTION...</code>
</p></td>
<td><p>
Sets the options used by DNS resolvers. These options are managed by the embedded
DNS server and will not be updated in the container's <code>/etc/resolv.conf</code> file.
</p>
<p>
See documentation for <code>resolv.conf</code> for a list of valid options
</p></td>
</tr>
</table>
In the absence of the `--dns=IP_ADDRESS...`, `--dns-search=DOMAIN...`, or
`--dns-opt=OPTION...` options, Docker uses the `/etc/resolv.conf` of the
host machine (where the `docker` daemon runs). While doing so the daemon
filters out all localhost IP address `nameserver` entries from the host's
original file.
Filtering is necessary because all localhost addresses on the host are
unreachable from the container's network. After this filtering, if there are
no more `nameserver` entries left in the container's `/etc/resolv.conf` file,
the daemon adds public Google DNS nameservers (8.8.8.8 and 8.8.4.4) to the
container's DNS configuration. If IPv6 is enabled on the daemon, the public
IPv6 Google DNS nameservers will also be added (2001:4860:4860::8888 and
2001:4860:4860::8844).
> **Note**: If you need access to a host's localhost resolver, you must modify
> your DNS service on the host to listen on a non-localhost address that is
> reachable from within the container.

View File

@@ -0,0 +1,103 @@
<!--[metadata]>
+++
title = "Bind container ports to the host"
description = "expose, port, docker, bind publish"
keywords = ["Examples, Usage, network, docker, documentation, user guide, multihost, cluster"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
# Bind container ports to the host
The information in this section explains binding container ports within the Docker default bridge. This is a `bridge` network named `bridge` created automatically when you install Docker.
> **Note**: The [Docker networks feature](../dockernetworks.md) allows you to
create user-defined networks in addition to the default bridge network.
By default Docker containers can make connections to the outside world, but the
outside world cannot connect to containers. Each outgoing connection will
appear to originate from one of the host machine's own IP addresses thanks to an
`iptables` masquerading rule on the host machine that the Docker server creates
when it starts:
```
$ sudo iptables -t nat -L -n
...
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
...
```
The Docker server creates a masquerade rule that let containers connect to IP
addresses in the outside world.
If you want containers to accept incoming connections, you will need to provide
special options when invoking `docker run`. There are two approaches.
First, you can supply `-P` or `--publish-all=true|false` to `docker run` which
is a blanket operation that identifies every port with an `EXPOSE` line in the
image's `Dockerfile` or `--expose <port>` commandline flag and maps it to a host
port somewhere within an _ephemeral port range_. The `docker port` command then
needs to be used to inspect created mapping. The _ephemeral port range_ is
configured by `/proc/sys/net/ipv4/ip_local_port_range` kernel parameter,
typically ranging from 32768 to 61000.
Mapping can be specified explicitly using `-p SPEC` or `--publish=SPEC` option.
It allows you to particularize which port on docker server - which can be any
port at all, not just one within the _ephemeral port range_ -- you want mapped
to which port in the container.
Either way, you should be able to peek at what Docker has accomplished in your
network stack by examining your NAT tables.
```
# What your NAT rules might look like when Docker
# is finished setting up a -P forward:
$ iptables -t nat -L -n
...
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80
# What your NAT rules might look like when Docker
# is finished setting up a -p 80:80 forward:
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
```
You can see that Docker has exposed these container ports on `0.0.0.0`, the
wildcard IP address that will match any possible incoming port on the host
machine. If you want to be more restrictive and only allow container services to
be contacted through a specific external interface on the host machine, you have
two choices. When you invoke `docker run` you can use either `-p
IP:host_port:container_port` or `-p IP::port` to specify the external interface
for one particular binding.
Or if you always want Docker port forwards to bind to one specific IP address,
you can edit your system-wide Docker server settings and add the option
`--ip=IP_ADDRESS`. Remember to restart your Docker server after editing this
setting.
> **Note**: With hairpin NAT enabled (`--userland-proxy=false`), containers port
exposure is achieved purely through iptables rules, and no attempt to bind the
exposed port is ever made. This means that nothing prevents shadowing a
previously listening service outside of Docker through exposing the same port
for a container. In such conflicting situation, Docker created iptables rules
will take precedence and route to the container.
The `--userland-proxy` parameter, true by default, provides a userland
implementation for inter-container and outside-to-container communication. When
disabled, Docker uses both an additional `MASQUERADE` iptable rule and the
`net.ipv4.route_localnet` kernel parameter which allow the host machine to
connect to a local container exposed port through the commonly used loopback
address: this alternative is preferred for performance reasons.
## Related information
- [Understand Docker container networks](../dockernetworks.md)
- [Work with network commands](../work-with-networks.md)
- [Legacy container links](dockerlinks.md)

View File

@@ -0,0 +1,78 @@
<!--[metadata]>
+++
title = "Build your own bridge"
description = "Learn how to build your own bridge interface"
keywords = ["docker, bridge, docker0, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
# Build your own bridge
This section explains how to build your own bridge to replace the Docker default
bridge. This is a `bridge` network named `bridge` created automatically when you
install Docker.
> **Note**: The [Docker networks feature](../dockernetworks.md) allows you to
create user-defined networks in addition to the default bridge network.
You can set up your own bridge before starting Docker and use `-b BRIDGE` or
`--bridge=BRIDGE` to tell Docker to use your bridge instead. If you already
have Docker up and running with its default `docker0` still configured,
you can directly create your bridge and restart Docker with it or want to begin by
stopping the service and removing the interface:
```
# Stopping Docker and removing docker0
$ sudo service docker stop
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0
$ sudo iptables -t nat -F POSTROUTING
```
Then, before starting the Docker service, create your own bridge and give it
whatever configuration you want. Here we will create a simple enough bridge
that we really could just have used the options in the previous section to
customize `docker0`, but it will be enough to illustrate the technique.
```
# Create our own bridge
$ sudo brctl addbr bridge0
$ sudo ip addr add 192.168.5.1/24 dev bridge0
$ sudo ip link set dev bridge0 up
# Confirming that our bridge is up and running
$ ip addr show bridge0
4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default
link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 scope global bridge0
valid_lft forever preferred_lft forever
# Tell Docker about it and restart (on Ubuntu)
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
$ sudo service docker start
# Confirming new outgoing NAT masquerade is set up
$ sudo iptables -t nat -L -n
...
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 192.168.5.0/24 0.0.0.0/0
```
The result should be that the Docker server starts successfully and is now
prepared to bind containers to the new bridge. After pausing to verify the
bridge's configuration, try creating a container -- you will see that its IP
address is in your new IP address range, which Docker will have auto-detected.
You can use the `brctl show` command to see Docker add and remove interfaces
from the bridge as you start and stop containers, and can run `ip addr` and `ip
route` inside a container to see that it has been given an address in the
bridge's IP address range and has been told to use the Docker host's IP address
on the bridge as its default gateway to the rest of the Internet.

View File

@@ -0,0 +1,132 @@
<!--[metadata]>
+++
title = "Configure container DNS"
description = "Learn how to configure DNS in Docker"
keywords = ["docker, bridge, docker0, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
# Configure container DNS
The information in this section explains configuring container DNS within
the Docker default bridge. This is a `bridge` network named `bridge` created
automatically when you install Docker.
> **Note**: The [Docker networks feature](../dockernetworks.md) allows you to create user-defined networks in addition to the default bridge network. Please refer to the [Docker Embedded DNS](../configure-dns.md) section for more information on DNS configurations in user-defined networks.
How can Docker supply each container with a hostname and DNS configuration, without having to build a custom image with the hostname written inside? Its trick is to overlay three crucial `/etc` files inside the container with virtual files where it can write fresh information. You can see this by running `mount` inside a container:
```
$$ mount
...
/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ...
/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ...
/dev/disk/by-uuid/1fec...ebdf on /etc/resolv.conf type ext4 ...
...
```
This arrangement allows Docker to do clever things like keep `resolv.conf` up to date across all containers when the host machine receives new configuration over DHCP later. The exact details of how Docker maintains these files inside the container can change from one Docker version to the next, so you should leave the files themselves alone and use the following Docker options instead.
Four different options affect container domain name services.
<table>
<tr>
<td>
<p>
<code>-h HOSTNAME</code> or <code>--hostname=HOSTNAME</code>
</p>
</td>
<td>
<p>
Sets the hostname by which the container knows itself. This is written
into <code>/etc/hostname</code>, into <code>/etc/hosts</code> as the name
of the container's host-facing IP address, and is the name that
<code>/bin/bash</code> inside the container will display inside its
prompt. But the hostname is not easy to see from outside the container.
It will not appear in <code>docker ps</code> nor in the
<code>/etc/hosts</code> file of any other container.
</p>
</td>
</tr>
<tr>
<td>
<p>
<code>--link=CONTAINER_NAME</code> or <code>ID:ALIAS</code>
</p>
</td>
<td>
<p>
Using this option as you <code>run</code> a container gives the new
container's <code>/etc/hosts</code> an extra entry named
<code>ALIAS</code> that points to the IP address of the container
identified by <code>CONTAINER_NAME_or_ID<c/ode>. This lets processes
inside the new container connect to the hostname <code>ALIAS</code>
without having to know its IP. The <code>--link=</code> option is
discussed in more detail below. Because Docker may assign a different IP
address to the linked containers on restart, Docker updates the
<code>ALIAS</code> entry in the <code>/etc/hosts</code> file of the
recipient containers.
</p>
</td>
</tr>
<tr>
<td><p>
<code>--dns=IP_ADDRESS...</code>
</p></td>
<td><p>
Sets the IP addresses added as <code>server</code> lines to the container's
<code>/etc/resolv.conf</code> file. Processes in the container, when
confronted with a hostname not in <code>/etc/hosts</code>, will connect to
these IP addresses on port 53 looking for name resolution services. </p></td>
</tr>
<tr>
<td><p>
<code>--dns-search=DOMAIN...</code>
</p></td>
<td><p>
Sets the domain names that are searched when a bare unqualified hostname is
used inside of the container, by writing <code>search</code> lines into the
container's <code>/etc/resolv.conf</code>. When a container process attempts
to access <code>host</code> and the search domain <code>example.com</code>
is set, for instance, the DNS logic will not only look up <code>host</code>
but also <code>host.example.com</code>.
</p>
<p>
Use <code>--dns-search=.</code> if you don't wish to set the search domain.
</p>
</td>
</tr>
<tr>
<td><p>
<code>--dns-opt=OPTION...</code>
</p></td>
<td><p>
Sets the options used by DNS resolvers by writing an <code>options<code>
line into the container's <code>/etc/resolv.conf<code>.
</p>
<p>
See documentation for <code>resolv.conf<code> for a list of valid options
</p></td>
</tr>
<tr>
<td><p></p></td>
<td><p></p></td>
</tr>
</table>
Regarding DNS settings, in the absence of the `--dns=IP_ADDRESS...`, `--dns-search=DOMAIN...`, or `--dns-opt=OPTION...` options, Docker makes each container's `/etc/resolv.conf` look like the `/etc/resolv.conf` of the host machine (where the `docker` daemon runs). When creating the container's `/etc/resolv.conf`, the daemon filters out all localhost IP address `nameserver` entries from the host's original file.
Filtering is necessary because all localhost addresses on the host are unreachable from the container's network. After this filtering, if there are no more `nameserver` entries left in the container's `/etc/resolv.conf` file, the daemon adds public Google DNS nameservers (8.8.8.8 and 8.8.4.4) to the container's DNS configuration. If IPv6 is enabled on the daemon, the public IPv6 Google DNS nameservers will also be added (2001:4860:4860::8888 and 2001:4860:4860::8844).
> **Note**: If you need access to a host's localhost resolver, you must modify your DNS service on the host to listen on a non-localhost address that is reachable from within the container.
You might wonder what happens when the host machine's `/etc/resolv.conf` file changes. The `docker` daemon has a file change notifier active which will watch for changes to the host DNS configuration.
> **Note**: The file change notifier relies on the Linux kernel's inotify feature. Because this feature is currently incompatible with the overlay filesystem driver, a Docker daemon using "overlay" will not be able to take advantage of the `/etc/resolv.conf` auto-update feature.
When the host file changes, all stopped containers which have a matching `resolv.conf` to the host will be updated immediately to this newest host configuration. Containers which are running when the host configuration changes will need to stop and start to pick up the host changes due to lack of a facility to ensure atomic writes of the `resolv.conf` file while the container is running. If the container's `resolv.conf` has been edited since it was started with the default configuration, no replacement will be attempted as it would overwrite the changes performed by the container. If the options (`--dns`, `--dns-search`, or `--dns-opt`) have been used to modify the default host configuration, then the replacement with an updated host's `/etc/resolv.conf` will not happen as well.
> **Note**: For containers which were created prior to the implementation of the `/etc/resolv.conf` update feature in Docker 1.5.0: those containers will **not** receive updates when the host `resolv.conf` file changes. Only containers created with Docker 1.5.0 and above will utilize this auto-update feature.

View File

@@ -0,0 +1,123 @@
<!--[metadata]>
+++
title = "Understand container communication"
description = "Understand container communication"
keywords = ["docker, container, communication, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
# Understand container communication
The information in this section explains container communication within the
Docker default bridge. This is a `bridge` network named `bridge` created
automatically when you install Docker.
**Note**: The [Docker networks feature](../dockernetworks.md) allows you to create user-defined networks in addition to the default bridge network.
## Communicating to the outside world
Whether a container can talk to the world is governed by two factors. The first
factor is whether the host machine is forwarding its IP packets. The second is
whether the host's `iptables` allow this particular connection.
IP packet forwarding is governed by the `ip_forward` system parameter. Packets
can only pass between containers if this parameter is `1`. Usually you will
simply leave the Docker server at its default setting `--ip-forward=true` and
Docker will go set `ip_forward` to `1` for you when the server starts up. If you
set `--ip-forward=false` and your system's kernel has it enabled, the
`--ip-forward=false` option has no effect. To check the setting on your kernel
or to turn it on manually:
```
$ sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 0
$ sysctl net.ipv4.conf.all.forwarding=1
$ sysctl net.ipv4.conf.all.forwarding
net.ipv4.conf.all.forwarding = 1
```
Many using Docker will want `ip_forward` to be on, to at least make
communication _possible_ between containers and the wider world. May also be
needed for inter-container communication if you are in a multiple bridge setup.
Docker will never make changes to your system `iptables` rules if you set
`--iptables=false` when the daemon starts. Otherwise the Docker server will
append forwarding rules to the `DOCKER` filter chain.
Docker will not delete or modify any pre-existing rules from the `DOCKER` filter
chain. This allows the user to create in advance any rules required to further
restrict access to the containers.
Docker's forward rules permit all external source IPs by default. To allow only
a specific IP or network to access the containers, insert a negated rule at the
top of the `DOCKER` filter chain. For example, to restrict external access such
that _only_ source IP 8.8.8.8 can access the containers, the following rule
could be added:
```
$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP
```
## Communication between containers
Whether two containers can communicate is governed, at the operating system level, by two factors.
- Does the network topology even connect the containers' network interfaces? By default Docker will attach all containers to a single `docker0` bridge, providing a path for packets to travel between them. See the later sections of this document for other possible topologies.
- Do your `iptables` allow this particular connection? Docker will never make changes to your system `iptables` rules if you set `--iptables=false` when the daemon starts. Otherwise the Docker server will add a default rule to the `FORWARD` chain with a blanket `ACCEPT` policy if you retain the default `--icc=true`, or else will set the policy to `DROP` if `--icc=false`.
It is a strategic question whether to leave `--icc=true` or change it to
`--icc=false` so that `iptables` will protect other containers -- and the main
host -- from having arbitrary ports probed or accessed by a container that gets
compromised.
If you choose the most secure setting of `--icc=false`, then how can containers
communicate in those cases where you _want_ them to provide each other services?
The answer is the `--link=CONTAINER_NAME_or_ID:ALIAS` option, which was
mentioned in the previous section because of its effect upon name services. If
the Docker daemon is running with both `--icc=false` and `--iptables=true`
then, when it sees `docker run` invoked with the `--link=` option, the Docker
server will insert a pair of `iptables` `ACCEPT` rules so that the new
container can connect to the ports exposed by the other container -- the ports
that it mentioned in the `EXPOSE` lines of its `Dockerfile`.
> **Note**: The value `CONTAINER_NAME` in `--link=` must either be an
auto-assigned Docker name like `stupefied_pare` or else the name you assigned
with `--name=` when you ran `docker run`. It cannot be a hostname, which Docker
will not recognize in the context of the `--link=` option.
You can run the `iptables` command on your Docker host to see whether the `FORWARD` chain has a default policy of `ACCEPT` or `DROP`:
```
# When --icc=false, you should see a DROP rule:
$ sudo iptables -L -n
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
...
# When a --link= has been created under --icc=false,
# you should see port-specific ACCEPT rules overriding
# the subsequent DROP policy for all other packets:
$ sudo iptables -L -n
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DOCKER all -- 0.0.0.0/0 0.0.0.0/0
DROP all -- 0.0.0.0/0 0.0.0.0/0
Chain DOCKER (1 references)
target prot opt source destination
ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80
ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80
```
> **Note**: Docker is careful that its host-wide `iptables` rules fully expose
containers to each other's raw IP addresses, so connections from one container
to another should always appear to be originating from the first container's own
IP address.

View File

@@ -0,0 +1,62 @@
<!--[metadata]>
+++
title = "Customize the docker0 bridge"
description = "Customizing docker0"
keywords = ["docker, bridge, docker0, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
# Customize the docker0 bridge
The information in this section explains how to customize the Docker default bridge. This is a `bridge` network named `bridge` created automatically when you install Docker.
**Note**: The [Docker networks feature](../dockernetworks.md) allows you to create user-defined networks in addition to the default bridge network.
By default, the Docker server creates and configures the host system's `docker0` interface as an _Ethernet bridge_ inside the Linux kernel that can pass packets back and forth between other physical or virtual network interfaces so that they behave as a single Ethernet network.
Docker configures `docker0` with an IP address, netmask and IP allocation range. The host machine can both receive and send packets to containers connected to the bridge, and gives it an MTU -- the _maximum transmission unit_ or largest packet length that the interface will allow -- of 1,500 bytes. These options are configurable at server startup:
- `--bip=CIDR` -- supply a specific IP address and netmask for the `docker0` bridge, using standard CIDR notation like `192.168.1.5/24`.
- `--fixed-cidr=CIDR` -- restrict the IP range from the `docker0` subnet, using the standard CIDR notation like `172.167.1.0/28`. This range must be an IPv4 range for fixed IPs (ex: 10.20.0.0/16) and must be a subset of the bridge IP range (`docker0` or set using `--bridge`). For example with `--fixed-cidr=192.168.1.0/25`, IPs for your containers will be chosen from the first half of `192.168.1.0/24` subnet.
- `--mtu=BYTES` -- override the maximum packet length on `docker0`.
Once you have one or more containers up and running, you can confirm that Docker has properly connected them to the `docker0` bridge by running the `brctl` command on the host machine and looking at the `interfaces` column of the output. Here is a host with two different containers connected:
```
# Display bridge info
$ sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.3a1d7362b4ee no veth65f9
vethdda6
```
If the `brctl` command is not installed on your Docker host, then on Ubuntu you should be able to run `sudo apt-get install bridge-utils` to install it.
Finally, the `docker0` Ethernet bridge settings are used every time you create a new container. Docker selects a free IP address from the range available on the bridge each time you `docker run` a new container, and configures the container's `eth0` interface with that IP address and the bridge's netmask. The Docker host's own IP address on the bridge is used as the default gateway by which each container reaches the rest of the Internet.
```
# The network, as seen from a container
$ docker run -i -t --rm base /bin/bash
$$ ip addr show eth0
24: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 32:6f:e0:35:57:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::306f:e0ff:fe35:5791/64 scope link
valid_lft forever preferred_lft forever
$$ ip route
default via 172.17.42.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3
$$ exit
```
Remember that the Docker host will not be willing to forward container packets out on to the Internet unless its `ip_forward` system setting is `1` -- see the section on [Communicating to the outside world](container-communication.md#communicating-to-the-outside-world) for details.

View File

@@ -0,0 +1,358 @@
<!--[metadata]>
+++
title = "Legacy container links"
description = "Learn how to connect Docker containers together."
keywords = ["Examples, Usage, user guide, links, linking, docker, documentation, examples, names, name, container naming, port, map, network port, network"]
[menu.main]
parent = "smn_networking_def"
weight=-2
+++
<![end-metadata]-->
# Legacy container links
The information in this section explains legacy container links within the Docker default bridge. This is a `bridge` network named `bridge` created automatically when you install Docker.
Before the [Docker networks feature](../dockernetworks.md), you could use the
Docker link feature to allow containers to discover each other and securely
transfer information about one container to another container. With the
introduction of the Docker networks feature, you can still create links but they
behave differently between default `bridge` network and
[user defined networks](../work-with-networks.md#linking-containers-in-user-defined-networks)
This section briefly discusses connecting via a network port and then goes into
detail on container linking in default `bridge` network.
## Connect using network port mapping
In [the Using Docker section](../../containers/usingdocker.md), you created a
container that ran a Python Flask application:
$ docker run -d -P training/webapp python app.py
> **Note:**
> Containers have an internal network and an IP address
> (as we saw when we used the `docker inspect` command to show the container's
> IP address in the [Using Docker](../../containers/usingdocker.md) section).
> Docker can have a variety of network configurations. You can see more
> information on Docker networking [here](../index.md).
When that container was created, the `-P` flag was used to automatically map
any network port inside it to a random high port within an *ephemeral port
range* on your Docker host. Next, when `docker ps` was run, you saw that port
5000 in the container was bound to port 49155 on the host.
$ docker ps nostalgic_morse
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
You also saw how you can bind a container's ports to a specific port using
the `-p` flag. Here port 80 of the host is mapped to port 5000 of the
container:
$ docker run -d -p 80:5000 training/webapp python app.py
And you saw why this isn't such a great idea because it constrains you to
only one container on that specific port.
Instead, you may specify a range of host ports to bind a container port to
that is different than the default *ephemeral port range*:
$ docker run -d -p 8000-9000:5000 training/webapp python app.py
This would bind port 5000 in the container to a randomly available port
between 8000 and 9000 on the host.
There are also a few other ways you can configure the `-p` flag. By
default the `-p` flag will bind the specified port to all interfaces on
the host machine. But you can also specify a binding to a specific
interface, for example only to the `localhost`.
$ docker run -d -p 127.0.0.1:80:5000 training/webapp python app.py
This would bind port 5000 inside the container to port 80 on the
`localhost` or `127.0.0.1` interface on the host machine.
Or, to bind port 5000 of the container to a dynamic port but only on the
`localhost`, you could use:
$ docker run -d -p 127.0.0.1::5000 training/webapp python app.py
You can also bind UDP ports by adding a trailing `/udp`. For example:
$ docker run -d -p 127.0.0.1:80:5000/udp training/webapp python app.py
You also learned about the useful `docker port` shortcut which showed us the
current port bindings. This is also useful for showing you specific port
configurations. For example, if you've bound the container port to the
`localhost` on the host machine, then the `docker port` output will reflect that.
$ docker port nostalgic_morse 5000
127.0.0.1:49155
> **Note:**
> The `-p` flag can be used multiple times to configure multiple ports.
## Connect with the linking system
> **Note**:
> This section covers the legacy link feature in the default `bridge` network.
> Please refer to [linking containers in user-defined networks]
> (../work-with-networks.md#linking-containers-in-user-defined-networks)
> for more information on links in user-defined networks.
Network port mappings are not the only way Docker containers can connect to one
another. Docker also has a linking system that allows you to link multiple
containers together and send connection information from one to another. When
containers are linked, information about a source container can be sent to a
recipient container. This allows the recipient to see selected data describing
aspects of the source container.
### The importance of naming
To establish links, Docker relies on the names of your containers.
You've already seen that each container you create has an automatically
created name; indeed you've become familiar with our old friend
`nostalgic_morse` during this guide. You can also name containers
yourself. This naming provides two useful functions:
1. It can be useful to name containers that do specific functions in a way
that makes it easier for you to remember them, for example naming a
container containing a web application `web`.
2. It provides Docker with a reference point that allows it to refer to other
containers, for example, you can specify to link the container `web` to container `db`.
You can name your container by using the `--name` flag, for example:
$ docker run -d -P --name web training/webapp python app.py
This launches a new container and uses the `--name` flag to
name the container `web`. You can see the container's name using the
`docker ps` command.
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web
You can also use `docker inspect` to return the container's name.
> **Note:**
> Container names have to be unique. That means you can only call
> one container `web`. If you want to re-use a container name you must delete
> the old container (with `docker rm`) before you can create a new
> container with the same name. As an alternative you can use the `--rm`
> flag with the `docker run` command. This will delete the container
> immediately after it is stopped.
## Communication across links
Links allow containers to discover each other and securely transfer information
about one container to another container. When you set up a link, you create a
conduit between a source container and a recipient container. The recipient can
then access select data about the source. To create a link, you use the `--link`
flag. First, create a new container, this time one containing a database.
$ docker run -d --name db training/postgres
This creates a new container called `db` from the `training/postgres`
image, which contains a PostgreSQL database.
Now, you need to delete the `web` container you created previously so you can replace it
with a linked one:
$ docker rm -f web
Now, create a new `web` container and link it with your `db` container.
$ docker run -d -P --name web --link db:db training/webapp python app.py
This will link the new `web` container with the `db` container you created
earlier. The `--link` flag takes the form:
--link <name or id>:alias
Where `name` is the name of the container we're linking to and `alias` is an
alias for the link name. You'll see how that alias gets used shortly.
The `--link` flag also takes the form:
--link <name or id>
In which case the alias will match the name. You could have written the previous
example as:
$ docker run -d -P --name web --link db training/webapp python app.py
Next, inspect your linked containers with `docker inspect`:
$ docker inspect -f "{{ .HostConfig.Links }}" web
[/db:/web/db]
You can see that the `web` container is now linked to the `db` container
`web/db`. Which allows it to access information about the `db` container.
So what does linking the containers actually do? You've learned that a link allows a
source container to provide information about itself to a recipient container. In
our example, the recipient, `web`, can access information about the source `db`. To do
this, Docker creates a secure tunnel between the containers that doesn't need to
expose any ports externally on the container; you'll note when we started the
`db` container we did not use either the `-P` or `-p` flags. That's a big benefit of
linking: we don't need to expose the source container, here the PostgreSQL database, to
the network.
Docker exposes connectivity information for the source container to the
recipient container in two ways:
* Environment variables,
* Updating the `/etc/hosts` file.
### Environment variables
Docker creates several environment variables when you link containers. Docker
automatically creates environment variables in the target container based on
the `--link` parameters. It will also expose all environment variables
originating from Docker from the source container. These include variables from:
* the `ENV` commands in the source container's Dockerfile
* the `-e`, `--env` and `--env-file` options on the `docker run`
command when the source container is started
These environment variables enable programmatic discovery from within the
target container of information related to the source container.
> **Warning**:
> It is important to understand that *all* environment variables originating
> from Docker within a container are made available to *any* container
> that links to it. This could have serious security implications if sensitive
> data is stored in them.
Docker sets an `<alias>_NAME` environment variable for each target container
listed in the `--link` parameter. For example, if a new container called
`web` is linked to a database container called `db` via `--link db:webdb`,
then Docker creates a `WEBDB_NAME=/web/webdb` variable in the `web` container.
Docker also defines a set of environment variables for each port exposed by the
source container. Each variable has a unique prefix in the form:
`<name>_PORT_<port>_<protocol>`
The components in this prefix are:
* the alias `<name>` specified in the `--link` parameter (for example, `webdb`)
* the `<port>` number exposed
* a `<protocol>` which is either TCP or UDP
Docker uses this prefix format to define three distinct environment variables:
* The `prefix_ADDR` variable contains the IP Address from the URL, for
example `WEBDB_PORT_5432_TCP_ADDR=172.17.0.82`.
* The `prefix_PORT` variable contains just the port number from the URL for
example `WEBDB_PORT_5432_TCP_PORT=5432`.
* The `prefix_PROTO` variable contains just the protocol from the URL for
example `WEBDB_PORT_5432_TCP_PROTO=tcp`.
If the container exposes multiple ports, an environment variable set is
defined for each one. This means, for example, if a container exposes 4 ports
that Docker creates 12 environment variables, 3 for each port.
Additionally, Docker creates an environment variable called `<alias>_PORT`.
This variable contains the URL of the source container's first exposed port.
The 'first' port is defined as the exposed port with the lowest number.
For example, consider the `WEBDB_PORT=tcp://172.17.0.82:5432` variable. If
that port is used for both tcp and udp, then the tcp one is specified.
Finally, Docker also exposes each Docker originated environment variable
from the source container as an environment variable in the target. For each
variable Docker creates an `<alias>_ENV_<name>` variable in the target
container. The variable's value is set to the value Docker used when it
started the source container.
Returning back to our database example, you can run the `env`
command to list the specified container's environment variables.
```
$ docker run --rm --name web2 --link db:db training/webapp env
. . .
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
. . .
```
You can see that Docker has created a series of environment variables with
useful information about the source `db` container. Each variable is prefixed
with
`DB_`, which is populated from the `alias` you specified above. If the `alias`
were `db1`, the variables would be prefixed with `DB1_`. You can use these
environment variables to configure your applications to connect to the database
on the `db` container. The connection will be secure and private; only the
linked `web` container will be able to talk to the `db` container.
### Important notes on Docker environment variables
Unlike host entries in the [`/etc/hosts` file](#updating-the-etchosts-file),
IP addresses stored in the environment variables are not automatically updated
if the source container is restarted. We recommend using the host entries in
`/etc/hosts` to resolve the IP address of linked containers.
These environment variables are only set for the first process in the
container. Some daemons, such as `sshd`, will scrub them when spawning shells
for connection.
### Updating the `/etc/hosts` file
In addition to the environment variables, Docker adds a host entry for the
source container to the `/etc/hosts` file. Here's an entry for the `web`
container:
$ docker run -t -i --rm --link db:webdb training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
. . .
172.17.0.5 webdb 6e5cdeb2d300 db
You can see two relevant host entries. The first is an entry for the `web`
container that uses the Container ID as a host name. The second entry uses the
link alias to reference the IP address of the `db` container. In addition to
the alias you provide, the linked container's name--if unique from the alias
provided to the `--link` parameter--and the linked container's hostname will
also be added in `/etc/hosts` for the linked container's IP address. You can ping
that host now via any of these entries:
root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping webdb
PING webdb (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms
> **Note:**
> In the example, you'll note you had to install `ping` because it was not included
> in the container initially.
Here, you used the `ping` command to ping the `db` container using its host entry,
which resolves to `172.17.0.5`. You can use this host entry to configure an application
to make use of your `db` container.
> **Note:**
> You can link multiple recipient containers to a single source. For
> example, you could have multiple (differently named) web containers attached to your
>`db` container.
If you restart the source container, the linked containers `/etc/hosts` files
will be automatically updated with the source container's new IP address,
allowing linked communication to continue.
$ docker restart db
db
$ docker run -t -i --rm --link db:db training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
. . .
172.17.0.9 db
# Related information

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 96 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 175 KiB

View File

@@ -0,0 +1,25 @@
<!--[metadata]>
+++
title = "Default bridge network"
description = "Docker networking"
keywords = ["network, networking, bridge, docker, documentation"]
[menu.main]
identifier="smn_networking_def"
parent= "smn_networking"
+++
<![end-metadata]-->
# Docker default bridge network
With the introduction of the Docker networks feature, you can create your own
user-defined networks. The Docker default bridge is created when you install
Docker Engine. It is a `bridge` network and is also named `bridge`. The topics
in this section are related to interacting with that default bridge network.
- [Understand container communication](container-communication.md)
- [Legacy container links](dockerlinks.md)
- [Binding container ports to the host](binding.md)
- [Build your own bridge](build-bridges.md)
- [Configure container DNS](configure-dns.md)
- [Customize the docker0 bridge](custom-docker0.md)
- [IPv6 with Docker](ipv6.md)

View File

@@ -0,0 +1,259 @@
<!--[metadata]>
+++
title = "IPv6 with Docker"
description = "How do we connect docker containers within and across hosts ?"
keywords = ["docker, network, IPv6"]
[menu.main]
parent = "smn_networking_def"
weight = 3
+++
<![end-metadata]-->
# IPv6 with Docker
The information in this section explains IPv6 with the Docker default bridge.
This is a `bridge` network named `bridge` created automatically when you install
Docker.
As we are [running out of IPv4
addresses](http://en.wikipedia.org/wiki/IPv4_address_exhaustion) the IETF has
standardized an IPv4 successor, [Internet Protocol Version
6](http://en.wikipedia.org/wiki/IPv6) , in [RFC
2460](https://www.ietf.org/rfc/rfc2460.txt). Both protocols, IPv4 and IPv6,
reside on layer 3 of the [OSI model](http://en.wikipedia.org/wiki/OSI_model).
## How IPv6 works on Docker
By default, the Docker server configures the container network for IPv4 only.
You can enable IPv4/IPv6 dualstack support by running the Docker daemon with the
`--ipv6` flag. Docker will set up the bridge `docker0` with the IPv6 [link-local
address](http://en.wikipedia.org/wiki/Link-local_address) `fe80::1`.
By default, containers that are created will only get a link-local IPv6 address.
To assign globally routable IPv6 addresses to your containers you have to
specify an IPv6 subnet to pick the addresses from. Set the IPv6 subnet via the
`--fixed-cidr-v6` parameter when starting Docker daemon:
```
docker daemon --ipv6 --fixed-cidr-v6="2001:db8:1::/64"
```
The subnet for Docker containers should at least have a size of `/80`. This way
an IPv6 address can end with the container's MAC address and you prevent NDP
neighbor cache invalidation issues in the Docker layer.
With the `--fixed-cidr-v6` parameter set Docker will add a new route to the
routing table. Further IPv6 routing will be enabled (you may prevent this by
starting Docker daemon with `--ip-forward=false`):
```
$ ip -6 route add 2001:db8:1::/64 dev docker0
$ sysctl net.ipv6.conf.default.forwarding=1
$ sysctl net.ipv6.conf.all.forwarding=1
```
All traffic to the subnet `2001:db8:1::/64` will now be routed via the `docker0` interface.
Be aware that IPv6 forwarding may interfere with your existing IPv6
configuration: If you are using Router Advertisements to get IPv6 settings for
your host's interfaces you should set `accept_ra` to `2`. Otherwise IPv6 enabled
forwarding will result in rejecting Router Advertisements. E.g., if you want to
configure `eth0` via Router Advertisements you should set:
```
$ sysctl net.ipv6.conf.eth0.accept_ra=2
```
![](images/ipv6_basic_host_config.svg)
Every new container will get an IPv6 address from the defined subnet. Further a
default route will be added on `eth0` in the container via the address specified
by the daemon option `--default-gateway-v6` if present, otherwise via `fe80::1`:
```
docker run -it ubuntu bash -c "ip -6 addr show dev eth0; ip -6 route show"
15: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500
inet6 2001:db8:1:0:0:242:ac11:3/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:3/64 scope link
valid_lft forever preferred_lft forever
2001:db8:1::/64 dev eth0 proto kernel metric 256
fe80::/64 dev eth0 proto kernel metric 256
default via fe80::1 dev eth0 metric 1024
```
In this example the Docker container is assigned a link-local address with the
network suffix `/64` (here: `fe80::42:acff:fe11:3/64`) and a globally routable
IPv6 address (here: `2001:db8:1:0:0:242:ac11:3/64`). The container will create
connections to addresses outside of the `2001:db8:1::/64` network via the
link-local gateway at `fe80::1` on `eth0`.
Often servers or virtual machines get a `/64` IPv6 subnet assigned (e.g.
`2001:db8:23:42::/64`). In this case you can split it up further and provide
Docker a `/80` subnet while using a separate `/80` subnet for other applications
on the host:
![](images/ipv6_slash64_subnet_config.svg)
In this setup the subnet `2001:db8:23:42::/80` with a range from
`2001:db8:23:42:0:0:0:0` to `2001:db8:23:42:0:ffff:ffff:ffff` is attached to
`eth0`, with the host listening at `2001:db8:23:42::1`. The subnet
`2001:db8:23:42:1::/80` with an address range from `2001:db8:23:42:1:0:0:0` to
`2001:db8:23:42:1:ffff:ffff:ffff` is attached to `docker0` and will be used by
containers.
### Using NDP proxying
If your Docker host is only part of an IPv6 subnet but has not got an IPv6
subnet assigned you can use NDP proxying to connect your containers via IPv6 to
the internet. For example your host has the IPv6 address `2001:db8::c001`, is
part of the subnet `2001:db8::/64` and your IaaS provider allows you to
configure the IPv6 addresses `2001:db8::c000` to `2001:db8::c00f`:
```
$ ip -6 addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000
inet6 2001:db8::c001/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::601:3fff:fea1:9c01/64 scope link
valid_lft forever preferred_lft forever
```
Let's split up the configurable address range into two subnets
`2001:db8::c000/125` and `2001:db8::c008/125`. The first one can be used by the
host itself, the latter by Docker:
```
docker daemon --ipv6 --fixed-cidr-v6 2001:db8::c008/125
```
You notice the Docker subnet is within the subnet managed by your router that is
connected to `eth0`. This means all devices (containers) with the addresses from
the Docker subnet are expected to be found within the router subnet. Therefore
the router thinks it can talk to these containers directly.
![](images/ipv6_ndp_proxying.svg)
As soon as the router wants to send an IPv6 packet to the first container it
will transmit a neighbor solicitation request, asking, who has `2001:db8::c009`?
But it will get no answer because no one on this subnet has this address. The
container with this address is hidden behind the Docker host. The Docker host
has to listen to neighbor solicitation requests for the container address and
send a response that itself is the device that is responsible for the address.
This is done by a Kernel feature called `NDP Proxy`. You can enable it by
executing
```
$ sysctl net.ipv6.conf.eth0.proxy_ndp=1
```
Now you can add the container's IPv6 address to the NDP proxy table:
```
$ ip -6 neigh add proxy 2001:db8::c009 dev eth0
```
This command tells the Kernel to answer to incoming neighbor solicitation
requests regarding the IPv6 address `2001:db8::c009` on the device `eth0`. As a
consequence of this all traffic to this IPv6 address will go into the Docker
host and it will forward it according to its routing table via the `docker0`
device to the container network:
```
$ ip -6 route show
2001:db8::c008/125 dev docker0 metric 1
2001:db8::/64 dev eth0 proto kernel metric 256
```
You have to execute the `ip -6 neigh add proxy ...` command for every IPv6
address in your Docker subnet. Unfortunately there is no functionality for
adding a whole subnet by executing one command. An alternative approach would be
to use an NDP proxy daemon such as
[ndppd](https://github.com/DanielAdolfsson/ndppd).
## Docker IPv6 cluster
### Switched network environment
Using routable IPv6 addresses allows you to realize communication between
containers on different hosts. Let's have a look at a simple Docker IPv6 cluster
example:
![](images/ipv6_switched_network_example.svg)
The Docker hosts are in the `2001:db8:0::/64` subnet. Host1 is configured to
provide addresses from the `2001:db8:1::/64` subnet to its containers. It has
three routes configured:
- Route all traffic to `2001:db8:0::/64` via `eth0`
- Route all traffic to `2001:db8:1::/64` via `docker0`
- Route all traffic to `2001:db8:2::/64` via Host2 with IP `2001:db8::2`
Host1 also acts as a router on OSI layer 3. When one of the network clients
tries to contact a target that is specified in Host1's routing table Host1 will
forward the traffic accordingly. It acts as a router for all networks it knows:
`2001:db8::/64`, `2001:db8:1::/64` and `2001:db8:2::/64`.
On Host2 we have nearly the same configuration. Host2's containers will get IPv6
addresses from `2001:db8:2::/64`. Host2 has three routes configured:
- Route all traffic to `2001:db8:0::/64` via `eth0`
- Route all traffic to `2001:db8:2::/64` via `docker0`
- Route all traffic to `2001:db8:1::/64` via Host1 with IP `2001:db8:0::1`
The difference to Host1 is that the network `2001:db8:2::/64` is directly
attached to the host via its `docker0` interface whereas it reaches
`2001:db8:1::/64` via Host1's IPv6 address `2001:db8::1`.
This way every container is able to contact every other container. The
containers `Container1-*` share the same subnet and contact each other directly.
The traffic between `Container1-*` and `Container2-*` will be routed via Host1
and Host2 because those containers do not share the same subnet.
In a switched environment every host has to know all routes to every subnet.
You always have to update the hosts' routing tables once you add or remove a
host to the cluster.
Every configuration in the diagram that is shown below the dashed line is
handled by Docker: The `docker0` bridge IP address configuration, the route to
the Docker subnet on the host, the container IP addresses and the routes on the
containers. The configuration above the line is up to the user and can be
adapted to the individual environment.
### Routed network environment
In a routed network environment you replace the layer 2 switch with a layer 3
router. Now the hosts just have to know their default gateway (the router) and
the route to their own containers (managed by Docker). The router holds all
routing information about the Docker subnets. When you add or remove a host to
this environment you just have to update the routing table in the router - not
on every host.
![](images/ipv6_routed_network_example.svg)
In this scenario containers of the same host can communicate directly with each
other. The traffic between containers on different hosts will be routed via
their hosts and the router. For example packet from `Container1-1` to
`Container2-1` will be routed through `Host1`, `Router` and `Host2` until it
arrives at `Container2-1`.
To keep the IPv6 addresses short in this example a `/48` network is assigned to
every host. The hosts use a `/64` subnet of this for its own services and one
for Docker. When adding a third host you would add a route for the subnet
`2001:db8:3::/48` in the router and configure Docker on Host3 with
`--fixed-cidr-v6=2001:db8:3:1::/64`.
Remember the subnet for Docker containers should at least have a size of `/80`.
This way an IPv6 address can end with the container's MAC address and you
prevent NDP neighbor cache invalidation issues in the Docker layer. So if you
have a `/64` for your whole environment use `/78` subnets for the hosts and
`/80` for the containers. This way you can use 4096 hosts with 16 `/80` subnets
each.
Every configuration in the diagram that is visualized below the dashed line is
handled by Docker: The `docker0` bridge IP address configuration, the route to
the Docker subnet on the host, the container IP addresses and the routes on the
containers. The configuration above the line is up to the user and can be
adapted to the individual environment.

View File

@@ -0,0 +1,141 @@
<!--[metadata]>
+++
draft=true
title = "Tools and Examples"
keywords = ["docker, bridge, docker0, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
<!--[metadata]>
We may want to add it back in later under another form. Labeled DRAFT for now. Won't be built.
<![end-metadata]-->
# Quick guide to the options
Here is a quick list of the networking-related Docker command-line options, in case it helps you find the section below that you are looking for.
Some networking command-line options can only be supplied to the Docker server when it starts up, and cannot be changed once it is running:
- `-b BRIDGE` or `--bridge=BRIDGE` -- see
[Building your own bridge](#bridge-building)
- `--bip=CIDR` -- see
[Customizing docker0](#docker0)
- `--default-gateway=IP_ADDRESS` -- see
[How Docker networks a container](#container-networking)
- `--default-gateway-v6=IP_ADDRESS` -- see
[IPv6](#ipv6)
- `--fixed-cidr` -- see
[Customizing docker0](#docker0)
- `--fixed-cidr-v6` -- see
[IPv6](#ipv6)
- `-H SOCKET...` or `--host=SOCKET...` --
This might sound like it would affect container networking,
but it actually faces in the other direction:
it tells the Docker server over what channels
it should be willing to receive commands
like "run container" and "stop container."
- `--icc=true|false` -- see
[Communication between containers](#between-containers)
- `--ip=IP_ADDRESS` -- see
[Binding container ports](#binding-ports)
- `--ipv6=true|false` -- see
[IPv6](#ipv6)
- `--ip-forward=true|false` -- see
[Communication between containers and the wider world](#the-world)
- `--iptables=true|false` -- see
[Communication between containers](#between-containers)
- `--mtu=BYTES` -- see
[Customizing docker0](#docker0)
- `--userland-proxy=true|false` -- see
[Binding container ports](#binding-ports)
There are three networking options that can be supplied either at startup or when `docker run` is invoked. When provided at startup, set the default value that `docker run` will later use if the options are not specified:
- `--dns=IP_ADDRESS...` -- see
[Configuring DNS](#dns)
- `--dns-search=DOMAIN...` -- see
[Configuring DNS](#dns)
- `--dns-opt=OPTION...` -- see
[Configuring DNS](#dns)
Finally, several networking options can only be provided when calling `docker run` because they specify something specific to one container:
- `-h HOSTNAME` or `--hostname=HOSTNAME` -- see
[Configuring DNS](#dns) and
[How Docker networks a container](#container-networking)
- `--link=CONTAINER_NAME_or_ID:ALIAS` -- see
[Configuring DNS](#dns) and
[Communication between containers](#between-containers)
- `--net=bridge|none|container:NAME_or_ID|host` -- see
[How Docker networks a container](#container-networking)
- `--mac-address=MACADDRESS...` -- see
[How Docker networks a container](#container-networking)
- `-p SPEC` or `--publish=SPEC` -- see
[Binding container ports](#binding-ports)
- `-P` or `--publish-all=true|false` -- see
[Binding container ports](#binding-ports)
To supply networking options to the Docker server at startup, use the `DOCKER_OPTS` variable in the Docker upstart configuration file. For Ubuntu, edit the variable in `/etc/default/docker` or `/etc/sysconfig/docker` for CentOS.
The following example illustrates how to configure Docker on Ubuntu to recognize a newly built bridge.
Edit the `/etc/default/docker` file:
```
$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
```
Then restart the Docker server.
```
$ sudo service docker start
```
For additional information on bridges, see [building your own bridge](#building-your-own-bridge) later on this page.

View File

@@ -0,0 +1,28 @@
<!--[metadata]>
+++
draft=true
title = "Saved text"
keywords = ["docker, bridge, docker0, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
<!--[metadata]>
This content was extracted from the original introduction. We may want to add it back in later under another form. Labeled DRAFT for now. Won't be built.
<![end-metadata]-->
## A Brief introduction to networking and docker
When Docker starts, it creates a virtual interface named `docker0` on the host machine. It randomly chooses an address and subnet from the private range defined by [RFC 1918](http://tools.ietf.org/html/rfc1918) that are not in use on the host machine, and assigns it to `docker0`. Docker made the choice `172.17.42.1/16` when I started it a few minutes ago, for example -- a 16-bit netmask providing 65,534 addresses for the host machine and its containers. The MAC address is generated using the IP address allocated to the container to avoid ARP collisions, using a range from `02:42:ac:11:00:00` to `02:42:ac:11:ff:ff`.
> **Note:** This document discusses advanced networking configuration and options for Docker. In most cases you won't need this information. If you're looking to get started with a simpler explanation of Docker networking and an introduction to the concept of container linking see the [Docker User Guide](dockerlinks.md).
But `docker0` is no ordinary interface. It is a virtual _Ethernet bridge_ that automatically forwards packets between any other network interfaces that are attached to it. This lets containers communicate both with the host machine and with each other. Every time Docker creates a container, it creates a pair of "peer" interfaces that are like opposite ends of a pipe -- a packet sent on one will be received on the other. It gives one of the peers to the container to become its `eth0` interface and keeps the other peer, with a unique name like `vethAQI2QT`, out in the namespace of the host machine. By binding every `veth*` interface to the `docker0` bridge, Docker creates a virtual subnet shared between the host machine and every Docker container.
The remaining sections of this document explain all of the ways that you can use Docker options and -- in advanced cases -- raw Linux networking commands to tweak, supplement, or entirely replace Docker's default networking configuration.
## Editing networking config files
Starting with Docker v.1.2.0, you can now edit `/etc/hosts`, `/etc/hostname` and `/etc/resolve.conf` in a running container. This is useful if you need to install bind or other services that might override one of those files.
Note, however, that changes to these files will not be saved by `docker commit`, nor will they be saved during `docker run`. That means they won't be saved in the image, nor will they persist when a container is restarted; they will only "stick" in a running container.

View File

@@ -0,0 +1,83 @@
<!--[metadata]>
+++
draft=true
title = "Tools and Examples"
keywords = ["docker, bridge, docker0, network"]
[menu.main]
parent = "smn_networking_def"
+++
<![end-metadata]-->
<!--[metadata]>
Dave Tucker instructed remove this. We may want to add it back in later under another form. Labeled DRAFT for now. Won't be built.
<![end-metadata]-->
# Tools and examples
Before diving into the following sections on custom network topologies, you might be interested in glancing at a few external tools or examples of the same kinds of configuration. Here are two:
- Jérôme Petazzoni has created a `pipework` shell script to help you
connect together containers in arbitrarily complex scenarios:
[https://github.com/jpetazzo/pipework](https://github.com/jpetazzo/pipework)
- Brandon Rhodes has created a whole network topology of Docker
containers for the next edition of Foundations of Python Network
Programming that includes routing, NAT'd firewalls, and servers that
offer HTTP, SMTP, POP, IMAP, Telnet, SSH, and FTP:
[https://github.com/brandon-rhodes/fopnp/tree/m/playground](https://github.com/brandon-rhodes/fopnp/tree/m/playground)
Both tools use networking commands very much like the ones you saw in the previous section, and will see in the following sections.
# Building a point-to-point connection
<a name="point-to-point"></a>
By default, Docker attaches all containers to the virtual subnet implemented by `docker0`. You can create containers that are each connected to some different virtual subnet by creating your own bridge as shown in [Building your own bridge](#bridge-building), starting each container with `docker run --net=none`, and then attaching the containers to your bridge with the shell commands shown in [How Docker networks a container](#container-networking).
But sometimes you want two particular containers to be able to communicate directly without the added complexity of both being bound to a host-wide Ethernet bridge.
The solution is simple: when you create your pair of peer interfaces, simply throw _both_ of them into containers, and configure them as classic point-to-point links. The two containers will then be able to communicate directly (provided you manage to tell each container the other's IP address, of course). You might adjust the instructions of the previous section to go something like this:
```
# Start up two containers in two terminal windows
$ docker run -i -t --rm --net=none base /bin/bash
root@1f1f4c1f931a:/#
$ docker run -i -t --rm --net=none base /bin/bash
root@12e343489d2f:/#
# Learn the container process IDs
# and create their namespace entries
$ docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a
2989
$ docker inspect -f '{{.State.Pid}}' 12e343489d2f
3004
$ sudo mkdir -p /var/run/netns
$ sudo ln -s /proc/2989/ns/net /var/run/netns/2989
$ sudo ln -s /proc/3004/ns/net /var/run/netns/3004
# Create the "peer" interfaces and hand them out
$ sudo ip link add A type veth peer name B
$ sudo ip link set A netns 2989
$ sudo ip netns exec 2989 ip addr add 10.1.1.1/32 dev A
$ sudo ip netns exec 2989 ip link set A up
$ sudo ip netns exec 2989 ip route add 10.1.1.2/32 dev A
$ sudo ip link set B netns 3004
$ sudo ip netns exec 3004 ip addr add 10.1.1.2/32 dev B
$ sudo ip netns exec 3004 ip link set B up
$ sudo ip netns exec 3004 ip route add 10.1.1.1/32 dev B
```
The two containers should now be able to ping each other and make connections successfully. Point-to-point links like this do not depend on a subnet nor a netmask, but on the bare assertion made by `ip route` that some other single IP address is connected to a particular network interface.
Note that point-to-point links can be safely combined with other kinds of network connectivity -- there is no need to start the containers with `--net=none` if you want point-to-point links to be an addition to the container's normal networking instead of a replacement.
A final permutation of this pattern is to create the point-to-point link between the Docker host and one container, which would allow the host to communicate with that one container on some single IP address and thus communicate "out-of-band" of the bridge that connects the other, more usual containers. But unless you have very specific networking needs that drive you to such a solution, it is probably far preferable to use `--icc=false` to lock down inter-container communication, as we explored earlier.

View File

@@ -0,0 +1,522 @@
<!--[metadata]>
+++
title = "Docker container networking"
description = "How do we connect docker containers within and across hosts ?"
keywords = ["Examples, Usage, network, docker, documentation, user guide, multihost, cluster"]
[menu.main]
parent = "smn_networking"
weight = -5
+++
<![end-metadata]-->
# Understand Docker container networks
To build web applications that act in concert but do so securely, use the Docker
networks feature. Networks, by definition, provide complete isolation for
containers. So, it is important to have control over the networks your
applications run on. Docker container networks give you that control.
This section provides an overview of the default networking behavior that Docker
Engine delivers natively. It describes the type of networks created by default
and how to create your own, user--defined networks. It also describes the
resources required to create networks on a single host or across a cluster of
hosts.
## Default Networks
When you install Docker, it creates three networks automatically. You can list
these networks using the `docker network ls` command:
```
$ docker network ls
NETWORK ID NAME DRIVER
7fca4eb8c647 bridge bridge
9f904ee27bf5 none null
cf03ee007fb4 host host
```
Historically, these three networks are part of Docker's implementation. When
you run a container you can use the `--net` flag to specify which network you
want to run a container on. These three networks are still available to you.
The `bridge` network represents the `docker0` network present in all Docker
installations. Unless you specify otherwise with the `docker run
--net=<NETWORK>` option, the Docker daemon connects containers to this network
by default. You can see this bridge as part of a host's network stack by using
the `ifconfig` command on the host.
```
ubuntu@ip-172-31-36-118:~$ ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:47:bc:3a:eb
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:47ff:febc:3aeb/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:17 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1100 (1.1 KB) TX bytes:648 (648.0 B)
```
The `none` network adds a container to a container-specific network stack. That container lacks a network interface. Attaching to such a container and looking at it's stack you see this:
```
ubuntu@ip-172-31-36-118:~$ docker attach nonenetcontainer
/ # cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
/ # ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ #
```
>**Note**: You can detach from the container and leave it running with `CTRL-p CTRL-q`.
The `host` network adds a container on the hosts network stack. You'll find the
network configuration inside the container is identical to the host.
With the exception of the the `bridge` network, you really don't need to
interact with these default networks. While you can list and inspect them, you
cannot remove them. They are required by your Docker installation. However, you
can add your own user-defined networks and these you can remove when you no
longer need them. Before you learn more about creating your own networks, it is
worth looking at the `default` network a bit.
### The default bridge network in detail
The default bridge network is present on all Docker hosts. The `docker network inspect`
```
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.17.0.1/16",
"Gateway": "172.17.0.1"
}
]
},
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "9001"
}
}
]
```
The Engine automatically creates a `Subnet` and `Gateway` to the network.
The `docker run` command automatically adds new containers to this network.
```
$ docker run -itd --name=container1 busybox
3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c
$ docker run -itd --name=container2 busybox
94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c
```
Inspecting the `bridge` network again after starting two containers shows both newly launched containers in the network. Their ids show up in the container
```
$ docker network inspect bridge
{[
{
"Name": "bridge",
"Id": "f7ab26d71dbd6f557852c7156ae0574bbf62c42f539b50c8ebde0f728a253b6f",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.17.0.1/16",
"Gateway": "172.17.0.1"
}
]
},
"Containers": {
"3386a527aa08b37ea9232cbcace2d2458d49f44bb05a6b775fba7ddd40d8f92c": {
"EndpointID": "647c12443e91faf0fd508b6edfe59c30b642abb60dfab890b4bdccee38750bc1",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"94447ca479852d29aeddca75c28f7104df3c3196d7b6d83061879e339946805c": {
"EndpointID": "b047d090f446ac49747d3c37d63e4307be745876db7f0ceef7b311cbba615f48",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "9001"
}
}
]
```
The `docker network inspect` command above shows all the connected containers and their network resources on a given network. Containers in this default network are able to communicate with each other using IP addresses. Docker does not support automatic service discovery on the default bridge network. If you want to communicate with container names in this default bridge network, you must connect the containers via the legacy `docker run --link` option.
You can `attach` to a running `container` and investigate its configuration:
```
$ docker attach container1
/ # ifconfig
ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:16 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1296 (1.2 KiB) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
Then use `ping` for about 3 seconds to test the connectivity of the containers on this `bridge` network.
```
/ # ping -w3 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.096 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.074 ms
--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.074/0.083/0.096 ms
```
Finally, use the `cat` command to check the `container1` network configuration:
```
/ # cat /etc/hosts
172.17.0.2 3386a527aa08
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
```
To detach from a `container1` and leave it running use `CTRL-p CTRL-q`.Then, attach to `container2` and repeat these three commands.
```
$ docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:15 errors:0 dropped:0 overruns:0 frame:0
TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1166 (1.1 KiB) TX bytes:1026 (1.0 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping -w3 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.067 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.072 ms
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.067/0.071/0.075 ms
/ # cat /etc/hosts
172.17.0.3 94447ca47985
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
```
The default `docker0` bridge network supports the use of port mapping and `docker run --link` to allow communications between containers in the `docker0` network. These techniques are cumbersome to set up and prone to error. While they are still available to you as techniques, it is better to avoid them and define your own bridge networks instead.
## User-defined networks
You can create your own user-defined networks that better isolate containers.
Docker provides some default **network drivers** for creating these
networks. You can create a new **bridge network** or **overlay network**. You
can also create a **network plugin** or **remote network** written to your own
specifications.
You can create multiple networks. You can add containers to more than one
network. Containers can only communicate within networks but not across
networks. A container attached to two networks can communicate with member
containers in either network.
The next few sections describe each of Docker's built-in network drivers in
greater detail.
### A bridge network
The easiest user-defined network to create is a `bridge` network. This network
is similar to the historical, default `docker0` network. There are some added
features and some old features that aren't available.
```
$ docker network create --driver bridge isolated_nw
1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b
$ docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1/16"
}
]
},
"Containers": {},
"Options": {}
}
]
$ docker network ls
NETWORK ID NAME DRIVER
9f904ee27bf5 none null
cf03ee007fb4 host host
7fca4eb8c647 bridge bridge
c5ee82f76de3 isolated_nw bridge
```
After you create the network, you can launch containers on it using the `docker run --net=<NETWORK>` option.
```
$ docker run --net=isolated_nw -itd --name=container3 busybox
8c1a0a5be480921d669a073393ade66a3fc49933f08bcc5515b37b8144f6d47c
$ docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{}
]
},
"Containers": {
"8c1a0a5be480921d669a073393ade66a3fc49933f08bcc5515b37b8144f6d47c": {
"EndpointID": "93b2db4a9b9a997beb912d28bcfc117f7b0eb924ff91d48cfa251d473e6a9b08",
"MacAddress": "02:42:ac:15:00:02",
"IPv4Address": "172.21.0.2/16",
"IPv6Address": ""
}
},
"Options": {}
}
]
```
The containers you launch into this network must reside on the same Docker host.
Each container in the network can immediately communicate with other containers
in the network. Though, the network itself isolates the containers from external
networks.
![An isolated network](images/bridge_network.png)
Within a user-defined bridge network, linking is not supported. You can
expose and publish container ports on containers in this network. This is useful
if you want to make a portion of the `bridge` network available to an outside
network.
![Bridge network](images/network_access.png)
A bridge network is useful in cases where you want to run a relatively small
network on a single host. You can, however, create significantly larger networks
by creating an `overlay` network.
### An overlay network
Docker's `overlay` network driver supports multi-host networking natively
out-of-the-box. This support is accomplished with the help of `libnetwork`, a
built-in VXLAN-based overlay network driver, and Docker's `libkv` library.
The `overlay` network requires a valid key-value store service. Currently,
Docker's `libkv` supports Consul, Etcd, and ZooKeeper (Distributed store). Before
creating a network you must install and configure your chosen key-value store
service. The Docker hosts that you intend to network and the service must be
able to communicate.
![Key-value store](images/key_value.png)
Each host in the network must run a Docker Engine instance. The easiest way to
provision the hosts are with Docker Machine.
![Engine on each host](images/engine_on_net.png)
You should open the following ports between each of your hosts.
| Protocol | Port | Description |
|----------|------|-----------------------|
| udp | 4789 | Data plane (VXLAN) |
| tcp/udp | 7946 | Control plane |
Your key-value store service may require additional ports.
Check your vendor's documentation and open any required ports.
Once you have several machines provisioned, you can use Docker Swarm to quickly
form them into a swarm which includes a discovery service as well.
To create an overlay network, you configure options on the `daemon` on each
Docker Engine for use with `overlay` network. There are two options to set:
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><pre>--cluster-store=PROVIDER://URL</pre></td>
<td>Describes the location of the KV service.</td>
</tr>
<tr>
<td><pre>--cluster-advertise=HOST_IP|HOST_IFACE:PORT</pre></td>
<td>The IP address or interface of the HOST used for clustering.</td>
</tr>
<tr>
<td><pre>--cluster-store-opt=KEY-VALUE OPTIONS</pre></td>
<td>Options such as TLS certificate or tuning discovery Timers</td>
</tr>
</tbody>
</table>
Create an `overlay` network on one of the machines in the Swarm.
$ docker network create --driver overlay my-multi-host-network
This results in a single network spanning multiple hosts. An `overlay` network
provides complete isolation for the containers.
![An overlay network](images/overlay_network.png)
Then, on each host, launch containers making sure to specify the network name.
$ docker run -itd --net=my-multi-host-network busybox
Once connected, each container has access to all the containers in the network
regardless of which Docker host the container was launched on.
![Published port](images/overlay-network-final.png)
If you would like to try this for yourself, see the [Getting started for
overlay](get-started-overlay.md).
### Custom network plugin
If you like, you can write your own network driver plugin. A network
driver plugin makes use of Docker's plugin infrastructure. In this
infrastructure, a plugin is a process running on the same Docker host as the
Docker `daemon`.
Network plugins follow the same restrictions and installation rules as other
plugins. All plugins make use of the plugin API. They have a lifecycle that
encompasses installation, starting, stopping and activation.
Once you have created and installed a custom network driver, you use it like the
built-in network drivers. For example:
$ docker network create --driver weave mynet
You can inspect it, add containers too and from it, and so forth. Of course,
different plugins may make use of different technologies or frameworks. Custom
networks can include features not present in Docker's default networks. For more
information on writing plugins, see [Extending Docker](../../extend/index.md) and
[Writing a network driver plugin](../../extend/plugins_network.md).
### Docker embedded DNS server
Docker daemon runs an embedded DNS server to provide automatic service discovery
for containers connected to user defined networks. Name resolution requests from
the containers are handled first by the embedded DNS server. If the embedded DNS
server is unable to resolve the request it will be forwarded to any external DNS
servers configured for the container. To facilitate this when the container is
created, only the embedded DNS server reachable at `127.0.0.11` will be listed
in the container's `resolv.conf` file. More information on embedded DNS server on
user-defined networks can be found in the [embedded DNS server in user-defined networks]
(configure-dns.md)
## Links
Before the Docker network feature, you could use the Docker link feature to
allow containers to discover each other. With the introduction of Docker networks,
containers can be discovered by its name automatically. But you can still create
links but they behave differently when used in the default `docker0` bridge network
compared to user-defined networks. For more information, please refer to
[Legacy Links](default_network/dockerlinks.md) for link feature in default `bridge` network
and the [linking containers in user-defined networks](work-with-networks.md#linking-containers-in-user-defined-networks) for links
functionality in user-defined networks.
## Related information
- [Work with network commands](work-with-networks.md)
- [Get started with multi-host networking](get-started-overlay.md)
- [Managing Data in Containers](../containers/dockervolumes.md)
- [Docker Machine overview](https://docs.docker.com/machine)
- [Docker Swarm overview](https://docs.docker.com/swarm)
- [Investigate the LibNetwork project](https://github.com/docker/libnetwork)

View File

@@ -0,0 +1,326 @@
<!--[metadata]>
+++
title = "Get started with multi-host networking"
description = "Use overlay for multi-host networking"
keywords = ["Examples, Usage, network, docker, documentation, user guide, multihost, cluster"]
[menu.main]
parent = "smn_networking"
weight=-3
+++
<![end-metadata]-->
# Get started with multi-host networking
This article uses an example to explain the basics of creating a multi-host
network. Docker Engine supports multi-host networking out-of-the-box through the
`overlay` network driver. Unlike `bridge` networks, overlay networks require
some pre-existing conditions before you can create one. These conditions are:
* Access to a key-value store. Docker supports Consul, Etcd, and ZooKeeper (Distributed store) key-value stores.
* A cluster of hosts with connectivity to the key-value store.
* A properly configured Engine `daemon` on each host in the cluster.
Though Docker Machine and Docker Swarm are not mandatory to experience Docker
multi-host networking, this example uses them to illustrate how they are
integrated. You'll use Machine to create both the key-value store
server and the host cluster. This example creates a Swarm cluster.
## Prerequisites
Before you begin, make sure you have a system on your network with the latest
version of Docker Engine and Docker Machine installed. The example also relies
on VirtualBox. If you installed on a Mac or Windows with Docker Toolbox, you
have all of these installed already.
If you have not already done so, make sure you upgrade Docker Engine and Docker
Machine to the latest versions.
## Step 1: Set up a key-value store
An overlay network requires a key-value store. The key-value store holds
information about the network state which includes discovery, networks,
endpoints, IP addresses, and more. Docker supports Consul, Etcd, and ZooKeeper
key-value stores. This example uses Consul.
1. Log into a system prepared with the prerequisite Docker Engine, Docker Machine, and VirtualBox software.
2. Provision a VirtualBox machine called `mh-keystore`.
$ docker-machine create -d virtualbox mh-keystore
When you provision a new machine, the process adds Docker Engine to the
host. This means rather than installing Consul manually, you can create an
instance using the [consul image from Docker
Hub](https://hub.docker.com/r/progrium/consul/). You'll do this in the next step.
3. Start a `progrium/consul` container running on the `mh-keystore` machine.
$ docker $(docker-machine config mh-keystore) run -d \
-p "8500:8500" \
-h "consul" \
progrium/consul -server -bootstrap
A bash expansion `$(docker-machine config mh-keystore)` is used to pass the
connection configuration to the `docker run` command. The client starts a
`progrium/consul` image running in the `mh-keystore` machine. The server is
called `consul` and is listening on port `8500`.
4. Set your local environment to the `mh-keystore` machine.
$ eval "$(docker-machine env mh-keystore)"
5. Run the `docker ps` command to see the `consul` container.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4d51392253b3 progrium/consul "/bin/start -server -" 25 minutes ago Up 25 minutes 53/tcp, 53/udp, 8300-8302/tcp, 0.0.0.0:8500->8500/tcp, 8400/tcp, 8301-8302/udp admiring_panini
Keep your terminal open and move onto the next step.
## Step 2: Create a Swarm cluster
In this step, you use `docker-machine` to provision the hosts for your network.
At this point, you won't actually create the network. You'll create several
machines in VirtualBox. One of the machines will act as the Swarm master;
you'll create that first. As you create each host, you'll pass the Engine on
that machine options that are needed by the `overlay` network driver.
1. Create a Swarm master.
$ docker-machine create \
-d virtualbox \
--swarm --swarm-master \
--swarm-discovery="consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
mhs-demo0
At creation time, you supply the Engine `daemon` with the ` --cluster-store` option. This option tells the Engine the location of the key-value store for the `overlay` network. The bash expansion `$(docker-machine ip mh-keystore)` resolves to the IP address of the Consul server you created in "STEP 1". The `--cluster-advertise` option advertises the machine on the network.
2. Create another host and add it to the Swarm cluster.
$ docker-machine create -d virtualbox \
--swarm \
--swarm-discovery="consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-store=consul://$(docker-machine ip mh-keystore):8500" \
--engine-opt="cluster-advertise=eth1:2376" \
mhs-demo1
3. List your machines to confirm they are all up and running.
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM
default - virtualbox Running tcp://192.168.99.100:2376
mh-keystore * virtualbox Running tcp://192.168.99.103:2376
mhs-demo0 - virtualbox Running tcp://192.168.99.104:2376 mhs-demo0 (master)
mhs-demo1 - virtualbox Running tcp://192.168.99.105:2376 mhs-demo0
At this point you have a set of hosts running on your network. You are ready to create a multi-host network for containers using these hosts.
Leave your terminal open and go onto the next step.
## Step 3: Create the overlay Network
To create an overlay network
1. Set your docker environment to the Swarm master.
$ eval $(docker-machine env --swarm mhs-demo0)
Using the `--swarm` flag with `docker-machine` restricts the `docker` commands to Swarm information alone.
2. Use the `docker info` command to view the Swarm.
$ docker info
Containers: 3
Images: 2
Role: primary
Strategy: spread
Filters: affinity, health, constraint, port, dependency
Nodes: 2
mhs-demo0: 192.168.99.104:2376
└ Containers: 2
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 1.021 GiB
└ Labels: executiondriver=native-0.2, kernelversion=4.1.10-boot2docker, operatingsystem=Boot2Docker 1.9.0-rc1 (TCL 6.4); master : 4187d2c - Wed Oct 14 14:00:28 UTC 2015, provider=virtualbox, storagedriver=aufs
mhs-demo1: 192.168.99.105:2376
└ Containers: 1
└ Reserved CPUs: 0 / 1
└ Reserved Memory: 0 B / 1.021 GiB
└ Labels: executiondriver=native-0.2, kernelversion=4.1.10-boot2docker, operatingsystem=Boot2Docker 1.9.0-rc1 (TCL 6.4); master : 4187d2c - Wed Oct 14 14:00:28 UTC 2015, provider=virtualbox, storagedriver=aufs
CPUs: 2
Total Memory: 2.043 GiB
Name: 30438ece0915
From this information, you can see that you are running three containers and two images on the Master.
3. Create your `overlay` network.
$ docker network create --driver overlay --subnet=10.0.9.0/24 my-net
You only need to create the network on a single host in the cluster. In this case, you used the Swarm master but you could easily have run it on any host in the cluster.
> **Note** : It is highly recommended to use the `--subnet` option when creating
> a network. If the `--subnet` is not specified, the docker daemon automatically
> chooses and assigns a subnet for the network and it could overlap with another subnet
> in your infrastructure that is not managed by docker. Such overlaps can cause
> connectivity issues or failures when containers are connected to that network.
4. Check that the network is running:
$ docker network ls
NETWORK ID NAME DRIVER
412c2496d0eb mhs-demo1/host host
dd51763e6dd2 mhs-demo0/bridge bridge
6b07d0be843f my-net overlay
b4234109bd9b mhs-demo0/none null
1aeead6dd890 mhs-demo0/host host
d0bb78cbe7bd mhs-demo1/bridge bridge
1c0eb8f69ebb mhs-demo1/none null
As you are in the Swarm master environment, you see all the networks on all
the Swarm agents: the default networks on each engine and the single overlay
network. Notice that each `NETWORK ID` is unique.
5. Switch to each Swarm agent in turn and list the networks.
$ eval $(docker-machine env mhs-demo0)
$ docker network ls
NETWORK ID NAME DRIVER
6b07d0be843f my-net overlay
dd51763e6dd2 bridge bridge
b4234109bd9b none null
1aeead6dd890 host host
$ eval $(docker-machine env mhs-demo1)
$ docker network ls
NETWORK ID NAME DRIVER
d0bb78cbe7bd bridge bridge
1c0eb8f69ebb none null
412c2496d0eb host host
6b07d0be843f my-net overlay
Both agents report they have the `my-net` network with the `6b07d0be843f` ID.
You now have a multi-host container network running!
## Step 4: Run an application on your Network
Once your network is created, you can start a container on any of the hosts and it automatically is part of the network.
1. Point your environment to the Swarm master.
$ eval $(docker-machine env --swarm mhs-demo0)
2. Start an Nginx web server on the `mhs-demo0` instance.
$ docker run -itd --name=web --net=my-net --env="constraint:node==mhs-demo0" nginx
4. Run a BusyBox instance on the `mhs-demo1` instance and get the contents of the Nginx server's home page.
$ docker run -it --rm --net=my-net --env="constraint:node==mhs-demo1" busybox wget -O- http://web
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
ab2b8a86ca6c: Pull complete
2c5ac3f849df: Pull complete
Digest: sha256:5551dbdfc48d66734d0f01cafee0952cb6e8eeecd1e2492240bf2fd9640c2279
Status: Downloaded newer image for busybox:latest
Connecting to web (10.0.0.2:80)
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
- 100% |*******************************| 612 0:00:00 ETA
## Step 5: Check external connectivity
As you've seen, Docker's built-in overlay network driver provides out-of-the-box
connectivity between the containers on multiple hosts within the same network.
Additionally, containers connected to the multi-host network are automatically
connected to the `docker_gwbridge` network. This network allows the containers
to have external connectivity outside of their cluster.
1. Change your environment to the Swarm agent.
$ eval $(docker-machine env mhs-demo1)
2. View the `docker_gwbridge` network, by listing the networks.
$ docker network ls
NETWORK ID NAME DRIVER
6b07d0be843f my-net overlay
dd51763e6dd2 bridge bridge
b4234109bd9b none null
1aeead6dd890 host host
e1dbd5dff8be docker_gwbridge bridge
3. Repeat steps 1 and 2 on the Swarm master.
$ eval $(docker-machine env mhs-demo0)
$ docker network ls
NETWORK ID NAME DRIVER
6b07d0be843f my-net overlay
d0bb78cbe7bd bridge bridge
1c0eb8f69ebb none null
412c2496d0eb host host
97102a22e8d2 docker_gwbridge bridge
2. Check the Nginx container's network interfaces.
$ docker exec web ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
22: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 02:42:0a:00:09:03 brd ff:ff:ff:ff:ff:ff
inet 10.0.9.3/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:aff:fe00:903/64 scope link
valid_lft forever preferred_lft forever
24: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe12:2/64 scope link
valid_lft forever preferred_lft forever
The `eth0` interface represents the container interface that is connected to
the `my-net` overlay network. While the `eth1` interface represents the
container interface that is connected to the `docker_gwbridge` network.
## Step 6: Extra Credit with Docker Compose
Please refer to the Networking feature introduced in [Compose V2 format]
(https://docs.docker.com/compose/networking/) and execute the
multi-host networking scenario in the Swarm cluster used above.
## Related information
* [Understand Docker container networks](dockernetworks.md)
* [Work with network commands](work-with-networks.md)
* [Docker Swarm overview](https://docs.docker.com/swarm)
* [Docker Machine overview](https://docs.docker.com/machine)

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,21 @@
<!--[metadata]>
+++
title = "Network configuration"
description = "Docker networking feature is introduced"
keywords = ["network, networking, bridge, docker, documentation"]
[menu.main]
identifier="smn_networking"
parent= "engine_guide"
weight=7
+++
<![end-metadata]-->
# Docker networks feature overview
This sections explains how to use the Docker networks feature. This feature allows users to define their own networks and connect containers to them. Using this feature you can create a network on a single host or a network that spans across multiple hosts.
- [Understand Docker container networks](dockernetworks.md)
- [Work with network commands](work-with-networks.md)
- [Get started with multi-host networking](get-started-overlay.md)
If you are already familiar with Docker's default bridge network, `docker0` that network continues to be supported. It is created automatically in every installation. The default bridge network is also named `bridge`. To see a list of topics related to that network, read the articles listed in the [Docker default bridge network](default_network/index.md).

View File

@@ -0,0 +1,856 @@
<!--[metadata]>
+++
title = "Work with network commands"
description = "How to work with docker networks"
keywords = ["commands, Usage, network, docker, cluster"]
[menu.main]
parent = "smn_networking"
weight=-4
+++
<![end-metadata]-->
# Work with network commands
This article provides examples of the network subcommands you can use to interact with Docker networks and the containers in them. The commands are available through the Docker Engine CLI. These commands are:
* `docker network create`
* `docker network connect`
* `docker network ls`
* `docker network rm`
* `docker network disconnect`
* `docker network inspect`
While not required, it is a good idea to read [Understanding Docker
network](dockernetworks.md) before trying the examples in this section. The
examples for the rely on a `bridge` network so that you can try them
immediately. If you would prefer to experiment with an `overlay` network see
the [Getting started with multi-host networks](get-started-overlay.md) instead.
## Create networks
Docker Engine creates a `bridge` network automatically when you install Engine.
This network corresponds to the `docker0` bridge that Engine has traditionally
relied on. In addition to this network, you can create your own `bridge` or `overlay` network.
A `bridge` network resides on a single host running an instance of Docker Engine. An `overlay` network can span multiple hosts running their own engines. If you run `docker network create` and supply only a network name, it creates a bridge network for you.
```bash
$ docker network create simple-network
69568e6336d8c96bbf57869030919f7c69524f71183b44d80948bd3927c87f6a
$ docker network inspect simple-network
[
{
"Name": "simple-network",
"Id": "69568e6336d8c96bbf57869030919f7c69524f71183b44d80948bd3927c87f6a",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.22.0.0/16",
"Gateway": "172.22.0.1/16"
}
]
},
"Containers": {},
"Options": {}
}
]
```
Unlike `bridge` networks, `overlay` networks require some pre-existing conditions
before you can create one. These conditions are:
* Access to a key-value store. Engine supports Consul Etcd, and ZooKeeper (Distributed store) key-value stores.
* A cluster of hosts with connectivity to the key-value store.
* A properly configured Engine `daemon` on each host in the swarm.
The `docker daemon` options that support the `overlay` network are:
* `--cluster-store`
* `--cluster-store-opt`
* `--cluster-advertise`
It is also a good idea, though not required, that you install Docker Swarm
to manage the cluster. Swarm provides sophisticated discovery and server
management that can assist your implementation.
When you create a network, Engine creates a non-overlapping subnetwork for the
network by default. You can override this default and specify a subnetwork
directly using the the `--subnet` option. On a `bridge` network you can only
specify a single subnet. An `overlay` network supports multiple subnets.
> **Note** : It is highly recommended to use the `--subnet` option while creating
> a network. If the `--subnet` is not specified, the docker daemon automatically
> chooses and assigns a subnet for the network and it could overlap with another subnet
> in your infrastructure that is not managed by docker. Such overlaps can cause
> connectivity issues or failures when containers are connected to that network.
In addition to the `--subnetwork` option, you also specify the `--gateway` `--ip-range` and `--aux-address` options.
```bash
$ docker network create -d overlay
--subnet=192.168.0.0/16 --subnet=192.170.0.0/16
--gateway=192.168.0.100 --gateway=192.170.0.100
--ip-range=192.168.1.0/24
--aux-address a=192.168.1.5 --aux-address b=192.168.1.6
--aux-address a=192.170.1.5 --aux-address b=192.170.1.6
my-multihost-network
```
Be sure that your subnetworks do not overlap. If they do, the network create fails and Engine returns an error.
When creating a custom network, the default network driver (i.e. `bridge`) has additional options that can be passed.
The following are those options and the equivalent docker daemon flags used for docker0 bridge:
| Option | Equivalent | Description |
|--------------------------------------------------|-------------|-------------------------------------------------------|
| `com.docker.network.bridge.name` | - | bridge name to be used when creating the Linux bridge |
| `com.docker.network.bridge.enable_ip_masquerade` | `--ip-masq` | Enable IP masquerading |
| `com.docker.network.bridge.enable_icc` | `--icc` | Enable or Disable Inter Container Connectivity |
| `com.docker.network.bridge.host_binding_ipv4` | `--ip` | Default IP when binding container ports |
| `com.docker.network.mtu` | `--mtu` | Set the containers network MTU |
| `com.docker.network.enable_ipv6` | `--ipv6` | Enable IPv6 networking |
For example, now let's use `-o` or `--opt` options to specify an IP address binding when publishing ports:
```bash
$ docker network create -o "com.docker.network.bridge.host_binding_ipv4"="172.23.0.1" my-network
b1a086897963e6a2e7fc6868962e55e746bee8ad0c97b54a5831054b5f62672a
$ docker network inspect my-network
[
{
"Name": "my-network",
"Id": "b1a086897963e6a2e7fc6868962e55e746bee8ad0c97b54a5831054b5f62672a",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.23.0.0/16",
"Gateway": "172.23.0.1/16"
}
]
},
"Containers": {},
"Options": {
"com.docker.network.bridge.host_binding_ipv4": "172.23.0.1"
}
}
]
$ docker run -d -P --name redis --net my-network redis
bafb0c808c53104b2c90346f284bda33a69beadcab4fc83ab8f2c5a4410cd129
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bafb0c808c53 redis "/entrypoint.sh redis" 4 seconds ago Up 3 seconds 172.23.0.1:32770->6379/tcp redis
```
## Connect containers
You can connect containers dynamically to one or more networks. These networks
can be backed the same or different network drivers. Once connected, the
containers can communicate using another container's IP address or name.
For `overlay` networks or custom plugins that support multi-host
connectivity, containers connected to the same multi-host network but launched
from different hosts can also communicate in this way.
Create two containers for this example:
```bash
$ docker run -itd --name=container1 busybox
18c062ef45ac0c026ee48a83afa39d25635ee5f02b58de4abc8f467bcaa28731
$ docker run -itd --name=container2 busybox
498eaaaf328e1018042c04b2de04036fc04719a6e39a097a4f4866043a2c2152
```
Then create an isolated, `bridge` network to test with.
```bash
$ docker network create -d bridge --subnet 172.25.0.0/16 isolated_nw
06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8
```
Connect `container2` to the network and then `inspect` the network to verify the connection:
```
$ docker network connect isolated_nw container2
$ docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1/16"
}
]
},
"Containers": {
"90e1f3ec71caf82ae776a827e0712a68a110a3f175954e5bd4222fd142ac9428": {
"Name": "container2",
"EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
"MacAddress": "02:42:ac:19:00:02",
"IPv4Address": "172.25.0.2/16",
"IPv6Address": ""
}
},
"Options": {}
}
]
```
You can see that the Engine automatically assigns an IP address to `container2`.
Given we specified a `--subnet` when creating the network, Engine picked
an address from that same subnet. Now, start a third container and connect it to
the network on launch using the `docker run` command's `--net` option:
```bash
$ docker run --net=isolated_nw --ip=172.25.3.3 -itd --name=container3 busybox
467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551
```
As you can see you were able to specify the ip address for your container.
As long as the network to which the container is connecting was created with
a user specified subnet, you will be able to select the IPv4 and/or IPv6 address(es)
for your container when executing `docker run` and `docker network connect` commands.
The selected IP address is part of the container networking configuration and will be
preserved across container reload. The feature is only available on user defined networks,
because they guarantee their subnets configuration does not change across daemon reload.
Now, inspect the network resources used by `container3`.
```bash
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container3
{"isolated_nw":{"IPAMConfig":{"IPv4Address":"172.25.3.3"},"NetworkID":"1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
"EndpointID":"dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103","Gateway":"172.25.0.1","IPAddress":"172.25.3.3","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:19:03:03"}}
```
Repeat this command for `container2`. If you have Python installed, you can pretty print the output.
```bash
$ docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | python -m json.tool
{
"bridge": {
"NetworkID":"7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
"EndpointID": "0099f9efb5a3727f6a554f176b1e96fca34cae773da68b3b6a26d046c12cb365",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAMConfig": null,
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:03"
},
"isolated_nw": {
"NetworkID":"1196a4c5af43a21ae38ef34515b6af19236a3fc48122cf585e3f3054d509679b",
"EndpointID": "11cedac1810e864d6b1589d92da12af66203879ab89f4ccd8c8fdaa9b1c48b1d",
"Gateway": "172.25.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAMConfig": null,
"IPAddress": "172.25.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:19:00:02"
}
}
```
You should find `container2` belongs to two networks. The `bridge` network
which it joined by default when you launched it and the `isolated_nw` which you
later connected it to.
![](images/working.png)
In the case of `container3`, you connected it through `docker run` to the
`isolated_nw` so that container is not connected to `bridge`.
Use the `docker attach` command to connect to the running `container2` and
examine its networking stack:
```bash
$ docker attach container2
```
If you look a the container's network stack you should see two Ethernet interfaces, one for the default bridge network and one for the `isolated_nw` network.
```bash
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
eth1 Link encap:Ethernet HWaddr 02:42:AC:15:00:02
inet addr:172.25.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe19:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
On the `isolated_nw` which was user defined, the Docker embedded DNS server enables name resolution for other containers in the network. Inside of `container2` it is possible to ping `container3` by name.
```bash
/ # ping -w 4 container3
PING container3 (172.25.3.3): 56 data bytes
64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.3.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.3.3: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.3.3: seq=3 ttl=64 time=0.097 ms
--- container3 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
This isn't the case for the default `bridge` network. Both `container2` and `container1` are connected to the default bridge network. Docker does not support automatic service discovery on this network. For this reason, pinging `container1` by name fails as you would expect based on the `/etc/hosts` file:
```bash
/ # ping -w 4 container1
ping: bad address 'container1'
```
A ping using the `container1` IP address does succeed though:
```bash
/ # ping -w 4 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.095 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.075 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.072 ms
64 bytes from 172.17.0.2: seq=3 ttl=64 time=0.101 ms
--- 172.17.0.2 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.072/0.085/0.101 ms
```
If you wanted you could connect `container1` to `container2` with the `docker
run --link` command and that would enable the two containers to interact by name
as well as IP.
Detach from a `container2` and leave it running using `CTRL-p CTRL-q`.
In this example, `container2` is attached to both networks and so can talk to
`container1` and `container3`. But `container3` and `container1` are not in the
same network and cannot communicate. Test, this now by attaching to
`container3` and attempting to ping `container1` by IP address.
```bash
$ docker attach container3
/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
^C
--- 172.17.0.2 ping statistics ---
10 packets transmitted, 0 packets received, 100% packet loss
```
You can connect both running and non-running containers to a network. However,
`docker network inspect` only displays information on running containers.
### Linking containers in user-defined networks
In the above example, container_2 was able to resolve container_3's name automatically
in the user defined network `isolated_nw`, but the name resolution did not succeed
automatically in the default `bridge` network. This is expected in order to maintain
backward compatibility with [legacy link](default_network/dockerlinks.md).
The `legacy link` provided 4 major functionalities to the default `bridge` network.
* name resolution
* name alias for the linked container using `--link=CONTAINER-NAME:ALIAS`
* secured container connectivity (in isolation via `--icc=false`)
* environment variable injection
Comparing the above 4 functionalities with the non-default user-defined networks such as
`isolated_nw` in this example, without any additional config, `docker network` provides
* automatic name resolution using DNS
* automatic secured isolated environment for the containers in a network
* ability to dynamically attach and detach to multiple networks
* supports the `--link` option to provide name alias for the linked container
Continuing with the above example, create another container `container_4` in `isolated_nw`
with `--link` to provide additional name resolution using alias for other containers in
the same network.
```bash
$ docker run --net=isolated_nw -itd --name=container4 --link container5:c5 busybox
01b5df970834b77a9eadbaff39051f237957bd35c4c56f11193e0594cfd5117c
```
With the help of `--link` container4 will be able to reach container5 using the
aliased name `c5` as well.
Please note that while creating container4, we linked to a container named `container5`
which is not created yet. That is one of the differences in behavior between the
`legacy link` in default `bridge` network and the new `link` functionality in user defined
networks. The `legacy link` is static in nature and it hard-binds the container with the
alias and it doesnt tolerate linked container restarts. While the new `link` functionality
in user defined networks are dynamic in nature and supports linked container restarts
including tolerating ip-address changes on the linked container.
Now let us launch another container named `container5` linking container4 to c4.
```bash
$ docker run --net=isolated_nw -itd --name=container5 --link container4:c4 busybox
72eccf2208336f31e9e33ba327734125af00d1e1d2657878e2ee8154fbb23c7a
```
As expected, container4 will be able to reach container5 by both its container name and
its alias c5 and container5 will be able to reach container4 by its container name and
its alias c4.
```bash
$ docker attach container4
/ # ping -w 4 c5
PING c5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- c5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
/ # ping -w 4 container5
PING container5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- container5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
```bash
$ docker attach container5
/ # ping -w 4 c4
PING c4 (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
--- c4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.065/0.070/0.082 ms
/ # ping -w 4 container4
PING container4 (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
--- container4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.065/0.070/0.082 ms
```
Similar to the legacy link functionality the new link alias is localized to a container
and the aliased name has no meaning outside of the container using the `--link`.
Also, it is important to note that if a container belongs to multiple networks, the
linked alias is scoped within a given network. Hence the containers can be linked to
different aliases in different networks.
Extending the example, let us create another network named `local_alias`
```bash
$ docker network create -d bridge --subnet 172.26.0.0/24 local_alias
76b7dc932e037589e6553f59f76008e5b76fa069638cd39776b890607f567aaa
```
let us connect container4 and container5 to the new network `local_alias`
```
$ docker network connect --link container5:foo local_alias container4
$ docker network connect --link container4:bar local_alias container5
```
```bash
$ docker attach container4
/ # ping -w 4 foo
PING foo (172.26.0.3): 56 data bytes
64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.26.0.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: seq=3 ttl=64 time=0.097 ms
--- foo ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
/ # ping -w 4 c5
PING c5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- c5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
Note that the ping succeeds for both the aliases but on different networks.
Let us conclude this section by disconnecting container5 from the `isolated_nw`
and observe the results
```
$ docker network disconnect isolated_nw container5
$ docker attach container4
/ # ping -w 4 c5
ping: bad address 'c5'
/ # ping -w 4 foo
PING foo (172.26.0.3): 56 data bytes
64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.26.0.3: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.26.0.3: seq=3 ttl=64 time=0.097 ms
--- foo ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
In conclusion, the new link functionality in user defined networks provides all the
benefits of legacy links while avoiding most of the well-known issues with `legacy links`.
One notable missing functionality compared to `legacy links` is the injection of
environment variables. Though very useful, environment variable injection is static
in nature and must be injected when the container is started. One cannot inject
environment variables into a running container without significant effort and hence
it is not compatible with `docker network` which provides a dynamic way to connect/
disconnect containers to/from a network.
### Network-scoped alias
While `links` provide private name resolution that is localized within a container,
the network-scoped alias provides a way for a container to be discovered by an
alternate name by any other container within the scope of a particular network.
Unlike the `link` alias, which is defined by the consumer of a service, the
network-scoped alias is defined by the container that is offering the service
to the network.
Continuing with the above example, create another container in `isolated_nw` with a
network alias.
```bash
$ docker run --net=isolated_nw -itd --name=container6 --net-alias app busybox
8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17
```
```bash
$ docker attach container4
/ # ping -w 4 app
PING app (172.25.0.6): 56 data bytes
64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms
--- app ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
/ # ping -w 4 container6
PING container5 (172.25.0.6): 56 data bytes
64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms
--- container6 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
```
Now let us connect `container6` to the `local_alias` network with a different network-scoped
alias.
```
$ docker network connect --alias scoped-app local_alias container6
```
`container6` in this example now is aliased as `app` in network `isolated_nw` and
as `scoped-app` in network `local_alias`.
Let's try to reach these aliases from `container4` (which is connected to both these networks)
and `container5` (which is connected only to `isolated_nw`).
```bash
$ docker attach container4
/ # ping -w 4 scoped-app
PING foo (172.26.0.5): 56 data bytes
64 bytes from 172.26.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.26.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.26.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.26.0.5: seq=3 ttl=64 time=0.097 ms
--- foo ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
$ docker attach container5
/ # ping -w 4 scoped-app
ping: bad address 'scoped-app'
```
As you can see, the alias is scoped to the network it is defined on and hence only
those containers that are connected to that network can access the alias.
In addition to the above features, multiple containers can share the same network-scoped
alias within the same network. For example, let's launch `container7` in `isolated_nw` with
the same alias as `container6`
```bash
$ docker run --net=isolated_nw -itd --name=container7 --net-alias app busybox
3138c678c123b8799f4c7cc6a0cecc595acbdfa8bf81f621834103cd4f504554
```
When multiple containers share the same alias, name resolution to that alias will happen
to one of the containers (typically the first container that is aliased). When the container
that backs the alias goes down or disconnected from the network, the next container that
backs the alias will be resolved.
Let us ping the alias `app` from `container4` and bring down `container6` to verify that
`container7` is resolving the `app` alias.
```bash
$ docker attach container4
/ # ping -w 4 app
PING app (172.25.0.6): 56 data bytes
64 bytes from 172.25.0.6: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.6: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.6: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.6: seq=3 ttl=64 time=0.097 ms
--- app ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
$ docker stop container6
$ docker attach container4
/ # ping -w 4 app
PING app (172.25.0.7): 56 data bytes
64 bytes from 172.25.0.7: seq=0 ttl=64 time=0.095 ms
64 bytes from 172.25.0.7: seq=1 ttl=64 time=0.075 ms
64 bytes from 172.25.0.7: seq=2 ttl=64 time=0.072 ms
64 bytes from 172.25.0.7: seq=3 ttl=64 time=0.101 ms
--- app ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.072/0.085/0.101 ms
```
## Disconnecting containers
You can disconnect a container from a network using the `docker network
disconnect` command.
```
$ docker network disconnect isolated_nw container2
docker inspect --format='{{json .NetworkSettings.Networks}}' container2 | python -m json.tool
{
"bridge": {
"NetworkID":"7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
"EndpointID": "9e4575f7f61c0f9d69317b7a4b92eefc133347836dd83ef65deffa16b9985dc0",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:03"
}
}
$ docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1/16"
}
]
},
"Containers": {
"467a7863c3f0277ef8e661b38427737f28099b61fa55622d6c30fb288d88c551": {
"Name": "container3",
"EndpointID": "dffc7ec2915af58cc827d995e6ebdc897342be0420123277103c40ae35579103",
"MacAddress": "02:42:ac:19:03:03",
"IPv4Address": "172.25.3.3/16",
"IPv6Address": ""
}
},
"Options": {}
}
]
```
Once a container is disconnected from a network, it cannot communicate with
other containers connected to that network. In this example, `container2` can no longer talk to `container3` on the `isolated_nw` network.
```
$ docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping container3
PING container3 (172.25.3.3): 56 data bytes
^C
--- container3 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
```
The `container2` still has full connectivity to the bridge network
```bash
/ # ping container1
PING container1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.119 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.174 ms
^C
--- container1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.119/0.146/0.174 ms
/ #
```
There are certain scenarios such as ungraceful docker daemon restarts in multi-host network,
where the daemon is unable to cleanup stale connectivity endpoints. Such stale endpoints
may cause an error `container already connected to network` when a new container is
connected to that network with the same name as the stale endpoint. In order to cleanup
these stale endpoints, first remove the container and force disconnect
(`docker network disconnect -f`) the endpoint from the network. Once the endpoint is
cleaned up, the container can be connected to the network.
```
$ docker run -d --name redis_db --net multihost redis
ERROR: Cannot start container bc0b19c089978f7845633027aa3435624ca3d12dd4f4f764b61eac4c0610f32e: container already connected to network multihost
$ docker rm -f redis_db
$ docker network disconnect -f multihost redis_db
$ docker run -d --name redis_db --net multihost redis
7d986da974aeea5e9f7aca7e510bdb216d58682faa83a9040c2f2adc0544795a
```
## Remove a network
When all the containers in a network are stopped or disconnected, you can remove a network.
```bash
$ docker network disconnect isolated_nw container3
```
```bash
docker network inspect isolated_nw
[
{
"Name": "isolated_nw",
"Id": "06a62f1c73c4e3107c0f555b7a5f163309827bfbbf999840166065a8f35455a8",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.21.0.0/16",
"Gateway": "172.21.0.1/16"
}
]
},
"Containers": {},
"Options": {}
}
]
$ docker network rm isolated_nw
```
List all your networks to verify the `isolated_nw` was removed:
```
$ docker network ls
NETWORK ID NAME DRIVER
72314fa53006 host host
f7ab26d71dbd bridge bridge
0f32e83e61ac none null
```
## Related information
* [network create](../../reference/commandline/network_create.md)
* [network inspect](../../reference/commandline/network_inspect.md)
* [network connect](../../reference/commandline/network_connect.md)
* [network disconnect](../../reference/commandline/network_disconnect.md)
* [network ls](../../reference/commandline/network_ls.md)
* [network rm](../../reference/commandline/network_rm.md)

View File

@@ -0,0 +1,216 @@
<!--[metadata]>
+++
title = "AUFS storage driver in practice"
description = "Learn how to optimize your use of AUFS driver."
keywords = ["container, storage, driver, AUFS "]
[menu.main]
parent = "engine_driver"
+++
<![end-metadata]-->
# Docker and AUFS in practice
AUFS was the first storage driver in use with Docker. As a result, it has a
long and close history with Docker, is very stable, has a lot of real-world
deployments, and has strong community support. AUFS has several features that
make it a good choice for Docker. These features enable:
- Fast container startup times.
- Efficient use of storage.
- Efficient use of memory.
Despite its capabilities and long history with Docker, some Linux distributions
do not support AUFS. This is usually because AUFS is not included in the
mainline (upstream) Linux kernel.
The following sections examine some AUFS features and how they relate to
Docker.
## Image layering and sharing with AUFS
AUFS is a *unification filesystem*. This means that it takes multiple
directories on a single Linux host, stacks them on top of each other, and
provides a single unified view. To achieve this, AUFS uses a *union mount*.
AUFS stacks multiple directories and exposes them as a unified view through a
single mount point. All of the directories in the stack, as well as the union
mount point, must all exist on the same Linux host. AUFS refers to each
directory that it stacks as a *branch*.
Within Docker, AUFS union mounts enable image layering. The AUFS storage driver
implements Docker image layers using this union mount system. AUFS branches
correspond to Docker image layers. The diagram below shows a Docker container
based on the `ubuntu:latest` image.
![](images/aufs_layers.jpg)
This diagram shows that each image layer, and the container layer, is
represented in the Docker hosts filesystem as a directory under
`/var/lib/docker/`. The union mount point provides the unified view of all
layers. As of Docker 1.10, image layer IDs do not correspond to the names of
the directories that contain their data.
AUFS also supports the copy-on-write technology (CoW). Not all storage drivers
do.
## Container reads and writes with AUFS
Docker leverages AUFS CoW technology to enable image sharing and minimize the
use of disk space. AUFS works at the file level. This means that all AUFS CoW
operations copy entire files - even if only a small part of the file is being
modified. This behavior can have a noticeable impact on container performance,
especially if the files being copied are large, below a lot of image layers,
or the CoW operation must search a deep directory tree.
Consider, for example, an application running in a container needs to add a
single new value to a large key-value store (file). If this is the first time
the file is modified, it does not yet exist in the container's top writable
layer. So, the CoW must *copy up* the file from the underlying image. The AUFS
storage driver searches each image layer for the file. The search order is from
top to bottom. When it is found, the entire file is *copied up* to the
container's top writable layer. From there, it can be opened and modified.
Larger files obviously take longer to *copy up* than smaller files, and files
that exist in lower image layers take longer to locate than those in higher
layers. However, a *copy up* operation only occurs once per file on any given
container. Subsequent reads and writes happen against the file's copy already
*copied-up* to the container's top layer.
## Deleting files with the AUFS storage driver
The AUFS storage driver deletes a file from a container by placing a *whiteout
file* in the container's top layer. The whiteout file effectively obscures the
existence of the file in the read-only image layers below. The simplified
diagram below shows a container based on an image with three image layers.
![](images/aufs_delete.jpg)
The `file3` was deleted from the container. So, the AUFS storage driver placed
a whiteout file in the container's top layer. This whiteout file effectively
"deletes" `file3` from the container by obscuring any of the original file's
existence in the image's read-only layers. This works the same no matter which
of the image's read-only layers the file exists in.
## Configure Docker with AUFS
You can only use the AUFS storage driver on Linux systems with AUFS installed.
Use the following command to determine if your system supports AUFS.
$ grep aufs /proc/filesystems
nodev aufs
This output indicates the system supports AUFS. Once you've verified your
system supports AUFS, you can must instruct the Docker daemon to use it. You do
this from the command line with the `docker daemon` command:
$ sudo docker daemon --storage-driver=aufs &
Alternatively, you can edit the Docker config file and add the
`--storage-driver=aufs` option to the `DOCKER_OPTS` line.
# Use DOCKER_OPTS to modify the daemon startup options.
DOCKER_OPTS="--storage-driver=aufs"
Once your daemon is running, verify the storage driver with the `docker info`
command.
$ sudo docker info
Containers: 1
Images: 4
Storage Driver: aufs
Root Dir: /var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 6
Dirperm1 Supported: false
Execution Driver: native-0.2
...output truncated...
The output above shows that the Docker daemon is running the AUFS storage
driver on top of an existing `ext4` backing filesystem.
## Local storage and AUFS
As the `docker daemon` runs with the AUFS driver, the driver stores images and
containers within the Docker host's local storage area under
`/var/lib/docker/aufs/`.
### Images
Image layers and their contents are stored under
`/var/lib/docker/aufs/diff/`. With Docker 1.10 and higher, image layer IDs do
not correspond to directory names.
The `/var/lib/docker/aufs/layers/` directory contains metadata about how image
layers are stacked. This directory contains one file for every image or
container layer on the Docker host (though file names no longer match image
layer IDs). Inside each file are the names of the directories that exist below
it in the stack
The command below shows the contents of a metadata file in
`/var/lib/docker/aufs/layers/` that lists the the three directories that are
stacked below it in the union mount. Remember, these directory names do no map
to image layer IDs with Docker 1.10 and higher.
$ cat /var/lib/docker/aufs/layers/91e54dfb11794fad694460162bf0cb0a4fa710cfa3f60979c177d920813e267c
d74508fb6632491cea586a1fd7d748dfc5274cd6fdfedee309ecdcbc2bf5cb82
c22013c8472965aa5b62559f2b540cd440716ef149756e7b958a1b2aba421e87
d3a1f33e8a5a513092f01bb7eb1c2abf4d711e5105390a3fe1ae2248cfde1391
The base layer in an image has no image layers below it, so its file is empty.
### Containers
Running containers are mounted below `/var/lib/docker/aufs/mnt/<container-id>`.
This is where the AUFS union mount point that exposes the container and all
underlying image layers as a single unified view exists. If a container is not
running, it still has a directory here but it is empty. This is because AUFS
only mounts a container when it is running. With Docker 1.10 and higher,
container IDs no longer correspond to directory names under
`/var/lib/docker/aufs/mnt/<container-id>`.
Container metadata and various config files that are placed into the running
container are stored in `/var/lib/docker/containers/<container-id>`. Files in
this directory exist for all containers on the system, including ones that are
stopped. However, when a container is running the container's log files are
also in this directory.
A container's thin writable layer is stored in a directory under
`/var/lib/docker/aufs/diff/`. With Docker 1.10 and higher, container IDs no
longer correspond to directory names. However, the containers thin writable
layer still exists under here and is stacked by AUFS as the top writable layer
and is where all changes to the container are stored. The directory exists even
if the container is stopped. This means that restarting a container will not
lose changes made to it. Once a container is deleted, it's thin writable layer
in this directory is deleted.
## AUFS and Docker performance
To summarize some of the performance related aspects already mentioned:
- The AUFS storage driver is a good choice for PaaS and other similar use-cases
where container density is important. This is because AUFS efficiently shares
images between multiple running containers, enabling fast container start times
and minimal use of disk space.
- The underlying mechanics of how AUFS shares files between image layers and
containers uses the systems page cache very efficiently.
- The AUFS storage driver can introduce significant latencies into container
write performance. This is because the first time a container writes to any
file, the file has be located and copied into the containers top writable
layer. These latencies increase and are compounded when these files exist below
many image layers and the files themselves are large.
One final point. Data volumes provide the best and most predictable
performance. This is because they bypass the storage driver and do not incur
any of the potential overheads introduced by thin provisioning and
copy-on-write. For this reason, you may want to place heavy write workloads on
data volumes.
## Related information
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)

View File

@@ -0,0 +1,315 @@
<!--[metadata]>
+++
title = "Btrfs storage in practice"
description = "Learn how to optimize your use of Btrfs driver."
keywords = ["container, storage, driver, Btrfs "]
[menu.main]
parent = "engine_driver"
+++
<![end-metadata]-->
# Docker and Btrfs in practice
Btrfs is a next generation copy-on-write filesystem that supports many advanced
storage technologies that make it a good fit for Docker. Btrfs is included in
the mainline Linux kernel and its on-disk-format is now considered stable.
However, many of its features are still under heavy development and users
should consider it a fast-moving target.
Docker's `btrfs` storage driver leverages many Btrfs features for image and
container management. Among these features are thin provisioning,
copy-on-write, and snapshotting.
This article refers to Docker's Btrfs storage driver as `btrfs` and the overall
Btrfs Filesystem as Btrfs.
>**Note**: The [Commercially Supported Docker Engine (CS-Engine)](https://www.docker.com/compatibility-maintenance) does not currently support the `btrfs` storage driver.
## The future of Btrfs
Btrfs has been long hailed as the future of Linux filesystems. With full
support in the mainline Linux kernel, a stable on-disk-format, and active
development with a focus on stability, this is now becoming more of a reality.
As far as Docker on the Linux platform goes, many people see the `btrfs`
storage driver as a potential long-term replacement for the `devicemapper`
storage driver. However, at the time of writing, the `devicemapper` storage
driver should be considered safer, more stable, and more *production ready*.
You should only consider the `btrfs` driver for production deployments if you
understand it well and have existing experience with Btrfs.
## Image layering and sharing with Btrfs
Docker leverages Btrfs *subvolumes* and *snapshots* for managing the on-disk
components of image and container layers. Btrfs subvolumes look and feel like
a normal Unix filesystem. As such, they can have their own internal directory
structure that hooks into the wider Unix filesystem.
Subvolumes are natively copy-on-write and have space allocated to them
on-demand from an underlying storage pool. They can also be nested and snapped.
The diagram blow shows 4 subvolumes. 'Subvolume 2' and 'Subvolume 3' are
nested, whereas 'Subvolume 4' shows its own internal directory tree.
![](images/btfs_subvolume.jpg)
Snapshots are a point-in-time read-write copy of an entire subvolume. They
exist directly below the subvolume they were created from. You can create
snapshots of snapshots as shown in the diagram below.
![](images/btfs_snapshots.jpg)
Btfs allocates space to subvolumes and snapshots on demand from an underlying
pool of storage. The unit of allocation is referred to as a *chunk*, and
*chunks* are normally ~1GB in size.
Snapshots are first-class citizens in a Btrfs filesystem. This means that they
look, feel, and operate just like regular subvolumes. The technology required
to create them is built directly into the Btrfs filesystem thanks to its
native copy-on-write design. This means that Btrfs snapshots are space
efficient with little or no performance overhead. The diagram below shows a
subvolume and its snapshot sharing the same data.
![](images/btfs_pool.jpg)
Docker's `btrfs` storage driver stores every image layer and container in its
own Btrfs subvolume or snapshot. The base layer of an image is stored as a
subvolume whereas child image layers and containers are stored as snapshots.
This is shown in the diagram below.
![](images/btfs_container_layer.jpg)
The high level process for creating images and containers on Docker hosts
running the `btrfs` driver is as follows:
1. The image's base layer is stored in a Btrfs *subvolume* under
`/var/lib/docker/btrfs/subvolumes`.
2. Subsequent image layers are stored as a Btrfs *snapshot* of the parent
layer's subvolume or snapshot.
The diagram below shows a three-layer image. The base layer is a subvolume.
Layer 1 is a snapshot of the base layer's subvolume. Layer 2 is a snapshot of
Layer 1's snapshot.
![](images/btfs_constructs.jpg)
As of Docker 1.10, image layer IDs no longer correspond to directory names
under `/var/lib/docker/`.
## Image and container on-disk constructs
Image layers and containers are visible in the Docker host's filesystem at
`/var/lib/docker/btrfs/subvolumes/`. However, as previously stated, directory
names no longer correspond to image layer IDs. That said, directories for
containers are present even for containers with a stopped status. This is
because the `btrfs` storage driver mounts a default, top-level subvolume at
`/var/lib/docker/subvolumes`. All other subvolumes and snapshots exist below
that as Btrfs filesystem objects and not as individual mounts.
Because Btrfs works at the filesystem level and not the block level, each image
and container layer can be browsed in the filesystem using normal Unix
commands. The example below shows a truncated output of an `ls -l` command an
image layer:
$ ls -l /var/lib/docker/btrfs/subvolumes/0a17decee4139b0de68478f149cc16346f5e711c5ae3bb969895f22dd6723751/
total 0
drwxr-xr-x 1 root root 1372 Oct 9 08:39 bin
drwxr-xr-x 1 root root 0 Apr 10 2014 boot
drwxr-xr-x 1 root root 882 Oct 9 08:38 dev
drwxr-xr-x 1 root root 2040 Oct 12 17:27 etc
drwxr-xr-x 1 root root 0 Apr 10 2014 home
...output truncated...
## Container reads and writes with Btrfs
A container is a space-efficient snapshot of an image. Metadata in the snapshot
points to the actual data blocks in the storage pool. This is the same as with
a subvolume. Therefore, reads performed against a snapshot are essentially the
same as reads performed against a subvolume. As a result, no performance
overhead is incurred from the Btrfs driver.
Writing a new file to a container invokes an allocate-on-demand operation to
allocate new data block to the container's snapshot. The file is then written to
this new space. The allocate-on-demand operation is native to all writes with
Btrfs and is the same as writing new data to a subvolume. As a result, writing
new files to a container's snapshot operate at native Btrfs speeds.
Updating an existing file in a container causes a copy-on-write operation
(technically *redirect-on-write*). The driver leaves the original data and
allocates new space to the snapshot. The updated data is written to this new
space. Then, the driver updates the filesystem metadata in the snapshot to
point to this new data. The original data is preserved in-place for subvolumes
and snapshots further up the tree. This behavior is native to copy-on-write
filesystems like Btrfs and incurs very little overhead.
With Btfs, writing and updating lots of small files can result in slow
performance. More on this later.
## Configuring Docker with Btrfs
The `btrfs` storage driver only operates on a Docker host where
`/var/lib/docker` is mounted as a Btrfs filesystem. The following procedure
shows how to configure Btrfs on Ubuntu 14.04 LTS.
### Prerequisites
If you have already used the Docker daemon on your Docker host and have images
you want to keep, `push` them to Docker Hub or your private Docker Trusted
Registry before attempting this procedure.
Stop the Docker daemon. Then, ensure that you have a spare block device at
`/dev/xvdb`. The device identifier may be different in your environment and you
should substitute your own values throughout the procedure.
The procedure also assumes your kernel has the appropriate Btrfs modules
loaded. To verify this, use the following command:
$ cat /proc/filesystems | grep btrfs
### Configure Btrfs on Ubuntu 14.04 LTS
Assuming your system meets the prerequisites, do the following:
1. Install the "btrfs-tools" package.
$ sudo apt-get install btrfs-tools
Reading package lists... Done
Building dependency tree
<output truncated>
2. Create the Btrfs storage pool.
Btrfs storage pools are created with the `mkfs.btrfs` command. Passing
multiple devices to the `mkfs.btrfs` command creates a pool across all of those
devices. Here you create a pool with a single device at `/dev/xvdb`.
$ sudo mkfs.btrfs -f /dev/xvdb
WARNING! - Btrfs v3.12 IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using
Turning ON incompat feature 'extref': increased hardlink limit per file to 65536
fs created label (null) on /dev/xvdb
nodesize 16384 leafsize 16384 sectorsize 4096 size 4.00GiB
Btrfs v3.12
Be sure to substitute `/dev/xvdb` with the appropriate device(s) on your
system.
> **Warning**: Take note of the warning about Btrfs being experimental. As
noted earlier, Btrfs is not currently recommended for production deployments
unless you already have extensive experience.
3. If it does not already exist, create a directory for the Docker host's local
storage area at `/var/lib/docker`.
$ sudo mkdir /var/lib/docker
4. Configure the system to automatically mount the Btrfs filesystem each time the system boots.
a. Obtain the Btrfs filesystem's UUID.
$ sudo blkid /dev/xvdb
/dev/xvdb: UUID="a0ed851e-158b-4120-8416-c9b072c8cf47" UUID_SUB="c3927a64-4454-4eef-95c2-a7d44ac0cf27" TYPE="btrfs"
b. Create an `/etc/fstab` entry to automatically mount `/var/lib/docker`
each time the system boots. Either of the following lines will work, just
remember to substitute the UUID value with the value obtained from the previous
command.
/dev/xvdb /var/lib/docker btrfs defaults 0 0
UUID="a0ed851e-158b-4120-8416-c9b072c8cf47" /var/lib/docker btrfs defaults 0 0
5. Mount the new filesystem and verify the operation.
$ sudo mount -a
$ mount
/dev/xvda1 on / type ext4 (rw,discard)
<output truncated>
/dev/xvdb on /var/lib/docker type btrfs (rw)
The last line in the output above shows the `/dev/xvdb` mounted at
`/var/lib/docker` as Btrfs.
Now that you have a Btrfs filesystem mounted at `/var/lib/docker`, the daemon
should automatically load with the `btrfs` storage driver.
1. Start the Docker daemon.
$ sudo service docker start
docker start/running, process 2315
The procedure for starting the Docker daemon may differ depending on the
Linux distribution you are using.
You can force the the Docker daemon to start with the `btrfs` storage
driver by either passing the `--storage-driver=btrfs` flag to the `docker
daemon` at startup, or adding it to the `DOCKER_OPTS` line to the Docker config
file.
2. Verify the storage driver with the `docker info` command.
$ sudo docker info
Containers: 0
Images: 0
Storage Driver: btrfs
[...]
Your Docker host is now configured to use the `btrfs` storage driver.
## Btrfs and Docker performance
There are several factors that influence Docker's performance under the `btrfs`
storage driver.
- **Page caching**. Btrfs does not support page cache sharing. This means that
*n* containers accessing the same file require *n* copies to be cached. As a
result, the `btrfs` driver may not be the best choice for PaaS and other high
density container use cases.
- **Small writes**. Containers performing lots of small writes (including
Docker hosts that start and stop many containers) can lead to poor use of Btrfs
chunks. This can ultimately lead to out-of-space conditions on your Docker
host and stop it working. This is currently a major drawback to using current
versions of Btrfs.
If you use the `btrfs` storage driver, closely monitor the free space on
your Btrfs filesystem using the `btrfs filesys show` command. Do not trust the
output of normal Unix commands such as `df`; always use the Btrfs native
commands.
- **Sequential writes**. Btrfs writes data to disk via journaling technique.
This can impact sequential writes, where performance can be up to half.
- **Fragmentation**. Fragmentation is a natural byproduct of copy-on-write
filesystems like Btrfs. Many small random writes can compound this issue. It
can manifest as CPU spikes on Docker hosts using SSD media and head thrashing
on Docker hosts using spinning media. Both of these result in poor performance.
Recent versions of Btrfs allow you to specify `autodefrag` as a mount
option. This mode attempts to detect random writes and defragment them. You
should perform your own tests before enabling this option on your Docker hosts.
Some tests have shown this option has a negative performance impact on Docker
hosts performing lots of small writes (including systems that start and stop
many containers).
- **Solid State Devices (SSD)**. Btrfs has native optimizations for SSD media.
To enable these, mount with the `-o ssd` mount option. These optimizations
include enhanced SSD write performance by avoiding things like *seek
optimizations* that have no use on SSD media.
Btfs also supports the TRIM/Discard primitives. However, mounting with the
`-o discard` mount option can cause performance issues. Therefore, it is
recommended you perform your own tests before using this option.
- **Use Data Volumes**. Data volumes provide the best and most predictable
performance. This is because they bypass the storage driver and do not incur
any of the potential overheads introduced by thin provisioning and
copy-on-write. For this reason, you should place heavy write workloads on data
volumes.
## Related Information
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)

View File

@@ -0,0 +1,412 @@
<!--[metadata]>
+++
title="Device mapper storage in practice"
description="Learn how to optimize your use of device mapper driver."
keywords=["container, storage, driver, device mapper"]
[menu.main]
parent="engine_driver"
+++
<![end-metadata]-->
# Docker and the Device Mapper storage driver
Device Mapper is a kernel-based framework that underpins many advanced
volume management technologies on Linux. Docker's `devicemapper` storage driver
leverages the thin provisioning and snapshotting capabilities of this framework
for image and container management. This article refers to the Device Mapper
storage driver as `devicemapper`, and the kernel framework as `Device Mapper`.
>**Note**: The [Commercially Supported Docker Engine (CS-Engine) running on RHEL and CentOS Linux](https://www.docker.com/compatibility-maintenance) requires that you use the `devicemapper` storage driver.
## An alternative to AUFS
Docker originally ran on Ubuntu and Debian Linux and used AUFS for its storage
backend. As Docker became popular, many of the companies that wanted to use it
were using Red Hat Enterprise Linux (RHEL). Unfortunately, because the upstream
mainline Linux kernel did not include AUFS, RHEL did not use AUFS either.
To correct this Red Hat developers investigated getting AUFS into the mainline
kernel. Ultimately, though, they decided a better idea was to develop a new
storage backend. Moreover, they would base this new storage backend on existing
`Device Mapper` technology.
Red Hat collaborated with Docker Inc. to contribute this new driver. As a result
of this collaboration, Docker's Engine was re-engineered to make the storage
backend pluggable. So it was that the `devicemapper` became the second storage
driver Docker supported.
Device Mapper has been included in the mainline Linux kernel since version
2.6.9. It is a core part of RHEL family of Linux distributions. This means that
the `devicemapper` storage driver is based on stable code that has a lot of
real-world production deployments and strong community support.
## Image layering and sharing
The `devicemapper` driver stores every image and container on its own virtual
device. These devices are thin-provisioned copy-on-write snapshot devices.
Device Mapper technology works at the block level rather than the file level.
This means that `devicemapper` storage driver's thin provisioning and
copy-on-write operations work with blocks rather than entire files.
>**Note**: Snapshots are also referred to as *thin devices* or *virtual
>devices*. They all mean the same thing in the context of the `devicemapper`
>storage driver.
With `devicemapper` the high level process for creating images is as follows:
1. The `devicemapper` storage driver creates a thin pool.
The pool is created from block devices or loop mounted sparse files (more
on this later).
2. Next it creates a *base device*.
A base device is a thin device with a filesystem. You can see which
filesystem is in use by running the `docker info` command and checking the
`Backing filesystem` value.
3. Each new image (and image layer) is a snapshot of this base device.
These are thin provisioned copy-on-write snapshots. This means that they
are initially empty and only consume space from the pool when data is written
to them.
With `devicemapper`, container layers are snapshots of the image they are
created from. Just as with images, container snapshots are thin provisioned
copy-on-write snapshots. The container snapshot stores all updates to the
container. The `devicemapper` allocates space to them on-demand from the pool
as and when data is written to the container.
The high level diagram below shows a thin pool with a base device and two
images.
![](images/base_device.jpg)
If you look closely at the diagram you'll see that it's snapshots all the way
down. Each image layer is a snapshot of the layer below it. The lowest layer of
each image is a snapshot of the the base device that exists in the pool. This
base device is a `Device Mapper` artifact and not a Docker image layer.
A container is a snapshot of the image it is created from. The diagram below
shows two containers - one based on the Ubuntu image and the other based on the
Busybox image.
![](images/two_dm_container.jpg)
## Reads with the devicemapper
Let's look at how reads and writes occur using the `devicemapper` storage
driver. The diagram below shows the high level process for reading a single
block (`0x44f`) in an example container.
![](images/dm_container.jpg)
1. An application makes a read request for block `0x44f` in the container.
Because the container is a thin snapshot of an image it does not have the
data. Instead, it has a pointer (PTR) to where the data is stored in the image
snapshot lower down in the image stack.
2. The storage driver follows the pointer to block `0xf33` in the snapshot
relating to image layer `a005...`.
3. The `devicemapper` copies the contents of block `0xf33` from the image
snapshot to memory in the container.
4. The storage driver returns the data to the requesting application.
### Write examples
With the `devicemapper` driver, writing new data to a container is accomplished
by an *allocate-on-demand* operation. Updating existing data uses a
copy-on-write operation. Because Device Mapper is a block-based technology
these operations occur at the block level.
For example, when making a small change to a large file in a container, the
`devicemapper` storage driver does not copy the entire file. It only copies the
blocks to be modified. Each block is 64KB.
#### Writing new data
To write 56KB of new data to a container:
1. An application makes a request to write 56KB of new data to the container.
2. The allocate-on-demand operation allocates a single new 64KB block to the
container's snapshot.
If the write operation is larger than 64KB, multiple new blocks are
allocated to the container's snapshot.
3. The data is written to the newly allocated block.
#### Overwriting existing data
To modify existing data for the first time:
1. An application makes a request to modify some data in the container.
2. A copy-on-write operation locates the blocks that need updating.
3. The operation allocates new empty blocks to the container snapshot and
copies the data into those blocks.
4. The modified data is written into the newly allocated blocks.
The application in the container is unaware of any of these
allocate-on-demand and copy-on-write operations. However, they may add latency
to the application's read and write operations.
## Configuring Docker with Device Mapper
The `devicemapper` is the default Docker storage driver on some Linux
distributions. This includes RHEL and most of its forks. Currently, the
following distributions support the driver:
* RHEL/CentOS/Fedora
* Ubuntu 12.04
* Ubuntu 14.04
* Debian
Docker hosts running the `devicemapper` storage driver default to a
configuration mode known as `loop-lvm`. This mode uses sparse files to build
the thin pool used by image and container snapshots. The mode is designed to
work out-of-the-box with no additional configuration. However, production
deployments should not run under `loop-lvm` mode.
You can detect the mode by viewing the `docker info` command:
$ sudo docker info
Containers: 0
Images: 0
Storage Driver: devicemapper
Pool Name: docker-202:2-25220302-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: xfs
...
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.93-RHEL7 (2015-01-28)
...
The output above shows a Docker host running with the `devicemapper` storage
driver operating in `loop-lvm` mode. This is indicated by the fact that the
`Data loop file` and a `Metadata loop file` are on files under
`/var/lib/docker/devicemapper/devicemapper`. These are loopback mounted sparse
files.
### Configure direct-lvm mode for production
The preferred configuration for production deployments is `direct lvm`. This
mode uses block devices to create the thin pool. The following procedure shows
you how to configure a Docker host to use the `devicemapper` storage driver in
a `direct-lvm` configuration.
> **Caution:** If you have already run the Docker daemon on your Docker host
> and have images you want to keep, `push` them Docker Hub or your private
> Docker Trusted Registry before attempting this procedure.
The procedure below will create a 90GB data volume and 4GB metadata volume to
use as backing for the storage pool. It assumes that you have a spare block
device at `/dev/xvdf` with enough free space to complete the task. The device
identifier and volume sizes may be be different in your environment and you
should substitute your own values throughout the procedure. The procedure also
assumes that the Docker daemon is in the `stopped` state.
1. Log in to the Docker host you want to configure and stop the Docker daemon.
2. If it exists, delete your existing image store by removing the
`/var/lib/docker` directory.
$ sudo rm -rf /var/lib/docker
3. Create an LVM physical volume (PV) on your spare block device using the
`pvcreate` command.
$ sudo pvcreate /dev/xvdf
Physical volume `/dev/xvdf` successfully created
The device identifier may be different on your system. Remember to
substitute your value in the command above.
4. Create a new volume group (VG) called `vg-docker` using the PV created in
the previous step.
$ sudo vgcreate vg-docker /dev/xvdf
Volume group `vg-docker` successfully created
5. Create a new 90GB logical volume (LV) called `data` from space in the
`vg-docker` volume group.
$ sudo lvcreate -L 90G -n data vg-docker
Logical volume `data` created.
The command creates an LVM logical volume called `data` and an associated
block device file at `/dev/vg-docker/data`. In a later step, you instruct the
`devicemapper` storage driver to use this block device to store image and
container data.
If you receive a signature detection warning, make sure you are working on
the correct devices before continuing. Signature warnings indicate that the
device you're working on is currently in use by LVM or has been used by LVM in
the past.
6. Create a new logical volume (LV) called `metadata` from space in the
`vg-docker` volume group.
$ sudo lvcreate -L 4G -n metadata vg-docker
Logical volume `metadata` created.
This creates an LVM logical volume called `metadata` and an associated
block device file at `/dev/vg-docker/metadata`. In the next step you instruct
the `devicemapper` storage driver to use this block device to store image and
container metadata.
7. Start the Docker daemon with the `devicemapper` storage driver and the
`--storage-opt` flags.
The `data` and `metadata` devices that you pass to the `--storage-opt`
options were created in the previous steps.
$ sudo docker daemon --storage-driver=devicemapper --storage-opt dm.datadev=/dev/vg-docker/data --storage-opt dm.metadatadev=/dev/vg-docker/metadata &
[1] 2163
[root@ip-10-0-0-75 centos]# INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)
INFO[0027] Option DefaultDriver: bridge
INFO[0027] Option DefaultNetwork: bridge
<output truncated>
INFO[0027] Daemon has completed initialization
INFO[0027] Docker daemon commit=0a8c2e3 execdriver=native-0.2 graphdriver=devicemapper version=1.8.2
It is also possible to set the `--storage-driver` and `--storage-opt` flags
in the Docker config file and start the daemon normally using the `service` or
`systemd` commands.
8. Use the `docker info` command to verify that the daemon is using `data` and
`metadata` devices you created.
$ sudo docker info
INFO[0180] GET /v1.20/info
Containers: 0
Images: 0
Storage Driver: devicemapper
Pool Name: docker-202:1-1032-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: xfs
Data file: /dev/vg-docker/data
Metadata file: /dev/vg-docker/metadata
[...]
The output of the command above shows the storage driver as `devicemapper`.
The last two lines also confirm that the correct devices are being used for
the `Data file` and the `Metadata file`.
### Examine devicemapper structures on the host
You can use the `lsblk` command to see the device files created above and the
`pool` that the `devicemapper` storage driver creates on top of them.
$ sudo lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 8G 0 disk
└─xvda1 202:1 0 8G 0 part /
xvdf 202:80 0 10G 0 disk
├─vg--docker-data 253:0 0 90G 0 lvm
│ └─docker-202:1-1032-pool 253:2 0 10G 0 dm
└─vg--docker-metadata 253:1 0 4G 0 lvm
└─docker-202:1-1032-pool 253:2 0 10G 0 dm
The diagram below shows the image from prior examples updated with the detail
from the `lsblk` command above.
![](http://farm1.staticflickr.com/703/22116692899_0471e5e160_b.jpg)
In the diagram, the pool is named `Docker-202:1-1032-pool` and spans the `data`
and `metadata` devices created earlier. The `devicemapper` constructs the pool
name as follows:
```
Docker-MAJ:MIN-INO-pool
```
`MAJ`, `MIN` and `INO` refer to the major and minor device numbers and inode.
Because Device Mapper operates at the block level it is more difficult to see
diffs between image layers and containers. Docker 1.10 and later no longer
matches image layer IDs with directory names in `/var/lib/docker`. However,
there are two key directories. The `/var/lib/docker/devicemapper/mnt` directory
contains the mount points for image and container layers. The
`/var/lib/docker/devicemapper/metadata`directory contains one file for every
image layer and container snapshot. The files contain metadata about each
snapshot in JSON format.
## Device Mapper and Docker performance
It is important to understand the impact that allocate-on-demand and
copy-on-write operations can have on overall container performance.
### Allocate-on-demand performance impact
The `devicemapper` storage driver allocates new blocks to a container via an
allocate-on-demand operation. This means that each time an app writes to
somewhere new inside a container, one or more empty blocks has to be located
from the pool and mapped into the container.
All blocks are 64KB. A write that uses less than 64KB still results in a single
64KB block being allocated. Writing more than 64KB of data uses multiple 64KB
blocks. This can impact container performance, especially in containers that
perform lots of small writes. However, once a block is allocated to a container
subsequent reads and writes can operate directly on that block.
### Copy-on-write performance impact
Each time a container updates existing data for the first time, the
`devicemapper` storage driver has to perform a copy-on-write operation. This
copies the data from the image snapshot to the container's snapshot. This
process can have a noticeable impact on container performance.
All copy-on-write operations have a 64KB granularity. As a results, updating
32KB of a 1GB file causes the driver to copy a single 64KB block into the
container's snapshot. This has obvious performance advantages over file-level
copy-on-write operations which would require copying the entire 1GB file into
the container layer.
In practice, however, containers that perform lots of small block writes
(<64KB) can perform worse with `devicemapper` than with AUFS.
### Other device mapper performance considerations
There are several other things that impact the performance of the
`devicemapper` storage driver.
- **The mode.** The default mode for Docker running the `devicemapper` storage
driver is `loop-lvm`. This mode uses sparse files and suffers from poor
performance. It is **not recommended for production**. The recommended mode for
production environments is `direct-lvm` where the storage driver writes
directly to raw block devices.
- **High speed storage.** For best performance you should place the `Data file`
and `Metadata file` on high speed storage such as SSD. This can be direct
attached storage or from a SAN or NAS array.
- **Memory usage.** `devicemapper` is not the most memory efficient Docker
storage driver. Launching *n* copies of the same container loads *n* copies of
its files into memory. This can have a memory impact on your Docker host. As a
result, the `devicemapper` storage driver may not be the best choice for PaaS
and other high density use cases.
One final point, data volumes provide the best and most predictable
performance. This is because they bypass the storage driver and do not incur
any of the potential overheads introduced by thin provisioning and
copy-on-write. For this reason, you should to place heavy write workloads on
data volumes.
## Related Information
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,495 @@
<!--[metadata]>
+++
title = "Understand images, containers, and storage drivers"
description = "Learn the technologies that support storage drivers."
keywords = ["container, storage, driver, AUFS, btfs, devicemapper,zvfs"]
[menu.main]
parent = "engine_driver"
weight = -2
+++
<![end-metadata]-->
# Understand images, containers, and storage drivers
To use storage drivers effectively, you must understand how Docker builds and
stores images. Then, you need an understanding of how these images are used by
containers. Finally, you'll need a short introduction to the technologies that
enable both images and container operations.
## Images and layers
Each Docker image references a list of read-only layers that represent
filesystem differences. Layers are stacked on top of each other to form a base
for a container's root filesystem. The diagram below shows the Ubuntu 15.04
image comprising 4 stacked image layers.
![](images/image-layers.jpg)
The Docker storage driver is responsible for stacking these layers and
providing a single unified view.
When you create a new container, you add a new, thin, writable layer on top of
the underlying stack. This layer is often called the "container layer". All
changes made to the running container - such as writing new files, modifying
existing files, and deleting files - are written to this thin writable
container layer. The diagram below shows a container based on the Ubuntu 15.04
image.
![](images/container-layers.jpg)
### Content addressable storage
Docker 1.10 introduced a new content addressable storage model. This is a
completely new way to address image and layer data on disk. Previously, image
and layer data was referenced and stored using a a randomly generated UUID. In
the new model this is replaced by a secure *content hash*.
The new model improves security, provides a built-in way to avoid ID
collisions, and guarantees data integrity after pull, push, load, and save
operations. It also enables better sharing of layers by allowing many images to
freely share their layers even if they didnt come from the same build.
The diagram below shows an updated version of the previous diagram,
highlighting the changes implemented by Docker 1.10.
![](images/container-layers-cas.jpg)
As can be seen, all image layer IDs are cryptographic hashes, whereas the
container ID is still a randomly generated UUID.
There are several things to note regarding the new model. These include:
1. Migration of existing images
2. Image and layer filesystem structures
Existing images, those created and pulled by earlier versions of Docker, need
to be migrated before they can be used with the new model. This migration
involves calculating new secure checksums and is performed automatically the
first time you start an updated Docker daemon. After the migration is complete,
all images and tags will have brand new secure IDs.
Although the migration is automatic and transparent, it is computationally
intensive. This means it and can take time if you have lots of image data.
During this time your Docker daemon will not respond to other requests.
A migration tool exists that allows you to migrate existing images to the new
format before upgrading your Docker daemon. This means that upgraded Docker
daemons do not need to perform the migration in-band, and therefore avoids any
associated downtime. It also provides a way to manually migrate existing images
so that they can be distributed to other Docker daemons in your environment
that are already running the latest versions of Docker.
The migration tool is provided by Docker, Inc., and runs as a container. You
can download it from [https://github.com/docker/v1.10-migrator/releases](https://github.com/docker/v1.10-migrator/releases).
While running the "migrator" image you need to expose your Docker host's data
directory to the container. If you are using the default Docker data path, the
command to run the container will look like this
$ sudo docker run --rm -v /var/lib/docker:/var/lib/docker docker/v1.10-migrator
If you use the `devicemapper` storage driver, you will need to include the
`--privileged` option so that the container has access to your storage devices.
#### Migration example
The following example shows the migration tool in use on a Docker host running
version 1.9.1 of the Docker daemon and the AUFS storage driver. The Docker host
is running on a **t2.micro** AWS EC2 instance with 1 vCPU, 1GB RAM, and a
single 8GB general purpose SSD EBS volume. The Docker data directory
(`/var/lib/docker`) was consuming 2GB of space.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jenkins latest 285c9f0f9d3d 17 hours ago 708.5 MB
mysql latest d39c3fa09ced 8 days ago 360.3 MB
mongo latest a74137af4532 13 days ago 317.4 MB
postgres latest 9aae83d4127f 13 days ago 270.7 MB
redis latest 8bccd73928d9 2 weeks ago 151.3 MB
centos latest c8a648134623 4 weeks ago 196.6 MB
ubuntu 15.04 c8be1ac8145a 7 weeks ago 131.3 MB
$ du -hs /var/lib/docker
2.0G /var/lib/docker
$ time docker run --rm -v /var/lib/docker:/var/lib/docker docker/v1.10-migrator
Unable to find image 'docker/v1.10-migrator:latest' locally
latest: Pulling from docker/v1.10-migrator
ed1f33c5883d: Pull complete
b3ca410aa2c1: Pull complete
2b9c6ed9099e: Pull complete
dce7e318b173: Pull complete
Digest: sha256:bd2b245d5d22dd94ec4a8417a9b81bb5e90b171031c6e216484db3fe300c2097
Status: Downloaded newer image for docker/v1.10-migrator:latest
time="2016-01-27T12:31:06Z" level=debug msg="Assembling tar data for 01e70da302a553ba13485ad020a0d77dbb47575a31c4f48221137bb08f45878d from /var/lib/docker/aufs/diff/01e70da302a553ba13485ad020a0d77dbb47575a31c4f48221137bb08f45878d"
time="2016-01-27T12:31:06Z" level=debug msg="Assembling tar data for 07ac220aeeef9febf1ac16a9d1a4eff7ef3c8cbf5ed0be6b6f4c35952ed7920d from /var/lib/docker/aufs/diff/07ac220aeeef9febf1ac16a9d1a4eff7ef3c8cbf5ed0be6b6f4c35952ed7920d"
<snip>
time="2016-01-27T12:32:00Z" level=debug msg="layer dbacfa057b30b1feaf15937c28bd8ca0d6c634fc311ccc35bd8d56d017595d5b took 10.80 seconds"
real 0m59.583s
user 0m0.046s
sys 0m0.008s
The Unix `time` command prepends the `docker run` command to produce timings
for the operation. As can be seen, the overall time taken to migrate 7 images
comprising 2GB of disk space took approximately 1 minute. However, this
included the time taken to pull the `docker/v1.10-migrator` image
(approximately 3.5 seconds). The same operation on an m4.10xlarge EC2 instance
with 40 vCPUs, 160GB RAM and an 8GB provisioned IOPS EBS volume resulted in the
following improved timings:
real 0m9.871s
user 0m0.094s
sys 0m0.021s
This shows that the migration operation is affected by the hardware spec of the
machine performing the migration.
## Container and layers
The major difference between a container and an image is the top writable
layer. All writes to the container that add new or modify existing data are
stored in this writable layer. When the container is deleted the writable layer
is also deleted. The underlying image remains unchanged.
Because each container has its own thin writable container layer, and all
changes are stored this container layer, this means that multiple containers
can share access to the same underlying image and yet have their own data
state. The diagram below shows multiple containers sharing the same Ubuntu
15.04 image.
![](images/sharing-layers.jpg)
The Docker storage driver is responsible for enabling and managing both the
image layers and the writable container layer. How a storage driver
accomplishes these can vary between drivers. Two key technologies behind Docker
image and container management are stackable image layers and copy-on-write
(CoW).
## The copy-on-write strategy
Sharing is a good way to optimize resources. People do this instinctively in
daily life. For example, twins Jane and Joseph taking an Algebra class at
different times from different teachers can share the same exercise book by
passing it between each other. Now, suppose Jane gets an assignment to complete
the homework on page 11 in the book. At that point, Jane copies page 11,
completes the homework, and hands in her copy. The original exercise book is
unchanged and only Jane has a copy of the changed page 11.
Copy-on-write is a similar strategy of sharing and copying. In this strategy,
system processes that need the same data share the same instance of that data
rather than having their own copy. At some point, if one process needs to
modify or write to the data, only then does the operating system make a copy of
the data for that process to use. Only the process that needs to write has
access to the data copy. All the other processes continue to use the original
data.
Docker uses a copy-on-write technology with both images and containers. This
CoW strategy optimizes both image disk space usage and the performance of
container start times. The next sections look at how copy-on-write is leveraged
with images and containers through sharing and copying.
### Sharing promotes smaller images
This section looks at image layers and copy-on-write technology. All image and
container layers exist inside the Docker host's *local storage area* and are
managed by the storage driver. On Linux-based Docker hosts this is usually
located under `/var/lib/docker/`.
The Docker client reports on image layers when instructed to pull and push
images with `docker pull` and `docker push`. The command below pulls the
`ubuntu:15.04` Docker image from Docker Hub.
$ docker pull ubuntu:15.04
15.04: Pulling from library/ubuntu
1ba8ac955b97: Pull complete
f157c4e5ede7: Pull complete
0b7e98f84c4c: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:5e279a9df07990286cce22e1b0f5b0490629ca6d187698746ae5e28e604a640e
Status: Downloaded newer image for ubuntu:15.04
From the output, you'll see that the command actually pulls 4 image layers.
Each of the above lines lists an image layer and its UUID or cryptographic
hash. The combination of these four layers makes up the `ubuntu:15.04` Docker
image.
Each of these layers is stored in its own directory inside the Docker host's
local storage are.
Versions of Docker prior to 1.10 stored each layer in a directory with the same
name as the image layer ID. However, this is not the case for images pulled
with Docker version 1.10 and later. For example, the command below shows an
image being pulled from Docker Hub, followed by a directory listing on a host
running version 1.9.1 of the Docker Engine.
$ docker pull ubuntu:15.04
15.04: Pulling from library/ubuntu
47984b517ca9: Pull complete
df6e891a3ea9: Pull complete
e65155041eed: Pull complete
c8be1ac8145a: Pull complete
Digest: sha256:5e279a9df07990286cce22e1b0f5b0490629ca6d187698746ae5e28e604a640e
Status: Downloaded newer image for ubuntu:15.04
$ ls /var/lib/docker/aufs/layers
47984b517ca9ca0312aced5c9698753ffa964c2015f2a5f18e5efa9848cf30e2
c8be1ac8145a6e59a55667f573883749ad66eaeef92b4df17e5ea1260e2d7356
df6e891a3ea9cdce2a388a2cf1b1711629557454fd120abd5be6d32329a0e0ac
e65155041eed7ec58dea78d90286048055ca75d41ea893c7246e794389ecf203
Notice how the four directories match up with the layer IDs of the downloaded
image. Now compare this with the same operations performed on a host running
version 1.10 of the Docker Engine.
$ docker pull ubuntu:15.04
15.04: Pulling from library/ubuntu
1ba8ac955b97: Pull complete
f157c4e5ede7: Pull complete
0b7e98f84c4c: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:5e279a9df07990286cce22e1b0f5b0490629ca6d187698746ae5e28e604a640e
Status: Downloaded newer image for ubuntu:15.04
$ ls /var/lib/docker/aufs/layers/
1d6674ff835b10f76e354806e16b950f91a191d3b471236609ab13a930275e24
5dbb0cbe0148cf447b9464a358c1587be586058d9a4c9ce079320265e2bb94e7
bef7199f2ed8e86fa4ada1309cfad3089e0542fec8894690529e4c04a7ca2d73
ebf814eccfe98f2704660ca1d844e4348db3b5ccc637eb905d4818fbfb00a06a
See how the four directories do not match up with the image layer IDs pulled in
the previous step.
Despite the differences between image management before and after version 1.10,
all versions of Docker still allow images to share layers. For example, If you
`pull` an image that shares some of the same image layers as an image that has
already been pulled, the Docker daemon recognizes this, and only pulls the
layers it doesn't already have stored locally. After the second pull, the two
images will share any common image layers.
You can illustrate this now for yourself. Starting with the `ubuntu:15.04`
image that you just pulled, make a change to it, and build a new image based on
the change. One way to do this is using a `Dockerfile` and the `docker build`
command.
1. In an empty directory, create a simple `Dockerfile` that starts with the
2. ubuntu:15.04 image.
FROM ubuntu:15.04
2. Add a new file called "newfile" in the image's `/tmp` directory with the
3. text "Hello world" in it.
When you are done, the `Dockerfile` contains two lines:
FROM ubuntu:15.04
RUN echo "Hello world" > /tmp/newfile
3. Save and close the file.
4. From a terminal in the same folder as your `Dockerfile`, run the following
5. command:
$ docker build -t changed-ubuntu .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:15.04
---> 3f7bcee56709
Step 2 : RUN echo "Hello world" > /tmp/newfile
---> Running in d14acd6fad4e
---> 94e6b7d2c720
Removing intermediate container d14acd6fad4e
Successfully built 94e6b7d2c720
> **Note:** The period (.) at the end of the above command is important. It
> tells the `docker build` command to use the current working directory as
> its build context.
The output above shows a new image with image ID `94e6b7d2c720`.
5. Run the `docker images` command to verify the new `changed-ubuntu` image is
6. in the Docker host's local storage area.
REPOSITORY TAG IMAGE ID CREATED SIZE
changed-ubuntu latest 03b964f68d06 33 seconds ago 131.4 MB
ubuntu 15.04 013f3d01d247 6 weeks ago 131.3 MB
6. Run the `docker history` command to see which image layers were used to
7. create the new `changed-ubuntu` image.
$ docker history changed-ubuntu
IMAGE CREATED CREATED BY SIZE COMMENT
94e6b7d2c720 2 minutes ago /bin/sh -c echo "Hello world" > /tmp/newfile 12 B
3f7bcee56709 6 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
<missing> 6 weeks ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.879 kB
<missing> 6 weeks ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 701 B
<missing> 6 weeks ago /bin/sh -c #(nop) ADD file:8e4943cd86e9b2ca13 131.3 MB
The `docker history` output shows the new `94e6b7d2c720` image layer at the
top. You know that this is the new image layer added because it was created
by the `echo "Hello world" > /tmp/newfile` command in your `Dockerfile`.
The 4 image layers below it are the exact same image layers
that make up the `ubuntu:15.04` image.
> **Note:** Under the content addressable storage model introduced with Docker
> 1.10, image history data is no longer stored in a config file with each image
> layer. It is now stored as a string of text in a single config file that
> relates to the overall image. This can result in some image layers showing as
> "missing" in the output of the `docker history` command. This is normal
> behaviour and can be ignored.
>
> You may hear images like these referred to as *flat images*.
Notice the new `changed-ubuntu` image does not have its own copies of every
layer. As can be seen in the diagram below, the new image is sharing its four
underlying layers with the `ubuntu:15.04` image.
![](images/saving-space.jpg)
The `docker history` command also shows the size of each image layer. As you
can see, the `94e6b7d2c720` layer is only consuming 12 Bytes of disk space.
This means that the `changed-ubuntu` image we just created is only consuming an
additional 12 Bytes of disk space on the Docker host - all layers below the
`94e6b7d2c720` layer already exist on the Docker host and are shared by other
images.
This sharing of image layers is what makes Docker images and containers so
space efficient.
### Copying makes containers efficient
You learned earlier that a container is a Docker image with a thin writable,
container layer added. The diagram below shows the layers of a container based
on the `ubuntu:15.04` image:
![](images/container-layers-cas.jpg)
All writes made to a container are stored in the thin writable container layer.
The other layers are read-only (RO) image layers and can't be changed. This
means that multiple containers can safely share a single underlying image. The
diagram below shows multiple containers sharing a single copy of the
`ubuntu:15.04` image. Each container has its own thin RW layer, but they all
share a single instance of the ubuntu:15.04 image:
![](images/sharing-layers.jpg)
When an existing file in a container is modified, Docker uses the storage
driver to perform a copy-on-write operation. The specifics of operation depends
on the storage driver. For the AUFS and OverlayFS storage drivers, the
copy-on-write operation is pretty much as follows:
* Search through the image layers for the file to update. The process starts
at the top, newest layer and works down to the base layer one layer at a
time.
* Perform a "copy-up" operation on the first copy of the file that is found. A
"copy up" copies the file up to the container's own thin writable layer.
* Modify the *copy of the file* in container's thin writable layer.
Btrfs, ZFS, and other drivers handle the copy-on-write differently. You can
read more about the methods of these drivers later in their detailed
descriptions.
Containers that write a lot of data will consume more space than containers
that do not. This is because most write operations consume new space in the
container's thin writable top layer. If your container needs to write a lot of
data, you should consider using a data volume.
A copy-up operation can incur a noticeable performance overhead. This overhead
is different depending on which storage driver is in use. However, large files,
lots of layers, and deep directory trees can make the impact more noticeable.
Fortunately, the operation only occurs the first time any particular file is
modified. Subsequent modifications to the same file do not cause a copy-up
operation and can operate directly on the file's existing copy already present
in the container layer.
Let's see what happens if we spin up 5 containers based on our `changed-ubuntu`
image we built earlier:
1. From a terminal on your Docker host, run the following `docker run` command
5 times.
$ docker run -dit changed-ubuntu bash
75bab0d54f3cf193cfdc3a86483466363f442fba30859f7dcd1b816b6ede82d4
$ docker run -dit changed-ubuntu bash
9280e777d109e2eb4b13ab211553516124a3d4d4280a0edfc7abf75c59024d47
$ docker run -dit changed-ubuntu bash
a651680bd6c2ef64902e154eeb8a064b85c9abf08ac46f922ad8dfc11bb5cd8a
$ docker run -dit changed-ubuntu bash
8eb24b3b2d246f225b24f2fca39625aaad71689c392a7b552b78baf264647373
$ docker run -dit changed-ubuntu bash
0ad25d06bdf6fca0dedc38301b2aff7478b3e1ce3d1acd676573bba57cb1cfef
This launches 5 containers based on the `changed-ubuntu` image. As each
container is created, Docker adds a writable layer and assigns it a random
UUID. This is the value returned from the `docker run` command.
2. Run the `docker ps` command to verify the 5 containers are running.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ad25d06bdf6 changed-ubuntu "bash" About a minute ago Up About a minute stoic_ptolemy
8eb24b3b2d24 changed-ubuntu "bash" About a minute ago Up About a minute pensive_bartik
a651680bd6c2 changed-ubuntu "bash" 2 minutes ago Up 2 minutes hopeful_turing
9280e777d109 changed-ubuntu "bash" 2 minutes ago Up 2 minutes backstabbing_mahavira
75bab0d54f3c changed-ubuntu "bash" 2 minutes ago Up 2 minutes boring_pasteur
The output above shows 5 running containers, all sharing the
`changed-ubuntu` image. Each `CONTAINER ID` is derived from the UUID when
creating each container.
3. List the contents of the local storage area.
$ sudo ls /var/lib/docker/containers
0ad25d06bdf6fca0dedc38301b2aff7478b3e1ce3d1acd676573bba57cb1cfef
9280e777d109e2eb4b13ab211553516124a3d4d4280a0edfc7abf75c59024d47
75bab0d54f3cf193cfdc3a86483466363f442fba30859f7dcd1b816b6ede82d4
a651680bd6c2ef64902e154eeb8a064b85c9abf08ac46f922ad8dfc11bb5cd8a
8eb24b3b2d246f225b24f2fca39625aaad71689c392a7b552b78baf264647373
Docker's copy-on-write strategy not only reduces the amount of space consumed
by containers, it also reduces the time required to start a container. At start
time, Docker only has to create the thin writable layer for each container.
The diagram below shows these 5 containers sharing a single read-only (RO)
copy of the `changed-ubuntu` image.
![](images/shared-uuid.jpg)
If Docker had to make an entire copy of the underlying image stack each time it
started a new container, container start times and disk space used would be
significantly increased.
## Data volumes and the storage driver
When a container is deleted, any data written to the container that is not
stored in a *data volume* is deleted along with the container.
A data volume is a directory or file in the Docker host's filesystem that is
mounted directly into a container. Data volumes are not controlled by the
storage driver. Reads and writes to data volumes bypass the storage driver and
operate at native host speeds. You can mount any number of data volumes into a
container. Multiple containers can also share one or more data volumes.
The diagram below shows a single Docker host running two containers. Each
container exists inside of its own address space within the Docker host's local
storage area (`/var/lib/docker/...`). There is also a single shared data
volume located at `/data` on the Docker host. This is mounted directly into
both containers.
![](images/shared-volume.jpg)
Data volumes reside outside of the local storage area on the Docker host,
further reinforcing their independence from the storage driver's control. When
a container is deleted, any data stored in data volumes persists on the Docker
host.
For detailed information about data volumes
[Managing data in containers](https://docs.docker.com/userguide/dockervolumes/).
## Related information
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)

View File

@@ -0,0 +1,38 @@
<!--[metadata]>
+++
title = "Docker storage drivers"
description = "Learn how select the proper storage driver for your container."
keywords = ["container, storage, driver, AUFS, btfs, devicemapper,zvfs"]
[menu.main]
identifier = "engine_driver"
parent = "engine_guide"
weight = 7
+++
<![end-metadata]-->
# Docker storage drivers
Docker relies on driver technology to manage the storage and interactions associated with images and the containers that run them. This section contains the following pages:
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [Select a storage driver](selectadriver.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)
* [OverlayFS in practice](overlayfs-driver.md)
* [ZFS storage in practice](zfs-driver.md)
If you are new to Docker containers make sure you read ["Understand images, containers, and storage drivers"](imagesandcontainers.md) first. It explains key concepts and technologies that can help you when working with storage drivers.
### Acknowledgement
The Docker storage driver material was created in large part by our guest author
Nigel Poulton with a bit of help from Docker's own Jérôme Petazzoni. In his
spare time Nigel creates [IT training
videos](http://www.pluralsight.com/author/nigel-poulton), co-hosts the weekly
[In Tech We Trust podcast](http://intechwetrustpodcast.com/), and lives it large
on [Twitter](https://twitter.com/nigelpoulton).
&nbsp;

View File

@@ -0,0 +1,299 @@
<!--[metadata]>
+++
title = "OverlayFS storage in practice"
description = "Learn how to optimize your use of OverlayFS driver."
keywords = ["container, storage, driver, OverlayFS "]
[menu.main]
parent = "engine_driver"
+++
<![end-metadata]-->
# Docker and OverlayFS in practice
OverlayFS is a modern *union filesystem* that is similar to AUFS. In comparison
to AUFS, OverlayFS:
* has a simpler design
* has been in the mainline Linux kernel since version 3.18
* is potentially faster
As a result, OverlayFS is rapidly gaining popularity in the Docker community
and is seen by many as a natural successor to AUFS. As promising as OverlayFS
is, it is still relatively young. Therefore caution should be taken before
using it in production Docker environments.
Docker's `overlay` storage driver leverages several OverlayFS features to build
and manage the on-disk structures of images and containers.
>**Note**: Since it was merged into the mainline kernel, the OverlayFS *kernel
>module* was renamed from "overlayfs" to "overlay". As a result you may see the
> two terms used interchangeably in some documentation. However, this document
> uses "OverlayFS" to refer to the overall filesystem, and `overlay` to refer
> to Docker's storage-driver.
## Image layering and sharing with OverlayFS
OverlayFS takes two directories on a single Linux host, layers one on top of
the other, and provides a single unified view. These directories are often
referred to as *layers* and the technology used to layer them is known as a
*union mount*. The OverlayFS terminology is "lowerdir" for the bottom layer and
"upperdir" for the top layer. The unified view is exposed through its own
directory called "merged".
The diagram below shows how a Docker image and a Docker container are layered.
The image layer is the "lowerdir" and the container layer is the "upperdir".
The unified view is exposed through a directory called "merged" which is
effectively the containers mount point. The diagram shows how Docker constructs
map to OverlayFS constructs.
![](images/overlay_constructs.jpg)
Notice how the image layer and container layer can contain the same files. When
this happens, the files in the container layer ("upperdir") are dominant and
obscure the existence of the same files in the image layer ("lowerdir"). The
container mount ("merged") presents the unified view.
OverlayFS only works with two layers. This means that multi-layered images
cannot be implemented as multiple OverlayFS layers. Instead, each image layer
is implemented as its own directory under `/var/lib/docker/overlay`.
Hard links are then used as a space-efficient way to reference data shared with
lower layers. As of Docker 1.10, image layer IDs no longer correspond to
directory names in `/var/lib/docker/`
To create a container, the `overlay` driver combines the directory representing
the image's top layer plus a new directory for the container. The image's top
layer is the "lowerdir" in the overlay and read-only. The new directory for the
container is the "upperdir" and is writable.
## Example: Image and container on-disk constructs
The following `docker pull` command shows a Docker host with downloading a
Docker image comprising four layers.
$ sudo docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
8387d9ff0016: Pull complete
3b52deaaf0ed: Pull complete
4bd501fad6de: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:457b05828bdb5dcc044d93d042863fba3f2158ae249a6db5ae3934307c757c54
Status: Downloaded newer image for ubuntu:latest
Each image layer has it's own directory under `/var/lib/docker/overlay/`. This
is where the the contents of each image layer are stored.
The output of the command below shows the four directories that store the
contents of each image layer just pulled. However, as can be seen, the image
layer IDs do not match the directory names in `/var/lib/docker/overlay`. This
is normal behavior in Docker 1.10 and later.
$ ls -l /var/lib/docker/overlay/
total 24
drwx------ 3 root root 4096 Oct 28 11:02 1d073211c498fd5022699b46a936b4e4bdacb04f637ad64d3475f558783f5c3e
drwx------ 3 root root 4096 Oct 28 11:02 5a4526e952f0aa24f3fcc1b6971f7744eb5465d572a48d47c492cb6bbf9cbcda
drwx------ 5 root root 4096 Oct 28 11:06 99fcaefe76ef1aa4077b90a413af57fd17d19dce4e50d7964a273aae67055235
drwx------ 3 root root 4096 Oct 28 11:01 c63fb41c2213f511f12f294dd729b9903a64d88f098c20d2350905ac1fdbcbba
The image layer directories contain the files unique to that layer as well as
hard links to the data that is shared with lower layers. This allows for
efficient use of disk space.
Containers also exist on-disk in the Docker host's filesystem under
`/var/lib/docker/overlay/`. If you inspect the directory relating to a running
container using the `ls -l` command, you find the following file and
directories.
$ ls -l /var/lib/docker/overlay/<directory-of-running-container>
total 16
-rw-r--r-- 1 root root 64 Oct 28 11:06 lower-id
drwxr-xr-x 1 root root 4096 Oct 28 11:06 merged
drwxr-xr-x 4 root root 4096 Oct 28 11:06 upper
drwx------ 3 root root 4096 Oct 28 11:06 work
These four filesystem objects are all artefacts of OverlayFS. The "lower-id"
file contains the ID of the top layer of the image the container is based on.
This is used by OverlayFS as the "lowerdir".
$ cat /var/lib/docker/overlay/73de7176c223a6c82fd46c48c5f152f2c8a7e49ecb795a7197c3bb795c4d879e/lower-id
1d073211c498fd5022699b46a936b4e4bdacb04f637ad64d3475f558783f5c3e
The "upper" directory is the containers read-write layer. Any changes made to
the container are written to this directory.
The "merged" directory is effectively the containers mount point. This is where
the unified view of the image ("lowerdir") and container ("upperdir") is
exposed. Any changes written to the container are immediately reflected in this
directory.
The "work" directory is required for OverlayFS to function. It is used for
things such as *copy_up* operations.
You can verify all of these constructs from the output of the `mount` command.
(Ellipses and line breaks are used in the output below to enhance readability.)
$ mount | grep overlay
overlay on /var/lib/docker/overlay/73de7176c223.../merged
type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay/1d073211c498.../root,
upperdir=/var/lib/docker/overlay/73de7176c223.../upper,
workdir=/var/lib/docker/overlay/73de7176c223.../work)
The output reflects that the overlay is mounted as read-write ("rw").
## Container reads and writes with overlay
Consider three scenarios where a container opens a file for read access with
overlay.
- **The file does not exist in the container layer**. If a container opens a
file for read access and the file does not already exist in the container
("upperdir") it is read from the image ("lowerdir"). This should incur very
little performance overhead.
- **The file only exists in the container layer**. If a container opens a file
for read access and the file exists in the container ("upperdir") and not in
the image ("lowerdir"), it is read directly from the container.
- **The file exists in the container layer and the image layer**. If a
container opens a file for read access and the file exists in the image layer
and the container layer, the file's version in the container layer is read.
This is because files in the container layer ("upperdir") obscure files with
the same name in the image layer ("lowerdir").
Consider some scenarios where files in a container are modified.
- **Writing to a file for the first time**. The first time a container writes
to an existing file, that file does not exist in the container ("upperdir").
The `overlay` driver performs a *copy_up* operation to copy the file from the
image ("lowerdir") to the container ("upperdir"). The container then writes the
changes to the new copy of the file in the container layer.
However, OverlayFS works at the file level not the block level. This means
that all OverlayFS copy-up operations copy entire files, even if the file is
very large and only a small part of it is being modified. This can have a
noticeable impact on container write performance. However, two things are
worth noting:
* The copy_up operation only occurs the first time any given file is
written to. Subsequent writes to the same file will operate against the copy of
the file already copied up to the container.
* OverlayFS only works with two layers. This means that performance should
be better than AUFS which can suffer noticeable latencies when searching for
files in images with many layers.
- **Deleting files and directories**. When files are deleted within a container
a *whiteout* file is created in the containers "upperdir". The version of the
file in the image layer ("lowerdir") is not deleted. However, the whiteout file
in the container obscures it.
Deleting a directory in a container results in *opaque directory* being
created in the "upperdir". This has the same effect as a whiteout file and
effectively masks the existence of the directory in the image's "lowerdir".
## Configure Docker with the overlay storage driver
To configure Docker to use the overlay storage driver your Docker host must be
running version 3.18 of the Linux kernel (preferably newer) with the overlay
kernel module loaded. OverlayFS can operate on top of most supported Linux
filesystems. However, ext4 is currently recommended for use in production
environments.
The following procedure shows you how to configure your Docker host to use
OverlayFS. The procedure assumes that the Docker daemon is in a stopped state.
> **Caution:** If you have already run the Docker daemon on your Docker host
> and have images you want to keep, `push` them Docker Hub or your private
> Docker Trusted Registry before attempting this procedure.
1. If it is running, stop the Docker `daemon`.
2. Verify your kernel version and that the overlay kernel module is loaded.
$ uname -r
3.19.0-21-generic
$ lsmod | grep overlay
overlay
3. Start the Docker daemon with the `overlay` storage driver.
$ docker daemon --storage-driver=overlay &
[1] 29403
root@ip-10-0-0-174:/home/ubuntu# INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)
INFO[0000] Option DefaultDriver: bridge
INFO[0000] Option DefaultNetwork: bridge
<output truncated>
Alternatively, you can force the Docker daemon to automatically start with
the `overlay` driver by editing the Docker config file and adding the
`--storage-driver=overlay` flag to the `DOCKER_OPTS` line. Once this option
is set you can start the daemon using normal startup scripts without having
to manually pass in the `--storage-driver` flag.
4. Verify that the daemon is using the `overlay` storage driver
$ docker info
Containers: 0
Images: 0
Storage Driver: overlay
Backing Filesystem: extfs
<output truncated>
Notice that the *Backing filesystem* in the output above is showing as
`extfs`. Multiple backing filesystems are supported but `extfs` (ext4) is
recommended for production use cases.
Your Docker host is now using the `overlay` storage driver. If you run the
`mount` command, you'll find Docker has automatically created the `overlay`
mount with the required "lowerdir", "upperdir", "merged" and "workdir"
constructs.
## OverlayFS and Docker Performance
As a general rule, the `overlay` driver should be fast. Almost certainly faster
than `aufs` and `devicemapper`. In certain circumstances it may also be faster
than `btrfs`. That said, there are a few things to be aware of relative to the
performance of Docker using the `overlay` storage driver.
- **Page Caching**. OverlayFS supports page cache sharing. This means multiple
containers accessing the same file can share a single page cache entry (or
entries). This makes the `overlay` driver efficient with memory and a good
option for PaaS and other high density use cases.
- **copy_up**. As with AUFS, OverlayFS has to perform copy-up operations any
time a container writes to a file for the first time. This can insert latency
into the write operation &mdash; especially if the file being copied up is
large. However, once the file has been copied up, all subsequent writes to that
file occur without the need for further copy-up operations.
The OverlayFS copy_up operation should be faster than the same operation
with AUFS. This is because AUFS supports more layers than OverlayFS and it is
possible to incur far larger latencies if searching through many AUFS layers.
- **RPMs and Yum**. OverlayFS only implements a subset of the POSIX standards.
This can result in certain OverlayFS operations breaking POSIX standards. One
such operation is the *copy-up* operation. Therefore, using `yum` inside of a
container on a Docker host using the `overlay` storage driver is unlikely to
work without implementing workarounds.
- **Inode limits**. Use of the `overlay` storage driver can cause excessive
inode consumption. This is especially so as the number of images and containers
on the Docker host grows. A Docker host with a large number of images and lots
of started and stopped containers can quickly run out of inodes.
Unfortunately you can only specify the number of inodes in a filesystem at the
time of creation. For this reason, you may wish to consider putting
`/var/lib/docker` on a separate device with its own filesystem, or manually
specifying the number of inodes when creating the filesystem.
The following generic performance best practices also apply to OverlayFS.
- **Solid State Devices (SSD)**. For best performance it is always a good idea
to use fast storage media such as solid state devices (SSD).
- **Use Data Volumes**. Data volumes provide the best and most predictable
performance. This is because they bypass the storage driver and do not incur
any of the potential overheads introduced by thin provisioning and
copy-on-write. For this reason, you should place heavy write workloads on data
volumes.

View File

@@ -0,0 +1,206 @@
<!--[metadata]>
+++
title = "Select a storage driver"
description = "Learn how select the proper storage driver for your container."
keywords = ["container, storage, driver, AUFS, btfs, devicemapper,zvfs"]
[menu.main]
parent = "engine_driver"
weight = -1
+++
<![end-metadata]-->
# Select a storage driver
This page describes Docker's storage driver feature. It lists the storage
driver's that Docker supports and the basic commands associated with managing
them. Finally, this page provides guidance on choosing a storage driver.
The material on this page is intended for readers who already have an
[understanding of the storage driver technology](imagesandcontainers.md).
## A pluggable storage driver architecture
Docker has a pluggable storage driver architecture. This gives you the
flexibility to "plug in" the storage driver that is best for your environment
and use-case. Each Docker storage driver is based on a Linux filesystem or
volume manager. Further, each storage driver is free to implement the
management of image layers and the container layer in its own unique way. This
means some storage drivers perform better than others in different
circumstances.
Once you decide which driver is best, you set this driver on the Docker daemon
at start time. As a result, the Docker daemon can only run one storage driver,
and all containers created by that daemon instance use the same storage driver.
The table below shows the supported storage driver technologies and their
driver names:
|Technology |Storage driver name |
|--------------|---------------------|
|OverlayFS |`overlay` |
|AUFS |`aufs` |
|Btrfs |`btrfs` |
|Device Mapper |`devicemapper` |
|VFS* |`vfs` |
|ZFS |`zfs` |
To find out which storage driver is set on the daemon , you use the
`docker info` command:
$ docker info
Containers: 0
Images: 0
Storage Driver: overlay
Backing Filesystem: extfs
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.19.0-15-generic
Operating System: Ubuntu 15.04
... output truncated ...
The `info` subcommand reveals that the Docker daemon is using the `overlay`
storage driver with a `Backing Filesystem` value of `extfs`. The `extfs` value
means that the `overlay` storage driver is operating on top of an existing
(ext) filesystem. The backing filesystem refers to the filesystem that was used
to create the Docker host's local storage area under `/var/lib/docker`.
Which storage driver you use, in part, depends on the backing filesystem you
plan to use for your Docker host's local storage area. Some storage drivers can
operate on top of different backing filesystems. However, other storage
drivers require the backing filesystem to be the same as the storage driver.
For example, the `btrfs` storage driver on a Btrfs backing filesystem. The
following table lists each storage driver and whether it must match the host's
backing file system:
|Storage driver |Must match backing filesystem |Incompatible with |
|---------------|------------------------------|--------------------|
|`overlay` |No |`btrfs` `aufs` `zfs`|
|`aufs` |No |`btrfs` `aufs` |
|`btrfs` |Yes | N/A |
|`devicemapper` |No | N/A |
|`vfs` |No | N/A |
|`zfs` |Yes | N/A |
> **Note**
> Incompatible with means some storage drivers can not run over certain backing
> filesystem.
You can set the storage driver by passing the `--storage-driver=<name>` option
to the `docker daemon` command line, or by setting the option on the
`DOCKER_OPTS` line in the `/etc/default/docker` file.
The following command shows how to start the Docker daemon with the
`devicemapper` storage driver using the `docker daemon` command:
$ docker daemon --storage-driver=devicemapper &
$ docker info
Containers: 0
Images: 0
Storage Driver: devicemapper
Pool Name: docker-252:0-147544-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: extfs
Data file: /dev/loop0
Metadata file: /dev/loop1
Data Space Used: 1.821 GB
Data Space Total: 107.4 GB
Data Space Available: 3.174 GB
Metadata Space Used: 1.479 MB
Metadata Space Total: 2.147 GB
Metadata Space Available: 2.146 GB
Udev Sync Supported: true
Deferred Removal Enabled: false
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.90 (2014-09-01)
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 3.19.0-15-generic
Operating System: Ubuntu 15.04
<output truncated>
Your choice of storage driver can affect the performance of your containerized
applications. So it's important to understand the different storage driver
options available and select the right one for your application. Later, in this
page you'll find some advice for choosing an appropriate driver.
## Shared storage systems and the storage driver
Many enterprises consume storage from shared storage systems such as SAN and
NAS arrays. These often provide increased performance and availability, as well
as advanced features such as thin provisioning, deduplication and compression.
The Docker storage driver and data volumes can both operate on top of storage
provided by shared storage systems. This allows Docker to leverage the
increased performance and availability these systems provide. However, Docker
does not integrate with these underlying systems.
Remember that each Docker storage driver is based on a Linux filesystem or
volume manager. Be sure to follow existing best practices for operating your
storage driver (filesystem or volume manager) on top of your shared storage
system. For example, if using the ZFS storage driver on top of *XYZ* shared
storage system, be sure to follow best practices for operating ZFS filesystems
on top of XYZ shared storage system.
## Which storage driver should you choose?
Several factors influence the selection of a storage driver. However, these two
facts must be kept in mind:
1. No single driver is well suited to every use-case
2. Storage drivers are improving and evolving all of the time
With these factors in mind, the following points, coupled with the table below,
should provide some guidance.
### Stability
For the most stable and hassle-free Docker experience, you should consider the
following:
- **Use the default storage driver for your distribution**. When Docker
installs, it chooses a default storage driver based on the configuration of
your system. Stability is an important factor influencing which storage driver
is used by default. Straying from this default may increase your chances of
encountering bugs and nuances.
- **Follow the configuration specified on the CS Engine
[compatibility matrix](https://www.docker.com/compatibility-maintenance)**. The
CS Engine is the commercially supported version of the Docker Engine. It's
code-base is identical to the open source Engine, but it has a limited set of
supported configurations. These *supported configurations* use the most stable
and mature storage drivers. Straying from these configurations may also
increase your chances of encountering bugs and nuances.
### Experience and expertise
Choose a storage driver that you and your team/organization have experience
with. For example, if you use RHEL or one of its downstream forks, you may
already have experience with LVM and Device Mapper. If so, you may wish to use
the `devicemapper` driver.
If you do not feel you have expertise with any of the storage drivers supported
by Docker, and you want an easy-to-use stable Docker experience, you should
consider using the default driver installed by your distribution's Docker
package.
### Future-proofing
Many people consider OverlayFS as the future of the Docker storage driver.
However, it is less mature, and potentially less stable than some of the more
mature drivers such as `aufs` and `devicemapper`. For this reason, you should
use the OverlayFS driver with caution and expect to encounter more bugs and
nuances than if you were using a more mature driver.
The following diagram lists each storage driver and provides insight into some
of their pros and cons. When selecting which storage driver to use, consider
the guidance offered by the table below along with the points mentioned above.
![](images/driver-pros-cons.png)
## Related information
* [Understand images, containers, and storage drivers](imagesandcontainers.md)
* [AUFS storage driver in practice](aufs-driver.md)
* [Btrfs storage driver in practice](btrfs-driver.md)
* [Device Mapper storage driver in practice](device-mapper-driver.md)

View File

@@ -0,0 +1,296 @@
<!--[metadata]>
+++
title = "ZFS storage in practice"
description = "Learn how to optimize your use of ZFS driver."
keywords = ["container, storage, driver, ZFS "]
[menu.main]
parent = "engine_driver"
+++
<![end-metadata]-->
# Docker and ZFS in practice
ZFS is a next generation filesystem that supports many advanced storage
technologies such as volume management, snapshots, checksumming, compression
and deduplication, replication and more.
It was created by Sun Microsystems (now Oracle Corporation) and is open sourced
under the CDDL license. Due to licensing incompatibilities between the CDDL
and GPL, ZFS cannot be shipped as part of the mainline Linux kernel. However,
the ZFS On Linux (ZoL) project provides an out-of-tree kernel module and
userspace tools which can be installed separately.
The ZFS on Linux (ZoL) port is healthy and maturing. However, at this point in
time it is not recommended to use the `zfs` Docker storage driver for
production use unless you have substantial experience with ZFS on Linux.
> **Note:** There is also a FUSE implementation of ZFS on the Linux platform.
> This should work with Docker but is not recommended. The native ZFS driver
> (ZoL) is more tested, more performant, and is more widely used. The remainder
> of this document will relate to the native ZoL port.
## Image layering and sharing with ZFS
The Docker `zfs` storage driver makes extensive use of three ZFS datasets:
- filesystems
- snapshots
- clones
ZFS filesystems are thinly provisioned and have space allocated to them from a
ZFS pool (zpool) via allocate on demand operations. Snapshots and clones are
space-efficient point-in-time copies of ZFS filesystems. Snapshots are
read-only. Clones are read-write. Clones can only be created from snapshots.
This simple relationship is shown in the diagram below.
![](images/zfs_clones.jpg)
The solid line in the diagram shows the process flow for creating a clone. Step
1 creates a snapshot of the filesystem, and step two creates the clone from
the snapshot. The dashed line shows the relationship between the clone and the
filesystem, via the snapshot. All three ZFS datasets draw space form the same
underlying zpool.
On Docker hosts using the `zfs` storage driver, the base layer of an image is a
ZFS filesystem. Each child layer is a ZFS clone based on a ZFS snapshot of the
layer below it. A container is a ZFS clone based on a ZFS Snapshot of the top
layer of the image it's created from. All ZFS datasets draw their space from a
common zpool. The diagram below shows how this is put together with a running
container based on a two-layer image.
![](images/zfs_zpool.jpg)
The following process explains how images are layered and containers created.
The process is based on the diagram above.
1. The base layer of the image exists on the Docker host as a ZFS filesystem.
This filesystem consumes space from the zpool used to create the Docker
host's local storage area at `/var/lib/docker`.
2. Additional image layers are clones of the dataset hosting the image layer
directly below it.
In the diagram, "Layer 1" is added by making a ZFS snapshot of the base
layer and then creating a clone from that snapshot. The clone is writable and
consumes space on-demand from the zpool. The snapshot is read-only, maintaining
the base layer as an immutable object.
3. When the container is launched, a read-write layer is added above the image.
In the diagram above, the container's read-write layer is created by making
a snapshot of the top layer of the image (Layer 1) and creating a clone from
that snapshot.
As changes are made to the container, space is allocated to it from the
zpool via allocate-on-demand operations. By default, ZFS will allocate space in
blocks of 128K.
This process of creating child layers and containers from *read-only* snapshots
allows images to be maintained as immutable objects.
## Container reads and writes with ZFS
Container reads with the `zfs` storage driver are very simple. A newly launched
container is based on a ZFS clone. This clone initially shares all of its data
with the dataset it was created from. This means that read operations with the
`zfs` storage driver are fast &ndash; even if the data being read was note
copied into the container yet. This sharing of data blocks is shown in the
diagram below.
![](images/zpool_blocks.jpg)
Writing new data to a container is accomplished via an allocate-on-demand
operation. Every time a new area of the container needs writing to, a new block
is allocated from the zpool. This means that containers consume additional
space as new data is written to them. New space is allocated to the container
(ZFS Clone) from the underlying zpool.
Updating *existing data* in a container is accomplished by allocating new
blocks to the containers clone and storing the changed data in those new
blocks. The original blocks are unchanged, allowing the underlying image
dataset to remain immutable. This is the same as writing to a normal ZFS
filesystem and is an implementation of copy-on-write semantics.
## Configure Docker with the ZFS storage driver
The `zfs` storage driver is only supported on a Docker host where
`/var/lib/docker` is mounted as a ZFS filesystem. This section shows you how to
install and configure native ZFS on Linux (ZoL) on an Ubuntu 14.04 system.
### Prerequisites
If you have already used the Docker daemon on your Docker host and have images
you want to keep, `push` them Docker Hub or your private Docker Trusted
Registry before attempting this procedure.
Stop the Docker daemon. Then, ensure that you have a spare block device at
`/dev/xvdb`. The device identifier may be be different in your environment and
you should substitute your own values throughout the procedure.
### Install Zfs on Ubuntu 14.04 LTS
1. If it is running, stop the Docker `daemon`.
1. Install `the software-properties-common` package.
This is required for the `add-apt-repository` command.
$ sudo apt-get install software-properties-common
Reading package lists... Done
Building dependency tree
<output truncated>
2. Add the `zfs-native` package archive.
$ sudo add-apt-repository ppa:zfs-native/stable
The native ZFS filesystem for Linux. Install the ubuntu-zfs package.
<output truncated>
gpg: key F6B0FC61: public key "Launchpad PPA for Native ZFS for Linux" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
OK
3. Get the latest package lists for all registered repositories and package
archives.
$ sudo apt-get update
Ign http://us-west-2.ec2.archive.ubuntu.com trusty InRelease
Get:1 http://us-west-2.ec2.archive.ubuntu.com trusty-updates InRelease [64.4 kB]
<output truncated>
Fetched 10.3 MB in 4s (2,370 kB/s)
Reading package lists... Done
4. Install the `ubuntu-zfs` package.
$ sudo apt-get install -y ubuntu-zfs
Reading package lists... Done
Building dependency tree
<output truncated>
5. Load the `zfs` module.
$ sudo modprobe zfs
6. Verify that it loaded correctly.
$ lsmod | grep zfs
zfs 2768247 0
zunicode 331170 1 zfs
zcommon 55411 1 zfs
znvpair 89086 2 zfs,zcommon
spl 96378 3 zfs,zcommon,znvpair
zavl 15236 1 zfs
## Configure ZFS for Docker
Once ZFS is installed and loaded, you're ready to configure ZFS for Docker.
1. Create a new `zpool`.
$ sudo zpool create -f zpool-docker /dev/xvdb
The command creates the `zpool` and gives it the name "zpool-docker". The name is arbitrary.
2. Check that the `zpool` exists.
$ sudo zfs list
NAME USED AVAIL REFER MOUNTPOINT
zpool-docker 55K 3.84G 19K /zpool-docker
3. Create and mount a new ZFS filesystem to `/var/lib/docker`.
$ sudo zfs create -o mountpoint=/var/lib/docker zpool-docker/docker
4. Check that the previous step worked.
$ sudo zfs list -t all
NAME USED AVAIL REFER MOUNTPOINT
zpool-docker 93.5K 3.84G 19K /zpool-docker
zpool-docker/docker 19K 3.84G 19K /var/lib/docker
Now that you have a ZFS filesystem mounted to `/var/lib/docker`, the daemon
should automatically load with the `zfs` storage driver.
5. Start the Docker daemon.
$ sudo service docker start
docker start/running, process 2315
The procedure for starting the Docker daemon may differ depending on the
Linux distribution you are using. It is possible to force the Docker daemon
to start with the `zfs` storage driver by passing the
`--storage-driver=zfs`flag to the `docker daemon` command, or to the
`DOCKER_OPTS` line in the Docker config file.
6. Verify that the daemon is using the `zfs` storage driver.
$ sudo docker info
Containers: 0
Images: 0
Storage Driver: zfs
Zpool: zpool-docker
Zpool Health: ONLINE
Parent Dataset: zpool-docker/docker
Space Used By Parent: 27648
Space Available: 4128139776
Parent Quota: no
Compression: off
Execution Driver: native-0.2
[...]
The output of the command above shows that the Docker daemon is using the
`zfs` storage driver and that the parent dataset is the
`zpool-docker/docker` filesystem created earlier.
Your Docker host is now using ZFS to store to manage its images and containers.
## ZFS and Docker performance
There are several factors that influence the performance of Docker using the
`zfs` storage driver.
- **Memory**. Memory has a major impact on ZFS performance. This goes back to
the fact that ZFS was originally designed for use on big Sun Solaris servers
with large amounts of memory. Keep this in mind when sizing your Docker hosts.
- **ZFS Features**. Using ZFS features, such as deduplication, can
significantly increase the amount of memory ZFS uses. For memory consumption
and performance reasons it is recommended to turn off ZFS deduplication.
However, deduplication at other layers in the stack (such as SAN or NAS arrays)
can still be used as these do not impact ZFS memory usage and performance. If
using SAN, NAS or other hardware RAID technologies you should continue to
follow existing best practices for using them with ZFS.
- **ZFS Caching**. ZFS caches disk blocks in a memory structure called the
adaptive replacement cache (ARC). The *Single Copy ARC* feature of ZFS allows a
single cached copy of a block to be shared by multiple clones of a filesystem.
This means that multiple running containers can share a single copy of cached
block. This means that ZFS is a good option for PaaS and other high density use
cases.
- **Fragmentation**. Fragmentation is a natural byproduct of copy-on-write
filesystems like ZFS. However, ZFS writes in 128K blocks and allocates *slabs*
(multiple 128K blocks) to CoW operations in an attempt to reduce fragmentation.
The ZFS intent log (ZIL) and the coalescing of writes (delayed writes) also
help to reduce fragmentation.
- **Use the native ZFS driver for Linux**. Although the Docker `zfs` storage
driver supports the ZFS FUSE implementation, it is not recommended when high
performance is required. The native ZFS on Linux driver tends to perform better
than the FUSE implementation.
The following generic performance best practices also apply to ZFS.
- **Use of SSD**. For best performance it is always a good idea to use fast
storage media such as solid state devices (SSD). However, if you only have a
limited amount of SSD storage available it is recommended to place the ZIL on
SSD.
- **Use Data Volumes**. Data volumes provide the best and most predictable
performance. This is because they bypass the storage driver and do not incur
any of the potential overheads introduced by thin provisioning and
copy-on-write. For this reason, you should place heavy write workloads on data
volumes.