Docker的应用场景:

  • 加速本地开发和构建流程,使其更加高效、更加轻量化。本地开发人员可以构建、运行并分享Docker容器。容器可以在开发环境中构建,然后轻松地提交到测试环境中,并最终进入生产环境。

  • 能够让独立服务或者应用程序在不同的环境中得到相同的运行结果。这一点在面向服务的架构和重度依赖微型服务的部署中尤其实用。

  • docker创建隔离的环境来进行测试。例如,使用Jenkins CI这样的持续集成工具启动一个用于测试的容器。

  • Docker可以让开发者现在本机上构建一个复杂的程序或者架构来进行测试,而不是一开始就在生产环境部署和测试。

  • 构建一个多用户的平台及服务(PaaS)基础设施。

  • 为开发、测试提供一个轻量级的独立沙盒环境,或者将独立的沙盒环境用于技术教学,如unix shell的使用、编程语言教学。提供软件即服务(Saas)应用程序,如Memcached即服务。

动手简单体验体验docker

下面的内容是对docker的简单操作,包括启动docker,下载image,运行image,查看容器的详细信息、在容器中安装新程序,保持刚刚修改的容器,发布自己的镜像到docke Hub上。所有操作步骤是一步一步进行的,能够让初学者直观地感受一下docker的作用。

说明: 
1、在看本博客之前,请先确认您已经安装好了docker。本人的docker运行在win上面。 
2、为了提高写博客的效率,本文统一复制CMD中的内容,替代截图。效果是一样的。

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.
 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.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 运行一个简单的container,helloworld
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
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.
 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.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 运行ubuntu容器,如果本地没有ubuntu镜像,就有一个下载的过程。
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker run -it ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
8aec416115fd: Pull complete
695f074e24e3: Pull complete
946d6c48c2a7: Pull complete
bc7277e579f0: Pull complete
2508cbcde94b: Pull complete
Digest: sha256:71cd81252a3563a03ad8daee81047b62ab5d892ebbfbf71cf53415f29c130950
Status: Downloaded newer image for ubuntu:latest
//这里已经进入ubuntu操作系统中了
root@8532da118b07:/# ls 
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@8532da118b07:/# whoami
root
root@8532da118b07:/# uname -a
Linux 8532da118b07 4.4.43-boot2docker #1 SMP Wed Jan 18 18:33:18 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
root@8532da118b07:/# exit
exit

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 上面步骤,我们运行了ubuntu容器,现在我们在这个容器里面安装ping软件。
  • 先更新ubuntu系统的软件源信息
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker run ubuntu apt-get update
Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
Get:3 http://archive.ubuntu.com/ubuntu xenial-security InRelease [102 kB]
Get:4 http://archive.ubuntu.com/ubuntu xenial/main Sources [1103 kB]
Get:5 http://archive.ubuntu.com/ubuntu xenial/restricted Sources [5179 B]
Get:6 http://archive.ubuntu.com/ubuntu xenial/universe Sources [9802 kB]
Get:7 http://archive.ubuntu.com/ubuntu xenial/main amd64 Packages [1558 kB]
Get:8 http://archive.ubuntu.com/ubuntu xenial/restricted amd64 Packages [14.1 kB]
Get:9 http://archive.ubuntu.com/ubuntu xenial/universe amd64 Packages [9827 kB]
Get:10 http://archive.ubuntu.com/ubuntu xenial-updates/main Sources [276 kB]
Get:11 http://archive.ubuntu.com/ubuntu xenial-updates/restricted Sources [1866 B]
Get:12 http://archive.ubuntu.com/ubuntu xenial-updates/universe Sources [150 kB]
Get:13 http://archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages [585 kB]
Get:14 http://archive.ubuntu.com/ubuntu xenial-updates/restricted amd64 Packages [11.7 kB]
Get:15 http://archive.ubuntu.com/ubuntu xenial-updates/universe amd64 Packages [486 kB]
Get:16 http://archive.ubuntu.com/ubuntu xenial-security/main Sources [66.9 kB]
Get:17 http://archive.ubuntu.com/ubuntu xenial-security/restricted Sources [1866 B]
Get:18 http://archive.ubuntu.com/ubuntu xenial-security/universe Sources [20.5 kB]
Get:19 http://archive.ubuntu.com/ubuntu xenial-security/main amd64 Packages [252 kB]
Get:20 http://archive.ubuntu.com/ubuntu xenial-security/restricted amd64 Packages [11.7 kB]
Get:21 http://archive.ubuntu.com/ubuntu xenial-security/universe amd64 Packages [87.2 kB]
Fetched 24.7 MB in 5min 58s (68.9 kB/s)
Reading package lists...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 查看正在运行的container,commit该container,使它成为一个新的image,起名叫learn/update
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
50ecefedccba        ubuntu              "apt-get update"    21 minutes ago      Exited (0) 14 minutes ago                       priceless_boyd

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker commit 50e learn/update
sha256:d3905d6682b23c0fbe79af0257f92f48d219bca8ce1a8405d1f35ac0fa1b1b5a
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 名字为ubuntu的镜像和名字为learn/update的镜像是不一样的,后者的apt-get是已经更新的。
  • 下面在已更新的ubuntu中下载安装ping软件。
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker run learn/update apt-get install -y inetutils-ping
Reading package lists...
Building dependency tree...
Reading state information...
The following additional packages will be installed:
、、、
Setting up isc-dhcp-client (4.3.3-5ubuntu12.6) ...
Setting up isc-dhcp-common (4.3.3-5ubuntu12.6) ...
Setting up libxtables11:amd64 (1.6.0-2ubuntu3) ...
Setting up netbase (5.3) ...
Setting up inetutils-ping (2:1.9.4-1build1) ...
Processing triggers for libc-bin (2.23-0ubuntu5) ...
Processing triggers for systemd (229-4ubuntu13) ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 提交保存安装了ping的ubuntu容器,起名叫ubuntu/ping
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker ps -l
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS                      PORTS               NAMES
53e26efb86af        learn/update        "apt-get install -y i"   About a minute ago   Exited (0) 39 seconds ago                       laughing_wiles

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker commit 53e26efb86af  ubuntu/ping
sha256:3aff8e300c6dca187ea2c6192acc0b85b73d59cc2ba569e40a912570939855ee

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu/ping         latest              3aff8e300c6d        33 seconds ago      174.4 MB
learn/update        latest              d3905d6682b2        6 minutes ago       169.1 MB
ubuntu              latest              f49eec89601e        4 days ago          129.5 MB
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 测试一下ping功能
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker run ubuntu/ping ping baidu.com
PING baidu.com (111.13.100.91): 56 data bytes
64 bytes from 111.13.100.91: icmp_seq=0 ttl=50 time=57.687 ms
64 bytes from 111.13.100.91: icmp_seq=1 ttl=49 time=54.970 ms
64 bytes from 111.13.100.91: icmp_seq=2 ttl=49 time=58.235 ms
64 bytes from 111.13.100.91: icmp_seq=3 ttl=49 time=59.382 ms

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

通过上面这个过程,我们发现总共有三个镜像文件,分别是最初的ubuntu、learn/update和ubuntu/ping。这体现了docker所采用的copy on write的思想。

  • 1
  • 1

查看某一container的详细信息

  • 先查看正在运行的container
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
1312ac6459c0        ubuntu/ping         "ping baidu.com"    16 minutes ago      Up 16 minutes                           happy_almeida
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
  • 根据上一步所查出来的container的ID查看其详细信息
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker inspect 1312
[
    {
        "Id": "1312ac6459c0b93907e3e2a72c59be22a3be89b53cd097226355bd81b56a932d",
        "Created": "2017-01-25T15:07:20.801240028Z",
        "Path": "ping",
        "Args": [
            "baidu.com"
        ],
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

将镜像推送到Docker Hub中

准备工作:注册一个docker账号。这里我的用户名是liangyhgood。

  • 登陆docker
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username (liangyhgood): liangyhgood
Password:
Login Succeeded
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 查看本地镜像,有一个hello-world的。修改它的tag,(这里的名字需要符合规范,那就是需要用斜线分割),这里先随便给这个tag起名,叫learn/helloworld,发现后面push失败。
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              48b5124b2768        12 days ago         1.84 kB

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker tag 48b5124b2768 learn/helloworld:latest

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker push learn/helloworld
The push refers to a repository [docker.io/learn/helloworld]
98c944e98de8: Preparing
denied: requested access to the resource is denied
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 为什么会失败呢?上面的信息显示是拒接访问,因为tag的名字斜线前面部分learn不是本人的用户名,下面把它修改为liangyhgood/helloworld就push成功。需要注意的是liangyhgood是本人的docker用户名。进入docker hub网站查看,发现多了一个公共的repository。
liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker tag 48b5124b2768 liangyhgood/helloworld:latest

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$ docker push liangyhgood/helloworld
The push refers to a repository [docker.io/liangyhgood/helloworld]
98c944e98de8: Pushed
latest: digest: sha256:2075ac87b043415d35bb6351b4a59df19b8ad154e578f7048335feeb02d0f759 size: 524

liangyh@DESKTOP-GE1EJH4 MINGW64 ~
$
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

停止所有的Container

docker stop $(docker ps -a -q)

删除所有container

docker rm $(docker ps -a -q)

删除全部image

docker rmi $(docker images -q)

docker容器重新启动之后沿用docker run命令时制定的参数来运行

docker attach containerName

使用一个数组来指定要运行的命令和传递给该命令的每个参数。

RUN [“apt-get”, “install”, “-y”, “nginx”]

docker架构

3 Docker总架构图

学习Docker的源码并不是一个枯燥的过程,反而可以从中理解Docker架构的设计原理。Docker对使用者来讲是一个C/S模式的架构,而Docker的后端是一个非常松耦合的架构,模块各司其职,并有机组合,支撑Docker的运行。

在此,先附上Docker总架构,如图3.1。

img

图3.1 Docker总架构图

如图3.1,不难看出,用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者。

而Docker Daemon作为Docker架构中的主体部分,首先提供Server的功能使其可以接受Docker Client的请求;而后Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。

Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。

而libcontainer是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。

当执行完运行容器的命令后,一个实际的Docker容器就处于运行状态,该容器拥有独立的文件系统,独立并且安全的运行环境等。

4 Docker架构内各模块的功能与实现分析

接下来,我们将从Docker总架构图入手,抽离出架构内各个模块,并对各个模块进行更为细化的架构分析与功能阐述。主要的模块有:Docker Client、Docker Daemon、Docker Registry、Graph、Driver、libcontainer以及Docker container。

4.1 Docker Client

Docker Client是Docker架构中用户用来和Docker Daemon建立通信的客户端。用户使用的可执行文件为docker,通过docker命令行工具可以发起众多管理container的请求。

Docker Client可以通过以下三种方式和Docker Daemon建立通信:tcp://host:port,unix://path_to_socket和fd://socketfd。为了简单起见,本文一律使用第一种方式作为讲述两者通信的原型。与此同时,与Docker Daemon建立连接并传输请求的时候,Docker Client可以通过设置命令行flag参数的形式设置安全传输层协议(TLS)的有关参数,保证传输的安全性。

Docker Client发送容器管理请求后,由Docker Daemon接受并处理请求,当Docker Client接收到返回的请求相应并简单处理后,Docker Client一次完整的生命周期就结束了。当需要继续发送容器管理请求时,用户必须再次通过docker可执行文件创建Docker Client。

4.2 Docker Daemon

Docker Daemon是Docker架构中一个常驻在后台的系统进程,功能是:接受并处理Docker Client发送的请求。该守护进程在后台启动了一个Server,Server负责接受Docker Client发送的请求;接受请求后,Server通过路由与分发调度,找到相应的Handler来执行请求。

Docker Daemon启动所使用的可执行文件也为docker,与Docker Client启动所使用的可执行文件docker相同。在docker命令执行时,通过传入的参数来判别Docker Daemon与Docker Client。

Docker Daemon的架构,大致可以分为以下三部分:Docker Server、Engine和Job。Daemon架构如图4.1。

img

图4.1 Docker Daemon架构示意图

4.2.1 Docker Server

Docker Server在Docker架构中是专门服务于Docker Client的server。该server的功能是:接受并调度分发Docker Client发送的请求。Docker Server的架构如图4.2。

img

图4.2 Docker Server架构示意图

在Docker的启动过程中,通过包gorilla/mux,创建了一个mux.Router,提供请求的路由功能。在Golang中,gorilla/mux是一个强大的URL路由器以及调度分发器。该mux.Router中添加了众多的路由项,每一个路由项由HTTP请求方法(PUT、POST、GET或DELETE)、URL、Handler三部分组成。

若Docker Client通过HTTP的形式访问Docker Daemon,创建完mux.Router之后,Docker将Server的监听地址以及mux.Router作为参数,创建一个httpSrv=http.Server{},最终执行httpSrv.Serve()为请求服务。

在Server的服务过程中,Server在listener上接受Docker Client的访问请求,并创建一个全新的goroutine来服务该请求。在goroutine中,首先读取请求内容,然后做解析工作,接着找到相应的路由项,随后调用相应的Handler来处理该请求,最后Handler处理完请求之后回复该请求。

需要注意的是:Docker Server的运行在Docker的启动过程中,是靠一个名为”serveapi”的job的运行来完成的。原则上,Docker Server的运行是众多job中的一个,但是为了强调Docker Server的重要性以及为后续job服务的重要特性,将该”serveapi”的job单独抽离出来分析,理解为Docker Server。

4.2.2 Engine

Engine是Docker架构中的运行引擎,同时也Docker运行的核心模块。它扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。

在Engine数据结构的设计与实现过程中,有一个handler对象。该handler对象存储的都是关于众多特定job的handler处理访问。举例说明,Engine的handler对象中有一项为:{“create”: daemon.ContainerCreate,},则说明当名为”create”的job在运行时,执行的是daemon.ContainerCreate的handler。

4.2.3 Job

一个Job可以认为是Docker架构中Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job。例如:在容器内部运行一个进程,这是一个job;创建一个新的容器,这是一个job,从Internet上下载一个文档,这是一个job;包括之前在Docker Server部分说过的,创建Server服务于HTTP的API,这也是一个job,等等。

Job的设计者,把Job设计得与Unix进程相仿。比如说:Job有一个名称,有参数,有环境变量,有标准的输入输出,有错误处理,有返回状态等。

4.3 Docker Registry

Docker Registry是一个存储容器镜像的仓库。而容器镜像是在容器被创建时,被加载用来初始化容器的文件架构与目录。

在Docker的运行过程中,Docker Daemon会与Docker Registry通信,并实现搜索镜像、下载镜像、上传镜像三个功能,这三个功能对应的job名称分别为”search”,”pull” 与 “push”。

其中,在Docker架构中,Docker可以使用公有的Docker Registry,即大家熟知的Docker Hub,如此一来,Docker获取容器镜像文件时,必须通过互联网访问Docker Hub;同时Docker也允许用户构建本地私有的Docker Registry,这样可以保证容器镜像的获取在内网完成。

4.4 Graph

Graph在Docker架构中扮演已下载容器镜像的保管者,以及已下载容器镜像之间关系的记录者。一方面,Graph存储着本地具有版本信息的文件系统镜像,另一方面也通过GraphDB记录着所有文件系统镜像彼此之间的关系。Graph的架构如图4.3。

img

图4.3 Graph架构示意图

其中,GraphDB是一个构建在SQLite之上的小型图数据库,实现了节点的命名以及节点之间关联关系的记录。它仅仅实现了大多数图数据库所拥有的一个小的子集,但是提供了简单的接口表示节点之间的关系。

同时在Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。

4.5 Driver

Driver是Docker架构中的驱动模块。通过Driver驱动,Docker可以实现对Docker容器执行环境的定制。由于Docker运行的生命周期中,并非用户所有的操作都是针对Docker容器的管理,另外还有关于Docker运行信息的获取,Graph的存储与记录等。因此,为了将Docker容器的管理从Docker Daemon内部业务逻辑中区分开来,设计了Driver层驱动来接管所有这部分请求。

在Docker Driver的实现中,可以分为以下三类驱动:graphdriver、networkdriver和execdriver。

graphdriver主要用于完成容器镜像的管理,包括存储与获取。即当用户需要下载指定的容器镜像时,graphdriver将容器镜像存储在本地的指定目录;同时当用户需要使用指定的容器镜像来创建容器的rootfs时,graphdriver从本地镜像存储目录中获取指定的容器镜像。

在graphdriver的初始化过程之前,有4种文件系统或类文件系统在其内部注册,它们分别是aufs、btrfs、vfs和devmapper。而Docker在初始化之时,通过获取系统环境变量”DOCKER_DRIVER”来提取所使用driver的指定类型。而之后所有的graph操作,都使用该driver来执行。

graphdriver的架构如图4.4:

img

图4.4 graphdriver架构示意图

networkdriver的用途是完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。networkdriver的架构如图4.5:

img

图4. 5 networkdriver架构示意图

execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。具体体现在Daemon启动过程中加载的ExecDriverflag参数,该参数在配置文件已经被设为”native”。这可以认为是Docker在1.2版本上一个很大的改变,或者说Docker实现跨平台的一个先兆。execdriver架构如图4.6:

img

图4.6 execdriver架构示意图

4.6 libcontainer

libcontainer是Docker架构中一个使用Go语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的API。

正是由于libcontainer的存在,Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包。libcontainer架构如图4.7:

img

图4.7 libcontainer示意图

另外,libcontainer提供了一整套标准的接口来满足上层对容器管理的需求。或者说,libcontainer屏蔽了Docker上层对容器的直接管理。又由于libcontainer使用go这种跨平台的语言开发实现,且本身又可以被上层多种不同的编程语言访问,因此很难说,未来的Docker就一定会紧紧地和Linux捆绑在一起。而于此同时,Microsoft在其著名云计算平台Azure中,也添加了对Docker的支持,可见Docker的开放程度与业界的火热度。

暂不谈Docker,由于libcontainer的功能以及其本身与系统的松耦合特性,很有可能会在其他以容器为原型的平台出现,同时也很有可能催生出云计算领域全新的项目。

4.7 Docker container

Docker container(Docker容器)是Docker架构中服务交付的最终体现形式。

Docker按照用户的需求与指令,订制相应的Docker容器:

  • 用户通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;
  • 用户通过指定计算资源的配额,使得Docker容器使用指定的计算资源;
  • 用户通过配置网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;
  • 用户通过指定运行的命令,使得Docker容器执行指定的工作。

Docker容器示意图如图4.8:

img

图4.8 Docker容器示意图

5 Docker运行案例分析

上一章节着重于Docker架构中各个部分的介绍。本章的内容,将以串联Docker各模块来简要分析,分析原型为Docker中的docker pull与docker run两个命令。

5.1 docker pull

docker pull命令的作用为:从Docker Registry中下载指定的容器镜像,并存储在本地的Graph中,以备后续创建Docker容器时的使用。docker pull命令执行流程如图5.1。

img

图5.1 docker pull命令执行流程示意图

如图,图中标记的红色箭头表示docker pull命令在发起后,Docker所做的一系列运行。以下逐一分析这些步骤。

(1) Docker Client接受docker pull命令,解析完请求以及收集完请求参数之后,发送一个HTTP请求给Docker Server,HTTP请求方法为POST,请求URL为”/images/create? “+”xxx”;

(2) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;

(3) mux.Router将请求路由分发至相应的handler,具体为PostImagesCreate;

(4) 在PostImageCreate这个handler之中,一个名为”pull”的job被创建,并开始执行;

(5) 名为”pull”的job在执行过程中,执行pullRepository操作,即从Docker Registry中下载相应的一个或者多个image;

(6) 名为”pull”的job将下载的image交给graphdriver;

(7) graphdriver负责将image进行存储,一方创建graph对象,另一方面在GraphDB中记录image之间的关系。

5.2 docker run

docker run命令的作用是在一个全新的Docker容器内部运行一条指令。Docker在执行这条命令的时候,所做工作可以分为两部分:第一,创建Docker容器所需的rootfs;第二,创建容器的网络等运行环境,并真正运行用户指令。因此,在整个执行流程中,Docker Client给Docker Server发送了两次HTTP请求,第二次请求的发起取决于第一次请求的返回状态。Docker run命令执行流程如图5.2。

img

图5.2 docker run命令执行流程示意图

如图,图中标记的红色箭头表示docker run命令在发起后,Docker所做的一系列运行。以下逐一分析这些步骤。

(1) Docker Client接受docker run命令,解析完请求以及收集完请求参数之后,发送一个HTTP请求给Docker Server,HTTP请求方法为POST,请求URL为”/containers/create? “+”xxx”;

(2) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;

(3) mux.Router将请求路由分发至相应的handler,具体为PostContainersCreate;

(4) 在PostImageCreate这个handler之中,一个名为”create”的job被创建,并开始让该job运行;

(5) 名为”create”的job在运行过程中,执行Container.Create操作,该操作需要获取容器镜像来为Docker容器创建rootfs,即调用graphdriver;

(6) graphdriver从Graph中获取创建Docker容器rootfs所需要的所有的镜像;

(7) graphdriver将rootfs所有镜像,加载安装至Docker容器指定的文件目录下;

(8) 若以上操作全部正常执行,没有返回错误或异常,则Docker Client收到Docker Server返回状态之后,发起第二次HTTP请求。请求方法为”POST”,请求URL为”/containers/”+container_ID+”/start”;

(9) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;

(10)mux.Router将请求路由分发至相应的handler,具体为PostContainersStart;

(11)在PostContainersStart这个handler之中,名为”start”的job被创建,并开始执行;

(12)名为”start”的job执行完初步的配置工作后,开始配置与创建网络环境,调用networkdriver;

(13)networkdriver需要为指定的Docker容器创建网络接口设备,并为其分配IP,port,以及设置防火墙规则,相应的操作转交至libcontainer中的netlink包来完成;

(14)netlink完成Docker容器的网络环境配置与创建;

(15)返回至名为”start”的job,执行完一些辅助性操作后,job开始执行用户指令,调用execdriver;

(16)execdriver被调用,初始化Docker容器内部的运行环境,如命名空间,资源控制与隔离,以及用户命令的执行,相应的操作转交至libcontainer来完成;

(17)libcontainer被调用,完成Docker容器内部的运行环境初始化,并最终执行用户要求启动的命令。

上面docker架构内容来自:http://www.infoq.com/cn/articles/docker-source-code-analysis-part1/

posted on 2017-08-22 11:36  阳光小白  阅读(402)  评论(0编辑  收藏  举报