1

Introduction

Docker helps developers build and ship higher-quality applications, faster." -- What is Docker

Reference

Tutorials & Docs

Practices & Resources

Books & Tools

Quick Start

Installation

Ubuntu

  • 通过系统自带包安装

Ubuntu 14.04 版本系统中已经自带了 Docker 包,可以直接安装。

$ sudo apt-get update
$ sudo apt-get install -y docker.io
$ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker
$ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io

如果使用操作系统自带包安装 Docker,目前安装的版本是比较旧的 0.9.1。 要安装更新的版本,可以通过使用 Docker 源的方式。

  • 通过Docker源安装最新版本

要安装最新的 Docker 版本,首先需要安装 apt-transport-https 支持,之后通过添加源来安装。

$ sudo apt-get install apt-transport-https
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
$ sudo bash -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
$ sudo apt-get update
$ sudo apt-get install lxc-docker
  • 14.04 之前版本

如果是较低版本的 Ubuntu 系统,需要先更新内核。

$ sudo apt-get update
$ sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring
$ sudo reboot

然后重复上面的步骤即可。

安装之后启动 Docker 服务。

$ sudo service docker start

CentOS

Docker 支持 CentOS6 及以后的版本。

  • CentOS6

对于 CentOS6,可以使用 EPEL 库安装 Docker,命令如下

$ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm
$ sudo yum install docker-io
  • CentOS7

CentOS7 系统 CentOS-Extras 库中已带 Docker,可以直接安装:

$ sudo yum install docker

安装之后启动 Docker 服务,并让它随系统启动自动加载。

$ sudo service docker start
$ sudo chkconfig docker on

Docker Toolbox(Mac & Windows)

早期在Windows或者MAC环境下使用Docker主要利用的是Boot2Docker这个命令行工具,而Toolbox可以安装你在开发中运行Docker时所需要的一切:Docker客户端、Compose(仅Mac需要)、Kitematic、Machine以及VirtualBox。Toolbox使用Machine和VirtualBox在虚拟机中创建了一个引擎来运行容器。在该虚拟机上,你可以使用Docker客户端、Compose以及Kitematic来运行容器。而Toolbox本身已经可以取代了Boot2Docker的作用。

Docker的Toolbox主要集成了如下几个不同的工具的集合:

  • Docker Client docker binary

  • Docker Machine docker-machine binary

  • Docker Compose docker-compose binary

  • Kitematic – Desktop GUI for Docker

  • Docker Quickstart Terminal app

以Mac系统为例,使用Docker Toolbox安装Docker环境与Linux系统的区别在于,在标准的Linux系统上的安装中,Docker客户端、Docker守护进程以及所有的容器都是直接运行在本地的:

而在OS X或者Windows系统的安装中,Docker的相关的程序是运行在一个内置的小型的Linux虚拟机中的。

这里可以下载Toolbox的安装程序,双击打开按照步骤提示安装好之后,即可以进行下一步,首先在Applications文件夹或者Launchpad中打开Docker QuickStart Terminal,该程序会直接打开一个终端控制台,并且创建或者连接到一个叫default的虚拟机并且将控制台登录到该虚拟机中。当然,用户也可以用docker-machine命令来手动创建或者登录到虚拟机中:

From your shell:从命令行创建
  • 创建一个新的Docker虚拟机

$ docker-machine create --driver virtualbox default
Creating VirtualBox VM...
Creating SSH key...
Starting VirtualBox VM...
Starting VM...
To see how to connect Docker to this machine, run: docker-machine env default

注意,该虚拟机相关的配置文件存放在~/.docker/machine/machines/default目录下。

  • 列举所有可用的机器

$ docker-machine ls
NAME                ACTIVE   DRIVER       STATE     URL                         SWARM
default             *        virtualbox   Running   tcp://192.168.99.101:2376  
  • 获取环境配置

$ docker-machine env default
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.101:2376"
export DOCKER_CERT_PATH="/Users/mary/.docker/machine/machines/default"
export DOCKER_MACHINE_NAME="default"
# Run this command to configure your shell: 
# eval "$(docker-machine env default)"
  • 连接到该虚拟机

$ eval "$(docker-machine env default)"

Proxy & Mirror

Proxy For boot2docker

如果是使用boot2docker配置的Docker的运行环境,那么配置文件并没有放置在Windows或者MAC系统种,而是附着在了小的Linux虚拟机的内核中。首先我们需要在boot2docker的命令行中进入到VM的设置:

Creating Machine default...
Creating VirtualBox VM...
Creating SSH key...
Starting VirtualBox VM...
Starting VM...
To see how to connect Docker to this machine, run: docker-machine env default
Starting machine default...
Started machines may have new IP addresses. You may need to re-run the `docker-machine env` command.
Setting environment variables for machine default...

. . .

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/


docker is configured to use the default machine with IP 192.168.99.100
For help getting started, check out the docs at https://docs.docker.com

现在可以修改 /var/lib/boot2docker/profile 这个文件来设置代理:

docker@boot2docker:~$ sudo vi /var/lib/boot2docker/profile 

Tinycore 需要按照如下格式进行配置: protocol://ip:port

安全起见我同时设置了 HTTP 与 HTTPS.

export HTTP_PROXY=http://your.proxy.name:8080
export HTTPS_PROXY=http://your.proxy.name:8080

现在你可以重启整个服务以及Docker容器:

docker@boot2docker:~$ sudo /etc/init.d/docker restart
docker@boot2docker:~$ exit
Connection to localhost closed.

接下来就可以顺利的运行镜像了:

bash-3.2$ docker search ubuntu
NAME                                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
ubuntu                                           Official Ubuntu base image                      356                  
stackbrew/ubuntu                                 Official Ubuntu base image                      39                   
crashsystems/gitlab-docker                       A trusted, regularly updated build of GitL...

这个改变即使以后的虚拟机重启重置也都一直会被保留,因此只需要被设置一次。

Docker Hub Mirror

DaoCloud提供Docker Hub Mirror服务。用户可以进入Docker Hub Mirror注册入口注册。在进入控制台页面后直接点击 启动你的加速器后,您即可得到一个Mirror的地址,将该地址配置在Docker Daemon的启动脚本中,重启Docker Daemon即可使得Docker Hub Mirror配置生效。

  • Ubuntu

安装或升级Docker

Docker 1.3.2版本以上才支持Docker Hub Mirror机制,如果您还没有安装Docker或者版本过低,请安装或升级版本。

配置Docker Hub Mirror

sudo echo "DOCKER_OPTS=\"$DOCKER_OPTS --registry-mirror=http://xxx.m.daocloud.io\"" >> /etc/default/docker service docker restart 

请将xxx替换为您在 DaoCloud 上注册后,为您专属生成的Mirror地址链接名。 该脚本可以将 --registry-mirror 加入到您的Docker配置文件 /etc/defaults/docker中。

尽情享受Docker Hub Mirror

Docker Hub Mirror使用不需要任何附件操作,就像这样下载官方Ubuntu镜像

docker pull ubuntu 
  • CentOS

安装或升级Docker

Docker 1.3.2版本以上才支持Docker Hub Mirror机制,如果您还没有安装Docker或者版本过低,请安装或升级版本。

配置Docker Hub Mirror

sudo sed -i 's|OPTIONS=|OPTIONS=--registry-mirror=http://xxx.m.daocloud.io |g' /etc/sysconfig/docker service docker restart 

请将xxx替换为您在 DaoCloud 上注册后,为您专属生成的Mirror地址链接名。

该脚本可以将 --registry-mirror 加入到您的Docker配置文件/etc/defaults/docker中。

尽情享受Docker Hub Mirror

Docker Hub Mirror使用不需要任何附件操作,就像这样下载官方Ubuntu镜像

docker pull ubuntu 
  • MAC

安装或升级MAC版本下的Boot2Docker

Docker 1.3.2版本以上才支持Docker Hub Mirror机制,如果您还没有安装Docker或者版本过低,请安装或升级版本。

配置Docker Hub Mirror

请确认你的Boot2Docker已经启动,并执行

boot2docker ssh sudo su echo "EXTRA_ARGS=\"--registry-mirror=http://xxx.m.daocloud.io\"" >> /var/lib/boot2docker/profile && exit exit boot2docker restart 

请将xxx替换为您在 DaoCloud 上注册后,为您专属生成的Mirror地址链接名。

尽情享受Docker Hub Mirror

Docker Hub Mirror使用不需要任何附件操作,就像这样下载官方Ubuntu镜像

docker pull ubuntu 
  • Windows

安装或升级MAC版本下的Boot2Docker

Docker 1.3.2版本以上才支持Docker Hub Mirror机制,如果您还没有安装Docker或者版本过低,请安装或升级版本。

配置Docker Hub Mirror

进入Boot2Docker Start Shell,并执行

sudo su echo "EXTRA_ARGS=\"--registry-mirror=http://xxx.m.daocloud.io\"" >> /var/lib/boot2docker/profile exit # 重新启动Boot2Docker 

请将xxx替换为您在 DaoCloud 上注册后,为您专属生成的Mirror地址链接名

尽情享受Docker Hub Mirror

Docker Hub Mirror使用不需要任何附件操作,就像这样下载官方Ubuntu镜像

docker pull ubuntu 

Hello World

在Docker的学习中我们依旧可以借鉴经典的HelloWorld,直接运行docker run hello-world即可,不过需要注意的是,由于总所周知的原因,国内往往无法顺利的下载镜像,如果遇到类似的网络的错误可以参考上文中的代理或者镜像的设置。

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
511136ea3c5a: Pull complete
31cbccb51277: Pull complete
e45a5af57b00: Pull complete
hello-world:latest: The image you are pulling has been verified.
Important: image verification is a tech preview feature and should not be
relied on to provide security.
Status: Downloaded newer image for hello-world:latest
Hello from Docker.
This message shows that your installation appears to be working correctly.


To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
   (Assuming it was not already locally available.)
3. The Docker daemon created a new container from that image which runs the
   executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
   to your terminal.


To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash


For more examples and ideas, visit:
http://docs.docker.com/userguide/

Run Commands(运行命令)

你的Container会在你结束命令之后自动退出,使用以下的命令选项可以将容器保持在激活状态:

  • -i 即使在没有附着的情况下依然保持STDIN处于开启。单纯使用-i命令是不会出现root@689d580b6416:/ 这种前缀。

  • -t 分配一个伪TTY控制台

所以run命令就变成了:

docker run -it -d shykes/pybuilder bin/bash

如果希望能够附着到一个已经存在的容器中,则利用exec命令:

docker exec -it CONTAINER_ID /bin/bash

Docker Commands

本章主要对于常见的Docker命令行命令进行详细介绍。

LifeCycle

Containers

如果只是希望有一个暂时性的容器,可以使用 docker run --rm 将会在容器运行完毕之后删除该容器。

如果希望在打开某个容器之后能够与其进行交互, docker run -t -i 会创建一个TTY控制台。

如果希望能够移除所有与该容器相关的Volume,可以使用-v参数: docker rm -v.

Images

Info

Containers
  • docker ps 会列举出所有正在运行的容器

docker ps -a 会展示出所有正在运行的和已经停止的容器

Images

Import&Export

  • docker cp 在容器与本地文件系统之间进行文件复制

  • docker export 将某个容器中的文件系统的内容输出到某个tar文件中

Executing Commands

如果需要在某个名字为foo的容器中运行交互命令,可以使用:

docker exec -it foo /bin/bash.

Fundamental

Containers

An instance of an image is called container

容器是Docker的核心概念,其相较于我们认为的虚拟机就好比进程中的线程一样。

LifeCycle

A docker container exits when its main process finishes.

Images

Export/Import

Docker 背后的驱动力之一就是通过所有的 Docker 使服务器 能创建一个一致的环境,并且能创建一个能运行在任何 Docker 服务器上的合适的模板或是镜像。因此,Docker 能非常完美的支持,能非常容易的导出一个正在运行的容器,并且重新导入另外一台 Docker 服务器。让我们假设一下,例如这个示例,你有一个你将移动到另外一台服务器去的正在运行的容器。这个进程的功能就是把这个容器保存为一个镜像,保存成 一个 tar 文件,把它移动到新的服务器,并且把这个镜像加载进新的 docker 服务器。

  • 将Container提交为镜像

$ docker commit 3a09b2588478 mynewimage
4d2eab1c0b9a13c83abd72b38e5d4b4315de3c9967165f78a7b817ca99bf191e
  • 将镜像打包

$ docker save mynewimage > /tmp/mynewimage.tar
  • 在新的机器上载入镜像

$ docker load < /tmp/mynewimage.tar
  • 查看镜像状态

最后,运行 docker images 命令检查镜像现在是否可用。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mynewimage          latest              4d2eab1c0b9a        5 minutes ago       278.1 MB
ubuntu              14.04               ad892dd21d60        11 days ago         275.5 MB
<none>              <none>              6b0a59aa7c48        11 days ago         169.4 MB
<none>              <none>              6cfa4d1f33fb        7 weeks ago         0 B

Dockerfile

Dockerfile最佳实践

Dockerfile优化浅谈

Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。例如:

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# Base image to use, this must be set as the first line
FROM ubuntu

# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com

# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# Commands when creating a new container
CMD /usr/sbin/nginx

其中,一开始必须指明所基于的镜像名称,接下来推荐说明维护者信息。后面则是镜像操作指令,例如 RUN 指令,RUN 指令将对镜像执行跟随的命令。每运行一条 RUN 指令,镜像添加新的一层,并提交。最后是 CMD 指令,来指定运行容器时的操作命令。

最后,这边有一个Docker官方MongoDB的例子:

#
# MongoDB Dockerfile
#
# https://github.com/dockerfile/mongodb
#

# Pull base image.
FROM dockerfile/ubuntu

# Install MongoDB.
RUN \
  apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && \
  echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' > /etc/apt/sources.list.d/mongodb.list && \
  apt-get update && \
  apt-get install -y mongodb-org && \
  rm -rf /var/lib/apt/lists/*

# Define mountable directories.
VOLUME ["/data/db"]

# Define working directory.
WORKDIR /data

# Define default command.
CMD ["mongod"]

# Expose ports.
#   - 27017: process
#   - 28017: http
EXPOSE 27017
EXPOSE 28017

指令

指令的一般格式为 INSTRUCTION arguments,指令包括 FROMMAINTAINERRUN 等。

  • FROM

格式为 FROM <image>FROM <image>:<tag>

第一条指令必须为 FROM 指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

  • MAINTAINER

格式为 MAINTAINER <name>,指定维护者信息。

  • RUN

格式为 RUN <command>RUN ["executable", "param1", "param2"]

前者将在 shell 终端中运行命令,即 /bin/sh -c;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN ["/bin/bash", "-c", "echo hello"]

每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。

  • CMD

支持三种格式

  • CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;

  • CMD command param1 param2/bin/sh 中执行,提供给需要交互的应用;

  • CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。

如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。

  • EXPOSE

格式为 EXPOSE <port> [<port>...]

告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。

  • ENV

格式为 ENV<key><value>。指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。

例如

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
  • ADD

格式为 ADD<src><dest>

该命令将复制指定的 <src> 到容器中的 <dest>。其中 <src> 可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。

  • COPY

格式为 COPY <src><dest>

复制本地主机的 <src>(为 Dockerfile 所在目录的相对路径)到容器中的 dest

当使用本地目录为源目录时,推荐使用 COPY

  • ENTRYPOINT

两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"]

  • ENTRYPOINT command param1 param2(shell中执行)。

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。

每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。

  • VOLUME

格式为 VOLUME ["/data"]

创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

  • USER

格式为 USER daemon

指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。

当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要临时获取管理员权限可以使用 gosu,而不推荐 sudo

  • WORKDIR

格式为 WORKDIR /path/to/workdir

为后续的 RUNCMDENTRYPOINT 指令配置工作目录。

可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为 /a/b/c

  • ONBUILD

格式为 ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

例如,Dockerfile 使用如下的内容创建了镜像 image-A

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。

FROM image-A

#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用 ONBUILD 指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild

创建镜像

编写完成 Dockerfile 之后,可以通过 docker build 命令来创建镜像。

基本的格式为 docker build [选项] 路径,该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下所有内容发送给 Docker 服务端,由服务端来创建镜像。因此一般建议放置 Dockerfile 的目录为空目录。也可以通过 .dockerignore 文件(每一行添加一条匹配模式)来让 Docker 忽略路径下的目录和文件。

要指定镜像的标签信息,可以通过 -t 选项,例如

$ sudo docker build -t myrepo/myapp /tmp/test1/

Storage:存储

Volume

docker-indepth-volumes

深入理解Docker Volume

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用

  • 对数据卷的修改会立马生效

  • 对数据卷的更新,不会影响镜像

  • 卷会一直存在,直到没有容器使用

*数据卷的使用,类似于 Linux 下对目录或文件进行 mount。

创建与挂载

创建

在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。

下面创建一个 web 容器,并加载一个数据卷到容器的 /data 目录。

$ sudo docker run -d -P --name web -v /data training/webapp python app.py

*注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。这将在容器内创建路径/data,它存在于联合文件系统外部并可以在主机上直接访问。任何在该镜像/data路径的文件将被复制到volume。我们可以使用docker inspect命令找出Volume在主机存储的地方:

$ docker inspect -f {{.Volumes}} web

你会看到以下类似内容:

map[/data:/var/lib/docker/vfs/dir/cde167197ccc3e138a14f1a4f...b32cec92e79059437a9]

这说明Docker把在/var/lib/docker下的某个目录挂载到了容器内的/data目录下。让我们从主机上添加文件到此文件夹下:

$ sudo touch /var/lib/docker/vfs/dir/cde167197ccc3e13814f...b32ce9059437a9/test-file

进入我们的容器内可以看到:

$ root@CONTAINER:/# ls /data test-file 

改变会立即生效只要将主机的目录挂载到容器的目录上。我们可以在Dockerfile中通过使用VOLUME指令来达到相同的效果:

FROM debian:wheezy 
VOLUME /data 
挂载目录

使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去。

$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

上面的命令加载主机的 /src/webapp 目录到容器的 /opt/webapp目录。这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。

*注意:Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。

Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。

$ sudo docker run -d -P --name web -v /src/webapp:/opt/webapp:ro
training/webapp python app.py

加了 :ro 之后,就挂载为只读了。

挂载文件

-v 标记也可以从主机挂载单个文件到容器中

$ sudo docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

这样就可以记录在容器输入过的命令了。

*注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed --in-place,可能会造成文件 inode 的改变,从 Docker 1.1.0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。

Data Volume Container(数据卷容器)

数据共享

Separate Volume From 1.9

自从Docker 1.9版本之后,volume不再仅仅作为docker run的一个flag,也作为一个单独的子命令出现在Docker中。底层也为volume添加了诸如ls、create、inspect和rm等volume子命令的api。新的volume子命令可以允许用户先创建volume,然后在启动的container的时候进行挂载,此举也更加方便了volume的管理。

$ docker volume create --name hello
hello
$ docker run -d -v hello:/world busybox ls /world

而在一个独立的Volume被创建之后,就可以通过inspect命令来进行信息查看:

Usage: docker volume inspect [OPTIONS] VOLUME [VOLUME...]

Inspect one or more volumes

  -f, --format=       Format the output using the given go template.
  --help=false        Print usage

该命令会返回某个数据卷的基本信息,大概样式如下图所示:

$ docker volume create
85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
$ docker volume inspect 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
[
  {
      "Name": "85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d",
      "Driver": "local",
      "Mountpoint": "/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data"
  }
]

$ docker volume inspect --format '{{ .Mountpoint }}' 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data

Data Transfer

copying-data-between-docker-containers

Network:网络

Docker Networking: Reborn

Docker 1.9中正式引入了所谓的"Container Network Model",也就适合所谓的 CNM的概念。CNM即用于创建小型微分割的网络来使得不同组的容器之间进行相互连接。

Basic Usage

端口映射

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。

使用 docker ps 可以看到,本地主机的 49155 被映射到了容器的 5000 端口。此时访问本机的 49155 端口即可访问容器内 web 应用提供的界面。

$ sudo docker run -d -P training/webapp python app.py
$ sudo 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

同样的,可以通过 docker logs 命令来查看应用的信息。

$ sudo 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 -

-p(小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort

  • 映射所有接口地址

使用 hostPort:containerPort 格式本地的 5000 端口映射到容器的 5000 端口,可以执行

$ sudo docker run -d -p 5000:5000 training/webapp python app.py

此时默认会绑定本地所有接口上的所有地址。

  • 映射到指定地址的指定端口

可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1

$ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
  • 映射到指定地址的任意端口

使用 ip::containerPort 绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口。

$ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py

还可以使用 udp 标记来指定 udp 端口

$ sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
  • 查看映射端口配置

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址

$ docker port nostalgic_morse 5000
127.0.0.1:49155.

注意:

  • 容器有自己的内部网络和 ip 地址(使用 docker inspect 可以获取所有的变量,Docker 还可以有一个可变的网络配置。)

  • -p 标记可以多次使用来绑定多个端口

例如

$ sudo docker run -d -p 5000:5000  -p 3000:80 training/webapp python app.py

注意,网络映射的操作只会在run命令中起作用,如果已经运行了一个容器,需要重新设置其网络映射情况,请使用commit将容器转化为镜像之后再创建新的容器。

容器互联(Links)

容器的连接(linking)系统是除了端口映射外,另一种跟容器中应用交互的方式。

该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。

自定义容器命名

连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。

虽然当创建容器的时候,系统默认会分配一个名字。自定义命名容器有2个好处:

  • 自定义的命名,比较好记,比如一个web应用容器我们可以给它起名叫web

  • 当要连接其他容器时候,可以作为一个有用的参考点,比如连接web容器到db容器

使用 --name 标记可以为容器自定义命名。

$ sudo docker run -d -P --name web training/webapp python app.py

使用 docker ps 来验证设定的命名。

$ sudo 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

也可以使用 docker inspect 来查看容器的名字

$ sudo docker inspect -f "{{ .Name }}" aed84ee21bde
/web

注意:容器的名称是唯一的。如果已经命名了一个叫 web 的容器,当你要再次使用 web 这个名称的时候,需要先用docker rm 来删除之前创建的同名容器。

在执行 docker run 的时候如果添加 --rm 标记,则容器在终止后会立刻删除。注意,--rm-d 参数不能同时使用。

容器互联

使用 --link 参数可以让容器之间安全的进行交互。

下面先创建一个新的数据库容器。

$ sudo docker run -d --name db training/postgres

删除之前创建的 web 容器

$ docker rm -f web

然后创建一个新的 web 容器,并将它连接到 db 容器

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

此时,db 容器和 web 容器建立互联关系。

--link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名。

使用 docker ps 来查看容器的连接

$ docker ps
CONTAINER ID  IMAGE                     COMMAND               CREATED             STATUS             PORTS                    NAMES
349169744e49  training/postgres:latest  su postgres -c '/usr  About a minute ago  Up About a minute  5432/tcp                 db, web/db
aed84ee21bde  training/webapp:latest    python app.py         16 hours ago        Up 2 minutes       0.0.0.0:49154->5000/tcp  web

可以看到自定义命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。这表示 web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。

Docker 在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动 db 容器的时候并没有使用 -p-P 标记,从而避免了暴露数据库端口到外部网络上。

Docker 通过 2 种方式为容器公开连接信息:

  • 环境变量

  • 更新 /etc/hosts 文件

使用 env 命令来查看 web 容器的环境变量

$ sudo 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_5000_TCP=tcp://172.17.0.5:5432
DB_PORT_5000_TCP_PROTO=tcp
DB_PORT_5000_TCP_PORT=5432
DB_PORT_5000_TCP_ADDR=172.17.0.5
. . .

其中 DB_ 开头的环境变量是供 web 容器连接 db 容器使用,前缀采用大写的连接别名。

除了环境变量,Docker 还添加 host 信息到父容器的 /etc/hosts 的文件。下面是父容器 web 的 hosts 文件

$ sudo 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.5  db

这里有 2 个 hosts,第一个是 web 容器,web 容器用 id 作为他的主机名,第二个是 db 容器的 ip 和主机名。可以在 web 容器中安装 ping 命令来测试跟db容器的连通。

root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping db
PING db (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

用 ping 来测试db容器,它会解析成 172.17.0.5。*注意:官方的 ubuntu 镜像默认没有安装 ping,需要自行安装。

用户可以链接多个父容器到子容器,比如可以链接多个 web 到 db 容器上。

Networking(网络)

在Docker 1.9之后,Docker正式宣布可以将Networking应用于生产环境中,并且可以与Swarm以及Compose进行较好的结合。与传统的Links相比,Networking具有以下优势:

  • 允许不同物理主机或者虚拟主机上的容器进行通信

  • 使用了Networking的容器可以很方便地进行停止、启动或者重启等操作而不用担心会影响到与其他容器之间的连接

  • 并不需要在连接到某个容器之前就直接创建它,换言之,Networking不再像原本的Links一样会依赖某个容器而存在

Single Host Networking

可以直接使用docker network命令来使用Networking,其中可以使用docker network create来创建一个新的网络,在这个示例中,我们会创建一个叫做frontend的网络并且在其中运行一个nginx容器:

$ docker network create frontend
$ docker run -itd --net=frontend --name web nginx

我们使用网络来分割应用,乃至于分割应用中的不同模块。在本例子中,我们可以创建另一个包含了应用程序的网络app,然后将这个网络与frontend网络相连,命令如下所示:

$ docker network create app
$ docker run -itd --name myapp --net=app <my application container>
$ docker network connect app web

这样我们的Nginx服务器就可以使用myapp.app这个主机名来连接到应用程序中。我们创建两个基于busybox的容器来进行尝试:

$ docker run -d --name rose --net=frontend busybox top
c1fa2dc7fa3a412b52b53f5facd25ba11e99c362d77be8cea4ff49f3d5e2cafc

$ docker run --rm --net=frontend busybox ping -c 4 rose
PING rose (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.122 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.078 ms
64 bytes from 172.19.0.2: seq=2 ttl=64 time=0.098 ms
64 bytes from 172.19.0.2: seq=3 ttl=64 time=0.241 ms

我们已将第二个容器附着到frontend网路中,并且用ping命令来进行内建的容器发现,我们可以用inspect命令来查看网络的详情:

$ docker network inspect frontend
[
    {
        "Name": "frontend",
        "Id": "a639a457122020faa69a4ab906bc33217c9c6d73048f3dbbb69e53dbe5e0952c",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Config": [
                {}
            ]
        },
        "Containers": {
            "c1fa2dc7fa3a412b52b53f5facd25ba11e99c362d77be8cea4ff49f3d5e2cafc": {
                "EndpointID": "976bab21d4a11cd21d5d1c1560f67f39ef15245662aeacf097eb1d5c148ed748",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {}
    }
]

在前端网络之外,我们也可以创建一个自定义的后端网络,用于连接其他容器:

$ docker network create backend
09733cac7890edca439cdc3d476b4cd1959e44065217aa581d359575b8d2288f
$ docker network connect backend rose
$ docker network inspect backend

    {
        "name": "backend",
        "id": "09733cac7890edca439cdc3d476b4cd1959e44065217aa581d359575b8d2288f",
        "scope": "local",
        "driver": "bridge",
        "ipam": {
            "driver": "default",
            "config": [
                {}
            ]
        },
        "containers": {
            "c1fa2dc7fa3a412b52b53f5facd25ba11e99c362d77be8cea4ff49f3d5e2cafc": {
                "endpoint": "438730c588915dd54dc694efdb3a15c77bc5e86c744f5f87a65f6ac46b43e5ad",
                "mac_address": "02:42:ac:14:00:02",
                "ipv4_address": "172.20.0.2/16",
                "ipv6_address": ""
            }
        },
        "options": {}
    }
]

再看一下容器中具体的网络的设置:

$ docker inspect -f '{{ json .NetworkSettings }}' rose
{
  "Bridge": "",
  "SandboxID": "b600bebe1e2bb6dee92335e6acfe49215c30c4964d7a982711ec12c6acca3309",
  "HairpinMode": false,
  "LinkLocalIPv6Address": "",
  "LinkLocalIPv6PrefixLen": 0,
  "Ports": {},
  "SandboxKey": "/var/run/docker/netns/b600bebe1e2b",
  "SecondaryIPAddresses": null,
  "SecondaryIPv6Addresses": null,
  "EndpointID": "",
  "Gateway": "",
  "GlobalIPv6Address": "",
  "GlobalIPv6PrefixLen": 0,
  "IPAddress": "",
  "IPPrefixLen": 0,
  "IPv6Gateway": "",
  "MacAddress": "",
  "Networks": {
    "backend": {
      "EndpointID": "438730c588915dd54dc694efdb3a15c77bc5e86c744f5f87a65f6ac46b43e5ad",
      "Gateway": "172.20.0.1",
      "IPAddress": "172.20.0.2",
      "IPPrefixLen": 16,
      "IPv6Gateway": "",
      "GlobalIPv6Address": "",
      "GlobalIPv6PrefixLen": 0,
      "MacAddress": "02:42:ac:14:00:02"
    },
    "frontend": {
      "EndpointID": "976bab21d4a11cd21d5d1c1560f67f39ef15245662aeacf097eb1d5c148ed748",
      "Gateway": "172.19.0.1",
      "IPAddress": "172.19.0.2",
      "IPPrefixLen": 16,
      "IPv6Gateway": "",
      "GlobalIPv6Address": "",
      "GlobalIPv6PrefixLen": 0,
      "MacAddress": "02:42:ac:13:00:02"
    }
  }
}

而在容器中使用ifconfig命令查看时:

$ docker exec rose ifconifg
eth0      Link encap:Ethernet  HWaddr 02:42:AC:13:00:02
          inet addr:172.19.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:27 errors:0 dropped:0 overruns:0 frame:0
          TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:2238 (2.1 KiB)  TX bytes:1208 (1.1 KiB)

eth1      Link encap:Ethernet  HWaddr 02:42:AC:14:00:02
          inet addr:172.20.0.2  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  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
          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)

而如果要将某个容器从网络中断开时,可以使用如下命令:

$ docker network disconnect backend rose

这种网络配置方式的初衷即将服务独立出来成为一个网络中不同的容器之间相互交流的唯一信息。换言之,在实践中,需要构建出大量的小型网络,每个网络中只负责某个服务的通信,并且不同的网络之间应该是相互隔离的。一个典型的例子就是负载均衡器、前端、后端以及一个数据库。

Network Customization

在创建网络的时候我们也可以设置很多的自定义的配置:

  --aux-address=map[]      auxiliary ipv4 or ipv6 addresses used by Network driver
  -d, --driver="bridge"    Driver to manage the Network
  --gateway=[]             ipv4 or ipv6 Gateway for the master subnet
  --help=false             Print usage
  --ip-range=[]            allocate container ip from a sub-range
  --ipam-driver=default    IP Address Management Driver
  -o, --opt=map[]          set driver specific options
  --subnet=[]              subnet in CIDR format that represents a network segment

我们首先来看下--driver选项,该选项可以来选定具体的网络驱动方式来管理网络,目前Docker提供了如下两种驱动器:

  • bridge -- This driver provides the same sort of networking via veth bridge devices that prior versions of docker use, it is the default.

  • overlay -- Not to be confused with the "overlay" storage driver (thanks overlayfs), this driver provides native multi-host networking for docker clusters. When using swarm, this is the default driver.

另一个常用的选项是--ipam-driver,用来确定IP地址是如何分配的。目前Docker仅内置了一种IP地址分配方案,未来会添加上DHCP IAMP方式。

Multi-Host Networking

Manager

Web Interface

DockerUI


王下邀月熊_Chevalier
22.5k 声望8.5k 粉丝

爱代码 爱生活 希望成为全栈整合师