1. 程式人生 > >docker 基本學習

docker 基本學習

替代 pos 啟動過程 要求 第一次 cgroups head 單體 ...

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
[email protected] 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系統的軟件源信息
[email protected] 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軟件。
[email protected] 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功能
[email protected] 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

[email protected] 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
[email protected] 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失敗。
[email protected]-GE1EJH4 MINGW64 ~
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              48b5124b2768        12 days ago         1.84 kB

[email protected]-GE1EJH4 MINGW64 ~
$ docker tag 48b5124b2768 learn/helloworld:latest

[email protected]-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。

技術分享

圖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。

技術分享

圖4.1 Docker Daemon架構示意圖

4.2.1 Docker Server

Docker Server在Docker架構中是專門服務於Docker Client的server。該server的功能是:接受並調度分發Docker Client發送的請求。Docker Server的架構如圖4.2。

技術分享

圖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。

技術分享

圖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:

技術分享

圖4.4 graphdriver架構示意圖

networkdriver的用途是完成Docker容器網絡環境的配置,其中包括Docker啟動時為Docker環境創建網橋;Docker容器創建時為其創建專屬虛擬網卡設備;以及為Docker容器分配IP、端口並與宿主機做端口映射,設置容器防火墻策略等。networkdriver的架構如圖4.5:

技術分享

圖4. 5 networkdriver架構示意圖

execdriver作為Docker容器的執行驅動,負責創建容器運行命名空間,負責容器資源使用的統計與限制,負責容器內部進程的真正運行等。在execdriver的實現過程中,原先可以使用LXC驅動調用LXC的接口,來操縱容器的配置以及生命周期,而現在execdriver默認使用native驅動,不依賴於LXC。具體體現在Daemon啟動過程中加載的ExecDriverflag參數,該參數在配置文件已經被設為”native”。這可以認為是Docker在1.2版本上一個很大的改變,或者說Docker實現跨平臺的一個先兆。execdriver架構如圖4.6:

技術分享

圖4.6 execdriver架構示意圖

4.6 libcontainer

libcontainer是Docker架構中一個使用Go語言設計實現的庫,設計初衷是希望該庫可以不依靠任何依賴,直接訪問內核中與容器相關的API。

正是由於libcontainer的存在,Docker可以直接調用libcontainer,而最終操縱容器的namespace、cgroups、apparmor、網絡設備以及防火墻規則等。這一系列操作的完成都不需要依賴LXC或者其他包。libcontainer架構如圖4.7:

技術分享

圖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:

技術分享

圖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。

技術分享

圖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。

技術分享

圖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/

docker 基本學習