Docker的主要組件


Docker 的主要組件

安裝 docker ,其實是安裝了 docker 客戶端、dockerd 等一系列的組件,其中比較重要的有下面幾個。

Docker CLI(docker)
docker 程序是一個客戶端工具,用來把用戶的請求發送給 docker daemon(dockerd)。該程序的安裝路徑為:

/usr/bin/docker

Dockerd
docker daemon(dockerd),一般也會被稱為 docker engine。該程序的安裝路徑為:

/usr/bin/dockerd

Containerd
詳情請參考《Containerd 簡介》。該程序的安裝路徑為:

/usr/bin/docker-containerd

Containerd-shim
它是 containerd 的組件,是容器的運行時載體,我們在 docker 宿主機上看到的 shim 也正是代表着一個個通過調用 containerd 啟動的 docker 容器。該程序的安裝路徑為:

/usr/bin/docker-containerd-shim

RunC
詳情請參考《RunC 簡介》。該程序的安裝路徑為:

/usr/bin/docker-runc

從 hello world 開始

Docker 很貼心的為我們提供了 hello-world 鏡像來驗證安裝是否成功,但是透過這個鏡像我們還能看到更多的信息:

$ docker run hello-world

上面的輸出信息指出,hello-world 容器的運行經歷了如下四步:

  1. Docker 客戶端向 docker daemon 發送請求
  2. Docker daemon 從 Docker Hub 上拉取鏡像
  3. Docker daemon 使用鏡像運行了一個容器並產生了輸出
  4. Docker daemon 把輸出的內容發送給了 docker 客戶端

這是一個很抽象也很容器理解的過程,但是我們還想知道更多:docker daemon 是如何創建並運行容器的?
其實容器部分的操作和管理都被 dockerd 外包給 containerd 了,下圖描述了運行一個容器時各個組件之間的關系:

Docker Engine API

從本質上說,docker 是一個客戶端/服務器架構的應用。Dockerd 以 Engine API (REST)的方式對外提供服務,Engine API 里描述了 dockerd 支持的所有請求。Docker 客戶端與 dockerd 之間就是通過 REST 的方式通信的。在 ubuntu 16.04 中,dockerd 默認是不監聽 tcp 端口的,為了方便演示,我們讓 dockerd 監聽 tcp 端口。這樣就可以使用 curl 代替 docker 客戶端向 dockerd 發送請求了。具體的操作為,先修改 /lib/systemd/system/docker.service 文件,注釋掉默認的 ExecStart 並添加新的 ExecStart 配置:

# ExecStart=/usr/bin/dockerd -H fd://
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock

然后重啟 docker.service:

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker.service

這樣 dockerd 就開始監聽 tcp 端口 2375 了:

Docker 與 Dockerd 的交互

Docker 客戶端與 dockerd 之間就是通過 REST 的方式通信的。前面我們已經讓 dockerd 監聽 tcp 端口了,所以我們可以使用 curl 來代替 docker 客戶端。這里我們簡單的演示如何請求 dockerd 從 docker hub 上下載 hello-world 鏡像:

$ curl '127.0.0.1:2375/v1.37/images/create?fromImage=hello-world&tag=latest' -X POST

如果去看看 Engine API,你會發現其它的請求也都是用類似方式發送的,是不是很簡單啊!

創建容器

容器鏡像的下載是由 dockerd 完成的,但容器的創建和運行就需要 containerd(docker-containerd) 來完成了。Dockerd 與 docker-containerd 之間是通過 grpc 協議通信的。當 docker-containerd 收到 dockerd 啟動容器的請求之后,會做一些初始化工作,然后啟動 docker-containerd-shim 進程,並將相關配置作為參數傳給它。docker-containerd 負責管理所有本機正在運行的容器,而一個 docker-containerd-shim 進程只負責管理一個運行的容器,它相當於 docker-runc 的一個封裝,充當 docker-containerd 和 docker-runc 之間的橋梁,docker-runc 能干的就交給 docker-runc 來做,docker-runc 做不了的就放到這里來做。下面我們用 ubuntu 鏡像運行一個容器:

$ docker run -id ubuntu bash

上圖中黃線框起來的是幾個主要的進程,它們之間是有父子關系的(systemd 沒有出現在上圖):

systemd---dockerd---docker-containerd---docker-containerd-shim---bash

細心的朋友一定發現了,上圖中沒有出現 docker-runc 進程,這是為什么呢?
實際上,在容器啟動的過程中,docker-runc 進程是作為 docker-containerd-shim 的子進程存在的。docker-runc 進程根據配置找到容器的 rootfs 並創建子進程 bash 作為容器中的第一個進程。當這一切都完成后 docker-runc 進程退出,然后容器進程 bash 由 docker-runc 的父進程 docker-containerd-shim 接管。

為什么需要 docker-containerd-shim?

也許大家會問,為什么在容器的啟動或運行過程中需要一個 docker-containerd-shim 進程呢?把它移除掉整個架構會更簡潔也更優美一些!事實上 docker-containerd-shim 的存在是非常有必要的,其目的有如下幾點:

  • 它允許容器運行時(即 runC)在啟動容器之后退出,簡單說就是不必為每個容器一直運行一個容器運行時(runC)
  • 即使在 containerd 和 dockerd 都掛掉的情況下,容器的標准 IO 和其它的文件描述符也都是可用的
  • 向 containerd 報告容器的退出狀態

前兩點尤其重要,有了它們就可以在不中斷容器運行的情況下升級或重啟 dockerd(這對於生產環境來說意義重大)。 從這里可以看到對 containerd-shim 的一些解釋。

 

作者: sparkdev


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM